mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-15 06:43:47 -05:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee5aeb80a4 | ||
|
|
02ceae15a2 | ||
|
|
e514739280 | ||
|
|
72265298f9 | ||
|
|
4b700192c1 | ||
|
|
fe83589ade | ||
|
|
fcf63c74f1 | ||
|
|
c95bb109c8 | ||
|
|
bd9c46ee34 | ||
|
|
736aeaa1d3 | ||
|
|
dd1f26522c | ||
|
|
712b7b2188 | ||
|
|
5b749e2d5c |
56
BUILD.md
56
BUILD.md
@@ -25,65 +25,22 @@ make install
|
||||
# Build 32-bit and 64-bit executables and tarballs
|
||||
make release
|
||||
|
||||
# Build executables and tarballs for Linux using Docker
|
||||
make linux
|
||||
# Make release archives for all supported platforms
|
||||
make release-all
|
||||
```
|
||||
|
||||
### Using `go get`
|
||||
|
||||
Alternatively, you can build fzf directly with `go get` command without
|
||||
cloning the repository.
|
||||
manually cloning the repository.
|
||||
|
||||
```sh
|
||||
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
|
||||
--------------------------
|
||||
|
||||
- [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)
|
||||
@@ -97,10 +54,3 @@ 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,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.16.4
|
||||
------
|
||||
- Added `--border` option to draw border above and below the finder
|
||||
- Bug fixes and improvements
|
||||
|
||||
0.16.3
|
||||
------
|
||||
- Fixed a bug where fzf incorrectly display the lines when straddling tab
|
||||
|
||||
41
README.md
41
README.md
@@ -72,16 +72,6 @@ But it's recommended that you use a plugin manager like
|
||||
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
|
||||
|
||||
Pre-built binaries for Windows can be downloaded [here][bin]. However, other
|
||||
@@ -91,6 +81,17 @@ flawlessly.
|
||||
|
||||
[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
|
||||
------------
|
||||
|
||||
@@ -140,10 +141,10 @@ vim $(fzf --height 40% --reverse)
|
||||
```
|
||||
|
||||
You can add these options to `$FZF_DEFAULT_OPTS` so that they're applied by
|
||||
default.
|
||||
default. For example,
|
||||
|
||||
```sh
|
||||
export FZF_DEFAULT_OPTS='--height 40% --reverse'
|
||||
export FZF_DEFAULT_OPTS='--height 40% --reverse --border'
|
||||
```
|
||||
|
||||
#### Search syntax
|
||||
@@ -357,7 +358,7 @@ If you have set up fzf for Vim, `:FZF` command will be added.
|
||||
" With options
|
||||
: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!
|
||||
```
|
||||
|
||||
@@ -408,20 +409,6 @@ command! -bang MyStuff
|
||||
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`
|
||||
|
||||
[ag](https://github.com/ggreer/the_silver_searcher) or
|
||||
|
||||
20
install
20
install
@@ -2,7 +2,7 @@
|
||||
|
||||
set -u
|
||||
|
||||
version=0.16.3
|
||||
version=0.16.4
|
||||
auto_completion=
|
||||
key_bindings=
|
||||
update_config=2
|
||||
@@ -88,17 +88,6 @@ check_binary() {
|
||||
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() {
|
||||
if which_fzf="$(command -v fzf)"; then
|
||||
echo " - Found in \$PATH"
|
||||
@@ -124,9 +113,6 @@ download() {
|
||||
echo " - Already exists"
|
||||
check_binary && return
|
||||
fi
|
||||
if [ -x "$fzf_base"/bin/$1 ]; then
|
||||
symlink $1 && check_binary && return
|
||||
fi
|
||||
link_fzf_in_path && return
|
||||
fi
|
||||
mkdir -p "$fzf_base"/bin && cd "$fzf_base"/bin
|
||||
@@ -147,12 +133,12 @@ download() {
|
||||
fi
|
||||
set +o pipefail
|
||||
|
||||
if [ ! -f $1 ]; then
|
||||
if [ ! -f fzf ]; then
|
||||
binary_error="Failed to download ${1}"
|
||||
return
|
||||
fi
|
||||
|
||||
chmod +x $1 && symlink $1 && check_binary
|
||||
chmod +x fzf && check_binary
|
||||
}
|
||||
|
||||
# Try to download binary executable
|
||||
|
||||
@@ -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 "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
|
||||
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 "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
|
||||
fzf - a command-line fuzzy finder
|
||||
@@ -156,6 +156,9 @@ Ignored when \fB--height\fR is not specified.
|
||||
.B "--reverse"
|
||||
Reverse orientation
|
||||
.TP
|
||||
.B "--border"
|
||||
Draw border above and below the finder
|
||||
.TP
|
||||
.BI "--margin=" MARGIN
|
||||
Comma-separated expression for margins around the finder.
|
||||
.br
|
||||
|
||||
@@ -28,10 +28,12 @@ let g:loaded_fzf = 1
|
||||
|
||||
let s:default_layout = { 'down': '~40%' }
|
||||
let s:layout_keys = ['window', 'up', 'down', 'left', 'right']
|
||||
let s:fzf_go = expand('<sfile>:h:h').'/bin/fzf'
|
||||
let s:install = expand('<sfile>:h:h').'/install'
|
||||
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'
|
||||
let s:installed = 0
|
||||
let s:fzf_tmux = expand('<sfile>:h:h').'/bin/fzf-tmux'
|
||||
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
@@ -42,6 +44,11 @@ function! s:fzf_exec()
|
||||
let s:exec = s:fzf_go
|
||||
elseif executable('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) &&
|
||||
\ input('fzf executable not found. Download binary? (y/n) ') =~? '^y'
|
||||
redraw
|
||||
@@ -201,6 +208,10 @@ function! fzf#wrap(...)
|
||||
endfor
|
||||
let [name, opts, bang] = args
|
||||
|
||||
if len(name)
|
||||
let opts.name = name
|
||||
end
|
||||
|
||||
" Layout: g:fzf_layout (and deprecated g:fzf_height)
|
||||
if bang
|
||||
for key in s:layout_keys
|
||||
@@ -242,7 +253,7 @@ function! fzf#wrap(...)
|
||||
endfunction
|
||||
|
||||
function! fzf#shellescape(path)
|
||||
if has('win32') || has('win64')
|
||||
if s:is_win
|
||||
let shellslash = &shellslash
|
||||
try
|
||||
set noshellslash
|
||||
@@ -259,7 +270,7 @@ try
|
||||
let oshell = &shell
|
||||
let useshellslash = &shellslash
|
||||
|
||||
if has('win32') || has('win64')
|
||||
if s:is_win
|
||||
set shell=cmd.exe
|
||||
set noshellslash
|
||||
else
|
||||
@@ -303,7 +314,7 @@ try
|
||||
|
||||
let prefer_tmux = get(g:, 'fzf_prefer_tmux', 0)
|
||||
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')
|
||||
let use_term = has('nvim')
|
||||
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())
|
||||
endfunction
|
||||
unlet! s:launcher
|
||||
if has('win32') || has('win64')
|
||||
if s:is_win
|
||||
let s:launcher = '%s'
|
||||
else
|
||||
let s:launcher = function('s:xterm_launcher')
|
||||
@@ -521,6 +532,7 @@ function! s:execute_term(dict, command, temps) abort
|
||||
let winrest = winrestcmd()
|
||||
let pbuf = bufnr('')
|
||||
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,
|
||||
\ 'winopts': winopts, 'winrest': winrest, 'lines': &lines,
|
||||
\ 'columns': &columns, 'command': a:command }
|
||||
|
||||
26
src/Makefile
26
src/Makefile
@@ -52,23 +52,23 @@ all: fzf/$(BINARY)
|
||||
|
||||
ifeq ($(GOOS),windows)
|
||||
release: fzf/$(BINARY32) fzf/$(BINARY64)
|
||||
cd fzf && cp $(BINARY32) $(RELEASE32).exe && zip $(RELEASE32).zip $(RELEASE32).exe
|
||||
cd fzf && cp $(BINARY64) $(RELEASE64).exe && zip $(RELEASE64).zip $(RELEASE64).exe
|
||||
cd fzf && rm -f $(RELEASE32).exe $(RELEASE64).exe
|
||||
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 $(BINARY32) $(RELEASE32) && tar -czf $(RELEASE32).tgz $(RELEASE32)
|
||||
cd fzf && cp $(BINARY64) $(RELEASE64) && tar -czf $(RELEASE64).tgz $(RELEASE64)
|
||||
cd fzf && cp $(BINARYARM5) $(RELEASEARM5) && tar -czf $(RELEASEARM5).tgz $(RELEASEARM5)
|
||||
cd fzf && cp $(BINARYARM6) $(RELEASEARM6) && tar -czf $(RELEASEARM6).tgz $(RELEASEARM6)
|
||||
cd fzf && cp $(BINARYARM7) $(RELEASEARM7) && tar -czf $(RELEASEARM7).tgz $(RELEASEARM7)
|
||||
cd fzf && cp $(BINARYARM8) $(RELEASEARM8) && tar -czf $(RELEASEARM8).tgz $(RELEASEARM8)
|
||||
cd fzf && rm -f $(RELEASE32) $(RELEASE64) $(RELEASEARM5) $(RELEASEARM6) $(RELEASEARM7) $(RELEASEARM8)
|
||||
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 $(BINARY32) $(RELEASE32) && tar -czf $(RELEASE32).tgz $(RELEASE32)
|
||||
cd fzf && cp $(BINARY64) $(RELEASE64) && tar -czf $(RELEASE64).tgz $(RELEASE64)
|
||||
cd fzf && rm -f $(RELEASE32) $(RELEASE64)
|
||||
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
|
||||
|
||||
@@ -44,7 +44,7 @@ func init() {
|
||||
*/
|
||||
// The following regular expression will include not all but most of the
|
||||
// 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) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
const (
|
||||
// Current version
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
|
||||
// Core
|
||||
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
||||
|
||||
2
src/deps
2
src/deps
@@ -13,6 +13,6 @@ reset() (
|
||||
)
|
||||
|
||||
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 golang.org/x/crypto abc5fa7ad02123a41f02bf1391c9760f7586e608
|
||||
|
||||
@@ -54,6 +54,7 @@ const usage = `usage: fzf [options]
|
||||
--min-height=HEIGHT Minimum height when --height is given in percent
|
||||
(default: 10)
|
||||
--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)
|
||||
--inline-info Display finder info inline with the query
|
||||
--prompt=STR Input prompt (default: '> ')
|
||||
@@ -183,6 +184,7 @@ type Options struct {
|
||||
Header []string
|
||||
HeaderLines int
|
||||
Margin [4]sizeSpec
|
||||
Bordered bool
|
||||
Tabstop int
|
||||
Version bool
|
||||
}
|
||||
@@ -1086,6 +1088,10 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
opts.Height = sizeSpec{}
|
||||
case "--no-margin":
|
||||
opts.Margin = defaultMargin()
|
||||
case "--no-border":
|
||||
opts.Bordered = false
|
||||
case "--border":
|
||||
opts.Bordered = true
|
||||
case "--margin":
|
||||
opts.Margin = parseMargin(
|
||||
nextString(allArgs, &i, "margin required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))
|
||||
|
||||
@@ -101,7 +101,7 @@ func BuildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case,
|
||||
for idx, term := range termSet {
|
||||
// If the query contains inverse search terms or OR operators,
|
||||
// we cannot cache the search scope
|
||||
if !cacheable || idx > 0 || term.inv {
|
||||
if !cacheable || idx > 0 || term.inv || !fuzzy && term.typ != termExact {
|
||||
cacheable = false
|
||||
break Loop
|
||||
}
|
||||
|
||||
@@ -186,3 +186,21 @@ func TestCacheKey(t *testing.T) {
|
||||
test(true, "foo | bar !baz", "", false)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -37,12 +37,14 @@ func buildResult(item *Item, offsets []Offset, score int, trimLen int) *Result {
|
||||
result := Result{item: item, rank: rank{index: item.index}}
|
||||
numChars := item.text.Length()
|
||||
minBegin := math.MaxUint16
|
||||
minEnd := math.MaxUint16
|
||||
maxEnd := 0
|
||||
validOffsetFound := false
|
||||
for _, offset := range offsets {
|
||||
b, e := int(offset[0]), int(offset[1])
|
||||
if b < e {
|
||||
minBegin = util.Min(b, minBegin)
|
||||
minEnd = util.Min(e, minEnd)
|
||||
maxEnd = util.Max(e, maxEnd)
|
||||
validOffsetFound = true
|
||||
}
|
||||
@@ -68,7 +70,7 @@ func buildResult(item *Item, offsets []Offset, score int, trimLen int) *Result {
|
||||
}
|
||||
}
|
||||
if criterion == byBegin {
|
||||
val = util.AsUint16(minBegin - whitePrefixLen)
|
||||
val = util.AsUint16(minEnd - whitePrefixLen)
|
||||
} else {
|
||||
val = util.AsUint16(math.MaxUint16 - math.MaxUint16*(maxEnd-whitePrefixLen)/trimLen)
|
||||
}
|
||||
|
||||
@@ -83,8 +83,10 @@ type Terminal struct {
|
||||
tabstop int
|
||||
margin [4]sizeSpec
|
||||
strong tui.Attr
|
||||
bordered bool
|
||||
border tui.Window
|
||||
window tui.Window
|
||||
bwindow tui.Window
|
||||
pborder tui.Window
|
||||
pwindow tui.Window
|
||||
count int
|
||||
progress int
|
||||
@@ -295,15 +297,22 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
maxHeightFunc := func(termHeight int) int {
|
||||
var maxHeight int
|
||||
if opts.Height.percent {
|
||||
maxHeight = util.Min(termHeight,
|
||||
util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight))
|
||||
maxHeight = util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight)
|
||||
} 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 {
|
||||
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)
|
||||
} else if tui.HasFullscreenRenderer() {
|
||||
@@ -343,6 +352,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
printQuery: opts.PrintQuery,
|
||||
history: opts.History,
|
||||
margin: opts.Margin,
|
||||
bordered: opts.Bordered,
|
||||
strong: strongAttr,
|
||||
cycle: opts.Cycle,
|
||||
header: header,
|
||||
@@ -499,6 +509,9 @@ func (t *Terminal) resizeWindows() {
|
||||
} else {
|
||||
marginInt[idx] = int(sizeSpec.size)
|
||||
}
|
||||
if t.bordered && idx%2 == 0 {
|
||||
marginInt[idx] += 1
|
||||
}
|
||||
}
|
||||
adjust := func(idx1 int, idx2 int, max int, min int) {
|
||||
if max >= min {
|
||||
@@ -524,19 +537,29 @@ func (t *Terminal) resizeWindows() {
|
||||
}
|
||||
adjust(1, 3, screenWidth, minAreaWidth)
|
||||
adjust(0, 2, screenHeight, minAreaHeight)
|
||||
if t.border != nil {
|
||||
t.border.Close()
|
||||
}
|
||||
if t.window != nil {
|
||||
t.window.Close()
|
||||
}
|
||||
if t.bwindow != nil {
|
||||
t.bwindow.Close()
|
||||
if t.pborder != nil {
|
||||
t.pborder.Close()
|
||||
t.pwindow.Close()
|
||||
}
|
||||
|
||||
width := screenWidth - marginInt[1] - marginInt[3]
|
||||
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 {
|
||||
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
|
||||
// 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
|
||||
@@ -544,28 +567,28 @@ func (t *Terminal) resizeWindows() {
|
||||
if !t.preview.wrap && t.tui.DoesAutoWrap() {
|
||||
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 {
|
||||
case posUp:
|
||||
pheight := calculateSize(height, t.preview.size, minHeight, 3)
|
||||
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)
|
||||
case posDown:
|
||||
pheight := calculateSize(height, t.preview.size, minHeight, 3)
|
||||
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)
|
||||
case posLeft:
|
||||
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
|
||||
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)
|
||||
case posRight:
|
||||
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
@@ -573,7 +596,7 @@ func (t *Terminal) resizeWindows() {
|
||||
marginInt[0],
|
||||
marginInt[3],
|
||||
width,
|
||||
height, false)
|
||||
height, tui.BorderNone)
|
||||
}
|
||||
if !t.tui.IsOptimized() && t.theme != nil && t.theme.HasBg() {
|
||||
for i := 0; i < t.window.Height(); i++ {
|
||||
@@ -978,11 +1001,15 @@ func (t *Terminal) printAll() {
|
||||
|
||||
func (t *Terminal) refresh() {
|
||||
if !t.suppress {
|
||||
if t.hasPreviewWindow() {
|
||||
t.tui.RefreshWindows([]tui.Window{t.bwindow, t.pwindow, t.window})
|
||||
} else {
|
||||
t.tui.RefreshWindows([]tui.Window{t.window})
|
||||
windows := make([]tui.Window, 0, 4)
|
||||
if t.bordered {
|
||||
windows = append(windows, t.border)
|
||||
}
|
||||
if t.hasPreviewWindow() {
|
||||
windows = append(windows, t.pborder, t.pwindow)
|
||||
}
|
||||
windows = append(windows, t.window)
|
||||
t.tui.RefreshWindows(windows)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,6 @@ func (r *FullscreenRenderer) MaxY() int { return 0 }
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ type LightRenderer struct {
|
||||
type LightWindow struct {
|
||||
renderer *LightRenderer
|
||||
colored bool
|
||||
border bool
|
||||
border BorderStyle
|
||||
top int
|
||||
left int
|
||||
width int
|
||||
@@ -600,11 +600,11 @@ func (r *LightRenderer) IsOptimized() bool {
|
||||
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{
|
||||
renderer: r,
|
||||
colored: r.theme != nil,
|
||||
border: border,
|
||||
border: borderStyle,
|
||||
top: top,
|
||||
left: left,
|
||||
width: width,
|
||||
@@ -614,13 +614,27 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, bord
|
||||
if r.theme != nil {
|
||||
w.bg = r.theme.Bg
|
||||
}
|
||||
if w.border {
|
||||
w.drawBorder()
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
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.CPrint(ColBorder, AttrRegular, "┌"+repeat("─", w.width-2)+"┐")
|
||||
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)
|
||||
}
|
||||
|
||||
func cleanse(str string) string {
|
||||
return strings.Replace(str, "\x1b", "?", -1)
|
||||
}
|
||||
|
||||
func (w *LightWindow) CPrint(pair ColorPair, attr Attr, text string) {
|
||||
if !w.colored {
|
||||
w.csiColor(colDefault, colDefault, attrFor(pair, attr))
|
||||
} else {
|
||||
w.csiColor(pair.Fg(), pair.Bg(), attr)
|
||||
}
|
||||
w.stderrInternal(text, false)
|
||||
w.stderrInternal(cleanse(text), false)
|
||||
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) {
|
||||
defer w.csi("m")
|
||||
}
|
||||
w.stderrInternal(text, false)
|
||||
w.stderrInternal(cleanse(text), false)
|
||||
}
|
||||
|
||||
type wrappedLine struct {
|
||||
@@ -850,9 +868,7 @@ func (w *LightWindow) FinishFill() {
|
||||
}
|
||||
|
||||
func (w *LightWindow) Erase() {
|
||||
if w.border {
|
||||
w.drawBorder()
|
||||
}
|
||||
// We don't erase the window here to avoid flickering during scroll
|
||||
w.Move(0, 0)
|
||||
}
|
||||
|
||||
@@ -189,12 +189,13 @@ func (r *FullscreenRenderer) Close() {
|
||||
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))
|
||||
if r.theme != nil {
|
||||
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)
|
||||
C.wcolor_set(win, pair, nil)
|
||||
C.wattron(win, attr)
|
||||
|
||||
@@ -35,7 +35,7 @@ type TcellWindow struct {
|
||||
lastX int
|
||||
lastY int
|
||||
moveCursor bool
|
||||
border bool
|
||||
borderStyle BorderStyle
|
||||
}
|
||||
|
||||
func (w *TcellWindow) Top() int {
|
||||
@@ -61,8 +61,11 @@ func (w *TcellWindow) Refresh() {
|
||||
}
|
||||
w.lastX = 0
|
||||
w.lastY = 0
|
||||
if w.border {
|
||||
w.drawBorder()
|
||||
switch w.borderStyle {
|
||||
case BorderAround:
|
||||
w.drawBorder(true)
|
||||
case BorderHorizontal:
|
||||
w.drawBorder(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +380,7 @@ func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
|
||||
_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
|
||||
return &TcellWindow{
|
||||
color: r.theme != nil,
|
||||
@@ -385,7 +388,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
||||
left: left,
|
||||
width: width,
|
||||
height: height,
|
||||
border: border}
|
||||
borderStyle: borderStyle}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (w *TcellWindow) drawBorder() {
|
||||
func (w *TcellWindow) drawBorder(around bool) {
|
||||
left := w.left
|
||||
right := left + w.width
|
||||
top := w.top
|
||||
@@ -554,6 +557,7 @@ func (w *TcellWindow) drawBorder() {
|
||||
_screen.SetContent(x, bot-1, tcell.RuneHLine, nil, style)
|
||||
}
|
||||
|
||||
if around {
|
||||
for y := top; y < bot; y++ {
|
||||
_screen.SetContent(left, y, tcell.RuneVLine, nil, style)
|
||||
_screen.SetContent(right-1, y, tcell.RuneVLine, nil, style)
|
||||
@@ -564,3 +568,4 @@ func (w *TcellWindow) drawBorder() {
|
||||
_screen.SetContent(left, bot-1, tcell.RuneLLCorner, nil, style)
|
||||
_screen.SetContent(right-1, bot-1, tcell.RuneLRCorner, nil, style)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +195,14 @@ type MouseEvent struct {
|
||||
Mod bool
|
||||
}
|
||||
|
||||
type BorderStyle int
|
||||
|
||||
const (
|
||||
BorderNone BorderStyle = iota
|
||||
BorderAround
|
||||
BorderHorizontal
|
||||
)
|
||||
|
||||
type Renderer interface {
|
||||
Init()
|
||||
Pause()
|
||||
@@ -211,7 +219,7 @@ type Renderer interface {
|
||||
DoesAutoWrap() 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 {
|
||||
|
||||
@@ -617,6 +617,17 @@ class TestGoFZF < TestBase
|
||||
], `#{FZF} -foo --tiebreak=begin,length < #{tempname}`.split($/)
|
||||
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
|
||||
writelines tempname, [
|
||||
'xoxxxxxxxx',
|
||||
@@ -1543,7 +1554,7 @@ module CompletionTest
|
||||
tmux.prepare
|
||||
|
||||
# Using tmux
|
||||
tmux.send_keys 'unset FZFFOO**', :Tab
|
||||
tmux.send_keys 'unset FZFFOOBR**', :Tab
|
||||
tmux.until { |lines| lines.match_count == 1 }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| lines[-1].include? 'unset FZFFOOBAR' }
|
||||
@@ -1551,7 +1562,7 @@ module CompletionTest
|
||||
|
||||
# FZF_TMUX=1
|
||||
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.send_keys :Enter
|
||||
tmux.until { |lines| lines[-1].include? 'unset FZFFOOBAR' }
|
||||
|
||||
Reference in New Issue
Block a user