m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-15 06:43:47 -05:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Junegunn Choi
ee5aeb80a4 0.16.4 2017-02-05 16:17:54 +09:00
Junegunn Choi
02ceae15a2 [vim] Download instruction for Windows 2017-02-05 02:07:54 +09:00
Junegunn Choi
e514739280 Fix failing test case 2017-02-04 22:49:17 +09:00
Junegunn Choi
72265298f9 [vim] Apply --no-height when running fzf in full screen mode
To override --height option in FZF_DEFAULT_OPTS
2017-02-04 21:52:05 +09:00
Junegunn Choi
4b700192c1 Add --border option to draw horizontal lines above and below the finder
Goes well with --height
2017-02-04 21:51:22 +09:00
Junegunn Choi
fe83589ade Add test case for --tiebreak=begin 2017-02-03 02:14:14 +09:00
Junegunn Choi
fcf63c74f1 Fix --tiebreak=begin with algo v2
Due to performance consideration, FuzzyMatchV2 does not return the exact
positions of the matching characters by default. However, the ommission
caused `--tiebreak=begin` to produce inaccurate result in some cases.

  (echo baz foo bar; echo foo bar baz) | fzf --tiebreak=begin -fbar | head -1

  # Expected: foo bar baz
  # Actual:   baz foo bar

This commit fixes the problem by using the end offset which is
guaranteed to be correct.
2017-02-02 13:46:46 +09:00
Junegunn Choi
c95bb109c8 Suppress CSI codes in the output 2017-02-02 13:14:27 +09:00
Junegunn Choi
bd9c46ee34 Update ANSI processor to strip ^H along with its preceding character 2017-02-02 13:00:41 +09:00
Junegunn Choi
736aeaa1d3 Update go-runewidth
https://github.com/junegunn/go-runewidth/pull/1

/cc @joshuarubin
2017-02-02 10:08:56 +09:00
Junegunn Choi
dd1f26522c Fix caching scheme when --exact is set and '-prefix is used 2017-02-01 02:06:56 +09:00
Kassio Borges
712b7b2188 [vim] Expose buffer variable with the current fzf setup (#828)
Exposing the `b:fzf` variable will be useful to get information about
which command is being executed on the current fzf window. With that,
now, it's possible to use the current command name on the statusline:

```viml
au User FzfStatusLine call <SID>fzf_statusline()

function! s:fzf_statusline()
  let fzf_cmd_name = get(b:fzf, 'name', 'FZF')
  let &l:statusline = '> '.fzf_cmd_name
endfunction
```
2017-02-01 01:06:52 +09:00
Junegunn Choi
5b749e2d5c Update documentation 2017-01-31 21:43:41 +09:00
22 changed files with 224 additions and 187 deletions

View File

@@ -25,65 +25,22 @@ make install
# Build 32-bit and 64-bit executables and tarballs # Build 32-bit and 64-bit executables and tarballs
make release make release
# Build executables and tarballs for Linux using Docker # Make release archives for all supported platforms
make linux make release-all
``` ```
### Using `go get` ### Using `go get`
Alternatively, you can build fzf directly with `go get` command without Alternatively, you can build fzf directly with `go get` command without
cloning the repository. manually cloning the repository.
```sh ```sh
go get -u github.com/junegunn/fzf/src/fzf go get -u github.com/junegunn/fzf/src/fzf
``` ```
Build options
-------------
### With ncurses 6
The official binaries of fzf are built with ncurses 5 because it's widely
supported by different platforms. However ncurses 5 is old and has a number of
limitations.
1. Does not support more than 256 color pairs (See [357][357])
2. Does not support italics
3. Does not support 24-bit color
[357]: https://github.com/junegunn/fzf/issues/357
But you can manually build fzf with ncurses 6 to overcome some of these
limitations. ncurses 6 supports up to 32767 color pairs (1), and supports
italics (2). To build fzf with ncurses 6, you have to install it first. On
macOS, you can use Homebrew to install it.
```sh
brew install homebrew/dupes/ncurses
LDFLAGS="-L/usr/local/opt/ncurses/lib" make install
```
### With tcell
[tcell][tcell] is a portable alternative to ncurses and we currently use it to
build Windows binaries. tcell has many benefits but most importantly, it
supports 24-bit colors. To build fzf with tcell:
```sh
TAGS=tcell make install
```
However, note that tcell has its own issues.
- Poor rendering performance compared to ncurses
- Does not support bracketed-paste mode
- Does not support italics unlike ncurses 6
- Some wide characters are not correctly displayed
Third-party libraries used Third-party libraries used
-------------------------- --------------------------
- [ncurses][ncurses]
- [mattn/go-runewidth](https://github.com/mattn/go-runewidth) - [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
- Licensed under [MIT](http://mattn.mit-license.org) - Licensed under [MIT](http://mattn.mit-license.org)
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords) - [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
@@ -97,10 +54,3 @@ License
------- -------
[MIT](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

View File

@@ -1,6 +1,11 @@
CHANGELOG CHANGELOG
========= =========
0.16.4
------
- Added `--border` option to draw border above and below the finder
- Bug fixes and improvements
0.16.3 0.16.3
------ ------
- Fixed a bug where fzf incorrectly display the lines when straddling tab - Fixed a bug where fzf incorrectly display the lines when straddling tab

View File

@@ -72,16 +72,6 @@ But it's recommended that you use a plugin manager like
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
``` ```
### Upgrading fzf
fzf is being actively developed and you might want to upgrade it once in a
while. Please follow the instruction below depending on the installation
method used.
- git: `cd ~/.fzf && git pull && ./install`
- brew: `brew update; brew reinstall fzf`
- vim-plug: `:PlugUpdate fzf`
### Windows ### Windows
Pre-built binaries for Windows can be downloaded [here][bin]. However, other Pre-built binaries for Windows can be downloaded [here][bin]. However, other
@@ -91,6 +81,17 @@ flawlessly.
[wsl]: https://blogs.msdn.microsoft.com/wsl/ [wsl]: https://blogs.msdn.microsoft.com/wsl/
Upgrading fzf
-------------
fzf is being actively developed and you might want to upgrade it once in a
while. Please follow the instruction below depending on the installation
method used.
- git: `cd ~/.fzf && git pull && ./install`
- brew: `brew update; brew reinstall fzf`
- vim-plug: `:PlugUpdate fzf`
Building fzf Building fzf
------------ ------------
@@ -140,10 +141,10 @@ vim $(fzf --height 40% --reverse)
``` ```
You can add these options to `$FZF_DEFAULT_OPTS` so that they're applied by You can add these options to `$FZF_DEFAULT_OPTS` so that they're applied by
default. default. For example,
```sh ```sh
export FZF_DEFAULT_OPTS='--height 40% --reverse' export FZF_DEFAULT_OPTS='--height 40% --reverse --border'
``` ```
#### Search syntax #### Search syntax
@@ -357,7 +358,7 @@ If you have set up fzf for Vim, `:FZF` command will be added.
" With options " With options
:FZF --no-sort --reverse --inline-info /tmp :FZF --no-sort --reverse --inline-info /tmp
" Bang version starts in fullscreen instead of using tmux pane or Neovim split " Bang version starts fzf in fullscreen mode
:FZF! :FZF!
``` ```
@@ -408,20 +409,6 @@ command! -bang MyStuff
Tips Tips
---- ----
#### Rendering issues
If you have any rendering issues, check the following:
1. Make sure `$TERM` is correctly set. fzf will use 256-color only if it
contains `256` (e.g. `xterm-256color`)
2. If you're on screen or tmux, `$TERM` should be either `screen` or
`screen-256color`
3. Some terminal emulators (e.g. mintty) have problem displaying default
background color and make some text unable to read. In that case, try
`--black` option. And if it solves your problem, I recommend including it
in `FZF_DEFAULT_OPTS` for further convenience.
4. If you still have problem, try `--no-256` option or even `--no-color`.
#### Respecting `.gitignore`, `.hgignore`, and `svn:ignore` #### Respecting `.gitignore`, `.hgignore`, and `svn:ignore`
[ag](https://github.com/ggreer/the_silver_searcher) or [ag](https://github.com/ggreer/the_silver_searcher) or

20
install
View File

@@ -2,7 +2,7 @@
set -u set -u
version=0.16.3 version=0.16.4
auto_completion= auto_completion=
key_bindings= key_bindings=
update_config=2 update_config=2
@@ -88,17 +88,6 @@ check_binary() {
return 1 return 1
} }
symlink() {
echo " - Creating symlink: bin/$1 -> bin/fzf"
(cd "$fzf_base"/bin &&
rm -f fzf &&
ln -sf $1 fzf)
if [ $? -ne 0 ]; then
binary_error="Failed to create symlink"
return 1
fi
}
link_fzf_in_path() { link_fzf_in_path() {
if which_fzf="$(command -v fzf)"; then if which_fzf="$(command -v fzf)"; then
echo " - Found in \$PATH" echo " - Found in \$PATH"
@@ -124,9 +113,6 @@ download() {
echo " - Already exists" echo " - Already exists"
check_binary && return check_binary && return
fi fi
if [ -x "$fzf_base"/bin/$1 ]; then
symlink $1 && check_binary && return
fi
link_fzf_in_path && return link_fzf_in_path && return
fi fi
mkdir -p "$fzf_base"/bin && cd "$fzf_base"/bin mkdir -p "$fzf_base"/bin && cd "$fzf_base"/bin
@@ -147,12 +133,12 @@ download() {
fi fi
set +o pipefail set +o pipefail
if [ ! -f $1 ]; then if [ ! -f fzf ]; then
binary_error="Failed to download ${1}" binary_error="Failed to download ${1}"
return return
fi fi
chmod +x $1 && symlink $1 && check_binary chmod +x fzf && check_binary
} }
# Try to download binary executable # Try to download binary executable

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf-tmux 1 "Jan 2017" "fzf 0.16.3" "fzf-tmux - open fzf in tmux split pane" .TH fzf-tmux 1 "Feb 2017" "fzf 0.16.4" "fzf-tmux - open fzf in tmux split pane"
.SH NAME .SH NAME
fzf-tmux - open fzf in tmux split pane fzf-tmux - open fzf in tmux split pane

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf 1 "Jan 2017" "fzf 0.16.3" "fzf - a command-line fuzzy finder" .TH fzf 1 "Feb 2017" "fzf 0.16.4" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@@ -156,6 +156,9 @@ Ignored when \fB--height\fR is not specified.
.B "--reverse" .B "--reverse"
Reverse orientation Reverse orientation
.TP .TP
.B "--border"
Draw border above and below the finder
.TP
.BI "--margin=" MARGIN .BI "--margin=" MARGIN
Comma-separated expression for margins around the finder. Comma-separated expression for margins around the finder.
.br .br

View File

@@ -28,10 +28,12 @@ let g:loaded_fzf = 1
let s:default_layout = { 'down': '~40%' } let s:default_layout = { 'down': '~40%' }
let s:layout_keys = ['window', 'up', 'down', 'left', 'right'] let s:layout_keys = ['window', 'up', 'down', 'left', 'right']
let s:fzf_go = expand('<sfile>:h:h').'/bin/fzf' let s:is_win = has('win32') || has('win64')
let s:install = expand('<sfile>:h:h').'/install' 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'
let s:installed = 0 let s:installed = 0
let s:fzf_tmux = expand('<sfile>:h:h').'/bin/fzf-tmux'
let s:cpo_save = &cpo let s:cpo_save = &cpo
set cpo&vim set cpo&vim
@@ -42,6 +44,11 @@ function! s:fzf_exec()
let s:exec = s:fzf_go let s:exec = s:fzf_go
elseif executable('fzf') elseif executable('fzf')
let s:exec = 'fzf' let s:exec = 'fzf'
elseif s:is_win
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')
throw 'fzf executable not found'
elseif !s:installed && executable(s:install) && elseif !s:installed && executable(s:install) &&
\ input('fzf executable not found. Download binary? (y/n) ') =~? '^y' \ input('fzf executable not found. Download binary? (y/n) ') =~? '^y'
redraw redraw
@@ -201,6 +208,10 @@ function! fzf#wrap(...)
endfor endfor
let [name, opts, bang] = args let [name, opts, bang] = args
if len(name)
let opts.name = name
end
" Layout: g:fzf_layout (and deprecated g:fzf_height) " Layout: g:fzf_layout (and deprecated g:fzf_height)
if bang if bang
for key in s:layout_keys for key in s:layout_keys
@@ -242,7 +253,7 @@ function! fzf#wrap(...)
endfunction endfunction
function! fzf#shellescape(path) function! fzf#shellescape(path)
if has('win32') || has('win64') if s:is_win
let shellslash = &shellslash let shellslash = &shellslash
try try
set noshellslash set noshellslash
@@ -259,7 +270,7 @@ try
let oshell = &shell let oshell = &shell
let useshellslash = &shellslash let useshellslash = &shellslash
if has('win32') || has('win64') if s:is_win
set shell=cmd.exe set shell=cmd.exe
set noshellslash set noshellslash
else else
@@ -303,7 +314,7 @@ try
let prefer_tmux = get(g:, 'fzf_prefer_tmux', 0) let prefer_tmux = get(g:, 'fzf_prefer_tmux', 0)
let use_height = has_key(dict, 'down') && let use_height = has_key(dict, 'down') &&
\ !(has('nvim') || has('win32') || has('win64') || s:present(dict, 'up', 'left', 'right')) && \ !(has('nvim') || s:is_win || s:present(dict, 'up', 'left', 'right')) &&
\ executable('tput') && filereadable('/dev/tty') \ executable('tput') && filereadable('/dev/tty')
let use_term = has('nvim') let use_term = has('nvim')
let use_tmux = (!use_height && !use_term || prefer_tmux) && s:tmux_enabled() && s:splittable(dict) let use_tmux = (!use_height && !use_term || prefer_tmux) && s:tmux_enabled() && s:splittable(dict)
@@ -401,7 +412,7 @@ function! s:xterm_launcher()
\ &columns, &lines/2, getwinposx(), getwinposy()) \ &columns, &lines/2, getwinposx(), getwinposy())
endfunction endfunction
unlet! s:launcher unlet! s:launcher
if has('win32') || has('win64') if s:is_win
let s:launcher = '%s' let s:launcher = '%s'
else else
let s:launcher = function('s:xterm_launcher') let s:launcher = function('s:xterm_launcher')
@@ -521,6 +532,7 @@ function! s:execute_term(dict, command, temps) abort
let winrest = winrestcmd() let winrest = winrestcmd()
let pbuf = bufnr('') let pbuf = bufnr('')
let [ppos, winopts] = s:split(a:dict) let [ppos, winopts] = s:split(a:dict)
let b:fzf = a:dict
let fzf = { 'buf': bufnr(''), 'pbuf': pbuf, 'ppos': ppos, 'dict': a:dict, 'temps': a:temps, let fzf = { 'buf': bufnr(''), 'pbuf': pbuf, 'ppos': ppos, 'dict': a:dict, 'temps': a:temps,
\ 'winopts': winopts, 'winrest': winrest, 'lines': &lines, \ 'winopts': winopts, 'winrest': winrest, 'lines': &lines,
\ 'columns': &columns, 'command': a:command } \ 'columns': &columns, 'command': a:command }

View File

@@ -52,23 +52,23 @@ all: fzf/$(BINARY)
ifeq ($(GOOS),windows) ifeq ($(GOOS),windows)
release: fzf/$(BINARY32) fzf/$(BINARY64) release: fzf/$(BINARY32) fzf/$(BINARY64)
cd fzf && cp $(BINARY32) $(RELEASE32).exe && zip $(RELEASE32).zip $(RELEASE32).exe cd fzf && cp -f $(BINARY32) fzf.exe && zip $(RELEASE32).zip fzf.exe
cd fzf && cp $(BINARY64) $(RELEASE64).exe && zip $(RELEASE64).zip $(RELEASE64).exe cd fzf && cp -f $(BINARY64) fzf.exe && zip $(RELEASE64).zip fzf.exe
cd fzf && rm -f $(RELEASE32).exe $(RELEASE64).exe cd fzf && rm -f fzf.exe
else ifeq ($(GOOS),linux) else ifeq ($(GOOS),linux)
release: fzf/$(BINARY32) fzf/$(BINARY64) fzf/$(BINARYARM5) fzf/$(BINARYARM6) fzf/$(BINARYARM7) fzf/$(BINARYARM8) release: fzf/$(BINARY32) fzf/$(BINARY64) fzf/$(BINARYARM5) fzf/$(BINARYARM6) fzf/$(BINARYARM7) fzf/$(BINARYARM8)
cd fzf && cp $(BINARY32) $(RELEASE32) && tar -czf $(RELEASE32).tgz $(RELEASE32) cd fzf && cp -f $(BINARY32) fzf && tar -czf $(RELEASE32).tgz fzf
cd fzf && cp $(BINARY64) $(RELEASE64) && tar -czf $(RELEASE64).tgz $(RELEASE64) cd fzf && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
cd fzf && cp $(BINARYARM5) $(RELEASEARM5) && tar -czf $(RELEASEARM5).tgz $(RELEASEARM5) cd fzf && cp -f $(BINARYARM5) fzf && tar -czf $(RELEASEARM5).tgz fzf
cd fzf && cp $(BINARYARM6) $(RELEASEARM6) && tar -czf $(RELEASEARM6).tgz $(RELEASEARM6) cd fzf && cp -f $(BINARYARM6) fzf && tar -czf $(RELEASEARM6).tgz fzf
cd fzf && cp $(BINARYARM7) $(RELEASEARM7) && tar -czf $(RELEASEARM7).tgz $(RELEASEARM7) cd fzf && cp -f $(BINARYARM7) fzf && tar -czf $(RELEASEARM7).tgz fzf
cd fzf && cp $(BINARYARM8) $(RELEASEARM8) && tar -czf $(RELEASEARM8).tgz $(RELEASEARM8) cd fzf && cp -f $(BINARYARM8) fzf && tar -czf $(RELEASEARM8).tgz fzf
cd fzf && rm -f $(RELEASE32) $(RELEASE64) $(RELEASEARM5) $(RELEASEARM6) $(RELEASEARM7) $(RELEASEARM8) cd fzf && rm -f fzf
else else
release: fzf/$(BINARY32) fzf/$(BINARY64) release: fzf/$(BINARY32) fzf/$(BINARY64)
cd fzf && cp $(BINARY32) $(RELEASE32) && tar -czf $(RELEASE32).tgz $(RELEASE32) cd fzf && cp -f $(BINARY32) fzf && tar -czf $(RELEASE32).tgz fzf
cd fzf && cp $(BINARY64) $(RELEASE64) && tar -czf $(RELEASE64).tgz $(RELEASE64) cd fzf && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
cd fzf && rm -f $(RELEASE32) $(RELEASE64) cd fzf && rm -f fzf
endif endif
release-all: clean test release-all: clean test

View File

@@ -44,7 +44,7 @@ func init() {
*/ */
// The following regular expression will include not all but most of the // The following regular expression will include not all but most of the
// frequently used ANSI sequences // frequently used ANSI sequences
ansiRegex = regexp.MustCompile("\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b.|[\x08\x0e\x0f]") ansiRegex = regexp.MustCompile("\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b.|[\x0e\x0f]|.\x08")
} }
func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) { func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {

View File

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

View File

@@ -13,6 +13,6 @@ reset() (
) )
reset github.com/junegunn/go-isatty 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8 reset github.com/junegunn/go-isatty 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
reset github.com/junegunn/go-runewidth 63c378b851290989b19ca955468386485f118c65 reset github.com/junegunn/go-runewidth 14207d285c6c197daabb5c9793d63e7af9ab2d50
reset github.com/junegunn/go-shellwords 33bd8f1ebe16d6e5eb688cc885749a63059e9167 reset github.com/junegunn/go-shellwords 33bd8f1ebe16d6e5eb688cc885749a63059e9167
reset golang.org/x/crypto abc5fa7ad02123a41f02bf1391c9760f7586e608 reset golang.org/x/crypto abc5fa7ad02123a41f02bf1391c9760f7586e608

View File

@@ -54,6 +54,7 @@ const usage = `usage: fzf [options]
--min-height=HEIGHT Minimum height when --height is given in percent --min-height=HEIGHT Minimum height when --height is given in percent
(default: 10) (default: 10)
--reverse Reverse orientation --reverse Reverse orientation
--border Draw border above and below the finder
--margin=MARGIN Screen margin (TRBL / TB,RL / T,RL,B / T,R,B,L) --margin=MARGIN Screen margin (TRBL / TB,RL / T,RL,B / T,R,B,L)
--inline-info Display finder info inline with the query --inline-info Display finder info inline with the query
--prompt=STR Input prompt (default: '> ') --prompt=STR Input prompt (default: '> ')
@@ -183,6 +184,7 @@ type Options struct {
Header []string Header []string
HeaderLines int HeaderLines int
Margin [4]sizeSpec Margin [4]sizeSpec
Bordered bool
Tabstop int Tabstop int
Version bool Version bool
} }
@@ -1086,6 +1088,10 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Height = sizeSpec{} opts.Height = sizeSpec{}
case "--no-margin": case "--no-margin":
opts.Margin = defaultMargin() opts.Margin = defaultMargin()
case "--no-border":
opts.Bordered = false
case "--border":
opts.Bordered = true
case "--margin": case "--margin":
opts.Margin = parseMargin( opts.Margin = parseMargin(
nextString(allArgs, &i, "margin required (TRBL / TB,RL / T,RL,B / T,R,B,L)")) nextString(allArgs, &i, "margin required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))

View File

@@ -101,7 +101,7 @@ func BuildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case,
for idx, term := range termSet { for idx, term := range termSet {
// If the query contains inverse search terms or OR operators, // If the query contains inverse search terms or OR operators,
// we cannot cache the search scope // we cannot cache the search scope
if !cacheable || idx > 0 || term.inv { if !cacheable || idx > 0 || term.inv || !fuzzy && term.typ != termExact {
cacheable = false cacheable = false
break Loop break Loop
} }

View File

@@ -186,3 +186,21 @@ func TestCacheKey(t *testing.T) {
test(true, "foo | bar !baz", "", false) test(true, "foo | bar !baz", "", false)
test(true, "| | | foo", "foo", true) test(true, "| | | foo", "foo", true)
} }
func TestCacheable(t *testing.T) {
test := func(fuzzy bool, str string, cacheable bool) {
clearPatternCache()
pat := BuildPattern(fuzzy, algo.FuzzyMatchV2, true, CaseSmart, true, true, true, []Range{}, Delimiter{}, []rune(str))
if cacheable != pat.cacheable {
t.Errorf("Invalid Pattern.cacheable for \"%s\": %v (expected: %v)", str, pat.cacheable, cacheable)
}
}
test(true, "foo bar", true)
test(true, "foo 'bar", true)
test(true, "foo !bar", false)
test(false, "foo bar", true)
test(false, "foo '", true)
test(false, "foo 'bar", false)
test(false, "foo !bar", false)
}

View File

@@ -37,12 +37,14 @@ func buildResult(item *Item, offsets []Offset, score int, trimLen int) *Result {
result := Result{item: item, rank: rank{index: item.index}} result := Result{item: item, rank: rank{index: item.index}}
numChars := item.text.Length() numChars := item.text.Length()
minBegin := math.MaxUint16 minBegin := math.MaxUint16
minEnd := math.MaxUint16
maxEnd := 0 maxEnd := 0
validOffsetFound := false validOffsetFound := false
for _, offset := range offsets { for _, offset := range offsets {
b, e := int(offset[0]), int(offset[1]) b, e := int(offset[0]), int(offset[1])
if b < e { if b < e {
minBegin = util.Min(b, minBegin) minBegin = util.Min(b, minBegin)
minEnd = util.Min(e, minEnd)
maxEnd = util.Max(e, maxEnd) maxEnd = util.Max(e, maxEnd)
validOffsetFound = true validOffsetFound = true
} }
@@ -68,7 +70,7 @@ func buildResult(item *Item, offsets []Offset, score int, trimLen int) *Result {
} }
} }
if criterion == byBegin { if criterion == byBegin {
val = util.AsUint16(minBegin - whitePrefixLen) val = util.AsUint16(minEnd - whitePrefixLen)
} else { } else {
val = util.AsUint16(math.MaxUint16 - math.MaxUint16*(maxEnd-whitePrefixLen)/trimLen) val = util.AsUint16(math.MaxUint16 - math.MaxUint16*(maxEnd-whitePrefixLen)/trimLen)
} }

View File

@@ -83,8 +83,10 @@ type Terminal struct {
tabstop int tabstop int
margin [4]sizeSpec margin [4]sizeSpec
strong tui.Attr strong tui.Attr
bordered bool
border tui.Window
window tui.Window window tui.Window
bwindow tui.Window pborder tui.Window
pwindow tui.Window pwindow tui.Window
count int count int
progress int progress int
@@ -295,15 +297,22 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
maxHeightFunc := func(termHeight int) int { maxHeightFunc := func(termHeight int) int {
var maxHeight int var maxHeight int
if opts.Height.percent { if opts.Height.percent {
maxHeight = util.Min(termHeight, maxHeight = util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight)
util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight))
} else { } else {
maxHeight = util.Min(termHeight, int(opts.Height.size)) maxHeight = int(opts.Height.size)
}
effectiveMinHeight := minHeight
if previewBox != nil && (opts.Preview.position == posUp || opts.Preview.position == posDown) {
effectiveMinHeight *= 2
} }
if opts.InlineInfo { if opts.InlineInfo {
return util.Max(maxHeight, minHeight-1) effectiveMinHeight -= 1
} }
return util.Max(maxHeight, minHeight) if opts.Bordered {
effectiveMinHeight += 2
}
return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight))
} }
renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, maxHeightFunc) renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, maxHeightFunc)
} else if tui.HasFullscreenRenderer() { } else if tui.HasFullscreenRenderer() {
@@ -343,6 +352,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
printQuery: opts.PrintQuery, printQuery: opts.PrintQuery,
history: opts.History, history: opts.History,
margin: opts.Margin, margin: opts.Margin,
bordered: opts.Bordered,
strong: strongAttr, strong: strongAttr,
cycle: opts.Cycle, cycle: opts.Cycle,
header: header, header: header,
@@ -499,6 +509,9 @@ func (t *Terminal) resizeWindows() {
} else { } else {
marginInt[idx] = int(sizeSpec.size) marginInt[idx] = int(sizeSpec.size)
} }
if t.bordered && idx%2 == 0 {
marginInt[idx] += 1
}
} }
adjust := func(idx1 int, idx2 int, max int, min int) { adjust := func(idx1 int, idx2 int, max int, min int) {
if max >= min { if max >= min {
@@ -524,19 +537,29 @@ func (t *Terminal) resizeWindows() {
} }
adjust(1, 3, screenWidth, minAreaWidth) adjust(1, 3, screenWidth, minAreaWidth)
adjust(0, 2, screenHeight, minAreaHeight) adjust(0, 2, screenHeight, minAreaHeight)
if t.border != nil {
t.border.Close()
}
if t.window != nil { if t.window != nil {
t.window.Close() t.window.Close()
} }
if t.bwindow != nil { if t.pborder != nil {
t.bwindow.Close() t.pborder.Close()
t.pwindow.Close() t.pwindow.Close()
} }
width := screenWidth - marginInt[1] - marginInt[3] width := screenWidth - marginInt[1] - marginInt[3]
height := screenHeight - marginInt[0] - marginInt[2] height := screenHeight - marginInt[0] - marginInt[2]
if t.bordered {
t.border = t.tui.NewWindow(
marginInt[0]-1,
marginInt[3],
width,
height+2, tui.BorderHorizontal)
}
if previewVisible { if previewVisible {
createPreviewWindow := func(y int, x int, w int, h int) { createPreviewWindow := func(y int, x int, w int, h int) {
t.bwindow = t.tui.NewWindow(y, x, w, h, true) t.pborder = t.tui.NewWindow(y, x, w, h, tui.BorderAround)
pwidth := w - 4 pwidth := w - 4
// ncurses auto-wraps the line when the cursor reaches the right-end of // ncurses auto-wraps the line when the cursor reaches the right-end of
// the window. To prevent unintended line-wraps, we use the width one // the window. To prevent unintended line-wraps, we use the width one
@@ -544,28 +567,28 @@ func (t *Terminal) resizeWindows() {
if !t.preview.wrap && t.tui.DoesAutoWrap() { if !t.preview.wrap && t.tui.DoesAutoWrap() {
pwidth += 1 pwidth += 1
} }
t.pwindow = t.tui.NewWindow(y+1, x+2, pwidth, h-2, false) t.pwindow = t.tui.NewWindow(y+1, x+2, pwidth, h-2, tui.BorderNone)
} }
switch t.preview.position { switch t.preview.position {
case posUp: case posUp:
pheight := calculateSize(height, t.preview.size, minHeight, 3) pheight := calculateSize(height, t.preview.size, minHeight, 3)
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0]+pheight, marginInt[3], width, height-pheight, false) marginInt[0]+pheight, marginInt[3], width, height-pheight, tui.BorderNone)
createPreviewWindow(marginInt[0], marginInt[3], width, pheight) createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
case posDown: case posDown:
pheight := calculateSize(height, t.preview.size, minHeight, 3) pheight := calculateSize(height, t.preview.size, minHeight, 3)
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0], marginInt[3], width, height-pheight, false) marginInt[0], marginInt[3], width, height-pheight, tui.BorderNone)
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight) createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
case posLeft: case posLeft:
pwidth := calculateSize(width, t.preview.size, minWidth, 5) pwidth := calculateSize(width, t.preview.size, minWidth, 5)
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0], marginInt[3]+pwidth, width-pwidth, height, false) marginInt[0], marginInt[3]+pwidth, width-pwidth, height, tui.BorderNone)
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height) createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
case posRight: case posRight:
pwidth := calculateSize(width, t.preview.size, minWidth, 5) pwidth := calculateSize(width, t.preview.size, minWidth, 5)
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0], marginInt[3], width-pwidth, height, false) marginInt[0], marginInt[3], width-pwidth, height, tui.BorderNone)
createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height) createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height)
} }
} else { } else {
@@ -573,7 +596,7 @@ func (t *Terminal) resizeWindows() {
marginInt[0], marginInt[0],
marginInt[3], marginInt[3],
width, width,
height, false) height, tui.BorderNone)
} }
if !t.tui.IsOptimized() && t.theme != nil && t.theme.HasBg() { if !t.tui.IsOptimized() && t.theme != nil && t.theme.HasBg() {
for i := 0; i < t.window.Height(); i++ { for i := 0; i < t.window.Height(); i++ {
@@ -978,11 +1001,15 @@ func (t *Terminal) printAll() {
func (t *Terminal) refresh() { func (t *Terminal) refresh() {
if !t.suppress { if !t.suppress {
if t.hasPreviewWindow() { windows := make([]tui.Window, 0, 4)
t.tui.RefreshWindows([]tui.Window{t.bwindow, t.pwindow, t.window}) if t.bordered {
} else { windows = append(windows, t.border)
t.tui.RefreshWindows([]tui.Window{t.window})
} }
if t.hasPreviewWindow() {
windows = append(windows, t.pborder, t.pwindow)
}
windows = append(windows, t.window)
t.tui.RefreshWindows(windows)
} }
} }

View File

@@ -40,6 +40,6 @@ func (r *FullscreenRenderer) MaxY() int { return 0 }
func (r *FullscreenRenderer) RefreshWindows(windows []Window) {} func (r *FullscreenRenderer) RefreshWindows(windows []Window) {}
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, border bool) Window { func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
return nil return nil
} }

View File

@@ -95,7 +95,7 @@ type LightRenderer struct {
type LightWindow struct { type LightWindow struct {
renderer *LightRenderer renderer *LightRenderer
colored bool colored bool
border bool border BorderStyle
top int top int
left int left int
width int width int
@@ -600,11 +600,11 @@ func (r *LightRenderer) IsOptimized() bool {
return false return false
} }
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, border bool) Window { func (r *LightRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
w := &LightWindow{ w := &LightWindow{
renderer: r, renderer: r,
colored: r.theme != nil, colored: r.theme != nil,
border: border, border: borderStyle,
top: top, top: top,
left: left, left: left,
width: width, width: width,
@@ -614,13 +614,27 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, bord
if r.theme != nil { if r.theme != nil {
w.bg = r.theme.Bg w.bg = r.theme.Bg
} }
if w.border { w.drawBorder()
w.drawBorder()
}
return w return w
} }
func (w *LightWindow) drawBorder() { func (w *LightWindow) drawBorder() {
switch w.border {
case BorderAround:
w.drawBorderAround()
case BorderHorizontal:
w.drawBorderHorizontal()
}
}
func (w *LightWindow) drawBorderHorizontal() {
w.Move(0, 0)
w.CPrint(ColBorder, AttrRegular, repeat("─", w.width))
w.Move(w.height-1, 0)
w.CPrint(ColBorder, AttrRegular, repeat("─", w.width))
}
func (w *LightWindow) drawBorderAround() {
w.Move(0, 0) w.Move(0, 0)
w.CPrint(ColBorder, AttrRegular, "┌"+repeat("─", w.width-2)+"┐") w.CPrint(ColBorder, AttrRegular, "┌"+repeat("─", w.width-2)+"┐")
for y := 1; y < w.height-1; y++ { for y := 1; y < w.height-1; y++ {
@@ -748,13 +762,17 @@ func (w *LightWindow) Print(text string) {
w.cprint2(colDefault, w.bg, AttrRegular, text) w.cprint2(colDefault, w.bg, AttrRegular, text)
} }
func cleanse(str string) string {
return strings.Replace(str, "\x1b", "?", -1)
}
func (w *LightWindow) CPrint(pair ColorPair, attr Attr, text string) { func (w *LightWindow) CPrint(pair ColorPair, attr Attr, text string) {
if !w.colored { if !w.colored {
w.csiColor(colDefault, colDefault, attrFor(pair, attr)) w.csiColor(colDefault, colDefault, attrFor(pair, attr))
} else { } else {
w.csiColor(pair.Fg(), pair.Bg(), attr) w.csiColor(pair.Fg(), pair.Bg(), attr)
} }
w.stderrInternal(text, false) w.stderrInternal(cleanse(text), false)
w.csi("m") w.csi("m")
} }
@@ -762,7 +780,7 @@ func (w *LightWindow) cprint2(fg Color, bg Color, attr Attr, text string) {
if w.csiColor(fg, bg, attr) { if w.csiColor(fg, bg, attr) {
defer w.csi("m") defer w.csi("m")
} }
w.stderrInternal(text, false) w.stderrInternal(cleanse(text), false)
} }
type wrappedLine struct { type wrappedLine struct {
@@ -850,9 +868,7 @@ func (w *LightWindow) FinishFill() {
} }
func (w *LightWindow) Erase() { func (w *LightWindow) Erase() {
if w.border { w.drawBorder()
w.drawBorder()
}
// We don't erase the window here to avoid flickering during scroll // We don't erase the window here to avoid flickering during scroll
w.Move(0, 0) w.Move(0, 0)
} }

View File

@@ -189,12 +189,13 @@ func (r *FullscreenRenderer) Close() {
C.delscreen(_screen) C.delscreen(_screen)
} }
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, border bool) Window { func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left)) win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
if r.theme != nil { if r.theme != nil {
C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal.index())))) C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal.index()))))
} }
if border { // FIXME Does not implement BorderHorizontal
if borderStyle != BorderNone {
pair, attr := _colorFn(ColBorder, 0) pair, attr := _colorFn(ColBorder, 0)
C.wcolor_set(win, pair, nil) C.wcolor_set(win, pair, nil)
C.wattron(win, attr) C.wattron(win, attr)

View File

@@ -27,15 +27,15 @@ func (p ColorPair) style() tcell.Style {
type Attr tcell.Style type Attr tcell.Style
type TcellWindow struct { type TcellWindow struct {
color bool color bool
top int top int
left int left int
width int width int
height int height int
lastX int lastX int
lastY int lastY int
moveCursor bool moveCursor bool
border bool borderStyle BorderStyle
} }
func (w *TcellWindow) Top() int { func (w *TcellWindow) Top() int {
@@ -61,8 +61,11 @@ func (w *TcellWindow) Refresh() {
} }
w.lastX = 0 w.lastX = 0
w.lastY = 0 w.lastY = 0
if w.border { switch w.borderStyle {
w.drawBorder() case BorderAround:
w.drawBorder(true)
case BorderHorizontal:
w.drawBorder(false)
} }
} }
@@ -377,15 +380,15 @@ func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
_screen.Show() _screen.Show()
} }
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, border bool) Window { func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
// TODO // TODO
return &TcellWindow{ return &TcellWindow{
color: r.theme != nil, color: r.theme != nil,
top: top, top: top,
left: left, left: left,
width: width, width: width,
height: height, height: height,
border: border} borderStyle: borderStyle}
} }
func (w *TcellWindow) Close() { func (w *TcellWindow) Close() {
@@ -536,7 +539,7 @@ func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
return w.fillString(str, ColorPair{fg, bg, -1}, a) return w.fillString(str, ColorPair{fg, bg, -1}, a)
} }
func (w *TcellWindow) drawBorder() { func (w *TcellWindow) drawBorder(around bool) {
left := w.left left := w.left
right := left + w.width right := left + w.width
top := w.top top := w.top
@@ -554,13 +557,15 @@ func (w *TcellWindow) drawBorder() {
_screen.SetContent(x, bot-1, tcell.RuneHLine, nil, style) _screen.SetContent(x, bot-1, tcell.RuneHLine, nil, style)
} }
for y := top; y < bot; y++ { if around {
_screen.SetContent(left, y, tcell.RuneVLine, nil, style) for y := top; y < bot; y++ {
_screen.SetContent(right-1, y, tcell.RuneVLine, nil, style) _screen.SetContent(left, y, tcell.RuneVLine, nil, style)
} _screen.SetContent(right-1, y, tcell.RuneVLine, nil, style)
}
_screen.SetContent(left, top, tcell.RuneULCorner, nil, style) _screen.SetContent(left, top, tcell.RuneULCorner, nil, style)
_screen.SetContent(right-1, top, tcell.RuneURCorner, nil, style) _screen.SetContent(right-1, top, tcell.RuneURCorner, nil, style)
_screen.SetContent(left, bot-1, tcell.RuneLLCorner, nil, style) _screen.SetContent(left, bot-1, tcell.RuneLLCorner, nil, style)
_screen.SetContent(right-1, bot-1, tcell.RuneLRCorner, nil, style) _screen.SetContent(right-1, bot-1, tcell.RuneLRCorner, nil, style)
}
} }

View File

@@ -195,6 +195,14 @@ type MouseEvent struct {
Mod bool Mod bool
} }
type BorderStyle int
const (
BorderNone BorderStyle = iota
BorderAround
BorderHorizontal
)
type Renderer interface { type Renderer interface {
Init() Init()
Pause() Pause()
@@ -211,7 +219,7 @@ type Renderer interface {
DoesAutoWrap() bool DoesAutoWrap() bool
IsOptimized() bool IsOptimized() bool
NewWindow(top int, left int, width int, height int, border bool) Window NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window
} }
type Window interface { type Window interface {

View File

@@ -617,6 +617,17 @@ class TestGoFZF < TestBase
], `#{FZF} -foo --tiebreak=begin,length < #{tempname}`.split($/) ], `#{FZF} -foo --tiebreak=begin,length < #{tempname}`.split($/)
end end
def test_tiebreak_begin_algo_v2
writelines tempname, [
'baz foo bar',
'foo bar baz',
]
assert_equal [
'foo bar baz',
'baz foo bar',
], `#{FZF} -fbar --tiebreak=begin --algo=v2 < #{tempname}`.split($/)
end
def test_tiebreak_end def test_tiebreak_end
writelines tempname, [ writelines tempname, [
'xoxxxxxxxx', 'xoxxxxxxxx',
@@ -1543,7 +1554,7 @@ module CompletionTest
tmux.prepare tmux.prepare
# Using tmux # Using tmux
tmux.send_keys 'unset FZFFOO**', :Tab tmux.send_keys 'unset FZFFOOBR**', :Tab
tmux.until { |lines| lines.match_count == 1 } tmux.until { |lines| lines.match_count == 1 }
tmux.send_keys :Enter tmux.send_keys :Enter
tmux.until { |lines| lines[-1].include? 'unset FZFFOOBAR' } tmux.until { |lines| lines[-1].include? 'unset FZFFOOBAR' }
@@ -1551,7 +1562,7 @@ module CompletionTest
# FZF_TMUX=1 # FZF_TMUX=1
new_shell new_shell
tmux.send_keys 'unset FZFFO**', :Tab, pane: 0 tmux.send_keys 'unset FZFFOOBR**', :Tab, pane: 0
tmux.until(false, 1) { |lines| lines.match_count == 1 } tmux.until(false, 1) { |lines| lines.match_count == 1 }
tmux.send_keys :Enter tmux.send_keys :Enter
tmux.until { |lines| lines[-1].include? 'unset FZFFOOBAR' } tmux.until { |lines| lines[-1].include? 'unset FZFFOOBAR' }