mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-14 19:55:49 -07:00
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e3e76fa8c5 | ||
|
2553806e79 | ||
|
1bcbc5a353 | ||
|
15d351b0f0 | ||
|
c144c95cda | ||
|
f08f4fd87d | ||
|
f8aaeef218 | ||
|
7915e365b3 | ||
|
1c68f81c37 | ||
|
d4c9db0a27 | ||
|
b5e0e29ec6 | ||
|
569be4c6c9 | ||
|
e7ca237b07 | ||
|
a7d3b72117 | ||
|
3ba7b5cf2d | ||
|
254e9765fe | ||
|
3304f284a5 | ||
|
0d5f862daf | ||
|
51dfacd542 | ||
|
c691d52fa7 | ||
|
de3d09fe79 | ||
|
eaa413c566 | ||
|
407205e52b | ||
|
552414978e | ||
|
607081bbaa | ||
|
e73383fbbb | ||
|
2e8e63fb0b | ||
|
874f7dd416 | ||
|
8b0e3b1624 | ||
|
9b946f2b7a | ||
|
11841f688b | ||
|
03c4f04246 | ||
|
a1f06ae27f | ||
|
69dffd78a6 | ||
|
2750e19657 | ||
|
b0987f727b | ||
|
a4d9b0b468 | ||
|
e2b87e0d74 | ||
|
2166b4ca17 | ||
|
d2d4d68585 | ||
|
faf68dbc5c | ||
|
305896fcb3 | ||
|
6c9adea0d3 | ||
|
fc7630a66d | ||
|
3248153d9f | ||
|
246b9f3130 | ||
|
865144850d | ||
|
d9752a4c21 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
bin/fzf
|
bin/fzf
|
||||||
bin/fzf.exe
|
bin/fzf.exe
|
||||||
|
dist
|
||||||
target
|
target
|
||||||
pkg
|
pkg
|
||||||
Gemfile.lock
|
Gemfile.lock
|
||||||
@@ -9,3 +10,5 @@ vendor
|
|||||||
gopath
|
gopath
|
||||||
*.zwc
|
*.zwc
|
||||||
fzf
|
fzf
|
||||||
|
tmp
|
||||||
|
*.patch
|
||||||
|
11
.gon.hcl
Normal file
11
.gon.hcl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
source = ["./dist/fzf-macos_darwin_amd64/fzf"]
|
||||||
|
bundle_id = "kr.junegunn.fzf"
|
||||||
|
|
||||||
|
apple_id {
|
||||||
|
username = "junegunn.c@gmail.com"
|
||||||
|
password = "@env:AC_PASSWORD"
|
||||||
|
}
|
||||||
|
|
||||||
|
sign {
|
||||||
|
application_identity = "Apple Development: junegunn.c@gmail.com"
|
||||||
|
}
|
69
.goreleaser.yml
Normal file
69
.goreleaser.yml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
project_name: fzf
|
||||||
|
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
- go mod download
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- id: fzf-macos
|
||||||
|
binary: fzf
|
||||||
|
goos:
|
||||||
|
- darwin
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
ldflags:
|
||||||
|
- "-s -w -X main.version={{ .Version }} -X main.revision={{ .ShortCommit }}"
|
||||||
|
hooks:
|
||||||
|
post: gon .gon.hcl
|
||||||
|
|
||||||
|
- goos:
|
||||||
|
- linux
|
||||||
|
- windows
|
||||||
|
- freebsd
|
||||||
|
- openbsd
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm
|
||||||
|
- arm64
|
||||||
|
goarm:
|
||||||
|
- 5
|
||||||
|
- 6
|
||||||
|
- 7
|
||||||
|
ldflags:
|
||||||
|
- "-s -w -X main.version={{ .Version }} -X main.revision={{ .ShortCommit }}"
|
||||||
|
ignore:
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm
|
||||||
|
- goos: openbsd
|
||||||
|
goarch: arm
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm64
|
||||||
|
- goos: openbsd
|
||||||
|
goarch: arm64
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||||
|
format: tar.gz
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
files:
|
||||||
|
- non-existent*
|
||||||
|
|
||||||
|
release:
|
||||||
|
github:
|
||||||
|
owner: junegunn
|
||||||
|
name: fzf
|
||||||
|
prerelease: auto
|
||||||
|
name_template: '{{ .Tag }}'
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ .Tag }}-devel"
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- README
|
||||||
|
- test
|
@@ -20,5 +20,9 @@ Style/MethodCallWithArgsParentheses:
|
|||||||
- ^refute_
|
- ^refute_
|
||||||
Style/NumericPredicate:
|
Style/NumericPredicate:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
Style/StringConcatenation:
|
||||||
|
Enabled: false
|
||||||
|
Style/OptionalBooleanParameter:
|
||||||
|
Enabled: false
|
||||||
Style/WordArray:
|
Style/WordArray:
|
||||||
MinSize: 1
|
MinSize: 1
|
||||||
|
@@ -18,7 +18,7 @@ addons:
|
|||||||
- fish
|
- fish
|
||||||
- tmux
|
- tmux
|
||||||
update: true
|
update: true
|
||||||
install: gem install minitest rubocop rubocop-minitest rubocop-performance
|
install: gem install --no-document minitest:5.14.2 rubocop:1.0.0 rubocop-minitest:0.10.1 rubocop-performance:1.8.1
|
||||||
script:
|
script:
|
||||||
- make test
|
- make test
|
||||||
# LC_ALL=C to avoid escape codes in
|
# LC_ALL=C to avoid escape codes in
|
||||||
|
22
BUILD.md
22
BUILD.md
@@ -17,21 +17,19 @@ make
|
|||||||
# Build fzf binary and copy it to bin directory
|
# Build fzf binary and copy it to bin directory
|
||||||
make install
|
make install
|
||||||
|
|
||||||
# Build 32-bit and 64-bit executables and tarballs in target
|
# Build fzf binaries and archives for all platforms using goreleaser
|
||||||
|
make build
|
||||||
|
|
||||||
|
# Publish GitHub release
|
||||||
make release
|
make release
|
||||||
|
|
||||||
# Make release archives for all supported platforms in target
|
|
||||||
make release-all
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using `go get`
|
> :warning: Makefile uses git commands to determine the version and the
|
||||||
|
> revision information for `fzf --version`. So if you're building fzf from an
|
||||||
Alternatively, you can build fzf directly with `go get` command without
|
> environment where its git information is not available, you have to manually
|
||||||
manually cloning the repository.
|
> set `$FZF_VERSION` and `$FZF_REVISION`.
|
||||||
|
>
|
||||||
```sh
|
> e.g. `FZF_VERSION=0.24.0 FZF_REVISION=tarball make`
|
||||||
go get -u github.com/junegunn/fzf
|
|
||||||
```
|
|
||||||
|
|
||||||
Third-party libraries used
|
Third-party libraries used
|
||||||
--------------------------
|
--------------------------
|
||||||
|
76
CHANGELOG.md
76
CHANGELOG.md
@@ -1,6 +1,82 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.24.2
|
||||||
|
------
|
||||||
|
- Bug fixes and improvements
|
||||||
|
|
||||||
|
0.24.1
|
||||||
|
------
|
||||||
|
- Fixed broken `--color=[bw|no]` option
|
||||||
|
|
||||||
|
0.24.0
|
||||||
|
------
|
||||||
|
- Real-time rendering of preview window
|
||||||
|
```sh
|
||||||
|
# fzf can render preview window before the command completes
|
||||||
|
fzf --preview 'sleep 1; for i in $(seq 100); do echo $i; sleep 0.01; done'
|
||||||
|
|
||||||
|
# Preview window can process ANSI escape sequence (CSI 2 J) for clearing the display
|
||||||
|
fzf --preview 'for i in $(seq 100000); do
|
||||||
|
(( i % 200 == 0 )) && printf "\033[2J"
|
||||||
|
echo "$i"
|
||||||
|
sleep 0.01
|
||||||
|
done'
|
||||||
|
```
|
||||||
|
- Updated `--color` option to support text styles
|
||||||
|
- `regular` / `bold` / `dim` / `underline` / `italic` / `reverse` / `blink`
|
||||||
|
```sh
|
||||||
|
# * Set -1 to keep the original color
|
||||||
|
# * Multiple style attributes can be combined
|
||||||
|
# * Italic style may not be supported by some terminals
|
||||||
|
rg --line-number --no-heading --color=always "" |
|
||||||
|
fzf --ansi --prompt "Rg: " \
|
||||||
|
--color fg+:italic,hl:underline:-1,hl+:italic:underline:reverse:-1 \
|
||||||
|
--color pointer:reverse,prompt:reverse,input:159 \
|
||||||
|
--pointer ' '
|
||||||
|
```
|
||||||
|
- More `--border` options
|
||||||
|
- `vertical`, `top`, `bottom`, `left`, `right`
|
||||||
|
- Updated Vim plugin to use these new `--border` options
|
||||||
|
```vim
|
||||||
|
" Floating popup window in the center of the screen
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
|
|
||||||
|
" Popup with 100% width
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 1.0, 'height': 0.5, 'border': 'horizontal' } }
|
||||||
|
|
||||||
|
" Popup with 100% height
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.5, 'height': 1.0, 'border': 'vertical' } }
|
||||||
|
|
||||||
|
" Similar to 'down' layout, but it uses a popup window and doesn't affect the window layout
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 1.0, 'height': 0.5, 'yoffset': 1.0, 'border': 'top' } }
|
||||||
|
|
||||||
|
" Opens on the right;
|
||||||
|
" 'highlight' option is still supported but it will only take the foreground color of the group
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.5, 'height': 1.0, 'xoffset': 1.0, 'border': 'left', 'highlight': 'Comment' } }
|
||||||
|
```
|
||||||
|
- To indicate if `--multi` mode is enabled, fzf will print the number of
|
||||||
|
selected items even when no item is selected
|
||||||
|
```sh
|
||||||
|
seq 100 | fzf
|
||||||
|
# 100/100
|
||||||
|
seq 100 | fzf --multi
|
||||||
|
# 100/100 (0)
|
||||||
|
seq 100 | fzf --multi 5
|
||||||
|
# 100/100 (0/5)
|
||||||
|
```
|
||||||
|
- Since 0.24.0, release binaries will be uploaded to https://github.com/junegunn/fzf/releases
|
||||||
|
|
||||||
|
0.23.1
|
||||||
|
------
|
||||||
|
- Added `--preview-window` options for disabling flags
|
||||||
|
- `nocycle`
|
||||||
|
- `nohidden`
|
||||||
|
- `nowrap`
|
||||||
|
- `default`
|
||||||
|
- Built with Go 1.14.9 due to performance regression
|
||||||
|
- https://github.com/golang/go/issues/40727
|
||||||
|
|
||||||
0.23.0
|
0.23.0
|
||||||
------
|
------
|
||||||
- Support preview scroll offset relative to window height
|
- Support preview scroll offset relative to window height
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
FROM archlinux/base:latest
|
FROM archlinux/base:latest
|
||||||
RUN pacman -Sy && pacman --noconfirm -S awk git tmux zsh fish ruby procps go make gcc
|
RUN pacman -Sy && pacman --noconfirm -S awk git tmux zsh fish ruby procps go make gcc
|
||||||
RUN gem install --no-document minitest
|
RUN gem install --no-document -v 5.14.2 minitest
|
||||||
RUN echo '. /usr/share/bash-completion/completions/git' >> ~/.bashrc
|
RUN echo '. /usr/share/bash-completion/completions/git' >> ~/.bashrc
|
||||||
RUN echo '. ~/.bashrc' >> ~/.bash_profile
|
RUN echo '. ~/.bashrc' >> ~/.bash_profile
|
||||||
|
|
||||||
|
102
Makefile
102
Makefile
@@ -5,8 +5,26 @@ MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
|
|||||||
ROOT_DIR := $(shell dirname $(MAKEFILE))
|
ROOT_DIR := $(shell dirname $(MAKEFILE))
|
||||||
SOURCES := $(wildcard *.go src/*.go src/*/*.go) $(MAKEFILE)
|
SOURCES := $(wildcard *.go src/*.go src/*/*.go) $(MAKEFILE)
|
||||||
|
|
||||||
REVISION := $(shell git log -n 1 --pretty=format:%h -- $(SOURCES))
|
ifdef FZF_VERSION
|
||||||
BUILD_FLAGS := -a -ldflags "-X main.revision=$(REVISION) -w '-extldflags=$(LDFLAGS)'" -tags "$(TAGS)"
|
VERSION := $(FZF_VERSION)
|
||||||
|
else
|
||||||
|
VERSION := $(shell git describe --abbrev=0 2> /dev/null)
|
||||||
|
endif
|
||||||
|
ifeq ($(VERSION),)
|
||||||
|
$(error Not on git repository; cannot determine $$FZF_VERSION)
|
||||||
|
endif
|
||||||
|
VERSION_TRIM := $(shell sed "s/-.*//" <<< $(VERSION))
|
||||||
|
VERSION_REGEX := $(subst .,\.,$(VERSION_TRIM))
|
||||||
|
|
||||||
|
ifdef FZF_REVISION
|
||||||
|
REVISION := $(FZF_REVISION)
|
||||||
|
else
|
||||||
|
REVISION := $(shell git log -n 1 --pretty=format:%h -- $(SOURCES) 2> /dev/null)
|
||||||
|
endif
|
||||||
|
ifeq ($(REVISION),)
|
||||||
|
$(error Not on git repository; cannot determine $$FZF_REVISION)
|
||||||
|
endif
|
||||||
|
BUILD_FLAGS := -a -ldflags "-s -w -X main.version=$(VERSION) -X main.revision=$(REVISION)" -tags "$(TAGS)"
|
||||||
|
|
||||||
BINARY64 := fzf-$(GOOS)_amd64
|
BINARY64 := fzf-$(GOOS)_amd64
|
||||||
BINARYARM5 := fzf-$(GOOS)_arm5
|
BINARYARM5 := fzf-$(GOOS)_arm5
|
||||||
@@ -14,13 +32,6 @@ BINARYARM6 := fzf-$(GOOS)_arm6
|
|||||||
BINARYARM7 := fzf-$(GOOS)_arm7
|
BINARYARM7 := fzf-$(GOOS)_arm7
|
||||||
BINARYARM8 := fzf-$(GOOS)_arm8
|
BINARYARM8 := fzf-$(GOOS)_arm8
|
||||||
BINARYPPC64LE := fzf-$(GOOS)_ppc64le
|
BINARYPPC64LE := fzf-$(GOOS)_ppc64le
|
||||||
VERSION := $(shell awk -F= '/version =/ {print $$2}' src/constants.go | tr -d "\" ")
|
|
||||||
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
|
|
||||||
RELEASEPPC64LE := fzf-$(VERSION)-$(GOOS)_ppc64le
|
|
||||||
|
|
||||||
# https://en.wikipedia.org/wiki/Uname
|
# https://en.wikipedia.org/wiki/Uname
|
||||||
UNAME_M := $(shell uname -m)
|
UNAME_M := $(shell uname -m)
|
||||||
@@ -41,40 +52,11 @@ else ifeq ($(UNAME_M),aarch64)
|
|||||||
else ifeq ($(UNAME_M),ppc64le)
|
else ifeq ($(UNAME_M),ppc64le)
|
||||||
BINARY := $(BINARYPPC64LE)
|
BINARY := $(BINARYPPC64LE)
|
||||||
else
|
else
|
||||||
$(error "Build on $(UNAME_M) is not supported, yet.")
|
$(error Build on $(UNAME_M) is not supported, yet.)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: target/$(BINARY)
|
all: target/$(BINARY)
|
||||||
|
|
||||||
target:
|
|
||||||
mkdir -p $@
|
|
||||||
|
|
||||||
ifeq ($(GOOS),windows)
|
|
||||||
release: target/$(BINARY64)
|
|
||||||
cd target && cp -f $(BINARY64) fzf.exe && zip $(RELEASE64).zip fzf.exe
|
|
||||||
cd target && rm -f fzf.exe
|
|
||||||
else ifeq ($(GOOS),linux)
|
|
||||||
release: target/$(BINARY64) target/$(BINARYARM5) target/$(BINARYARM6) target/$(BINARYARM7) target/$(BINARYARM8) target/$(BINARYPPC64LE)
|
|
||||||
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 && cp -f $(BINARYPPC64LE) fzf && tar -czf $(RELEASEPPC64LE).tgz fzf
|
|
||||||
cd target && rm -f fzf
|
|
||||||
else
|
|
||||||
release: target/$(BINARY64)
|
|
||||||
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
|
|
||||||
|
|
||||||
test: $(SOURCES)
|
test: $(SOURCES)
|
||||||
SHELL=/bin/sh GOOS= $(GO) test -v -tags "$(TAGS)" \
|
SHELL=/bin/sh GOOS= $(GO) test -v -tags "$(TAGS)" \
|
||||||
github.com/junegunn/fzf/src \
|
github.com/junegunn/fzf/src \
|
||||||
@@ -84,8 +66,46 @@ test: $(SOURCES)
|
|||||||
|
|
||||||
install: bin/fzf
|
install: bin/fzf
|
||||||
|
|
||||||
|
build:
|
||||||
|
goreleaser --rm-dist --snapshot
|
||||||
|
|
||||||
|
release:
|
||||||
|
ifndef GITHUB_TOKEN
|
||||||
|
$(error GITHUB_TOKEN is not defined)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we are on master branch
|
||||||
|
ifneq ($(shell git symbolic-ref --short HEAD),master)
|
||||||
|
$(error Not on master branch)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if version numbers are properly updated
|
||||||
|
grep -q ^$(VERSION_REGEX)$$ CHANGELOG.md
|
||||||
|
grep -qF '"fzf $(VERSION_TRIM)"' man/man1/fzf.1
|
||||||
|
grep -qF '"fzf $(VERSION_TRIM)"' man/man1/fzf-tmux.1
|
||||||
|
grep -qF $(VERSION) install
|
||||||
|
grep -qF $(VERSION) install.ps1
|
||||||
|
|
||||||
|
# Make release note out of CHANGELOG.md
|
||||||
|
sed -n '/^$(VERSION_REGEX)$$/,/^[0-9]/p' CHANGELOG.md | tail -r | \
|
||||||
|
sed '1,/^ *$$/d' | tail -r | sed 1,2d | tee tmp/release-note
|
||||||
|
|
||||||
|
# Push to temp branch first so that install scripts always works on master branch
|
||||||
|
git checkout -B temp master
|
||||||
|
git push origin temp --follow-tags --force
|
||||||
|
|
||||||
|
# Make a GitHub release
|
||||||
|
goreleaser --rm-dist --release-notes tmp/release-note
|
||||||
|
|
||||||
|
# Push to master
|
||||||
|
git checkout master
|
||||||
|
git push origin master
|
||||||
|
|
||||||
|
# Delete temp branch
|
||||||
|
git push origin --delete temp
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) -r target
|
$(RM) -r dist target
|
||||||
|
|
||||||
target/$(BINARY64): $(SOURCES)
|
target/$(BINARY64): $(SOURCES)
|
||||||
GOARCH=amd64 $(GO) build $(BUILD_FLAGS) -o $@
|
GOARCH=amd64 $(GO) build $(BUILD_FLAGS) -o $@
|
||||||
@@ -121,4 +141,4 @@ update:
|
|||||||
$(GO) get -u
|
$(GO) get -u
|
||||||
$(GO) mod tidy
|
$(GO) mod tidy
|
||||||
|
|
||||||
.PHONY: all release release-all test install clean docker docker-test update
|
.PHONY: all build release test install clean docker docker-test update
|
||||||
|
@@ -127,11 +127,13 @@ let g:fzf_action = {
|
|||||||
\ 'ctrl-v': 'vsplit' }
|
\ 'ctrl-v': 'vsplit' }
|
||||||
|
|
||||||
" Default fzf layout
|
" Default fzf layout
|
||||||
" - down / up / left / right / window
|
" - Popup window
|
||||||
let g:fzf_layout = { 'down': '40%' }
|
|
||||||
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
|
|
||||||
" You can set up fzf window using a Vim command (Neovim or latest Vim 8 required)
|
" - down / up / left / right
|
||||||
|
let g:fzf_layout = { 'down': '40%' }
|
||||||
|
|
||||||
|
" - Window using a Vim command
|
||||||
let g:fzf_layout = { 'window': 'enew' }
|
let g:fzf_layout = { 'window': 'enew' }
|
||||||
let g:fzf_layout = { 'window': '-tabnew' }
|
let g:fzf_layout = { 'window': '-tabnew' }
|
||||||
let g:fzf_layout = { 'window': '10new' }
|
let g:fzf_layout = { 'window': '10new' }
|
||||||
@@ -296,9 +298,8 @@ following options are allowed:
|
|||||||
- Optional:
|
- Optional:
|
||||||
- `yoffset` [float default 0.5 range [0 ~ 1]]
|
- `yoffset` [float default 0.5 range [0 ~ 1]]
|
||||||
- `xoffset` [float default 0.5 range [0 ~ 1]]
|
- `xoffset` [float default 0.5 range [0 ~ 1]]
|
||||||
- `highlight` [string default `'Comment'`]: Highlight group for border
|
|
||||||
- `border` [string default `rounded`]: Border style
|
- `border` [string default `rounded`]: Border style
|
||||||
- `rounded` / `sharp` / `horizontal` / `vertical` / `top` / `bottom` / `left` / `right`
|
- `rounded` / `sharp` / `horizontal` / `vertical` / `top` / `bottom` / `left` / `right` / `no[ne]`
|
||||||
|
|
||||||
`fzf#wrap`
|
`fzf#wrap`
|
||||||
----------
|
----------
|
||||||
@@ -332,8 +333,9 @@ After we *"wrap"* our spec, we pass it to `fzf#run`.
|
|||||||
call fzf#run(fzf#wrap({'source': 'ls'}))
|
call fzf#run(fzf#wrap({'source': 'ls'}))
|
||||||
```
|
```
|
||||||
|
|
||||||
Now it supports `CTRL-T`, `CTRL-V`, and `CTRL-X` key bindings and it opens fzf
|
Now it supports `CTRL-T`, `CTRL-V`, and `CTRL-X` key bindings (configurable
|
||||||
window according to `g:fzf_layout` setting.
|
via `g:fzf_action`) and it opens fzf window according to `g:fzf_layout`
|
||||||
|
setting.
|
||||||
|
|
||||||
To make it easier to use, let's define `LS` command.
|
To make it easier to use, let's define `LS` command.
|
||||||
|
|
||||||
@@ -370,6 +372,17 @@ command! -bang -complete=dir -nargs=* LS
|
|||||||
\ call fzf#run(fzf#wrap('ls', {'source': 'ls', 'dir': <q-args>}, <bang>0))
|
\ call fzf#run(fzf#wrap('ls', {'source': 'ls', 'dir': <q-args>}, <bang>0))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Global options supported by `fzf#wrap`
|
||||||
|
|
||||||
|
- `g:fzf_layout`
|
||||||
|
- `g:fzf_action`
|
||||||
|
- **Works only when no custom `sink` (or `sink*`) is provided**
|
||||||
|
- Having custom sink usually means that each entry is not an ordinary
|
||||||
|
file path (e.g. name of color scheme), so we can't blindly apply the
|
||||||
|
same strategy (i.e. `tabedit some-color-scheme` doesn't make sense)
|
||||||
|
- `g:fzf_colors`
|
||||||
|
- `g:fzf_history_dir`
|
||||||
|
|
||||||
Tips
|
Tips
|
||||||
----
|
----
|
||||||
|
|
||||||
|
17
README.md
17
README.md
@@ -82,7 +82,7 @@ fzf project consists of the following components:
|
|||||||
You can [download fzf executable][bin] alone if you don't need the extra
|
You can [download fzf executable][bin] alone if you don't need the extra
|
||||||
stuff.
|
stuff.
|
||||||
|
|
||||||
[bin]: https://github.com/junegunn/fzf-bin/releases
|
[bin]: https://github.com/junegunn/fzf/releases
|
||||||
|
|
||||||
### Using Homebrew or Linuxbrew
|
### Using Homebrew or Linuxbrew
|
||||||
|
|
||||||
@@ -125,9 +125,10 @@ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
|
|||||||
| XBPS | Void Linux | `sudo xbps-install -S fzf` |
|
| XBPS | Void Linux | `sudo xbps-install -S fzf` |
|
||||||
| Zypper | openSUSE | `sudo zypper install fzf` |
|
| Zypper | openSUSE | `sudo zypper install fzf` |
|
||||||
|
|
||||||
Shell extensions (key bindings and fuzzy auto-completion) and Vim/Neovim
|
> :warning: **Key bindings (CTRL-T / CTRL-R / ALT-C) and fuzzy auto-completion
|
||||||
plugin may or may not be enabled by default depending on the package manager.
|
> may not be enabled by default.**
|
||||||
Refer to the package documentation for more information.
|
>
|
||||||
|
> Refer to the package documentation for more information. (e.g. `apt-cache show fzf`)
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
@@ -592,14 +593,6 @@ The window can be scrolled using the mouse or custom key bindings.
|
|||||||
fzf --preview 'cat {}'
|
fzf --preview 'cat {}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Since the preview window is updated only after the process is complete, it's
|
|
||||||
important that the command finishes quickly.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Use head instead of cat so that the command doesn't take too long to finish
|
|
||||||
fzf --preview 'head -100 {}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Preview window supports ANSI colors, so you can use any program that
|
Preview window supports ANSI colors, so you can use any program that
|
||||||
syntax-highlights the content of a file, such as
|
syntax-highlights the content of a file, such as
|
||||||
[Bat](https://github.com/sharkdp/bat) or
|
[Bat](https://github.com/sharkdp/bat) or
|
||||||
|
42
bin/fzf-tmux
42
bin/fzf-tmux
@@ -136,7 +136,7 @@ if [[ -z "$TMUX" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# --height option is not allowed
|
# --height option is not allowed
|
||||||
args=("--no-height" "${args[@]}")
|
args=("${args[@]}" "--no-height")
|
||||||
|
|
||||||
# Handle zoomed tmux pane without popup options by moving it to a temp window
|
# Handle zoomed tmux pane without popup options by moving it to a temp window
|
||||||
if [[ ! "$opt" =~ "-K -E" ]] && tmux list-panes -F '#F' | grep -q Z; then
|
if [[ ! "$opt" =~ "-K -E" ]] && tmux list-panes -F '#F' | grep -q Z; then
|
||||||
@@ -154,12 +154,13 @@ argsf="${TMPDIR:-/tmp}/fzf-args-$id"
|
|||||||
fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id"
|
fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id"
|
||||||
fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id"
|
fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id"
|
||||||
fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id"
|
fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id"
|
||||||
|
tmux_win_opts=( $(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/') )
|
||||||
cleanup() {
|
cleanup() {
|
||||||
\rm -f $argsf $fifo1 $fifo2 $fifo3
|
\rm -f $argsf $fifo1 $fifo2 $fifo3
|
||||||
|
|
||||||
# Restore tmux window options
|
# Restore tmux window options
|
||||||
if [[ "${#tmux_win_opts[@]}" -gt 0 ]]; then
|
if [[ "${#tmux_win_opts[@]}" -gt 0 ]]; then
|
||||||
eval "tmux ${tmux_win_opts[@]}"
|
eval "tmux ${tmux_win_opts[*]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove temp window if we were zoomed without popup options
|
# Remove temp window if we were zoomed without popup options
|
||||||
@@ -179,58 +180,45 @@ cleanup() {
|
|||||||
trap 'cleanup 1' SIGUSR1
|
trap 'cleanup 1' SIGUSR1
|
||||||
trap 'cleanup' EXIT
|
trap 'cleanup' EXIT
|
||||||
|
|
||||||
envs="env TERM=$TERM "
|
envs="export TERM=$TERM "
|
||||||
[[ "$opt" =~ "-K -E" ]] && FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS"
|
[[ "$opt" =~ "-K -E" ]] && FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS"
|
||||||
[[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
|
[[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
|
||||||
[[ -n "$FZF_DEFAULT_COMMAND" ]] && envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
|
[[ -n "$FZF_DEFAULT_COMMAND" ]] && envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
|
||||||
|
echo "$envs;" > "$argsf"
|
||||||
mkfifo -m o+w $fifo2
|
|
||||||
mkfifo -m o+w $fifo3
|
|
||||||
|
|
||||||
# Build arguments to fzf
|
# Build arguments to fzf
|
||||||
opts=""
|
opts=$(printf "%q " "${args[@]}")
|
||||||
for arg in "${args[@]}"; do
|
|
||||||
arg="${arg//\\/\\\\}"
|
|
||||||
arg="${arg//\"/\\\"}"
|
|
||||||
arg="${arg//\`/\\\`}"
|
|
||||||
arg="${arg//$/\\$}"
|
|
||||||
opts="$opts \"$arg\""
|
|
||||||
done
|
|
||||||
|
|
||||||
pppid=$$
|
pppid=$$
|
||||||
echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" > $argsf
|
echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" >> $argsf
|
||||||
close="; trap - EXIT SIGINT SIGTERM $close"
|
close="; trap - EXIT SIGINT SIGTERM $close"
|
||||||
|
|
||||||
tmux_win_opts=( $(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/') )
|
export TMUX=$(cut -d , -f 1,2 <<< "$TMUX")
|
||||||
|
mkfifo -m o+w $fifo2
|
||||||
if [[ "$opt" =~ "-K -E" ]]; then
|
if [[ "$opt" =~ "-K -E" ]]; then
|
||||||
cat $fifo2 &
|
cat $fifo2 &
|
||||||
if [[ -n "$term" ]] || [[ -t 0 ]]; then
|
if [[ -n "$term" ]] || [[ -t 0 ]]; then
|
||||||
cat <<< "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
|
cat <<< "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
|
||||||
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux popup -d "$PWD" "${tmux_args[@]}" $opt -R "$envs bash $argsf" > /dev/null 2>&1
|
|
||||||
else
|
else
|
||||||
mkfifo $fifo1
|
mkfifo $fifo1
|
||||||
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; out=\$? $close; exit \$out" >> $argsf
|
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; out=\$? $close; exit \$out" >> $argsf
|
||||||
cat <&0 > $fifo1 &
|
cat <&0 > $fifo1 &
|
||||||
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux popup -d "$PWD" "${tmux_args[@]}" $opt -R "$envs bash $argsf" > /dev/null 2>&1
|
|
||||||
fi
|
fi
|
||||||
|
tmux popup -d "$PWD" "${tmux_args[@]}" $opt -R "bash $argsf" > /dev/null 2>&1
|
||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mkfifo -m o+w $fifo3
|
||||||
if [[ -n "$term" ]] || [[ -t 0 ]]; then
|
if [[ -n "$term" ]] || [[ -t 0 ]]; then
|
||||||
cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf
|
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 "${tmux_args[@]}" "$envs bash -c 'cd $(printf %q "$PWD"); exec -a fzf bash $argsf'" $swap \
|
|
||||||
> /dev/null 2>&1 || { "$fzf" "${args[@]}"; exit $?; }
|
|
||||||
else
|
else
|
||||||
mkfifo $fifo1
|
mkfifo $fifo1
|
||||||
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; echo \$? > $fifo3 $close" >> $argsf
|
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 "${tmux_args[@]}" "$envs bash -c 'exec -a fzf bash $argsf'" $swap \
|
|
||||||
> /dev/null 2>&1 || { "$fzf" "${args[@]}"; exit $?; }
|
|
||||||
cat <&0 > $fifo1 &
|
cat <&0 > $fifo1 &
|
||||||
fi
|
fi
|
||||||
|
tmux set-window-option synchronize-panes off \;\
|
||||||
|
set-window-option remain-on-exit off \;\
|
||||||
|
split-window -c "$PWD" $opt "${tmux_args[@]}" "bash -c 'exec -a fzf bash $argsf'" $swap \
|
||||||
|
> /dev/null 2>&1 || { "$fzf" "${args[@]}"; exit $?; }
|
||||||
cat $fifo2
|
cat $fifo2
|
||||||
exit "$(cat $fifo3)"
|
exit "$(cat $fifo3)"
|
||||||
|
41
doc/fzf.txt
41
doc/fzf.txt
@@ -1,4 +1,4 @@
|
|||||||
fzf.txt fzf Last change: April 4 2020
|
fzf.txt fzf Last change: October 18 2020
|
||||||
FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
|
FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ FZF - TABLE OF CONTENTS *fzf* *fzf-to
|
|||||||
Explanation of g:fzf_colors
|
Explanation of g:fzf_colors
|
||||||
fzf#run
|
fzf#run
|
||||||
fzf#wrap
|
fzf#wrap
|
||||||
|
Global options supported by fzf#wrap
|
||||||
Tips
|
Tips
|
||||||
fzf inside terminal buffer
|
fzf inside terminal buffer
|
||||||
Starting fzf in a popup window
|
Starting fzf in a popup window
|
||||||
@@ -105,14 +106,14 @@ the whole if we start off with `:FZF` command.
|
|||||||
" Bang version starts fzf in fullscreen mode
|
" Bang version starts fzf in fullscreen mode
|
||||||
:FZF!
|
:FZF!
|
||||||
<
|
<
|
||||||
Similarly to {ctrlp.vim}{2}, use enter key, CTRL-T, CTRL-X or CTRL-V to open
|
Similarly to {ctrlp.vim}{3}, 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
|
selected files in the current window, in new tabs, in horizontal splits, or in
|
||||||
vertical splits respectively.
|
vertical splits respectively.
|
||||||
|
|
||||||
Note that the environment variables `FZF_DEFAULT_COMMAND` and
|
Note that the environment variables `FZF_DEFAULT_COMMAND` and
|
||||||
`FZF_DEFAULT_OPTS` also apply here.
|
`FZF_DEFAULT_OPTS` also apply here.
|
||||||
|
|
||||||
{2} https://github.com/kien/ctrlp.vim
|
{3} https://github.com/kien/ctrlp.vim
|
||||||
|
|
||||||
|
|
||||||
< Configuration >_____________________________________________________________~
|
< Configuration >_____________________________________________________________~
|
||||||
@@ -154,11 +155,13 @@ Examples~
|
|||||||
\ 'ctrl-v': 'vsplit' }
|
\ 'ctrl-v': 'vsplit' }
|
||||||
|
|
||||||
" Default fzf layout
|
" Default fzf layout
|
||||||
" - down / up / left / right / window
|
" - Popup window
|
||||||
let g:fzf_layout = { 'down': '~40%' }
|
|
||||||
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
|
|
||||||
" You can set up fzf window using a Vim command (Neovim or latest Vim 8 required)
|
" - down / up / left / right
|
||||||
|
let g:fzf_layout = { 'down': '40%' }
|
||||||
|
|
||||||
|
" - Window using a Vim command
|
||||||
let g:fzf_layout = { 'window': 'enew' }
|
let g:fzf_layout = { 'window': 'enew' }
|
||||||
let g:fzf_layout = { 'window': '-tabnew' }
|
let g:fzf_layout = { 'window': '-tabnew' }
|
||||||
let g:fzf_layout = { 'window': '10new' }
|
let g:fzf_layout = { 'window': '10new' }
|
||||||
@@ -347,8 +350,8 @@ After we "wrap" our spec, we pass it to `fzf#run`.
|
|||||||
>
|
>
|
||||||
call fzf#run(fzf#wrap({'source': 'ls'}))
|
call fzf#run(fzf#wrap({'source': 'ls'}))
|
||||||
<
|
<
|
||||||
Now it supports CTRL-T, CTRL-V, and CTRL-X key bindings and it opens fzf
|
Now it supports CTRL-T, CTRL-V, and CTRL-X key bindings (configurable via
|
||||||
window according to `g:fzf_layout` setting.
|
`g:fzf_action`) and it opens fzf window according to `g:fzf_layout` setting.
|
||||||
|
|
||||||
To make it easier to use, let's define `LS` command.
|
To make it easier to use, let's define `LS` command.
|
||||||
>
|
>
|
||||||
@@ -378,6 +381,19 @@ unique name to our command and pass it as the first argument to `fzf#wrap`.
|
|||||||
\ call fzf#run(fzf#wrap('ls', {'source': 'ls', 'dir': <q-args>}, <bang>0))
|
\ call fzf#run(fzf#wrap('ls', {'source': 'ls', 'dir': <q-args>}, <bang>0))
|
||||||
<
|
<
|
||||||
|
|
||||||
|
< Global options supported by fzf#wrap >______________________________________~
|
||||||
|
*fzf-global-options-supported-by-fzf#wrap*
|
||||||
|
|
||||||
|
- `g:fzf_layout`
|
||||||
|
- `g:fzf_action`
|
||||||
|
- Works only when no custom `sink` (or `sink*`) is provided
|
||||||
|
- Having custom sink usually means that each entry is not an ordinary
|
||||||
|
file path (e.g. name of color scheme), so we can't blindly apply the
|
||||||
|
same strategy (i.e. `tabedit some-color-scheme` doesn't make sense)
|
||||||
|
- `g:fzf_colors`
|
||||||
|
- `g:fzf_history_dir`
|
||||||
|
|
||||||
|
|
||||||
TIPS *fzf-tips*
|
TIPS *fzf-tips*
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
@@ -398,8 +414,8 @@ Starting fzf in a popup window~
|
|||||||
*fzf-starting-fzf-in-a-popup-window*
|
*fzf-starting-fzf-in-a-popup-window*
|
||||||
>
|
>
|
||||||
" Required:
|
" Required:
|
||||||
" - width [float range [0 ~ 1]]
|
" - width [float range [0 ~ 1]] or [integer range [8 ~ ]]
|
||||||
" - height [float range [0 ~ 1]]
|
" - height [float range [0 ~ 1]] or [integer range [4 ~ ]]
|
||||||
"
|
"
|
||||||
" Optional:
|
" Optional:
|
||||||
" - xoffset [float default 0.5 range [0 ~ 1]]
|
" - xoffset [float default 0.5 range [0 ~ 1]]
|
||||||
@@ -427,9 +443,8 @@ When fzf starts in a terminal buffer, the file type of the buffer is set to
|
|||||||
`fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
|
`fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
|
||||||
the window.
|
the window.
|
||||||
|
|
||||||
For example, if you use a non-popup layout (e.g. `{'down': '40%'}`) on
|
For example, if you use a non-popup layout (e.g. `{'down': '40%'}`) on Neovim,
|
||||||
Neovim, you might want to temporarily disable the statusline for a cleaner
|
you might want to temporarily disable the statusline for a cleaner look.
|
||||||
look.
|
|
||||||
>
|
>
|
||||||
if has('nvim') && !exists('g:fzf_layout')
|
if has('nvim') && !exists('g:fzf_layout')
|
||||||
autocmd! FileType fzf
|
autocmd! FileType fzf
|
||||||
|
14
go.mod
14
go.mod
@@ -2,14 +2,14 @@ module github.com/junegunn/fzf
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/tcell v1.4.0
|
github.com/gdamore/tcell v1.4.0
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.12
|
github.com/mattn/go-isatty v0.0.12
|
||||||
github.com/mattn/go-runewidth v0.0.8
|
github.com/mattn/go-runewidth v0.0.9
|
||||||
github.com/mattn/go-shellwords v1.0.9
|
github.com/mattn/go-shellwords v1.0.10
|
||||||
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e
|
github.com/saracen/walker v0.1.1
|
||||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||||
golang.org/x/text v0.3.2 // indirect
|
golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1
|
||||||
|
golang.org/x/text v0.3.3 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
40
go.sum
40
go.sum
@@ -1,45 +1,35 @@
|
|||||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
|
||||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=
|
github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=
|
||||||
github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
|
github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
|
||||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-shellwords v1.0.9 h1:eaB5JspOwiKKcHdqcjbfe5lA9cNn/4NRRtddXJCimqk=
|
github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw=
|
||||||
github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||||
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e h1:NO86zOn5ScSKW8wRbMaSIcjDZUFpWdCQQnexRqZ9h9A=
|
github.com/saracen/walker v0.1.1 h1:Ou2QIKTWqo0QxhtuHVmtObbmhjMCEUyJ82xp0uV+MGI=
|
||||||
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e/go.mod h1:G0Z6yVPru183i2MuRJx1DcR4dgIZtLcTdaaE/pC1BJU=
|
github.com/saracen/walker v0.1.1/go.mod h1:0oKYMsKVhSJ+ful4p/XbjvXbMgLEkLITZaxozsl4CGE=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
|
golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1 h1:/DtoiOYKoQCcIFXQjz07RnWNPRCbqmSXSpgEzhC9ZHM=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191011211836-4c025a95b26e h1:1o2bDs9pCd2xFhdwqJTrCIswAeEsn4h/PCNelWpfcsI=
|
|
||||||
golang.org/x/tools v0.0.0-20191011211836-4c025a95b26e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
|
41
install
41
install
@@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
version=0.23.0
|
version=0.24.2
|
||||||
auto_completion=
|
auto_completion=
|
||||||
key_bindings=
|
key_bindings=
|
||||||
update_config=2
|
update_config=2
|
||||||
binary_arch=
|
|
||||||
shells="bash zsh fish"
|
shells="bash zsh fish"
|
||||||
prefix='~/.fzf'
|
prefix='~/.fzf'
|
||||||
prefix_expand=~/.fzf
|
prefix_expand=~/.fzf
|
||||||
@@ -115,7 +114,7 @@ link_fzf_in_path() {
|
|||||||
|
|
||||||
try_curl() {
|
try_curl() {
|
||||||
command -v curl > /dev/null &&
|
command -v curl > /dev/null &&
|
||||||
if [[ $1 =~ tgz$ ]]; then
|
if [[ $1 =~ tar.gz$ ]]; then
|
||||||
curl -fL $1 | tar -xzf -
|
curl -fL $1 | tar -xzf -
|
||||||
else
|
else
|
||||||
local temp=${TMPDIR:-/tmp}/fzf.zip
|
local temp=${TMPDIR:-/tmp}/fzf.zip
|
||||||
@@ -125,7 +124,7 @@ try_curl() {
|
|||||||
|
|
||||||
try_wget() {
|
try_wget() {
|
||||||
command -v wget > /dev/null &&
|
command -v wget > /dev/null &&
|
||||||
if [[ $1 =~ tgz$ ]]; then
|
if [[ $1 =~ tar.gz$ ]]; then
|
||||||
wget -O - $1 | tar -xzf -
|
wget -O - $1 | tar -xzf -
|
||||||
else
|
else
|
||||||
local temp=${TMPDIR:-/tmp}/fzf.zip
|
local temp=${TMPDIR:-/tmp}/fzf.zip
|
||||||
@@ -135,13 +134,11 @@ try_wget() {
|
|||||||
|
|
||||||
download() {
|
download() {
|
||||||
echo "Downloading bin/fzf ..."
|
echo "Downloading bin/fzf ..."
|
||||||
if [[ ! "$version" =~ alpha ]]; then
|
|
||||||
if [ -x "$fzf_base"/bin/fzf ]; then
|
if [ -x "$fzf_base"/bin/fzf ]; then
|
||||||
echo " - Already exists"
|
echo " - Already exists"
|
||||||
check_binary && return
|
check_binary && return
|
||||||
fi
|
fi
|
||||||
link_fzf_in_path && return
|
link_fzf_in_path && return
|
||||||
fi
|
|
||||||
mkdir -p "$fzf_base"/bin && cd "$fzf_base"/bin
|
mkdir -p "$fzf_base"/bin && cd "$fzf_base"/bin
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
binary_error="Failed to create bin directory"
|
binary_error="Failed to create bin directory"
|
||||||
@@ -149,9 +146,7 @@ download() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local url
|
local url
|
||||||
[[ "$version" =~ alpha ]] &&
|
url=https://github.com/junegunn/fzf/releases/download/$version/${1}
|
||||||
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
|
set -o pipefail
|
||||||
if ! (try_curl $url || try_wget $url); then
|
if ! (try_curl $url || try_wget $url); then
|
||||||
set +o pipefail
|
set +o pipefail
|
||||||
@@ -173,19 +168,19 @@ archi=$(uname -sm)
|
|||||||
binary_available=1
|
binary_available=1
|
||||||
binary_error=""
|
binary_error=""
|
||||||
case "$archi" in
|
case "$archi" in
|
||||||
Darwin\ *64) download fzf-$version-darwin_${binary_arch:-amd64}.tgz ;;
|
Darwin\ *64) download fzf-$version-darwin_amd64.tar.gz ;;
|
||||||
Linux\ armv5*) download fzf-$version-linux_${binary_arch:-arm5}.tgz ;;
|
Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;;
|
||||||
Linux\ armv6*) download fzf-$version-linux_${binary_arch:-arm6}.tgz ;;
|
Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;;
|
||||||
Linux\ armv7*) download fzf-$version-linux_${binary_arch:-arm7}.tgz ;;
|
Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;;
|
||||||
Linux\ armv8*) download fzf-$version-linux_${binary_arch:-arm8}.tgz ;;
|
Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;;
|
||||||
Linux\ aarch64*) download fzf-$version-linux_${binary_arch:-arm8}.tgz ;;
|
Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;;
|
||||||
Linux\ *64) download fzf-$version-linux_${binary_arch:-amd64}.tgz ;;
|
Linux\ *64) download fzf-$version-linux_amd64.tar.gz ;;
|
||||||
FreeBSD\ *64) download fzf-$version-freebsd_${binary_arch:-amd64}.tgz ;;
|
FreeBSD\ *64) download fzf-$version-freebsd_amd64.tar.gz ;;
|
||||||
OpenBSD\ *64) download fzf-$version-openbsd_${binary_arch:-amd64}.tgz ;;
|
OpenBSD\ *64) download fzf-$version-openbsd_amd64.tar.gz ;;
|
||||||
CYGWIN*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
CYGWIN*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||||
MINGW*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
MINGW*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||||
MSYS*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
MSYS*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||||
Windows*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
Windows*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||||
*) binary_available=0 binary_error=1 ;;
|
*) binary_available=0 binary_error=1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -202,7 +197,7 @@ if [ -n "$binary_error" ]; then
|
|||||||
export GOPATH="${TMPDIR:-/tmp}/fzf-gopath"
|
export GOPATH="${TMPDIR:-/tmp}/fzf-gopath"
|
||||||
mkdir -p "$GOPATH"
|
mkdir -p "$GOPATH"
|
||||||
fi
|
fi
|
||||||
if go get -u github.com/junegunn/fzf; then
|
if go get -ldflags "-s -w -X main.version=$version -X main.revision=go-get" github.com/junegunn/fzf; then
|
||||||
echo "OK"
|
echo "OK"
|
||||||
cp "$GOPATH/bin/fzf" "$fzf_base/bin/"
|
cp "$GOPATH/bin/fzf" "$fzf_base/bin/"
|
||||||
else
|
else
|
||||||
|
18
install.ps1
18
install.ps1
@@ -1,10 +1,4 @@
|
|||||||
$version="0.23.0"
|
$version="0.24.2"
|
||||||
|
|
||||||
if ([Environment]::Is64BitProcess) {
|
|
||||||
$binary_arch="amd64"
|
|
||||||
} else {
|
|
||||||
$binary_arch="386"
|
|
||||||
}
|
|
||||||
|
|
||||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
@@ -32,14 +26,12 @@ function check_binary () {
|
|||||||
function download {
|
function download {
|
||||||
param($file)
|
param($file)
|
||||||
Write-Host "Downloading bin/fzf ..."
|
Write-Host "Downloading bin/fzf ..."
|
||||||
if ("$version" -ne "alpha") {
|
|
||||||
if (Test-Path "$fzf_base\bin\fzf.exe") {
|
if (Test-Path "$fzf_base\bin\fzf.exe") {
|
||||||
Write-Host " - Already exists"
|
Write-Host " - Already exists"
|
||||||
if (check_binary) {
|
if (check_binary) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (-not (Test-Path "$fzf_base\bin")) {
|
if (-not (Test-Path "$fzf_base\bin")) {
|
||||||
md "$fzf_base\bin"
|
md "$fzf_base\bin"
|
||||||
}
|
}
|
||||||
@@ -48,11 +40,7 @@ function download {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
cd "$fzf_base\bin"
|
cd "$fzf_base\bin"
|
||||||
if ("$version" -eq "alpha") {
|
$url="https://github.com/junegunn/fzf/releases/download/$version/$file"
|
||||||
$url="https://github.com/junegunn/fzf-bin/releases/download/alpha/$file"
|
|
||||||
} else {
|
|
||||||
$url="https://github.com/junegunn/fzf-bin/releases/download/$version/$file"
|
|
||||||
}
|
|
||||||
$temp=$env:TMP + "\fzf.zip"
|
$temp=$env:TMP + "\fzf.zip"
|
||||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
(New-Object Net.WebClient).DownloadFile($url, $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$temp"))
|
(New-Object Net.WebClient).DownloadFile($url, $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$temp"))
|
||||||
@@ -68,6 +56,6 @@ function download {
|
|||||||
check_binary >$null
|
check_binary >$null
|
||||||
}
|
}
|
||||||
|
|
||||||
download "fzf-$version-windows_$binary_arch.zip"
|
download "fzf-$version-windows_amd64.zip"
|
||||||
|
|
||||||
Write-Host 'For more information, see: https://github.com/junegunn/fzf'
|
Write-Host 'For more information, see: https://github.com/junegunn/fzf'
|
||||||
|
5
main.go
5
main.go
@@ -5,9 +5,10 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/protector"
|
"github.com/junegunn/fzf/src/protector"
|
||||||
)
|
)
|
||||||
|
|
||||||
var revision string
|
var version string = "0.24"
|
||||||
|
var revision string = "devel"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
protector.Protect()
|
protector.Protect()
|
||||||
fzf.Run(fzf.ParseOptions(), revision)
|
fzf.Run(fzf.ParseOptions(), version, 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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
..
|
..
|
||||||
.TH fzf-tmux 1 "Oct 2020" "fzf 0.23.0" "fzf-tmux - open fzf in tmux split pane"
|
.TH fzf-tmux 1 "Nov 2020" "fzf 0.24.2" "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
|
||||||
|
@@ -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 "Oct 2020" "fzf 0.23.0" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Nov 2020" "fzf 0.24.2" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@@ -192,6 +192,16 @@ Draw border around the finder
|
|||||||
.br
|
.br
|
||||||
.BR horizontal " Horizontal lines above and below the finder"
|
.BR horizontal " Horizontal lines above and below the finder"
|
||||||
.br
|
.br
|
||||||
|
.BR vertical " Vertical lines on each side of the finder"
|
||||||
|
.br
|
||||||
|
.BR top
|
||||||
|
.br
|
||||||
|
.BR bottom
|
||||||
|
.br
|
||||||
|
.BR left
|
||||||
|
.br
|
||||||
|
.BR right
|
||||||
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B "--no-unicode"
|
.B "--no-unicode"
|
||||||
@@ -267,11 +277,9 @@ Enable processing of ANSI color codes
|
|||||||
.BI "--tabstop=" SPACES
|
.BI "--tabstop=" SPACES
|
||||||
Number of spaces for a tab character (default: 8)
|
Number of spaces for a tab character (default: 8)
|
||||||
.TP
|
.TP
|
||||||
.BI "--color=" "[BASE_SCHEME][,COLOR:ANSI]"
|
.BI "--color=" "[BASE_SCHEME][,COLOR_NAME[:ANSI_COLOR][:ANSI_ATTRIBUTES]]..."
|
||||||
Color configuration. The name of the base color scheme is followed by custom
|
Color configuration. The name of the base color scheme is followed by custom
|
||||||
color mappings. Ansi color code of -1 denotes terminal default
|
color mappings.
|
||||||
foreground/background color. You can also specify 24-bit color in \fB#rrggbb\fR
|
|
||||||
format.
|
|
||||||
|
|
||||||
.RS
|
.RS
|
||||||
.B BASE SCHEME:
|
.B BASE SCHEME:
|
||||||
@@ -282,7 +290,7 @@ format.
|
|||||||
\fB16 \fRColor scheme for 16-color terminal
|
\fB16 \fRColor scheme for 16-color terminal
|
||||||
\fBbw \fRNo colors (equivalent to \fB--no-color\fR)
|
\fBbw \fRNo colors (equivalent to \fB--no-color\fR)
|
||||||
|
|
||||||
.B COLOR:
|
.B COLOR NAMES:
|
||||||
\fBfg \fRText
|
\fBfg \fRText
|
||||||
\fBbg \fRBackground
|
\fBbg \fRBackground
|
||||||
\fBpreview-fg \fRPreview window text
|
\fBpreview-fg \fRPreview window text
|
||||||
@@ -300,6 +308,21 @@ format.
|
|||||||
\fBspinner \fRStreaming input indicator
|
\fBspinner \fRStreaming input indicator
|
||||||
\fBheader \fRHeader
|
\fBheader \fRHeader
|
||||||
|
|
||||||
|
.B ANSI COLORS:
|
||||||
|
\fB-1 \fRDefault terminal foreground/background color
|
||||||
|
\fB \fR(or the original color of the text)
|
||||||
|
\fB0 ~ 15 \fR16 base colors
|
||||||
|
\fB16 ~ 255 \fRANSI 256 colors
|
||||||
|
\fB#rrggbb \fR24-bit colors
|
||||||
|
|
||||||
|
.B ANSI ATTRIBUTES: (Only applies to foreground colors)
|
||||||
|
\fBregular \fRClears previously set attributes; should precede the other ones
|
||||||
|
\fBbold\fR
|
||||||
|
\fBunderline\fR
|
||||||
|
\fBreverse\fR
|
||||||
|
\fBdim\fR
|
||||||
|
\fBitalic\fR
|
||||||
|
|
||||||
.B EXAMPLES:
|
.B EXAMPLES:
|
||||||
|
|
||||||
\fB# Seoul256 theme with 8-bit colors
|
\fB# Seoul256 theme with 8-bit colors
|
||||||
@@ -379,13 +402,34 @@ Note that you can escape a placeholder pattern by prepending a backslash.
|
|||||||
|
|
||||||
Preview window will be updated even when there is no match for the current
|
Preview window will be updated even when there is no match for the current
|
||||||
query if any of the placeholder expressions evaluates to a non-empty string.
|
query if any of the placeholder expressions evaluates to a non-empty string.
|
||||||
|
|
||||||
|
Since 0.24.0, fzf can render partial preview content before the preview command
|
||||||
|
completes. ANSI escape sequence for clearing the display (\fBCSI 2 J\fR) is
|
||||||
|
supported, so you can use it to implement preview window that is constantly
|
||||||
|
updating.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
\fBfzf --preview 'for i in $(seq 100000); do
|
||||||
|
(( i % 200 == 0 )) && printf "\\033[2J"
|
||||||
|
echo "$i"
|
||||||
|
sleep 0.01
|
||||||
|
done'\fR
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.BI "--preview-window=" "[POSITION][:SIZE[%]][:rounded|sharp|noborder][:wrap][:hidden][:+SCROLL[-OFFSET]]"
|
.BI "--preview-window=" "[POSITION][:SIZE[%]][:rounded|sharp|noborder][:[no]wrap][:[no]cycle][:[no]hidden][:+SCROLL[-OFFSET]][:default]"
|
||||||
Determines the layout of the preview window. If the argument contains
|
|
||||||
|
.RS
|
||||||
|
.B POSITION: (default: right)
|
||||||
|
\fBup
|
||||||
|
\fBdown
|
||||||
|
\fBleft
|
||||||
|
\fBright
|
||||||
|
|
||||||
|
\fRDetermines the layout of the preview window. If the argument contains
|
||||||
\fB:hidden\fR, the preview window will be hidden by default until
|
\fB:hidden\fR, the preview window will be hidden by default until
|
||||||
\fBtoggle-preview\fR action is triggered. Long lines are truncated by default.
|
\fBtoggle-preview\fR action is triggered. Long lines are truncated by default.
|
||||||
Line wrap can be enabled with \fB:wrap\fR flag.
|
Line wrap can be enabled with \fB:wrap\fR flag. Cyclic scrolling is enabled
|
||||||
|
with \fB:cycle\fR flag.
|
||||||
|
|
||||||
If size is given as 0, preview window will not be visible, but fzf will still
|
If size is given as 0, preview window will not be visible, but fzf will still
|
||||||
execute the command in the background.
|
execute the command in the background.
|
||||||
@@ -401,13 +445,7 @@ for adjusting the base offset so that you can see the text above it. It should
|
|||||||
be given as a numeric integer (\fB-INTEGER\fR), or as a denominator form
|
be given as a numeric integer (\fB-INTEGER\fR), or as a denominator form
|
||||||
(\fB-/INTEGER\fR) for specifying a fraction of the preview window height.
|
(\fB-/INTEGER\fR) for specifying a fraction of the preview window height.
|
||||||
|
|
||||||
.RS
|
\fBdefault\fR resets all options previously set to the default.
|
||||||
.B POSITION: (default: right)
|
|
||||||
\fBup
|
|
||||||
\fBdown
|
|
||||||
\fBleft
|
|
||||||
\fBright
|
|
||||||
.RE
|
|
||||||
|
|
||||||
.RS
|
.RS
|
||||||
e.g.
|
e.g.
|
||||||
@@ -427,6 +465,7 @@ e.g.
|
|||||||
--preview-window +{2}-/2\fR
|
--preview-window +{2}-/2\fR
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.SS Scripting
|
.SS Scripting
|
||||||
.TP
|
.TP
|
||||||
.BI "-q, --query=" "STR"
|
.BI "-q, --query=" "STR"
|
||||||
|
121
plugin/fzf.vim
121
plugin/fzf.vim
@@ -128,7 +128,7 @@ endfunction
|
|||||||
|
|
||||||
function! s:default_layout()
|
function! s:default_layout()
|
||||||
return s:popup_support()
|
return s:popup_support()
|
||||||
\ ? { 'window' : { 'width': 0.9, 'height': 0.6, 'highlight': 'Normal' } }
|
\ ? { 'window' : { 'width': 0.9, 'height': 0.6 } }
|
||||||
\ : { 'down': '~40%' }
|
\ : { 'down': '~40%' }
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@@ -154,7 +154,20 @@ function! fzf#install()
|
|||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! fzf#exec()
|
function! s:version_requirement(val, min)
|
||||||
|
let val = split(a:val, '\.')
|
||||||
|
let min = split(a:min, '\.')
|
||||||
|
for idx in range(0, len(min) - 1)
|
||||||
|
let v = get(val, idx, 0)
|
||||||
|
if v < min[idx] | return 0
|
||||||
|
elseif v > min[idx] | return 1
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
return 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:checked = {}
|
||||||
|
function! fzf#exec(...)
|
||||||
if !exists('s:exec')
|
if !exists('s:exec')
|
||||||
if executable(s:fzf_go)
|
if executable(s:fzf_go)
|
||||||
let s:exec = s:fzf_go
|
let s:exec = s:fzf_go
|
||||||
@@ -169,6 +182,26 @@ function! fzf#exec()
|
|||||||
throw 'fzf executable not found'
|
throw 'fzf executable not found'
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if a:0 && !has_key(s:checked, a:1)
|
||||||
|
let command = s:exec . ' --version'
|
||||||
|
let output = systemlist(command)
|
||||||
|
if v:shell_error || empty(output)
|
||||||
|
throw printf('Failed to run "%s": %s', command, output)
|
||||||
|
endif
|
||||||
|
let fzf_version = matchstr(output[-1], '[0-9.]\+')
|
||||||
|
if s:version_requirement(fzf_version, a:1)
|
||||||
|
let s:checked[a:1] = 1
|
||||||
|
return s:exec
|
||||||
|
elseif a:0 < 2 && input(printf('You need fzf %s or above. Found: %s. Download binary? (y/n) ', a:1, fzf_version)) =~? '^y'
|
||||||
|
redraw
|
||||||
|
call fzf#install()
|
||||||
|
return fzf#exec(a:1, 1)
|
||||||
|
else
|
||||||
|
throw printf('You need to upgrade fzf (required: %s or above)', a:1)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
return s:exec
|
return s:exec
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@@ -404,7 +437,7 @@ try
|
|||||||
let prefix = '( '.source.' )|'
|
let prefix = '( '.source.' )|'
|
||||||
elseif type == 3
|
elseif type == 3
|
||||||
let temps.input = s:fzf_tempname()
|
let temps.input = s:fzf_tempname()
|
||||||
call writefile(map(source, '<SID>enc_to_cp(v:val)'), temps.input)
|
call writefile(source, temps.input)
|
||||||
let prefix = (s:is_win ? 'type ' : 'cat ').fzf#shellescape(temps.input).'|'
|
let prefix = (s:is_win ? 'type ' : 'cat ').fzf#shellescape(temps.input).'|'
|
||||||
else
|
else
|
||||||
throw 'Invalid source type'
|
throw 'Invalid source type'
|
||||||
@@ -432,6 +465,7 @@ try
|
|||||||
elseif use_term
|
elseif use_term
|
||||||
let optstr .= ' --no-height'
|
let optstr .= ' --no-height'
|
||||||
endif
|
endif
|
||||||
|
let optstr .= s:border_opt(get(dict, 'window', 0))
|
||||||
let command = prefix.(use_tmux ? s:fzf_tmux(dict) : fzf_exec).' '.optstr.' > '.temps.result
|
let command = prefix.(use_tmux ? s:fzf_tmux(dict) : fzf_exec).' '.optstr.' > '.temps.result
|
||||||
|
|
||||||
if use_term
|
if use_term
|
||||||
@@ -656,6 +690,32 @@ function! s:getpos()
|
|||||||
return {'tab': tabpagenr(), 'win': winnr(), 'winid': win_getid(), 'cnt': winnr('$'), 'tcnt': tabpagenr('$')}
|
return {'tab': tabpagenr(), 'win': winnr(), 'winid': win_getid(), 'cnt': winnr('$'), 'tcnt': tabpagenr('$')}
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:border_opt(window)
|
||||||
|
if type(a:window) != type({})
|
||||||
|
return ''
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Border style
|
||||||
|
let style = tolower(get(a:window, 'border', 'rounded'))
|
||||||
|
if !has_key(a:window, 'border') && !get(a:window, 'rounded', 1)
|
||||||
|
let style = 'sharp'
|
||||||
|
endif
|
||||||
|
if style == 'none' || style == 'no'
|
||||||
|
return ''
|
||||||
|
endif
|
||||||
|
|
||||||
|
" For --border styles, we need fzf 0.24.0 or above
|
||||||
|
call fzf#exec('0.24.0')
|
||||||
|
let opt = ' --border=' . style
|
||||||
|
if has_key(a:window, 'highlight')
|
||||||
|
let color = s:get_color('fg', a:window.highlight)
|
||||||
|
if len(color)
|
||||||
|
let opt .= ' --color=border:' . color
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
return opt
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! s:split(dict)
|
function! s:split(dict)
|
||||||
let directions = {
|
let directions = {
|
||||||
\ 'up': ['topleft', 'resize', &lines],
|
\ 'up': ['topleft', 'resize', &lines],
|
||||||
@@ -848,33 +908,22 @@ if has('nvim')
|
|||||||
endfunction
|
endfunction
|
||||||
else
|
else
|
||||||
function! s:create_popup(hl, opts) abort
|
function! s:create_popup(hl, opts) abort
|
||||||
let is_frame = has_key(a:opts, 'border')
|
|
||||||
let s:popup_create = {buf -> popup_create(buf, #{
|
let s:popup_create = {buf -> popup_create(buf, #{
|
||||||
\ line: a:opts.row,
|
\ line: a:opts.row,
|
||||||
\ col: a:opts.col,
|
\ col: a:opts.col,
|
||||||
\ minwidth: a:opts.width,
|
\ minwidth: a:opts.width,
|
||||||
|
\ maxwidth: a:opts.width,
|
||||||
\ minheight: a:opts.height,
|
\ minheight: a:opts.height,
|
||||||
\ zindex: 50 - is_frame,
|
\ maxheight: a:opts.height,
|
||||||
|
\ zindex: 1000,
|
||||||
\ })}
|
\ })}
|
||||||
if is_frame
|
|
||||||
let id = s:popup_create('')
|
|
||||||
call setwinvar(id, '&wincolor', a:hl)
|
|
||||||
call setbufline(winbufnr(id), 1, a:opts.border)
|
|
||||||
execute 'autocmd BufWipeout * ++once call popup_close('..id..')'
|
|
||||||
return winbufnr(id)
|
|
||||||
else
|
|
||||||
autocmd TerminalOpen * ++once call s:popup_create(str2nr(expand('<abuf>')))
|
autocmd TerminalOpen * ++once call s:popup_create(str2nr(expand('<abuf>')))
|
||||||
endif
|
|
||||||
endfunction
|
endfunction
|
||||||
endif
|
endif
|
||||||
|
|
||||||
function! s:popup(opts) abort
|
function! s:popup(opts) abort
|
||||||
" Support ambiwidth == 'double'
|
|
||||||
let ambidouble = &ambiwidth == 'double' ? 2 : 1
|
|
||||||
|
|
||||||
" Size and position
|
" Size and position
|
||||||
let width = min([max([8, a:opts.width > 1 ? a:opts.width : float2nr(&columns * a:opts.width)]), &columns])
|
let width = min([max([8, a:opts.width > 1 ? a:opts.width : float2nr(&columns * a:opts.width)]), &columns])
|
||||||
let width += width % ambidouble
|
|
||||||
let height = min([max([4, a:opts.height > 1 ? a:opts.height : float2nr(&lines * a:opts.height)]), &lines - has('nvim')])
|
let height = min([max([4, a:opts.height > 1 ? a:opts.height : float2nr(&lines * a:opts.height)]), &lines - has('nvim')])
|
||||||
let row = float2nr(get(a:opts, 'yoffset', 0.5) * (&lines - height))
|
let row = float2nr(get(a:opts, 'yoffset', 0.5) * (&lines - height))
|
||||||
let col = float2nr(get(a:opts, 'xoffset', 0.5) * (&columns - width))
|
let col = float2nr(get(a:opts, 'xoffset', 0.5) * (&columns - width))
|
||||||
@@ -885,45 +934,9 @@ function! s:popup(opts) abort
|
|||||||
let row += !has('nvim')
|
let row += !has('nvim')
|
||||||
let col += !has('nvim')
|
let col += !has('nvim')
|
||||||
|
|
||||||
" Border style
|
|
||||||
let style = tolower(get(a:opts, 'border', 'rounded'))
|
|
||||||
if !has_key(a:opts, 'border') && !get(a:opts, 'rounded', 1)
|
|
||||||
let style = 'sharp'
|
|
||||||
endif
|
|
||||||
|
|
||||||
if style =~ 'vertical\|left\|right'
|
|
||||||
let mid = style == 'vertical' ? '│' .. repeat(' ', width - 2 * ambidouble) .. '│' :
|
|
||||||
\ style == 'left' ? '│' .. repeat(' ', width - 1 * ambidouble)
|
|
||||||
\ : repeat(' ', width - 1 * ambidouble) .. '│'
|
|
||||||
let border = repeat([mid], height)
|
|
||||||
let shift = { 'row': 0, 'col': style == 'right' ? 0 : 2, 'width': style == 'vertical' ? -4 : -2, 'height': 0 }
|
|
||||||
elseif style =~ 'horizontal\|top\|bottom'
|
|
||||||
let hor = repeat('─', width / ambidouble)
|
|
||||||
let mid = repeat(' ', width)
|
|
||||||
let border = style == 'horizontal' ? [hor] + repeat([mid], height - 2) + [hor] :
|
|
||||||
\ style == 'top' ? [hor] + repeat([mid], height - 1)
|
|
||||||
\ : repeat([mid], height - 1) + [hor]
|
|
||||||
let shift = { 'row': style == 'bottom' ? 0 : 1, 'col': 0, 'width': 0, 'height': style == 'horizontal' ? -2 : -1 }
|
|
||||||
else
|
|
||||||
let edges = style == 'sharp' ? ['┌', '┐', '└', '┘'] : ['╭', '╮', '╰', '╯']
|
|
||||||
let bar = repeat('─', width / ambidouble - 2)
|
|
||||||
let top = edges[0] .. bar .. edges[1]
|
|
||||||
let mid = '│' .. repeat(' ', width - 2 * ambidouble) .. '│'
|
|
||||||
let bot = edges[2] .. bar .. edges[3]
|
|
||||||
let border = [top] + repeat([mid], height - 2) + [bot]
|
|
||||||
let shift = { 'row': 1, 'col': 2, 'width': -4, 'height': -2 }
|
|
||||||
endif
|
|
||||||
|
|
||||||
let highlight = get(a:opts, 'highlight', 'Comment')
|
|
||||||
let frame = s:create_popup(highlight, {
|
|
||||||
\ 'row': row, 'col': col, 'width': width, 'height': height, 'border': border
|
|
||||||
\ })
|
|
||||||
call s:create_popup('Normal', {
|
call s:create_popup('Normal', {
|
||||||
\ 'row': row + shift.row, 'col': col + shift.col, 'width': width + shift.width, 'height': height + shift.height
|
\ 'row': row, 'col': col, 'width': width, 'height': height
|
||||||
\ })
|
\ })
|
||||||
if has('nvim')
|
|
||||||
execute 'autocmd BufWipeout <buffer> bwipeout '..frame
|
|
||||||
endif
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
let s:default_action = {
|
let s:default_action = {
|
||||||
|
@@ -127,12 +127,12 @@ function fzf_key_bindings
|
|||||||
else
|
else
|
||||||
set dir (__fzf_get_dir $commandline)
|
set dir (__fzf_get_dir $commandline)
|
||||||
|
|
||||||
if [ "$dir" = "." -a (string sub -l 1 $commandline) != '.' ]
|
if [ "$dir" = "." -a (string sub -l 1 -- $commandline) != '.' ]
|
||||||
# if $dir is "." but commandline is not a relative path, this means no file path found
|
# if $dir is "." but commandline is not a relative path, this means no file path found
|
||||||
set fzf_query $commandline
|
set fzf_query $commandline
|
||||||
else
|
else
|
||||||
# Also remove trailing slash after dir, to "split" input properly
|
# Also remove trailing slash after dir, to "split" input properly
|
||||||
set fzf_query (string replace -r "^$dir/?" '' "$commandline")
|
set fzf_query (string replace -r "^$dir/?" -- '' "$commandline")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -144,15 +144,15 @@ function fzf_key_bindings
|
|||||||
set dir $argv
|
set dir $argv
|
||||||
|
|
||||||
# Strip all trailing slashes. Ignore if $dir is root dir (/)
|
# Strip all trailing slashes. Ignore if $dir is root dir (/)
|
||||||
if [ (string length $dir) -gt 1 ]
|
if [ (string length -- $dir) -gt 1 ]
|
||||||
set dir (string replace -r '/*$' '' $dir)
|
set dir (string replace -r '/*$' -- '' $dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Iteratively check if dir exists and strip tail end of path
|
# Iteratively check if dir exists and strip tail end of path
|
||||||
while [ ! -d "$dir" ]
|
while [ ! -d "$dir" ]
|
||||||
# If path is absolute, this can keep going until ends up at /
|
# If path is absolute, this can keep going until ends up at /
|
||||||
# If path is relative, this can keep going until entire input is consumed, dirname returns "."
|
# If path is relative, this can keep going until entire input is consumed, dirname returns "."
|
||||||
set dir (dirname "$dir")
|
set dir (dirname -- "$dir")
|
||||||
end
|
end
|
||||||
|
|
||||||
echo $dir
|
echo $dir
|
||||||
|
@@ -9,9 +9,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Current version
|
|
||||||
version = "0.23.0"
|
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
||||||
coordinatorDelayStep time.Duration = 10 * time.Millisecond
|
coordinatorDelayStep time.Duration = 10 * time.Millisecond
|
||||||
@@ -27,6 +24,8 @@ const (
|
|||||||
initialDelayTac = 100 * time.Millisecond
|
initialDelayTac = 100 * time.Millisecond
|
||||||
spinnerDuration = 100 * time.Millisecond
|
spinnerDuration = 100 * time.Millisecond
|
||||||
previewCancelWait = 500 * time.Millisecond
|
previewCancelWait = 500 * time.Millisecond
|
||||||
|
previewChunkDelay = 100 * time.Millisecond
|
||||||
|
previewDelayed = 500 * time.Millisecond
|
||||||
maxPatternLength = 300
|
maxPatternLength = 300
|
||||||
maxMulti = math.MaxInt32
|
maxMulti = math.MaxInt32
|
||||||
|
|
||||||
|
@@ -43,7 +43,7 @@ Matcher -> EvtHeader -> Terminal (update header)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Run starts fzf
|
// Run starts fzf
|
||||||
func Run(opts *Options, revision string) {
|
func Run(opts *Options, version string, revision string) {
|
||||||
sort := opts.Sort > 0
|
sort := opts.Sort > 0
|
||||||
sortCriteria = opts.Criteria
|
sortCriteria = opts.Criteria
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ func Run(opts *Options, revision string) {
|
|||||||
|
|
||||||
var lineAnsiState, prevLineAnsiState *ansiState
|
var lineAnsiState, prevLineAnsiState *ansiState
|
||||||
if opts.Ansi {
|
if opts.Ansi {
|
||||||
if opts.Theme != nil {
|
if opts.Theme.Colored {
|
||||||
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
||||||
prevLineAnsiState = lineAnsiState
|
prevLineAnsiState = lineAnsiState
|
||||||
trimmed, offsets, newState := extractColor(string(data), lineAnsiState, nil)
|
trimmed, offsets, newState := extractColor(string(data), lineAnsiState, nil)
|
||||||
@@ -102,7 +102,7 @@ func Run(opts *Options, revision string) {
|
|||||||
} else {
|
} else {
|
||||||
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
||||||
tokens := Tokenize(string(data), opts.Delimiter)
|
tokens := Tokenize(string(data), opts.Delimiter)
|
||||||
if opts.Ansi && opts.Theme != nil && len(tokens) > 1 {
|
if opts.Ansi && opts.Theme.Colored && len(tokens) > 1 {
|
||||||
var ansiState *ansiState
|
var ansiState *ansiState
|
||||||
if prevLineAnsiState != nil {
|
if prevLineAnsiState != nil {
|
||||||
ansiStateDup := *prevLineAnsiState
|
ansiStateDup := *prevLineAnsiState
|
||||||
|
133
src/options.go
133
src/options.go
@@ -58,7 +58,8 @@ const usage = `usage: fzf [options]
|
|||||||
(default: 10)
|
(default: 10)
|
||||||
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
||||||
--border[=STYLE] Draw border around the finder
|
--border[=STYLE] Draw border around the finder
|
||||||
[rounded|sharp|horizontal] (default: rounded)
|
[rounded|sharp|horizontal|vertical|
|
||||||
|
top|bottom|left|right] (default: rounded)
|
||||||
--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)
|
||||||
--info=STYLE Finder info style [default|inline|hidden]
|
--info=STYLE Finder info style [default|inline|hidden]
|
||||||
--prompt=STR Input prompt (default: '> ')
|
--prompt=STR Input prompt (default: '> ')
|
||||||
@@ -80,9 +81,11 @@ const usage = `usage: fzf [options]
|
|||||||
Preview
|
Preview
|
||||||
--preview=COMMAND Command to preview highlighted line ({})
|
--preview=COMMAND Command to preview highlighted line ({})
|
||||||
--preview-window=OPT Preview window layout (default: right:50%)
|
--preview-window=OPT Preview window layout (default: right:50%)
|
||||||
[up|down|left|right][:SIZE[%]][:wrap][:cycle][:hidden]
|
[up|down|left|right][:SIZE[%]]
|
||||||
[:+SCROLL[-OFFSET]]
|
[:[no]wrap][:[no]cycle][:[no]hidden]
|
||||||
[:rounded|sharp|noborder]
|
[:rounded|sharp|noborder]
|
||||||
|
[:+SCROLL[-OFFSET]]
|
||||||
|
[:default]
|
||||||
|
|
||||||
Scripting
|
Scripting
|
||||||
-q, --query=STR Start the finder with the given query
|
-q, --query=STR Start the finder with the given query
|
||||||
@@ -225,6 +228,10 @@ type Options struct {
|
|||||||
Version bool
|
Version bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func defaultPreviewOpts(command string) previewOpts {
|
||||||
|
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, tui.BorderRounded}
|
||||||
|
}
|
||||||
|
|
||||||
func defaultOptions() *Options {
|
func defaultOptions() *Options {
|
||||||
return &Options{
|
return &Options{
|
||||||
Fuzzy: true,
|
Fuzzy: true,
|
||||||
@@ -264,7 +271,7 @@ func defaultOptions() *Options {
|
|||||||
ToggleSort: false,
|
ToggleSort: false,
|
||||||
Expect: make(map[int]string),
|
Expect: make(map[int]string),
|
||||||
Keymap: make(map[int][]action),
|
Keymap: make(map[int][]action),
|
||||||
Preview: previewOpts{"", posRight, sizeSpec{50, true}, "", false, false, false, tui.BorderRounded},
|
Preview: defaultPreviewOpts(""),
|
||||||
PrintQuery: false,
|
PrintQuery: false,
|
||||||
ReadZero: false,
|
ReadZero: false,
|
||||||
Printer: func(str string) { fmt.Println(str) },
|
Printer: func(str string) { fmt.Println(str) },
|
||||||
@@ -415,11 +422,21 @@ func parseBorder(str string, optional bool) tui.BorderShape {
|
|||||||
return tui.BorderSharp
|
return tui.BorderSharp
|
||||||
case "horizontal":
|
case "horizontal":
|
||||||
return tui.BorderHorizontal
|
return tui.BorderHorizontal
|
||||||
|
case "vertical":
|
||||||
|
return tui.BorderVertical
|
||||||
|
case "top":
|
||||||
|
return tui.BorderTop
|
||||||
|
case "bottom":
|
||||||
|
return tui.BorderBottom
|
||||||
|
case "left":
|
||||||
|
return tui.BorderLeft
|
||||||
|
case "right":
|
||||||
|
return tui.BorderRight
|
||||||
default:
|
default:
|
||||||
if optional && str == "" {
|
if optional && str == "" {
|
||||||
return tui.BorderRounded
|
return tui.BorderRounded
|
||||||
}
|
}
|
||||||
errorExit("invalid border style (expected: rounded|sharp|horizontal)")
|
errorExit("invalid border style (expected: rounded|sharp|horizontal|vertical|top|bottom|left|right)")
|
||||||
}
|
}
|
||||||
return tui.BorderNone
|
return tui.BorderNone
|
||||||
}
|
}
|
||||||
@@ -584,12 +601,9 @@ func parseTiebreak(str string) []criterion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
|
func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
|
||||||
if theme != nil {
|
|
||||||
dupe := *theme
|
dupe := *theme
|
||||||
return &dupe
|
return &dupe
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
||||||
theme := dupeTheme(defaultTheme)
|
theme := dupeTheme(defaultTheme)
|
||||||
@@ -603,7 +617,7 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
|||||||
case "16":
|
case "16":
|
||||||
theme = dupeTheme(tui.Default16)
|
theme = dupeTheme(tui.Default16)
|
||||||
case "bw", "no":
|
case "bw", "no":
|
||||||
theme = nil
|
theme = tui.NoColorTheme()
|
||||||
default:
|
default:
|
||||||
fail := func() {
|
fail := func() {
|
||||||
errorExit("invalid color specification: " + str)
|
errorExit("invalid color specification: " + str)
|
||||||
@@ -613,54 +627,76 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pair := strings.Split(str, ":")
|
components := strings.Split(str, ":")
|
||||||
if len(pair) != 2 {
|
if len(components) < 2 {
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
var ansi tui.Color
|
cattr := tui.NewColorAttr()
|
||||||
if rrggbb.MatchString(pair[1]) {
|
for _, component := range components[1:] {
|
||||||
ansi = tui.HexToColor(pair[1])
|
switch component {
|
||||||
|
case "regular":
|
||||||
|
cattr.Attr = tui.AttrRegular
|
||||||
|
case "bold", "strong":
|
||||||
|
cattr.Attr |= tui.Bold
|
||||||
|
case "dim":
|
||||||
|
cattr.Attr |= tui.Dim
|
||||||
|
case "italic":
|
||||||
|
cattr.Attr |= tui.Italic
|
||||||
|
case "underline":
|
||||||
|
cattr.Attr |= tui.Underline
|
||||||
|
case "blink":
|
||||||
|
cattr.Attr |= tui.Blink
|
||||||
|
case "reverse":
|
||||||
|
cattr.Attr |= tui.Reverse
|
||||||
|
case "":
|
||||||
|
default:
|
||||||
|
if rrggbb.MatchString(component) {
|
||||||
|
cattr.Color = tui.HexToColor(component)
|
||||||
} else {
|
} else {
|
||||||
ansi32, err := strconv.Atoi(pair[1])
|
ansi32, err := strconv.Atoi(component)
|
||||||
if err != nil || ansi32 < -1 || ansi32 > 255 {
|
if err != nil || ansi32 < -1 || ansi32 > 255 {
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
ansi = tui.Color(ansi32)
|
cattr.Color = tui.Color(ansi32)
|
||||||
}
|
}
|
||||||
switch pair[0] {
|
}
|
||||||
|
}
|
||||||
|
switch components[0] {
|
||||||
|
case "input":
|
||||||
|
theme.Input = cattr
|
||||||
case "fg":
|
case "fg":
|
||||||
theme.Fg = ansi
|
theme.Fg = cattr
|
||||||
case "bg":
|
case "bg":
|
||||||
theme.Bg = ansi
|
theme.Bg = cattr
|
||||||
case "preview-fg":
|
case "preview-fg":
|
||||||
theme.PreviewFg = ansi
|
theme.PreviewFg = cattr
|
||||||
case "preview-bg":
|
case "preview-bg":
|
||||||
theme.PreviewBg = ansi
|
theme.PreviewBg = cattr
|
||||||
case "fg+":
|
case "fg+":
|
||||||
theme.Current = ansi
|
theme.Current = cattr
|
||||||
case "bg+":
|
case "bg+":
|
||||||
theme.DarkBg = ansi
|
theme.DarkBg = cattr
|
||||||
case "gutter":
|
case "gutter":
|
||||||
theme.Gutter = ansi
|
theme.Gutter = cattr
|
||||||
case "hl":
|
case "hl":
|
||||||
theme.Match = ansi
|
theme.Match = cattr
|
||||||
case "hl+":
|
case "hl+":
|
||||||
theme.CurrentMatch = ansi
|
theme.CurrentMatch = cattr
|
||||||
case "border":
|
case "border":
|
||||||
theme.Border = ansi
|
theme.Border = cattr
|
||||||
case "prompt":
|
case "prompt":
|
||||||
theme.Prompt = ansi
|
theme.Prompt = cattr
|
||||||
case "spinner":
|
case "spinner":
|
||||||
theme.Spinner = ansi
|
theme.Spinner = cattr
|
||||||
case "info":
|
case "info":
|
||||||
theme.Info = ansi
|
theme.Info = cattr
|
||||||
case "pointer":
|
case "pointer":
|
||||||
theme.Cursor = ansi
|
theme.Cursor = cattr
|
||||||
case "marker":
|
case "marker":
|
||||||
theme.Selected = ansi
|
theme.Selected = cattr
|
||||||
case "header":
|
case "header":
|
||||||
theme.Header = ansi
|
theme.Header = cattr
|
||||||
default:
|
default:
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
@@ -1000,12 +1036,20 @@ func parsePreviewWindow(opts *previewOpts, input string) {
|
|||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
switch token {
|
switch token {
|
||||||
case "":
|
case "":
|
||||||
|
case "default":
|
||||||
|
*opts = defaultPreviewOpts(opts.command)
|
||||||
case "hidden":
|
case "hidden":
|
||||||
opts.hidden = true
|
opts.hidden = true
|
||||||
|
case "nohidden":
|
||||||
|
opts.hidden = false
|
||||||
case "wrap":
|
case "wrap":
|
||||||
opts.wrap = true
|
opts.wrap = true
|
||||||
|
case "nowrap":
|
||||||
|
opts.wrap = false
|
||||||
case "cycle":
|
case "cycle":
|
||||||
opts.cycle = true
|
opts.cycle = true
|
||||||
|
case "nocycle":
|
||||||
|
opts.cycle = false
|
||||||
case "up", "top":
|
case "up", "top":
|
||||||
opts.position = posUp
|
opts.position = posUp
|
||||||
case "down", "bottom":
|
case "down", "bottom":
|
||||||
@@ -1166,7 +1210,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
case "--no-mouse":
|
case "--no-mouse":
|
||||||
opts.Mouse = false
|
opts.Mouse = false
|
||||||
case "+c", "--no-color":
|
case "+c", "--no-color":
|
||||||
opts.Theme = nil
|
opts.Theme = tui.NoColorTheme()
|
||||||
case "+2", "--no-256":
|
case "+2", "--no-256":
|
||||||
opts.Theme = tui.Default16
|
opts.Theme = tui.Default16
|
||||||
case "--black":
|
case "--black":
|
||||||
@@ -1271,7 +1315,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Preview.command = ""
|
opts.Preview.command = ""
|
||||||
case "--preview-window":
|
case "--preview-window":
|
||||||
parsePreviewWindow(&opts.Preview,
|
parsePreviewWindow(&opts.Preview,
|
||||||
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:rounded|sharp|noborder][:wrap][:cycle][:hidden][:+SCROLL[-OFFSET]]"))
|
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:rounded|sharp|noborder][:wrap][:cycle][:hidden][:+SCROLL[-OFFSET]][:default]"))
|
||||||
case "--height":
|
case "--height":
|
||||||
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
|
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
|
||||||
case "--min-height":
|
case "--min-height":
|
||||||
@@ -1464,6 +1508,25 @@ func postProcessOptions(opts *Options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Bold {
|
||||||
|
theme := opts.Theme
|
||||||
|
boldify := func(c tui.ColorAttr) tui.ColorAttr {
|
||||||
|
dup := c
|
||||||
|
if !theme.Colored {
|
||||||
|
dup.Attr |= tui.Bold
|
||||||
|
} else if (c.Attr & tui.AttrRegular) == 0 {
|
||||||
|
dup.Attr |= tui.Bold
|
||||||
|
}
|
||||||
|
return dup
|
||||||
|
}
|
||||||
|
theme.Current = boldify(theme.Current)
|
||||||
|
theme.CurrentMatch = boldify(theme.CurrentMatch)
|
||||||
|
theme.Prompt = boldify(theme.Prompt)
|
||||||
|
theme.Input = boldify(theme.Input)
|
||||||
|
theme.Cursor = boldify(theme.Cursor)
|
||||||
|
theme.Spinner = boldify(theme.Spinner)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseOptions parses command-line options
|
// ParseOptions parses command-line options
|
||||||
|
@@ -295,7 +295,7 @@ func TestColorSpec(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
customized := parseTheme(theme, "fg:231,bg:232")
|
customized := parseTheme(theme, "fg:231,bg:232")
|
||||||
if customized.Fg != 231 || customized.Bg != 232 {
|
if customized.Fg.Color != 231 || customized.Bg.Color != 232 {
|
||||||
t.Errorf("color not customized")
|
t.Errorf("color not customized")
|
||||||
}
|
}
|
||||||
if *tui.Dark256 == *customized {
|
if *tui.Dark256 == *customized {
|
||||||
@@ -313,18 +313,6 @@ func TestColorSpec(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseNilTheme(t *testing.T) {
|
|
||||||
var theme *tui.ColorTheme
|
|
||||||
newTheme := parseTheme(theme, "prompt:12")
|
|
||||||
if newTheme != nil {
|
|
||||||
t.Errorf("color is disabled. keep it that way.")
|
|
||||||
}
|
|
||||||
newTheme = parseTheme(theme, "prompt:12,dark,prompt:13")
|
|
||||||
if newTheme.Prompt != 13 {
|
|
||||||
t.Errorf("color should now be enabled and customized")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultCtrlNP(t *testing.T) {
|
func TestDefaultCtrlNP(t *testing.T) {
|
||||||
check := func(words []string, key int, expected actionType) {
|
check := func(words []string, key int, expected actionType) {
|
||||||
opts := defaultOptions()
|
opts := defaultOptions()
|
||||||
@@ -387,21 +375,23 @@ func TestPreviewOpts(t *testing.T) {
|
|||||||
opts.Preview.size.size == 50) {
|
opts.Preview.size.size == 50) {
|
||||||
t.Error()
|
t.Error()
|
||||||
}
|
}
|
||||||
opts = optsFor("--preview", "cat {}", "--preview-window=left:15:hidden:wrap")
|
opts = optsFor("--preview", "cat {}", "--preview-window=left:15:hidden:wrap:+{1}-/2")
|
||||||
if !(opts.Preview.command == "cat {}" &&
|
if !(opts.Preview.command == "cat {}" &&
|
||||||
opts.Preview.hidden == true &&
|
opts.Preview.hidden == true &&
|
||||||
opts.Preview.wrap == true &&
|
opts.Preview.wrap == true &&
|
||||||
opts.Preview.position == posLeft &&
|
opts.Preview.position == posLeft &&
|
||||||
|
opts.Preview.scroll == "{1}-/2" &&
|
||||||
opts.Preview.size.percent == false &&
|
opts.Preview.size.percent == false &&
|
||||||
opts.Preview.size.size == 15) {
|
opts.Preview.size.size == 15) {
|
||||||
t.Error(opts.Preview)
|
t.Error(opts.Preview)
|
||||||
}
|
}
|
||||||
opts = optsFor("--preview-window=up:15:wrap:hidden", "--preview-window=down", "--preview-window=cycle")
|
opts = optsFor("--preview-window=up:15:wrap:hidden:+{1}-/2", "--preview-window=down", "--preview-window=cycle")
|
||||||
if !(opts.Preview.command == "" &&
|
if !(opts.Preview.command == "" &&
|
||||||
opts.Preview.hidden == true &&
|
opts.Preview.hidden == true &&
|
||||||
opts.Preview.wrap == true &&
|
opts.Preview.wrap == true &&
|
||||||
opts.Preview.cycle == true &&
|
opts.Preview.cycle == true &&
|
||||||
opts.Preview.position == posDown &&
|
opts.Preview.position == posDown &&
|
||||||
|
opts.Preview.scroll == "{1}-/2" &&
|
||||||
opts.Preview.size.percent == false &&
|
opts.Preview.size.percent == false &&
|
||||||
opts.Preview.size.size == 15) {
|
opts.Preview.size.size == 15) {
|
||||||
t.Error(opts.Preview.size.size)
|
t.Error(opts.Preview.size.size)
|
||||||
@@ -415,6 +405,13 @@ func TestPreviewOpts(t *testing.T) {
|
|||||||
opts.Preview.size.size == 15) {
|
opts.Preview.size.size == 15) {
|
||||||
t.Error(opts.Preview)
|
t.Error(opts.Preview)
|
||||||
}
|
}
|
||||||
|
opts = optsFor("--preview=foo", "--preview-window=up", "--preview-window=default:70%")
|
||||||
|
if !(opts.Preview.command == "foo" &&
|
||||||
|
opts.Preview.position == posRight &&
|
||||||
|
opts.Preview.size.percent == true &&
|
||||||
|
opts.Preview.size.size == 70) {
|
||||||
|
t.Error(opts.Preview)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdditiveExpect(t *testing.T) {
|
func TestAdditiveExpect(t *testing.T) {
|
||||||
|
@@ -15,7 +15,6 @@ type Offset [2]int32
|
|||||||
type colorOffset struct {
|
type colorOffset struct {
|
||||||
offset [2]int32
|
offset [2]int32
|
||||||
color tui.ColorPair
|
color tui.ColorPair
|
||||||
attr tui.Attr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
@@ -87,14 +86,14 @@ func minRank() Result {
|
|||||||
return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
|
return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, color tui.ColorPair, attr tui.Attr, current bool) []colorOffset {
|
func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, colBase tui.ColorPair, colMatch tui.ColorPair, current bool) []colorOffset {
|
||||||
itemColors := result.item.Colors()
|
itemColors := result.item.Colors()
|
||||||
|
|
||||||
// No ANSI code, or --color=no
|
// No ANSI codes
|
||||||
if len(itemColors) == 0 {
|
if len(itemColors) == 0 {
|
||||||
var offsets []colorOffset
|
var offsets []colorOffset
|
||||||
for _, off := range matchOffsets {
|
for _, off := range matchOffsets {
|
||||||
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, attr: attr})
|
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: colMatch})
|
||||||
}
|
}
|
||||||
return offsets
|
return offsets
|
||||||
}
|
}
|
||||||
@@ -111,17 +110,19 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
|
|||||||
maxCol = ansi.offset[1]
|
maxCol = ansi.offset[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cols := make([]int, maxCol)
|
|
||||||
|
|
||||||
|
cols := make([]int, maxCol)
|
||||||
for colorIndex, ansi := range itemColors {
|
for colorIndex, ansi := range itemColors {
|
||||||
for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
|
for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
|
||||||
cols[i] = colorIndex + 1 // XXX
|
cols[i] = colorIndex + 1 // 1-based index of itemColors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, off := range matchOffsets {
|
for _, off := range matchOffsets {
|
||||||
for i := off[0]; i < off[1]; i++ {
|
for i := off[0]; i < off[1]; i++ {
|
||||||
cols[i] = -1
|
// Negative of 1-based index of itemColors
|
||||||
|
// - The extra -1 means highlighted
|
||||||
|
cols[i] = cols[i]*-1 - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,36 +134,41 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
|
|||||||
// --++++++++-- --++++++++++---
|
// --++++++++-- --++++++++++---
|
||||||
curr := 0
|
curr := 0
|
||||||
start := 0
|
start := 0
|
||||||
var colors []colorOffset
|
ansiToColorPair := func(ansi ansiOffset, base tui.ColorPair) tui.ColorPair {
|
||||||
add := func(idx int) {
|
|
||||||
if curr != 0 && idx > start {
|
|
||||||
if curr == -1 {
|
|
||||||
colors = append(colors, colorOffset{
|
|
||||||
offset: [2]int32{int32(start), int32(idx)}, color: color, attr: attr})
|
|
||||||
} else {
|
|
||||||
ansi := itemColors[curr-1]
|
|
||||||
fg := ansi.color.fg
|
fg := ansi.color.fg
|
||||||
bg := ansi.color.bg
|
bg := ansi.color.bg
|
||||||
if theme != nil {
|
|
||||||
if fg == -1 {
|
if fg == -1 {
|
||||||
if current {
|
if current {
|
||||||
fg = theme.Current
|
fg = theme.Current.Color
|
||||||
} else {
|
} else {
|
||||||
fg = theme.Fg
|
fg = theme.Fg.Color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if bg == -1 {
|
if bg == -1 {
|
||||||
if current {
|
if current {
|
||||||
bg = theme.DarkBg
|
bg = theme.DarkBg.Color
|
||||||
} else {
|
} else {
|
||||||
bg = theme.Bg
|
bg = theme.Bg.Color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return tui.NewColorPair(fg, bg, ansi.color.attr).MergeAttr(base)
|
||||||
|
}
|
||||||
|
var colors []colorOffset
|
||||||
|
add := func(idx int) {
|
||||||
|
if curr != 0 && idx > start {
|
||||||
|
if curr < 0 {
|
||||||
|
color := colMatch
|
||||||
|
if curr < -1 && theme.Colored {
|
||||||
|
origColor := ansiToColorPair(itemColors[-curr-2], colMatch)
|
||||||
|
color = origColor.MergeNonDefault(color)
|
||||||
}
|
}
|
||||||
|
colors = append(colors, colorOffset{
|
||||||
|
offset: [2]int32{int32(start), int32(idx)}, color: color})
|
||||||
|
} else {
|
||||||
|
ansi := itemColors[curr-1]
|
||||||
colors = append(colors, colorOffset{
|
colors = append(colors, colorOffset{
|
||||||
offset: [2]int32{int32(start), int32(idx)},
|
offset: [2]int32{int32(start), int32(idx)},
|
||||||
color: tui.NewColorPair(fg, bg),
|
color: ansiToColorPair(ansi, colBase)})
|
||||||
attr: ansi.color.attr.Merge(attr)})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -105,32 +105,55 @@ func TestColorOffset(t *testing.T) {
|
|||||||
// ++++++++ ++++++++++
|
// ++++++++ ++++++++++
|
||||||
// --++++++++-- --++++++++++---
|
// --++++++++-- --++++++++++---
|
||||||
|
|
||||||
offsets := []Offset{Offset{5, 15}, Offset{25, 35}}
|
offsets := []Offset{{5, 15}, {25, 35}}
|
||||||
item := Result{
|
item := Result{
|
||||||
item: &Item{
|
item: &Item{
|
||||||
colors: &[]ansiOffset{
|
colors: &[]ansiOffset{
|
||||||
ansiOffset{[2]int32{0, 20}, ansiState{1, 5, 0}},
|
{[2]int32{0, 20}, ansiState{1, 5, 0}},
|
||||||
ansiOffset{[2]int32{22, 27}, ansiState{2, 6, tui.Bold}},
|
{[2]int32{22, 27}, ansiState{2, 6, tui.Bold}},
|
||||||
ansiOffset{[2]int32{30, 32}, ansiState{3, 7, 0}},
|
{[2]int32{30, 32}, ansiState{3, 7, 0}},
|
||||||
ansiOffset{[2]int32{33, 40}, ansiState{4, 8, tui.Bold}}}}}
|
{[2]int32{33, 40}, ansiState{4, 8, tui.Bold}}}}}
|
||||||
// [{[0 5] 9 false} {[5 15] 99 false} {[15 20] 9 false} {[22 25] 10 true} {[25 35] 99 false} {[35 40] 11 true}]
|
|
||||||
|
|
||||||
pair := tui.NewColorPair(99, 199)
|
colBase := tui.NewColorPair(89, 189, tui.AttrUndefined)
|
||||||
colors := item.colorOffsets(offsets, tui.Dark256, pair, tui.AttrRegular, true)
|
colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined)
|
||||||
assert := func(idx int, b int32, e int32, c tui.ColorPair, bold bool) {
|
colors := item.colorOffsets(offsets, tui.Dark256, colBase, colMatch, true)
|
||||||
var attr tui.Attr
|
assert := func(idx int, b int32, e int32, c tui.ColorPair) {
|
||||||
if bold {
|
|
||||||
attr = tui.Bold
|
|
||||||
}
|
|
||||||
o := colors[idx]
|
o := colors[idx]
|
||||||
if o.offset[0] != b || o.offset[1] != e || o.color != c || o.attr != attr {
|
if o.offset[0] != b || o.offset[1] != e || o.color != c {
|
||||||
t.Error(o)
|
t.Error(o, b, e, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(0, 0, 5, tui.NewColorPair(1, 5), false)
|
// [{[0 5] {1 5 0}} {[5 15] {99 199 0}} {[15 20] {1 5 0}}
|
||||||
assert(1, 5, 15, pair, false)
|
// {[22 25] {2 6 1}} {[25 27] {99 199 1}} {[27 30] {99 199 0}}
|
||||||
assert(2, 15, 20, tui.NewColorPair(1, 5), false)
|
// {[30 32] {99 199 0}} {[32 33] {99 199 0}} {[33 35] {99 199 1}}
|
||||||
assert(3, 22, 25, tui.NewColorPair(2, 6), true)
|
// {[35 40] {4 8 1}}]
|
||||||
assert(4, 25, 35, pair, false)
|
assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
|
||||||
assert(5, 35, 40, tui.NewColorPair(4, 8), true)
|
assert(1, 5, 15, colMatch)
|
||||||
|
assert(2, 15, 20, tui.NewColorPair(1, 5, tui.AttrUndefined))
|
||||||
|
assert(3, 22, 25, tui.NewColorPair(2, 6, tui.Bold))
|
||||||
|
assert(4, 25, 27, colMatch.WithAttr(tui.Bold))
|
||||||
|
assert(5, 27, 30, colMatch)
|
||||||
|
assert(6, 30, 32, colMatch)
|
||||||
|
assert(7, 32, 33, colMatch) // TODO: Should we merge consecutive blocks?
|
||||||
|
assert(8, 33, 35, colMatch.WithAttr(tui.Bold))
|
||||||
|
assert(9, 35, 40, tui.NewColorPair(4, 8, tui.Bold))
|
||||||
|
|
||||||
|
colRegular := tui.NewColorPair(-1, -1, tui.AttrUndefined)
|
||||||
|
colUnderline := tui.NewColorPair(-1, -1, tui.Underline)
|
||||||
|
colors = item.colorOffsets(offsets, tui.Dark256, colRegular, colUnderline, true)
|
||||||
|
|
||||||
|
// [{[0 5] {1 5 0}} {[5 15] {1 5 8}} {[15 20] {1 5 0}}
|
||||||
|
// {[22 25] {2 6 1}} {[25 27] {2 6 9}} {[27 30] {-1 -1 8}}
|
||||||
|
// {[30 32] {3 7 8}} {[32 33] {-1 -1 8}} {[33 35] {4 8 9}}
|
||||||
|
// {[35 40] {4 8 1}}]
|
||||||
|
assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
|
||||||
|
assert(1, 5, 15, tui.NewColorPair(1, 5, tui.Underline))
|
||||||
|
assert(2, 15, 20, tui.NewColorPair(1, 5, tui.AttrUndefined))
|
||||||
|
assert(3, 22, 25, tui.NewColorPair(2, 6, tui.Bold))
|
||||||
|
assert(4, 25, 27, tui.NewColorPair(2, 6, tui.Bold|tui.Underline))
|
||||||
|
assert(5, 27, 30, colUnderline)
|
||||||
|
assert(6, 30, 32, tui.NewColorPair(3, 7, tui.Underline))
|
||||||
|
assert(7, 32, 33, colUnderline)
|
||||||
|
assert(8, 33, 35, tui.NewColorPair(4, 8, tui.Bold|tui.Underline))
|
||||||
|
assert(9, 35, 40, tui.NewColorPair(4, 8, tui.Bold))
|
||||||
}
|
}
|
||||||
|
450
src/terminal.go
450
src/terminal.go
@@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -24,13 +23,16 @@ import (
|
|||||||
|
|
||||||
var placeholder *regexp.Regexp
|
var placeholder *regexp.Regexp
|
||||||
var numericPrefix *regexp.Regexp
|
var numericPrefix *regexp.Regexp
|
||||||
|
var whiteSuffix *regexp.Regexp
|
||||||
var activeTempFiles []string
|
var activeTempFiles []string
|
||||||
|
|
||||||
const ellipsis string = ".."
|
const ellipsis string = ".."
|
||||||
|
const clearCode string = "\x1b[2J"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
|
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
|
||||||
numericPrefix = regexp.MustCompile(`^[[:punct:]]*([0-9]+)`)
|
numericPrefix = regexp.MustCompile(`^[[:punct:]]*([0-9]+)`)
|
||||||
|
whiteSuffix = regexp.MustCompile(`\s*$`)
|
||||||
activeTempFiles = []string{}
|
activeTempFiles = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,11 +45,25 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type previewer struct {
|
type previewer struct {
|
||||||
text string
|
version int64
|
||||||
lines int
|
lines []string
|
||||||
offset int
|
offset int
|
||||||
enabled bool
|
enabled bool
|
||||||
more bool
|
scrollable bool
|
||||||
|
final bool
|
||||||
|
spinner string
|
||||||
|
}
|
||||||
|
|
||||||
|
type previewed struct {
|
||||||
|
version int64
|
||||||
|
numLines int
|
||||||
|
offset int
|
||||||
|
filled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type eachLine struct {
|
||||||
|
line string
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
type itemLine struct {
|
type itemLine struct {
|
||||||
@@ -125,6 +141,7 @@ type Terminal struct {
|
|||||||
reqBox *util.EventBox
|
reqBox *util.EventBox
|
||||||
preview previewOpts
|
preview previewOpts
|
||||||
previewer previewer
|
previewer previewer
|
||||||
|
previewed previewed
|
||||||
previewBox *util.EventBox
|
previewBox *util.EventBox
|
||||||
eventBox *util.EventBox
|
eventBox *util.EventBox
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
@@ -171,6 +188,7 @@ const (
|
|||||||
reqPreviewEnqueue
|
reqPreviewEnqueue
|
||||||
reqPreviewDisplay
|
reqPreviewDisplay
|
||||||
reqPreviewRefresh
|
reqPreviewRefresh
|
||||||
|
reqPreviewDelayed
|
||||||
reqQuit
|
reqQuit
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -263,12 +281,15 @@ type searchRequest struct {
|
|||||||
|
|
||||||
type previewRequest struct {
|
type previewRequest struct {
|
||||||
template string
|
template string
|
||||||
|
pwindow tui.Window
|
||||||
list []*Item
|
list []*Item
|
||||||
}
|
}
|
||||||
|
|
||||||
type previewResult struct {
|
type previewResult struct {
|
||||||
content string
|
version int64
|
||||||
|
lines []string
|
||||||
offset int
|
offset int
|
||||||
|
spinner string
|
||||||
}
|
}
|
||||||
|
|
||||||
func toActions(types ...actionType) []action {
|
func toActions(types ...actionType) []action {
|
||||||
@@ -353,6 +374,13 @@ func hasPreviewAction(opts *Options) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeSpinner(unicode bool) []string {
|
||||||
|
if unicode {
|
||||||
|
return []string{`⠋`, `⠙`, `⠹`, `⠸`, `⠼`, `⠴`, `⠦`, `⠧`, `⠇`, `⠏`}
|
||||||
|
}
|
||||||
|
return []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
||||||
|
}
|
||||||
|
|
||||||
// NewTerminal returns new Terminal object
|
// NewTerminal returns new Terminal object
|
||||||
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||||
input := trimQuery(opts.Query)
|
input := trimQuery(opts.Query)
|
||||||
@@ -416,14 +444,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
|
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
|
||||||
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
|
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
|
||||||
}
|
}
|
||||||
spinner := []string{`⠋`, `⠙`, `⠹`, `⠸`, `⠼`, `⠴`, `⠦`, `⠧`, `⠇`, `⠏`}
|
|
||||||
if !opts.Unicode {
|
|
||||||
spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
|
||||||
}
|
|
||||||
t := Terminal{
|
t := Terminal{
|
||||||
initDelay: delay,
|
initDelay: delay,
|
||||||
infoStyle: opts.InfoStyle,
|
infoStyle: opts.InfoStyle,
|
||||||
spinner: spinner,
|
spinner: makeSpinner(opts.Unicode),
|
||||||
queryLen: [2]int{0, 0},
|
queryLen: [2]int{0, 0},
|
||||||
layout: opts.Layout,
|
layout: opts.Layout,
|
||||||
fullscreen: fullscreen,
|
fullscreen: fullscreen,
|
||||||
@@ -467,7 +491,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
selected: make(map[int32]selectedItem),
|
selected: make(map[int32]selectedItem),
|
||||||
reqBox: util.NewEventBox(),
|
reqBox: util.NewEventBox(),
|
||||||
preview: opts.Preview,
|
preview: opts.Preview,
|
||||||
previewer: previewer{"", 0, 0, previewBox != nil && !opts.Preview.hidden, false},
|
previewer: previewer{0, []string{}, 0, previewBox != nil && !opts.Preview.hidden, false, true, ""},
|
||||||
|
previewed: previewed{0, 0, 0, false},
|
||||||
previewBox: previewBox,
|
previewBox: previewBox,
|
||||||
eventBox: eventBox,
|
eventBox: eventBox,
|
||||||
mutex: sync.Mutex{},
|
mutex: sync.Mutex{},
|
||||||
@@ -492,9 +517,29 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
|
|||||||
var state *ansiState
|
var state *ansiState
|
||||||
trimmed, colors, _ := extractColor(prompt, state, nil)
|
trimmed, colors, _ := extractColor(prompt, state, nil)
|
||||||
item := &Item{text: util.ToChars([]byte(trimmed)), colors: colors}
|
item := &Item{text: util.ToChars([]byte(trimmed)), colors: colors}
|
||||||
|
|
||||||
|
// "Prompt> "
|
||||||
|
// ------- // Do not apply ANSI attributes to the trailing whitespaces
|
||||||
|
// // unless the part has a non-default ANSI state
|
||||||
|
loc := whiteSuffix.FindStringIndex(trimmed)
|
||||||
|
if loc != nil {
|
||||||
|
blankState := ansiOffset{[2]int32{int32(loc[0]), int32(loc[1])}, ansiState{-1, -1, tui.AttrClear}}
|
||||||
|
if item.colors != nil {
|
||||||
|
lastColor := (*item.colors)[len(*item.colors)-1]
|
||||||
|
fmt.Println(lastColor.offset[1], int32(loc[1]))
|
||||||
|
if lastColor.offset[1] < int32(loc[1]) {
|
||||||
|
blankState.offset[0] = lastColor.offset[1]
|
||||||
|
colors := append(*item.colors, blankState)
|
||||||
|
item.colors = &colors
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
colors := []ansiOffset{blankState}
|
||||||
|
item.colors = &colors
|
||||||
|
}
|
||||||
|
}
|
||||||
output := func() {
|
output := func() {
|
||||||
t.printHighlighted(
|
t.printHighlighted(
|
||||||
Result{item: item}, t.strong, tui.ColPrompt, tui.ColPrompt, false, false)
|
Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false)
|
||||||
}
|
}
|
||||||
_, promptLen := t.processTabs([]rune(trimmed), 0)
|
_, promptLen := t.processTabs([]rune(trimmed), 0)
|
||||||
|
|
||||||
@@ -608,7 +653,7 @@ func (t *Terminal) displayWidth(runes []rune) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minWidth = 16
|
minWidth = 4
|
||||||
minHeight = 4
|
minHeight = 4
|
||||||
|
|
||||||
maxDisplayWidthCalc = 1024
|
maxDisplayWidthCalc = 1024
|
||||||
@@ -625,7 +670,7 @@ func calculateSize(base int, size sizeSpec, occupied int, minSize int, pad int)
|
|||||||
func (t *Terminal) resizeWindows() {
|
func (t *Terminal) resizeWindows() {
|
||||||
screenWidth := t.tui.MaxX()
|
screenWidth := t.tui.MaxX()
|
||||||
screenHeight := t.tui.MaxY()
|
screenHeight := t.tui.MaxY()
|
||||||
marginInt := [4]int{}
|
marginInt := [4]int{} // TRBL
|
||||||
t.prevLines = make([]itemLine, screenHeight)
|
t.prevLines = make([]itemLine, screenHeight)
|
||||||
for idx, sizeSpec := range t.margin {
|
for idx, sizeSpec := range t.margin {
|
||||||
if sizeSpec.percent {
|
if sizeSpec.percent {
|
||||||
@@ -642,6 +687,24 @@ func (t *Terminal) resizeWindows() {
|
|||||||
switch t.borderShape {
|
switch t.borderShape {
|
||||||
case tui.BorderHorizontal:
|
case tui.BorderHorizontal:
|
||||||
marginInt[idx] += 1 - idx%2
|
marginInt[idx] += 1 - idx%2
|
||||||
|
case tui.BorderVertical:
|
||||||
|
marginInt[idx] += 2 * (idx % 2)
|
||||||
|
case tui.BorderTop:
|
||||||
|
if idx == 0 {
|
||||||
|
marginInt[idx]++
|
||||||
|
}
|
||||||
|
case tui.BorderRight:
|
||||||
|
if idx == 1 {
|
||||||
|
marginInt[idx] += 2
|
||||||
|
}
|
||||||
|
case tui.BorderBottom:
|
||||||
|
if idx == 2 {
|
||||||
|
marginInt[idx]++
|
||||||
|
}
|
||||||
|
case tui.BorderLeft:
|
||||||
|
if idx == 3 {
|
||||||
|
marginInt[idx] += 2
|
||||||
|
}
|
||||||
case tui.BorderRounded, tui.BorderSharp:
|
case tui.BorderRounded, tui.BorderSharp:
|
||||||
marginInt[idx] += 1 + idx%2
|
marginInt[idx] += 1 + idx%2
|
||||||
}
|
}
|
||||||
@@ -682,23 +745,39 @@ func (t *Terminal) resizeWindows() {
|
|||||||
if t.pwindow != nil {
|
if t.pwindow != nil {
|
||||||
t.pwindow.Close()
|
t.pwindow.Close()
|
||||||
}
|
}
|
||||||
|
// Reset preview version so that full redraw occurs
|
||||||
|
t.previewed.version = 0
|
||||||
|
|
||||||
width := screenWidth - marginInt[1] - marginInt[3]
|
width := screenWidth - marginInt[1] - marginInt[3]
|
||||||
height := screenHeight - marginInt[0] - marginInt[2]
|
height := screenHeight - marginInt[0] - marginInt[2]
|
||||||
switch t.borderShape {
|
switch t.borderShape {
|
||||||
case tui.BorderHorizontal:
|
case tui.BorderHorizontal:
|
||||||
t.border = t.tui.NewWindow(
|
t.border = t.tui.NewWindow(
|
||||||
marginInt[0]-1,
|
marginInt[0]-1, marginInt[3], width, height+2,
|
||||||
marginInt[3],
|
|
||||||
width,
|
|
||||||
height+2,
|
|
||||||
false, tui.MakeBorderStyle(tui.BorderHorizontal, t.unicode))
|
false, tui.MakeBorderStyle(tui.BorderHorizontal, t.unicode))
|
||||||
|
case tui.BorderVertical:
|
||||||
|
t.border = t.tui.NewWindow(
|
||||||
|
marginInt[0], marginInt[3]-2, width+4, height,
|
||||||
|
false, tui.MakeBorderStyle(tui.BorderVertical, t.unicode))
|
||||||
|
case tui.BorderTop:
|
||||||
|
t.border = t.tui.NewWindow(
|
||||||
|
marginInt[0]-1, marginInt[3], width, height+1,
|
||||||
|
false, tui.MakeBorderStyle(tui.BorderTop, t.unicode))
|
||||||
|
case tui.BorderBottom:
|
||||||
|
t.border = t.tui.NewWindow(
|
||||||
|
marginInt[0], marginInt[3], width, height+1,
|
||||||
|
false, tui.MakeBorderStyle(tui.BorderBottom, t.unicode))
|
||||||
|
case tui.BorderLeft:
|
||||||
|
t.border = t.tui.NewWindow(
|
||||||
|
marginInt[0], marginInt[3]-2, width+2, height,
|
||||||
|
false, tui.MakeBorderStyle(tui.BorderLeft, t.unicode))
|
||||||
|
case tui.BorderRight:
|
||||||
|
t.border = t.tui.NewWindow(
|
||||||
|
marginInt[0], marginInt[3], width+2, height,
|
||||||
|
false, tui.MakeBorderStyle(tui.BorderRight, t.unicode))
|
||||||
case tui.BorderRounded, tui.BorderSharp:
|
case tui.BorderRounded, tui.BorderSharp:
|
||||||
t.border = t.tui.NewWindow(
|
t.border = t.tui.NewWindow(
|
||||||
marginInt[0]-1,
|
marginInt[0]-1, marginInt[3]-2, width+4, height+2,
|
||||||
marginInt[3]-2,
|
|
||||||
width+4,
|
|
||||||
height+2,
|
|
||||||
false, tui.MakeBorderStyle(t.borderShape, t.unicode))
|
false, tui.MakeBorderStyle(t.borderShape, t.unicode))
|
||||||
}
|
}
|
||||||
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
|
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
|
||||||
@@ -719,26 +798,22 @@ func (t *Terminal) resizeWindows() {
|
|||||||
pwidth -= 4
|
pwidth -= 4
|
||||||
x += 2
|
x += 2
|
||||||
}
|
}
|
||||||
// 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
|
|
||||||
// column larger than the desired value.
|
|
||||||
if !t.preview.wrap && t.tui.DoesAutoWrap() {
|
|
||||||
pwidth += 1
|
|
||||||
}
|
|
||||||
t.pwindow = t.tui.NewWindow(y, x, pwidth, pheight, true, noBorder)
|
t.pwindow = t.tui.NewWindow(y, x, pwidth, pheight, true, noBorder)
|
||||||
}
|
}
|
||||||
verticalPad := 2
|
verticalPad := 2
|
||||||
|
minPreviewHeight := 3
|
||||||
if t.preview.border == tui.BorderNone {
|
if t.preview.border == tui.BorderNone {
|
||||||
verticalPad = 0
|
verticalPad = 0
|
||||||
|
minPreviewHeight = 1
|
||||||
}
|
}
|
||||||
switch t.preview.position {
|
switch t.preview.position {
|
||||||
case posUp:
|
case posUp:
|
||||||
pheight := calculateSize(height, t.preview.size, minHeight, 3, verticalPad)
|
pheight := calculateSize(height, t.preview.size, minHeight, minPreviewHeight, verticalPad)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
marginInt[0]+pheight, marginInt[3], width, height-pheight, false, noBorder)
|
marginInt[0]+pheight, marginInt[3], width, height-pheight, false, noBorder)
|
||||||
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, verticalPad)
|
pheight := calculateSize(height, t.preview.size, minHeight, minPreviewHeight, verticalPad)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
marginInt[0], marginInt[3], width, height-pheight, false, noBorder)
|
marginInt[0], marginInt[3], width, height-pheight, false, noBorder)
|
||||||
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
|
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
|
||||||
@@ -820,8 +895,16 @@ func (t *Terminal) printPrompt() {
|
|||||||
t.prompt()
|
t.prompt()
|
||||||
|
|
||||||
before, after := t.updatePromptOffset()
|
before, after := t.updatePromptOffset()
|
||||||
t.window.CPrint(tui.ColNormal, t.strong, string(before))
|
t.window.CPrint(tui.ColInput, string(before))
|
||||||
t.window.CPrint(tui.ColNormal, t.strong, string(after))
|
t.window.CPrint(tui.ColInput, string(after))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) trimMessage(message string, maxWidth int) string {
|
||||||
|
if len(message) <= maxWidth {
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
runes, _ := t.trimRight([]rune(message), maxWidth-2)
|
||||||
|
return string(runes) + strings.Repeat(".", util.Constrain(maxWidth, 0, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printInfo() {
|
func (t *Terminal) printInfo() {
|
||||||
@@ -832,7 +915,7 @@ func (t *Terminal) printInfo() {
|
|||||||
if t.reading {
|
if t.reading {
|
||||||
duration := int64(spinnerDuration)
|
duration := int64(spinnerDuration)
|
||||||
idx := (time.Now().UnixNano() % (duration * int64(len(t.spinner)))) / duration
|
idx := (time.Now().UnixNano() % (duration * int64(len(t.spinner)))) / duration
|
||||||
t.window.CPrint(tui.ColSpinner, t.strong, t.spinner[idx])
|
t.window.CPrint(tui.ColSpinner, t.spinner[idx])
|
||||||
}
|
}
|
||||||
t.move(1, 2, false)
|
t.move(1, 2, false)
|
||||||
pos = 2
|
pos = 2
|
||||||
@@ -843,9 +926,9 @@ func (t *Terminal) printInfo() {
|
|||||||
}
|
}
|
||||||
t.move(0, pos, true)
|
t.move(0, pos, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
t.window.CPrint(tui.ColSpinner, t.strong, " < ")
|
t.window.CPrint(tui.ColSpinner, " < ")
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(tui.ColPrompt, t.strong, " < ")
|
t.window.CPrint(tui.ColPrompt, " < ")
|
||||||
}
|
}
|
||||||
pos += len(" < ")
|
pos += len(" < ")
|
||||||
case infoHidden:
|
case infoHidden:
|
||||||
@@ -862,7 +945,7 @@ func (t *Terminal) printInfo() {
|
|||||||
output += " -S"
|
output += " -S"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(t.selected) > 0 {
|
if t.multi > 0 {
|
||||||
if t.multi == maxMulti {
|
if t.multi == maxMulti {
|
||||||
output += fmt.Sprintf(" (%d)", len(t.selected))
|
output += fmt.Sprintf(" (%d)", len(t.selected))
|
||||||
} else {
|
} else {
|
||||||
@@ -875,12 +958,8 @@ func (t *Terminal) printInfo() {
|
|||||||
if t.failed != nil && t.count == 0 {
|
if t.failed != nil && t.count == 0 {
|
||||||
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
||||||
}
|
}
|
||||||
maxWidth := t.window.Width() - pos
|
output = t.trimMessage(output, t.window.Width()-pos)
|
||||||
if len(output) > maxWidth {
|
t.window.CPrint(tui.ColInfo, output)
|
||||||
outputRunes, _ := t.trimRight([]rune(output), maxWidth-2)
|
|
||||||
output = string(outputRunes) + strings.Repeat(".", util.Constrain(maxWidth, 0, 2))
|
|
||||||
}
|
|
||||||
t.window.CPrint(tui.ColInfo, 0, output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printHeader() {
|
func (t *Terminal) printHeader() {
|
||||||
@@ -905,7 +984,7 @@ func (t *Terminal) printHeader() {
|
|||||||
|
|
||||||
t.move(line, 2, true)
|
t.move(line, 2, true)
|
||||||
t.printHighlighted(Result{item: item},
|
t.printHighlighted(Result{item: item},
|
||||||
tui.AttrRegular, tui.ColHeader, tui.ColHeader, false, false)
|
tui.ColHeader, tui.ColHeader, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -935,7 +1014,7 @@ func (t *Terminal) printList() {
|
|||||||
func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
||||||
item := result.item
|
item := result.item
|
||||||
_, selected := t.selected[item.Index()]
|
_, selected := t.selected[item.Index()]
|
||||||
label := t.pointerEmpty
|
label := ""
|
||||||
if t.jumping != jumpDisabled {
|
if t.jumping != jumpDisabled {
|
||||||
if i < len(t.jumpLabels) {
|
if i < len(t.jumpLabels) {
|
||||||
// Striped
|
// Striped
|
||||||
@@ -960,21 +1039,29 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
|||||||
|
|
||||||
t.move(line, 0, false)
|
t.move(line, 0, false)
|
||||||
if current {
|
if current {
|
||||||
t.window.CPrint(tui.ColCurrentCursor, t.strong, label)
|
if len(label) == 0 {
|
||||||
if selected {
|
t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty)
|
||||||
t.window.CPrint(tui.ColCurrentSelected, t.strong, t.marker)
|
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(tui.ColCurrentSelected, t.strong, t.markerEmpty)
|
t.window.CPrint(tui.ColCurrentCursor, label)
|
||||||
}
|
}
|
||||||
newLine.width = t.printHighlighted(result, t.strong, tui.ColCurrent, tui.ColCurrentMatch, true, true)
|
|
||||||
} else {
|
|
||||||
t.window.CPrint(tui.ColCursor, t.strong, label)
|
|
||||||
if selected {
|
if selected {
|
||||||
t.window.CPrint(tui.ColSelected, t.strong, t.marker)
|
t.window.CPrint(tui.ColCurrentSelected, t.marker)
|
||||||
|
} else {
|
||||||
|
t.window.CPrint(tui.ColCurrentSelectedEmpty, t.markerEmpty)
|
||||||
|
}
|
||||||
|
newLine.width = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true)
|
||||||
|
} else {
|
||||||
|
if len(label) == 0 {
|
||||||
|
t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty)
|
||||||
|
} else {
|
||||||
|
t.window.CPrint(tui.ColCursor, label)
|
||||||
|
}
|
||||||
|
if selected {
|
||||||
|
t.window.CPrint(tui.ColSelected, t.marker)
|
||||||
} else {
|
} else {
|
||||||
t.window.Print(t.markerEmpty)
|
t.window.Print(t.markerEmpty)
|
||||||
}
|
}
|
||||||
newLine.width = t.printHighlighted(result, 0, tui.ColNormal, tui.ColMatch, false, true)
|
newLine.width = t.printHighlighted(result, tui.ColNormal, tui.ColMatch, false, true)
|
||||||
}
|
}
|
||||||
fillSpaces := prevLine.width - newLine.width
|
fillSpaces := prevLine.width - newLine.width
|
||||||
if fillSpaces > 0 {
|
if fillSpaces > 0 {
|
||||||
@@ -1028,7 +1115,7 @@ func (t *Terminal) overflow(runes []rune, max int) bool {
|
|||||||
return t.displayWidthWithLimit(runes, 0, max) > max
|
return t.displayWidthWithLimit(runes, 0, max) > max
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.ColorPair, col2 tui.ColorPair, current bool, match bool) int {
|
func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool) int {
|
||||||
item := result.item
|
item := result.item
|
||||||
|
|
||||||
// Overflow
|
// Overflow
|
||||||
@@ -1053,7 +1140,7 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
|
|||||||
maxe = util.Max(maxe, int(offset[1]))
|
maxe = util.Max(maxe, int(offset[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
offsets := result.colorOffsets(charOffsets, t.theme, col2, attr, current)
|
offsets := result.colorOffsets(charOffsets, t.theme, colBase, colMatch, current)
|
||||||
maxWidth := t.window.Width() - (t.pointerLen + t.markerLen + 1)
|
maxWidth := t.window.Width() - (t.pointerLen + t.markerLen + 1)
|
||||||
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
|
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
|
||||||
displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
|
displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
|
||||||
@@ -1111,11 +1198,11 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
|
|||||||
e := util.Constrain32(offset.offset[1], index, maxOffset)
|
e := util.Constrain32(offset.offset[1], index, maxOffset)
|
||||||
|
|
||||||
substr, prefixWidth = t.processTabs(text[index:b], prefixWidth)
|
substr, prefixWidth = t.processTabs(text[index:b], prefixWidth)
|
||||||
t.window.CPrint(col1, attr, substr)
|
t.window.CPrint(colBase, substr)
|
||||||
|
|
||||||
if b < e {
|
if b < e {
|
||||||
substr, prefixWidth = t.processTabs(text[b:e], prefixWidth)
|
substr, prefixWidth = t.processTabs(text[b:e], prefixWidth)
|
||||||
t.window.CPrint(offset.color, offset.attr, substr)
|
t.window.CPrint(offset.color, substr)
|
||||||
}
|
}
|
||||||
|
|
||||||
index = e
|
index = e
|
||||||
@@ -1125,33 +1212,53 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
|
|||||||
}
|
}
|
||||||
if index < maxOffset {
|
if index < maxOffset {
|
||||||
substr, _ = t.processTabs(text[index:], prefixWidth)
|
substr, _ = t.processTabs(text[index:], prefixWidth)
|
||||||
t.window.CPrint(col1, attr, substr)
|
t.window.CPrint(colBase, substr)
|
||||||
}
|
}
|
||||||
return displayWidth
|
return displayWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printPreview() {
|
func (t *Terminal) renderPreviewSpinner() {
|
||||||
if !t.hasPreviewWindow() {
|
numLines := len(t.previewer.lines)
|
||||||
return
|
spin := t.previewer.spinner
|
||||||
}
|
if len(spin) > 0 || t.previewer.scrollable {
|
||||||
t.pwindow.Erase()
|
|
||||||
|
|
||||||
maxWidth := t.pwindow.Width()
|
maxWidth := t.pwindow.Width()
|
||||||
if t.tui.DoesAutoWrap() {
|
if !t.previewer.scrollable {
|
||||||
maxWidth -= 1
|
if maxWidth > 0 {
|
||||||
|
t.pwindow.Move(0, maxWidth-1)
|
||||||
|
t.pwindow.CPrint(tui.ColSpinner, spin)
|
||||||
}
|
}
|
||||||
reader := bufio.NewReader(strings.NewReader(t.previewer.text))
|
} else {
|
||||||
|
offsetString := fmt.Sprintf("%d/%d", t.previewer.offset+1, numLines)
|
||||||
|
if len(spin) > 0 {
|
||||||
|
spin += " "
|
||||||
|
maxWidth -= 2
|
||||||
|
}
|
||||||
|
offsetRunes, _ := t.trimRight([]rune(offsetString), maxWidth)
|
||||||
|
pos := maxWidth - t.displayWidth(offsetRunes)
|
||||||
|
t.pwindow.Move(0, pos)
|
||||||
|
if maxWidth > 0 {
|
||||||
|
t.pwindow.CPrint(tui.ColSpinner, spin)
|
||||||
|
t.pwindow.CPrint(tui.ColInfo.WithAttr(tui.Reverse), string(offsetRunes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) renderPreviewText(unchanged bool) {
|
||||||
|
maxWidth := t.pwindow.Width()
|
||||||
lineNo := -t.previewer.offset
|
lineNo := -t.previewer.offset
|
||||||
height := t.pwindow.Height()
|
height := t.pwindow.Height()
|
||||||
t.previewer.more = t.previewer.offset > 0
|
if unchanged {
|
||||||
var ansi *ansiState
|
t.pwindow.MoveAndClear(0, 0)
|
||||||
for ; ; lineNo++ {
|
} else {
|
||||||
line, err := reader.ReadString('\n')
|
t.previewed.filled = false
|
||||||
eof := err == io.EOF
|
t.pwindow.Erase()
|
||||||
if !eof {
|
|
||||||
line = line[:len(line)-1]
|
|
||||||
}
|
}
|
||||||
|
var ansi *ansiState
|
||||||
|
for _, line := range t.previewer.lines {
|
||||||
|
line = strings.TrimSuffix(line, "\n")
|
||||||
if lineNo >= height || t.pwindow.Y() == height-1 && t.pwindow.X() > 0 {
|
if lineNo >= height || t.pwindow.Y() == height-1 && t.pwindow.X() > 0 {
|
||||||
|
t.previewed.filled = true
|
||||||
break
|
break
|
||||||
} else if lineNo >= 0 {
|
} else if lineNo >= 0 {
|
||||||
var fillRet tui.FillReturn
|
var fillRet tui.FillReturn
|
||||||
@@ -1163,36 +1270,61 @@ func (t *Terminal) printPreview() {
|
|||||||
}
|
}
|
||||||
str, width := t.processTabs(trimmed, prefixWidth)
|
str, width := t.processTabs(trimmed, prefixWidth)
|
||||||
prefixWidth += width
|
prefixWidth += width
|
||||||
if t.theme != nil && ansi != nil && ansi.colored() {
|
if t.theme.Colored && ansi != nil && ansi.colored() {
|
||||||
fillRet = t.pwindow.CFill(ansi.fg, ansi.bg, ansi.attr, str)
|
fillRet = t.pwindow.CFill(ansi.fg, ansi.bg, ansi.attr, str)
|
||||||
} else {
|
} else {
|
||||||
fillRet = t.pwindow.CFill(tui.ColPreview.Fg(), tui.ColPreview.Bg(), tui.AttrRegular, str)
|
fillRet = t.pwindow.CFill(tui.ColPreview.Fg(), tui.ColPreview.Bg(), tui.AttrRegular, str)
|
||||||
}
|
}
|
||||||
return fillRet == tui.FillContinue
|
return fillRet == tui.FillContinue
|
||||||
})
|
})
|
||||||
t.previewer.more = t.previewer.more || t.pwindow.Y() == height-1 && t.pwindow.X() == t.pwindow.Width()
|
t.previewer.scrollable = t.previewer.scrollable || t.pwindow.Y() == height-1 && t.pwindow.X() == t.pwindow.Width()
|
||||||
if fillRet == tui.FillNextLine {
|
if fillRet == tui.FillNextLine {
|
||||||
continue
|
continue
|
||||||
} else if fillRet == tui.FillSuspend {
|
} else if fillRet == tui.FillSuspend {
|
||||||
|
t.previewed.filled = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if unchanged && lineNo == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
t.pwindow.Fill("\n")
|
t.pwindow.Fill("\n")
|
||||||
}
|
}
|
||||||
if eof {
|
lineNo++
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if !unchanged {
|
||||||
t.pwindow.FinishFill()
|
t.pwindow.FinishFill()
|
||||||
if t.previewer.lines > height {
|
|
||||||
t.previewer.more = true
|
|
||||||
offset := fmt.Sprintf("%d/%d", t.previewer.offset+1, t.previewer.lines)
|
|
||||||
pos := t.pwindow.Width() - len(offset)
|
|
||||||
if t.tui.DoesAutoWrap() {
|
|
||||||
pos -= 1
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) printPreview() {
|
||||||
|
if !t.hasPreviewWindow() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
numLines := len(t.previewer.lines)
|
||||||
|
height := t.pwindow.Height()
|
||||||
|
unchanged := (t.previewed.filled || numLines == t.previewed.numLines) &&
|
||||||
|
t.previewer.version == t.previewed.version &&
|
||||||
|
t.previewer.offset == t.previewed.offset
|
||||||
|
t.previewer.scrollable = t.previewer.offset > 0 || numLines > height
|
||||||
|
t.renderPreviewText(unchanged)
|
||||||
|
t.renderPreviewSpinner()
|
||||||
|
t.previewed.numLines = numLines
|
||||||
|
t.previewed.version = t.previewer.version
|
||||||
|
t.previewed.offset = t.previewer.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) printPreviewDelayed() {
|
||||||
|
if !t.hasPreviewWindow() || len(t.previewer.lines) > 0 && t.previewed.version == t.previewer.version {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.previewer.scrollable = false
|
||||||
|
t.renderPreviewText(true)
|
||||||
|
|
||||||
|
message := t.trimMessage("Loading ..", t.pwindow.Width())
|
||||||
|
pos := t.pwindow.Width() - len(message)
|
||||||
t.pwindow.Move(0, pos)
|
t.pwindow.Move(0, pos)
|
||||||
t.pwindow.CPrint(tui.ColInfo, tui.Reverse, offset)
|
t.pwindow.CPrint(tui.ColInfo.WithAttr(tui.Reverse), message)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) processTabs(runes []rune, prefixWidth int) (string, int) {
|
func (t *Terminal) processTabs(runes []rune, prefixWidth int) (string, int) {
|
||||||
@@ -1686,9 +1818,11 @@ func (t *Terminal) Loop() {
|
|||||||
|
|
||||||
if t.hasPreviewer() {
|
if t.hasPreviewer() {
|
||||||
go func() {
|
go func() {
|
||||||
|
var version int64
|
||||||
for {
|
for {
|
||||||
var items []*Item
|
var items []*Item
|
||||||
var commandTemplate string
|
var commandTemplate string
|
||||||
|
var pwindow tui.Window
|
||||||
t.previewBox.Wait(func(events *util.Events) {
|
t.previewBox.Wait(func(events *util.Events) {
|
||||||
for req, value := range *events {
|
for req, value := range *events {
|
||||||
switch req {
|
switch req {
|
||||||
@@ -1696,63 +1830,136 @@ func (t *Terminal) Loop() {
|
|||||||
request := value.(previewRequest)
|
request := value.(previewRequest)
|
||||||
commandTemplate = request.template
|
commandTemplate = request.template
|
||||||
items = request.list
|
items = request.list
|
||||||
|
pwindow = request.pwindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
events.Clear()
|
events.Clear()
|
||||||
})
|
})
|
||||||
|
version++
|
||||||
// We don't display preview window if no match
|
// We don't display preview window if no match
|
||||||
if items[0] != nil {
|
if items[0] != nil {
|
||||||
command := t.replacePlaceholder(commandTemplate, false, string(t.Input()), items)
|
command := t.replacePlaceholder(commandTemplate, false, string(t.Input()), items)
|
||||||
offset := 0
|
initialOffset := 0
|
||||||
cmd := util.ExecCommand(command, true)
|
cmd := util.ExecCommand(command, true)
|
||||||
if t.pwindow != nil {
|
if pwindow != nil {
|
||||||
height := t.pwindow.Height()
|
height := pwindow.Height()
|
||||||
offset = t.evaluateScrollOffset(items, height)
|
initialOffset = util.Max(0, t.evaluateScrollOffset(items, height))
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
lines := fmt.Sprintf("LINES=%d", height)
|
lines := fmt.Sprintf("LINES=%d", height)
|
||||||
columns := fmt.Sprintf("COLUMNS=%d", t.pwindow.Width())
|
columns := fmt.Sprintf("COLUMNS=%d", pwindow.Width())
|
||||||
env = append(env, lines)
|
env = append(env, lines)
|
||||||
env = append(env, "FZF_PREVIEW_"+lines)
|
env = append(env, "FZF_PREVIEW_"+lines)
|
||||||
env = append(env, columns)
|
env = append(env, columns)
|
||||||
env = append(env, "FZF_PREVIEW_"+columns)
|
env = append(env, "FZF_PREVIEW_"+columns)
|
||||||
cmd.Env = env
|
cmd.Env = env
|
||||||
}
|
}
|
||||||
var out bytes.Buffer
|
|
||||||
cmd.Stdout = &out
|
out, _ := cmd.StdoutPipe()
|
||||||
cmd.Stderr = &out
|
cmd.Stderr = cmd.Stdout
|
||||||
err := cmd.Start()
|
reader := bufio.NewReader(out)
|
||||||
if err != nil {
|
eofChan := make(chan bool)
|
||||||
out.Write([]byte(err.Error()))
|
|
||||||
}
|
|
||||||
finishChan := make(chan bool, 1)
|
finishChan := make(chan bool, 1)
|
||||||
updateChan := make(chan bool)
|
err := cmd.Start()
|
||||||
|
if err == nil {
|
||||||
|
reapChan := make(chan bool)
|
||||||
|
lineChan := make(chan eachLine)
|
||||||
|
// Goroutine 1 reads process output
|
||||||
go func() {
|
go func() {
|
||||||
|
for {
|
||||||
|
line, err := reader.ReadString('\n')
|
||||||
|
lineChan <- eachLine{line, err}
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eofChan <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Goroutine 2 periodically requests rendering
|
||||||
|
go func(version int64) {
|
||||||
|
lines := []string{}
|
||||||
|
spinner := makeSpinner(t.unicode)
|
||||||
|
spinnerIndex := -1 // Delay initial rendering by an extra tick
|
||||||
|
ticker := time.NewTicker(previewChunkDelay)
|
||||||
|
offset := initialOffset
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
if len(lines) > 0 && len(lines) >= initialOffset {
|
||||||
|
if spinnerIndex >= 0 {
|
||||||
|
spin := spinner[spinnerIndex%len(spinner)]
|
||||||
|
t.reqBox.Set(reqPreviewDisplay, previewResult{version, lines, offset, spin})
|
||||||
|
offset = -1
|
||||||
|
}
|
||||||
|
spinnerIndex++
|
||||||
|
}
|
||||||
|
case eachLine := <-lineChan:
|
||||||
|
line := eachLine.line
|
||||||
|
err := eachLine.err
|
||||||
|
if len(line) > 0 {
|
||||||
|
clearIndex := strings.Index(line, clearCode)
|
||||||
|
if clearIndex >= 0 {
|
||||||
|
lines = []string{}
|
||||||
|
line = line[clearIndex+len(clearCode):]
|
||||||
|
version--
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.reqBox.Set(reqPreviewDisplay, previewResult{version, lines, offset, ""})
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ticker.Stop()
|
||||||
|
reapChan <- true
|
||||||
|
}(version)
|
||||||
|
|
||||||
|
// Goroutine 3 is responsible for cancelling running preview command
|
||||||
|
go func(version int64) {
|
||||||
|
timer := time.NewTimer(previewDelayed)
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
t.reqBox.Set(reqPreviewDelayed, version)
|
||||||
case code := <-t.killChan:
|
case code := <-t.killChan:
|
||||||
if code != exitCancel {
|
if code != exitCancel {
|
||||||
util.KillCommand(cmd)
|
util.KillCommand(cmd)
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
} else {
|
} else {
|
||||||
|
timer := time.NewTimer(previewCancelWait)
|
||||||
select {
|
select {
|
||||||
case <-time.After(previewCancelWait):
|
case <-timer.C:
|
||||||
util.KillCommand(cmd)
|
util.KillCommand(cmd)
|
||||||
updateChan <- true
|
|
||||||
case <-finishChan:
|
case <-finishChan:
|
||||||
updateChan <- false
|
|
||||||
}
|
}
|
||||||
|
timer.Stop()
|
||||||
}
|
}
|
||||||
|
break Loop
|
||||||
case <-finishChan:
|
case <-finishChan:
|
||||||
updateChan <- false
|
break Loop
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
cmd.Wait()
|
|
||||||
finishChan <- true
|
|
||||||
if out.Len() > 0 || !<-updateChan {
|
|
||||||
t.reqBox.Set(reqPreviewDisplay, previewResult{out.String(), offset})
|
|
||||||
}
|
}
|
||||||
|
timer.Stop()
|
||||||
|
reapChan <- true
|
||||||
|
}(version)
|
||||||
|
|
||||||
|
<-eofChan // Goroutine 1 finished
|
||||||
|
cmd.Wait() // NOTE: We should not call Wait before EOF
|
||||||
|
finishChan <- true // Tell Goroutine 3 to stop
|
||||||
|
<-reapChan // Goroutine 2 and 3 finished
|
||||||
|
<-reapChan
|
||||||
|
} else {
|
||||||
|
// Failed to start the command. Report the error immediately.
|
||||||
|
t.reqBox.Set(reqPreviewDisplay, previewResult{version, []string{err.Error()}, 0, ""})
|
||||||
|
}
|
||||||
|
|
||||||
cleanTemporaryFiles()
|
cleanTemporaryFiles()
|
||||||
} else {
|
} else {
|
||||||
t.reqBox.Set(reqPreviewDisplay, previewResult{"", 0})
|
t.reqBox.Set(reqPreviewDisplay, previewResult{version, nil, 0, ""})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -1772,7 +1979,7 @@ func (t *Terminal) Loop() {
|
|||||||
if len(command) > 0 && t.isPreviewEnabled() {
|
if len(command) > 0 && t.isPreviewEnabled() {
|
||||||
_, list := t.buildPlusList(command, false)
|
_, list := t.buildPlusList(command, false)
|
||||||
t.cancelPreview()
|
t.cancelPreview()
|
||||||
t.previewBox.Set(reqPreviewEnqueue, previewRequest{command, list})
|
t.previewBox.Set(reqPreviewEnqueue, previewRequest{command, t.pwindow, list})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1827,12 +2034,18 @@ func (t *Terminal) Loop() {
|
|||||||
})
|
})
|
||||||
case reqPreviewDisplay:
|
case reqPreviewDisplay:
|
||||||
result := value.(previewResult)
|
result := value.(previewResult)
|
||||||
t.previewer.text = result.content
|
t.previewer.version = result.version
|
||||||
t.previewer.lines = strings.Count(t.previewer.text, "\n")
|
t.previewer.lines = result.lines
|
||||||
t.previewer.offset = util.Constrain(result.offset, 0, t.previewer.lines-1)
|
t.previewer.spinner = result.spinner
|
||||||
|
if result.offset >= 0 {
|
||||||
|
t.previewer.offset = util.Constrain(result.offset, 0, len(t.previewer.lines)-1)
|
||||||
|
}
|
||||||
t.printPreview()
|
t.printPreview()
|
||||||
case reqPreviewRefresh:
|
case reqPreviewRefresh:
|
||||||
t.printPreview()
|
t.printPreview()
|
||||||
|
case reqPreviewDelayed:
|
||||||
|
t.previewer.version = value.(int64)
|
||||||
|
t.printPreviewDelayed()
|
||||||
case reqPrintQuery:
|
case reqPrintQuery:
|
||||||
exit(func() int {
|
exit(func() int {
|
||||||
t.printer(string(t.input))
|
t.printer(string(t.input))
|
||||||
@@ -1885,14 +2098,15 @@ func (t *Terminal) Loop() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
scrollPreview := func(amount int) {
|
scrollPreview := func(amount int) {
|
||||||
if !t.previewer.more {
|
if !t.previewer.scrollable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newOffset := t.previewer.offset + amount
|
newOffset := t.previewer.offset + amount
|
||||||
|
numLines := len(t.previewer.lines)
|
||||||
if t.preview.cycle {
|
if t.preview.cycle {
|
||||||
newOffset = (newOffset + t.previewer.lines) % t.previewer.lines
|
newOffset = (newOffset + numLines) % numLines
|
||||||
}
|
}
|
||||||
newOffset = util.Constrain(newOffset, 0, t.previewer.lines-1)
|
newOffset = util.Constrain(newOffset, 0, numLines-1)
|
||||||
if t.previewer.offset != newOffset {
|
if t.previewer.offset != newOffset {
|
||||||
t.previewer.offset = newOffset
|
t.previewer.offset = newOffset
|
||||||
req(reqPreviewRefresh)
|
req(reqPreviewRefresh)
|
||||||
@@ -1934,7 +2148,7 @@ func (t *Terminal) Loop() {
|
|||||||
if valid {
|
if valid {
|
||||||
t.cancelPreview()
|
t.cancelPreview()
|
||||||
t.previewBox.Set(reqPreviewEnqueue,
|
t.previewBox.Set(reqPreviewEnqueue,
|
||||||
previewRequest{t.preview.command, list})
|
previewRequest{t.preview.command, t.pwindow, list})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,10 @@ func (a Attr) Merge(b Attr) Attr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AttrRegular Attr = Attr(0)
|
AttrUndefined = Attr(0)
|
||||||
|
AttrRegular = Attr(1 << 7)
|
||||||
|
AttrClear = Attr(1 << 8)
|
||||||
|
|
||||||
Bold = Attr(1)
|
Bold = Attr(1)
|
||||||
Dim = Attr(1 << 1)
|
Dim = Attr(1 << 1)
|
||||||
Italic = Attr(1 << 2)
|
Italic = Attr(1 << 2)
|
||||||
@@ -32,7 +35,6 @@ func (r *FullscreenRenderer) Clear() {}
|
|||||||
func (r *FullscreenRenderer) Refresh() {}
|
func (r *FullscreenRenderer) Refresh() {}
|
||||||
func (r *FullscreenRenderer) Close() {}
|
func (r *FullscreenRenderer) Close() {}
|
||||||
|
|
||||||
func (r *FullscreenRenderer) DoesAutoWrap() bool { return false }
|
|
||||||
func (r *FullscreenRenderer) GetChar() Event { return Event{} }
|
func (r *FullscreenRenderer) GetChar() Event { return Event{} }
|
||||||
func (r *FullscreenRenderer) MaxX() int { return 0 }
|
func (r *FullscreenRenderer) MaxX() int { return 0 }
|
||||||
func (r *FullscreenRenderer) MaxY() int { return 0 }
|
func (r *FullscreenRenderer) MaxY() int { return 0 }
|
||||||
|
@@ -624,14 +624,10 @@ func (r *LightRenderer) MaxY() int {
|
|||||||
return r.height
|
return r.height
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LightRenderer) DoesAutoWrap() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
|
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
|
||||||
w := &LightWindow{
|
w := &LightWindow{
|
||||||
renderer: r,
|
renderer: r,
|
||||||
colored: r.theme != nil,
|
colored: r.theme.Colored,
|
||||||
preview: preview,
|
preview: preview,
|
||||||
border: borderStyle,
|
border: borderStyle,
|
||||||
top: top,
|
top: top,
|
||||||
@@ -641,14 +637,12 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
|
|||||||
tabstop: r.tabstop,
|
tabstop: r.tabstop,
|
||||||
fg: colDefault,
|
fg: colDefault,
|
||||||
bg: colDefault}
|
bg: colDefault}
|
||||||
if r.theme != nil {
|
|
||||||
if preview {
|
if preview {
|
||||||
w.fg = r.theme.PreviewFg
|
w.fg = r.theme.PreviewFg.Color
|
||||||
w.bg = r.theme.PreviewBg
|
w.bg = r.theme.PreviewBg.Color
|
||||||
} else {
|
} else {
|
||||||
w.fg = r.theme.Fg
|
w.fg = r.theme.Fg.Color
|
||||||
w.bg = r.theme.Bg
|
w.bg = r.theme.Bg.Color
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w.drawBorder()
|
w.drawBorder()
|
||||||
return w
|
return w
|
||||||
@@ -659,15 +653,46 @@ func (w *LightWindow) drawBorder() {
|
|||||||
case BorderRounded, BorderSharp:
|
case BorderRounded, BorderSharp:
|
||||||
w.drawBorderAround()
|
w.drawBorderAround()
|
||||||
case BorderHorizontal:
|
case BorderHorizontal:
|
||||||
w.drawBorderHorizontal()
|
w.drawBorderHorizontal(true, true)
|
||||||
|
case BorderVertical:
|
||||||
|
w.drawBorderVertical(true, true)
|
||||||
|
case BorderTop:
|
||||||
|
w.drawBorderHorizontal(true, false)
|
||||||
|
case BorderBottom:
|
||||||
|
w.drawBorderHorizontal(false, true)
|
||||||
|
case BorderLeft:
|
||||||
|
w.drawBorderVertical(true, false)
|
||||||
|
case BorderRight:
|
||||||
|
w.drawBorderVertical(false, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *LightWindow) drawBorderHorizontal() {
|
func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
|
||||||
|
if top {
|
||||||
w.Move(0, 0)
|
w.Move(0, 0)
|
||||||
w.CPrint(ColBorder, AttrRegular, repeat(w.border.horizontal, w.width))
|
w.CPrint(ColBorder, repeat(w.border.horizontal, w.width))
|
||||||
|
}
|
||||||
|
if bottom {
|
||||||
w.Move(w.height-1, 0)
|
w.Move(w.height-1, 0)
|
||||||
w.CPrint(ColBorder, AttrRegular, repeat(w.border.horizontal, w.width))
|
w.CPrint(ColBorder, repeat(w.border.horizontal, w.width))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LightWindow) drawBorderVertical(left, right bool) {
|
||||||
|
width := w.width - 2
|
||||||
|
if !left || !right {
|
||||||
|
width++
|
||||||
|
}
|
||||||
|
for y := 0; y < w.height; y++ {
|
||||||
|
w.Move(y, 0)
|
||||||
|
if left {
|
||||||
|
w.CPrint(ColBorder, string(w.border.vertical))
|
||||||
|
}
|
||||||
|
w.CPrint(ColBorder, repeat(' ', width))
|
||||||
|
if right {
|
||||||
|
w.CPrint(ColBorder, string(w.border.vertical))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *LightWindow) drawBorderAround() {
|
func (w *LightWindow) drawBorderAround() {
|
||||||
@@ -676,17 +701,15 @@ func (w *LightWindow) drawBorderAround() {
|
|||||||
if w.preview {
|
if w.preview {
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
w.CPrint(color, AttrRegular,
|
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
|
||||||
string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
|
|
||||||
for y := 1; y < w.height-1; y++ {
|
for y := 1; y < w.height-1; y++ {
|
||||||
w.Move(y, 0)
|
w.Move(y, 0)
|
||||||
w.CPrint(color, AttrRegular, string(w.border.vertical))
|
w.CPrint(color, string(w.border.vertical))
|
||||||
w.CPrint(color, AttrRegular, repeat(' ', w.width-2))
|
w.CPrint(color, repeat(' ', w.width-2))
|
||||||
w.CPrint(color, AttrRegular, string(w.border.vertical))
|
w.CPrint(color, string(w.border.vertical))
|
||||||
}
|
}
|
||||||
w.Move(w.height-1, 0)
|
w.Move(w.height-1, 0)
|
||||||
w.CPrint(color, AttrRegular,
|
w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
|
||||||
string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *LightWindow) csi(code string) {
|
func (w *LightWindow) csi(code string) {
|
||||||
@@ -749,6 +772,9 @@ func (w *LightWindow) MoveAndClear(y int, x int) {
|
|||||||
|
|
||||||
func attrCodes(attr Attr) []string {
|
func attrCodes(attr Attr) []string {
|
||||||
codes := []string{}
|
codes := []string{}
|
||||||
|
if (attr & AttrClear) > 0 {
|
||||||
|
return codes
|
||||||
|
}
|
||||||
if (attr & Bold) > 0 {
|
if (attr & Bold) > 0 {
|
||||||
codes = append(codes, "1")
|
codes = append(codes, "1")
|
||||||
}
|
}
|
||||||
@@ -808,12 +834,8 @@ func cleanse(str string) string {
|
|||||||
return strings.Replace(str, "\x1b", "", -1)
|
return strings.Replace(str, "\x1b", "", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *LightWindow) CPrint(pair ColorPair, attr Attr, text string) {
|
func (w *LightWindow) CPrint(pair ColorPair, text string) {
|
||||||
if !w.colored {
|
w.csiColor(pair.Fg(), pair.Bg(), pair.Attr())
|
||||||
w.csiColor(colDefault, colDefault, attrFor(pair, attr))
|
|
||||||
} else {
|
|
||||||
w.csiColor(pair.Fg(), pair.Bg(), attr)
|
|
||||||
}
|
|
||||||
w.stderrInternal(cleanse(text), false)
|
w.stderrInternal(cleanse(text), false)
|
||||||
w.csi("m")
|
w.csi("m")
|
||||||
}
|
}
|
||||||
@@ -835,7 +857,7 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin
|
|||||||
width := 0
|
width := 0
|
||||||
line := ""
|
line := ""
|
||||||
for _, r := range input {
|
for _, r := range input {
|
||||||
w := util.Max(util.RuneWidth(r, prefixLength+width, 8), 1)
|
w := util.RuneWidth(r, prefixLength+width, 8)
|
||||||
width += w
|
width += w
|
||||||
str := string(r)
|
str := string(r)
|
||||||
if r == '\t' {
|
if r == '\t' {
|
||||||
|
@@ -77,11 +77,13 @@ const (
|
|||||||
Blink = Attr(tcell.AttrBlink)
|
Blink = Attr(tcell.AttrBlink)
|
||||||
Reverse = Attr(tcell.AttrReverse)
|
Reverse = Attr(tcell.AttrReverse)
|
||||||
Underline = Attr(tcell.AttrUnderline)
|
Underline = Attr(tcell.AttrUnderline)
|
||||||
Italic = Attr(tcell.AttrNone) // Not supported
|
Italic = Attr(tcell.AttrItalic)
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AttrRegular Attr = 0
|
AttrUndefined = Attr(0)
|
||||||
|
AttrRegular = Attr(1 << 7)
|
||||||
|
AttrClear = Attr(1 << 8)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
|
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
|
||||||
@@ -166,10 +168,6 @@ func (w *TcellWindow) Y() int {
|
|||||||
return w.lastY
|
return w.lastY
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *FullscreenRenderer) DoesAutoWrap() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *FullscreenRenderer) Clear() {
|
func (r *FullscreenRenderer) Clear() {
|
||||||
_screen.Sync()
|
_screen.Sync()
|
||||||
_screen.Clear()
|
_screen.Clear()
|
||||||
@@ -418,7 +416,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
|||||||
normal = ColPreview
|
normal = ColPreview
|
||||||
}
|
}
|
||||||
return &TcellWindow{
|
return &TcellWindow{
|
||||||
color: r.theme != nil,
|
color: r.theme.Colored,
|
||||||
preview: preview,
|
preview: preview,
|
||||||
top: top,
|
top: top,
|
||||||
left: left,
|
left: left,
|
||||||
@@ -464,27 +462,23 @@ func (w *TcellWindow) MoveAndClear(y int, x int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) Print(text string) {
|
func (w *TcellWindow) Print(text string) {
|
||||||
w.printString(text, w.normal, 0)
|
w.printString(text, w.normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) printString(text string, pair ColorPair, a Attr) {
|
func (w *TcellWindow) printString(text string, pair ColorPair) {
|
||||||
t := text
|
t := text
|
||||||
lx := 0
|
lx := 0
|
||||||
|
a := pair.Attr()
|
||||||
|
|
||||||
var style tcell.Style
|
style := pair.style()
|
||||||
if w.color {
|
if a&AttrClear == 0 {
|
||||||
style = pair.style().
|
|
||||||
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
|
||||||
Underline(a&Attr(tcell.AttrUnderline) != 0)
|
|
||||||
} else {
|
|
||||||
style = w.normal.style().
|
|
||||||
Reverse(a&Attr(tcell.AttrReverse) != 0 || pair == ColCurrent || pair == ColCurrentMatch).
|
|
||||||
Underline(a&Attr(tcell.AttrUnderline) != 0 || pair == ColMatch || pair == ColCurrentMatch)
|
|
||||||
}
|
|
||||||
style = style.
|
style = style.
|
||||||
|
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
||||||
|
Underline(a&Attr(tcell.AttrUnderline) != 0).
|
||||||
|
Italic(a&Attr(tcell.AttrItalic) != 0).
|
||||||
Blink(a&Attr(tcell.AttrBlink) != 0).
|
Blink(a&Attr(tcell.AttrBlink) != 0).
|
||||||
Bold(a&Attr(tcell.AttrBold) != 0).
|
|
||||||
Dim(a&Attr(tcell.AttrDim) != 0)
|
Dim(a&Attr(tcell.AttrDim) != 0)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if len(t) == 0 {
|
if len(t) == 0 {
|
||||||
@@ -517,12 +511,13 @@ func (w *TcellWindow) printString(text string, pair ColorPair, a Attr) {
|
|||||||
w.lastX += lx
|
w.lastX += lx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) CPrint(pair ColorPair, attr Attr, text string) {
|
func (w *TcellWindow) CPrint(pair ColorPair, text string) {
|
||||||
w.printString(text, pair, attr)
|
w.printString(text, pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) FillReturn {
|
func (w *TcellWindow) fillString(text string, pair ColorPair) FillReturn {
|
||||||
lx := 0
|
lx := 0
|
||||||
|
a := pair.Attr()
|
||||||
|
|
||||||
var style tcell.Style
|
var style tcell.Style
|
||||||
if w.color {
|
if w.color {
|
||||||
@@ -535,7 +530,8 @@ func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) FillReturn
|
|||||||
Bold(a&Attr(tcell.AttrBold) != 0).
|
Bold(a&Attr(tcell.AttrBold) != 0).
|
||||||
Dim(a&Attr(tcell.AttrDim) != 0).
|
Dim(a&Attr(tcell.AttrDim) != 0).
|
||||||
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
||||||
Underline(a&Attr(tcell.AttrUnderline) != 0)
|
Underline(a&Attr(tcell.AttrUnderline) != 0).
|
||||||
|
Italic(a&Attr(tcell.AttrItalic) != 0)
|
||||||
|
|
||||||
for _, r := range text {
|
for _, r := range text {
|
||||||
if r == '\n' {
|
if r == '\n' {
|
||||||
@@ -563,12 +559,17 @@ func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) FillReturn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.lastX += lx
|
w.lastX += lx
|
||||||
|
if w.lastX == w.width {
|
||||||
|
w.lastY++
|
||||||
|
w.lastX = 0
|
||||||
|
return FillNextLine
|
||||||
|
}
|
||||||
|
|
||||||
return FillContinue
|
return FillContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) Fill(str string) FillReturn {
|
func (w *TcellWindow) Fill(str string) FillReturn {
|
||||||
return w.fillString(str, w.normal, 0)
|
return w.fillString(str, w.normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
|
func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
|
||||||
@@ -578,11 +579,12 @@ func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
|
|||||||
if bg == colDefault {
|
if bg == colDefault {
|
||||||
bg = w.normal.Bg()
|
bg = w.normal.Bg()
|
||||||
}
|
}
|
||||||
return w.fillString(str, NewColorPair(fg, bg), a)
|
return w.fillString(str, NewColorPair(fg, bg, a))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) drawBorder() {
|
func (w *TcellWindow) drawBorder() {
|
||||||
if w.borderStyle.shape == BorderNone {
|
shape := w.borderStyle.shape
|
||||||
|
if shape == BorderNone {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,17 +604,32 @@ func (w *TcellWindow) drawBorder() {
|
|||||||
style = w.normal.style()
|
style = w.normal.style()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch shape {
|
||||||
|
case BorderRounded, BorderSharp, BorderHorizontal, BorderTop:
|
||||||
for x := left; x < right; x++ {
|
for x := left; x < right; x++ {
|
||||||
_screen.SetContent(x, top, w.borderStyle.horizontal, nil, style)
|
_screen.SetContent(x, top, w.borderStyle.horizontal, nil, style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch shape {
|
||||||
|
case BorderRounded, BorderSharp, BorderHorizontal, BorderBottom:
|
||||||
|
for x := left; x < right; x++ {
|
||||||
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
|
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if w.borderStyle.shape != BorderHorizontal {
|
switch shape {
|
||||||
|
case BorderRounded, BorderSharp, BorderVertical, BorderLeft:
|
||||||
for y := top; y < bot; y++ {
|
for y := top; y < bot; y++ {
|
||||||
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
|
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch shape {
|
||||||
|
case BorderRounded, BorderSharp, BorderVertical, BorderRight:
|
||||||
|
for y := top; y < bot; y++ {
|
||||||
_screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)
|
_screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
switch shape {
|
||||||
|
case BorderRounded, BorderSharp:
|
||||||
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
|
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
|
||||||
_screen.SetContent(right-1, top, w.borderStyle.topRight, nil, style)
|
_screen.SetContent(right-1, top, w.borderStyle.topRight, nil, style)
|
||||||
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
|
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
|
||||||
|
319
src/tui/tui.go
319
src/tui/tui.go
@@ -123,6 +123,15 @@ func (c Color) is24() bool {
|
|||||||
return c > 0 && (c&(1<<24)) > 0
|
return c > 0 && (c&(1<<24)) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ColorAttr struct {
|
||||||
|
Color Color
|
||||||
|
Attr Attr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewColorAttr() ColorAttr {
|
||||||
|
return ColorAttr{Color: colUndefined, Attr: AttrUndefined}
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
colUndefined Color = -2
|
colUndefined Color = -2
|
||||||
colDefault Color = -1
|
colDefault Color = -1
|
||||||
@@ -150,7 +159,7 @@ const (
|
|||||||
type ColorPair struct {
|
type ColorPair struct {
|
||||||
fg Color
|
fg Color
|
||||||
bg Color
|
bg Color
|
||||||
id int
|
attr Attr
|
||||||
}
|
}
|
||||||
|
|
||||||
func HexToColor(rrggbb string) Color {
|
func HexToColor(rrggbb string) Color {
|
||||||
@@ -160,8 +169,8 @@ func HexToColor(rrggbb string) Color {
|
|||||||
return Color((1 << 24) + (r << 16) + (g << 8) + b)
|
return Color((1 << 24) + (r << 16) + (g << 8) + b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewColorPair(fg Color, bg Color) ColorPair {
|
func NewColorPair(fg Color, bg Color, attr Attr) ColorPair {
|
||||||
return ColorPair{fg, bg, -1}
|
return ColorPair{fg, bg, attr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ColorPair) Fg() Color {
|
func (p ColorPair) Fg() Color {
|
||||||
@@ -172,23 +181,59 @@ func (p ColorPair) Bg() Color {
|
|||||||
return p.bg
|
return p.bg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p ColorPair) Attr() Attr {
|
||||||
|
return p.attr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ColorPair) merge(other ColorPair, except Color) ColorPair {
|
||||||
|
dup := p
|
||||||
|
dup.attr = dup.attr.Merge(other.attr)
|
||||||
|
if other.fg != except {
|
||||||
|
dup.fg = other.fg
|
||||||
|
}
|
||||||
|
if other.bg != except {
|
||||||
|
dup.bg = other.bg
|
||||||
|
}
|
||||||
|
return dup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ColorPair) WithAttr(attr Attr) ColorPair {
|
||||||
|
dup := p
|
||||||
|
dup.attr = dup.attr.Merge(attr)
|
||||||
|
return dup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ColorPair) MergeAttr(other ColorPair) ColorPair {
|
||||||
|
return p.WithAttr(other.attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ColorPair) Merge(other ColorPair) ColorPair {
|
||||||
|
return p.merge(other, colUndefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ColorPair) MergeNonDefault(other ColorPair) ColorPair {
|
||||||
|
return p.merge(other, colDefault)
|
||||||
|
}
|
||||||
|
|
||||||
type ColorTheme struct {
|
type ColorTheme struct {
|
||||||
Fg Color
|
Colored bool
|
||||||
Bg Color
|
Input ColorAttr
|
||||||
PreviewFg Color
|
Fg ColorAttr
|
||||||
PreviewBg Color
|
Bg ColorAttr
|
||||||
DarkBg Color
|
PreviewFg ColorAttr
|
||||||
Gutter Color
|
PreviewBg ColorAttr
|
||||||
Prompt Color
|
DarkBg ColorAttr
|
||||||
Match Color
|
Gutter ColorAttr
|
||||||
Current Color
|
Prompt ColorAttr
|
||||||
CurrentMatch Color
|
Match ColorAttr
|
||||||
Spinner Color
|
Current ColorAttr
|
||||||
Info Color
|
CurrentMatch ColorAttr
|
||||||
Cursor Color
|
Spinner ColorAttr
|
||||||
Selected Color
|
Info ColorAttr
|
||||||
Header Color
|
Cursor ColorAttr
|
||||||
Border Color
|
Selected ColorAttr
|
||||||
|
Header ColorAttr
|
||||||
|
Border ColorAttr
|
||||||
}
|
}
|
||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
@@ -214,6 +259,11 @@ const (
|
|||||||
BorderRounded
|
BorderRounded
|
||||||
BorderSharp
|
BorderSharp
|
||||||
BorderHorizontal
|
BorderHorizontal
|
||||||
|
BorderVertical
|
||||||
|
BorderTop
|
||||||
|
BorderBottom
|
||||||
|
BorderLeft
|
||||||
|
BorderRight
|
||||||
)
|
)
|
||||||
|
|
||||||
type BorderStyle struct {
|
type BorderStyle struct {
|
||||||
@@ -286,7 +336,6 @@ type Renderer interface {
|
|||||||
|
|
||||||
MaxX() int
|
MaxX() int
|
||||||
MaxY() int
|
MaxY() int
|
||||||
DoesAutoWrap() bool
|
|
||||||
|
|
||||||
NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window
|
NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window
|
||||||
}
|
}
|
||||||
@@ -308,7 +357,7 @@ type Window interface {
|
|||||||
Move(y int, x int)
|
Move(y int, x int)
|
||||||
MoveAndClear(y int, x int)
|
MoveAndClear(y int, x int)
|
||||||
Print(text string)
|
Print(text string)
|
||||||
CPrint(color ColorPair, attr Attr, text string)
|
CPrint(color ColorPair, text string)
|
||||||
Fill(text string) FillReturn
|
Fill(text string) FillReturn
|
||||||
CFill(fg Color, bg Color, attr Attr, text string) FillReturn
|
CFill(fg Color, bg Color, attr Attr, text string) FillReturn
|
||||||
Erase()
|
Erase()
|
||||||
@@ -339,13 +388,17 @@ var (
|
|||||||
|
|
||||||
ColPrompt ColorPair
|
ColPrompt ColorPair
|
||||||
ColNormal ColorPair
|
ColNormal ColorPair
|
||||||
|
ColInput ColorPair
|
||||||
ColMatch ColorPair
|
ColMatch ColorPair
|
||||||
ColCursor ColorPair
|
ColCursor ColorPair
|
||||||
|
ColCursorEmpty ColorPair
|
||||||
ColSelected ColorPair
|
ColSelected ColorPair
|
||||||
ColCurrent ColorPair
|
ColCurrent ColorPair
|
||||||
ColCurrentMatch ColorPair
|
ColCurrentMatch ColorPair
|
||||||
ColCurrentCursor ColorPair
|
ColCurrentCursor ColorPair
|
||||||
|
ColCurrentCursorEmpty ColorPair
|
||||||
ColCurrentSelected ColorPair
|
ColCurrentSelected ColorPair
|
||||||
|
ColCurrentSelectedEmpty ColorPair
|
||||||
ColSpinner ColorPair
|
ColSpinner ColorPair
|
||||||
ColInfo ColorPair
|
ColInfo ColorPair
|
||||||
ColHeader ColorPair
|
ColHeader ColorPair
|
||||||
@@ -356,22 +409,46 @@ var (
|
|||||||
|
|
||||||
func EmptyTheme() *ColorTheme {
|
func EmptyTheme() *ColorTheme {
|
||||||
return &ColorTheme{
|
return &ColorTheme{
|
||||||
Fg: colUndefined,
|
Colored: true,
|
||||||
Bg: colUndefined,
|
Input: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewFg: colUndefined,
|
Fg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewBg: colUndefined,
|
Bg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
DarkBg: colUndefined,
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Gutter: colUndefined,
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Prompt: colUndefined,
|
DarkBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Match: colUndefined,
|
Gutter: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Current: colUndefined,
|
Prompt: ColorAttr{colUndefined, AttrUndefined},
|
||||||
CurrentMatch: colUndefined,
|
Match: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Spinner: colUndefined,
|
Current: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Info: colUndefined,
|
CurrentMatch: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Cursor: colUndefined,
|
Spinner: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Selected: colUndefined,
|
Info: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Header: colUndefined,
|
Cursor: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Border: colUndefined}
|
Selected: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Header: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Border: ColorAttr{colUndefined, AttrUndefined}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NoColorTheme() *ColorTheme {
|
||||||
|
return &ColorTheme{
|
||||||
|
Colored: false,
|
||||||
|
Input: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Fg: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Bg: ColorAttr{colDefault, AttrRegular},
|
||||||
|
PreviewFg: ColorAttr{colDefault, AttrRegular},
|
||||||
|
PreviewBg: ColorAttr{colDefault, AttrRegular},
|
||||||
|
DarkBg: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Gutter: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Prompt: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Match: ColorAttr{colDefault, Underline},
|
||||||
|
Current: ColorAttr{colDefault, Reverse},
|
||||||
|
CurrentMatch: ColorAttr{colDefault, Reverse | Underline},
|
||||||
|
Spinner: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Info: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Cursor: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Selected: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Header: ColorAttr{colDefault, AttrRegular},
|
||||||
|
Border: ColorAttr{colDefault, AttrRegular}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func errorExit(message string) {
|
func errorExit(message string) {
|
||||||
@@ -381,74 +458,80 @@ func errorExit(message string) {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Default16 = &ColorTheme{
|
Default16 = &ColorTheme{
|
||||||
Fg: colDefault,
|
Colored: true,
|
||||||
Bg: colDefault,
|
Input: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewFg: colUndefined,
|
Fg: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewBg: colUndefined,
|
Bg: ColorAttr{colDefault, AttrUndefined},
|
||||||
DarkBg: colBlack,
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Gutter: colUndefined,
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Prompt: colBlue,
|
DarkBg: ColorAttr{colBlack, AttrUndefined},
|
||||||
Match: colGreen,
|
Gutter: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Current: colYellow,
|
Prompt: ColorAttr{colBlue, AttrUndefined},
|
||||||
CurrentMatch: colGreen,
|
Match: ColorAttr{colGreen, AttrUndefined},
|
||||||
Spinner: colGreen,
|
Current: ColorAttr{colYellow, AttrUndefined},
|
||||||
Info: colWhite,
|
CurrentMatch: ColorAttr{colGreen, AttrUndefined},
|
||||||
Cursor: colRed,
|
Spinner: ColorAttr{colGreen, AttrUndefined},
|
||||||
Selected: colMagenta,
|
Info: ColorAttr{colWhite, AttrUndefined},
|
||||||
Header: colCyan,
|
Cursor: ColorAttr{colRed, AttrUndefined},
|
||||||
Border: colBlack}
|
Selected: ColorAttr{colMagenta, AttrUndefined},
|
||||||
|
Header: ColorAttr{colCyan, AttrUndefined},
|
||||||
|
Border: ColorAttr{colBlack, AttrUndefined}}
|
||||||
Dark256 = &ColorTheme{
|
Dark256 = &ColorTheme{
|
||||||
Fg: colDefault,
|
Colored: true,
|
||||||
Bg: colDefault,
|
Input: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewFg: colUndefined,
|
Fg: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewBg: colUndefined,
|
Bg: ColorAttr{colDefault, AttrUndefined},
|
||||||
DarkBg: 236,
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Gutter: colUndefined,
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Prompt: 110,
|
DarkBg: ColorAttr{236, AttrUndefined},
|
||||||
Match: 108,
|
Gutter: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Current: 254,
|
Prompt: ColorAttr{110, AttrUndefined},
|
||||||
CurrentMatch: 151,
|
Match: ColorAttr{108, AttrUndefined},
|
||||||
Spinner: 148,
|
Current: ColorAttr{254, AttrUndefined},
|
||||||
Info: 144,
|
CurrentMatch: ColorAttr{151, AttrUndefined},
|
||||||
Cursor: 161,
|
Spinner: ColorAttr{148, AttrUndefined},
|
||||||
Selected: 168,
|
Info: ColorAttr{144, AttrUndefined},
|
||||||
Header: 109,
|
Cursor: ColorAttr{161, AttrUndefined},
|
||||||
Border: 59}
|
Selected: ColorAttr{168, AttrUndefined},
|
||||||
|
Header: ColorAttr{109, AttrUndefined},
|
||||||
|
Border: ColorAttr{59, AttrUndefined}}
|
||||||
Light256 = &ColorTheme{
|
Light256 = &ColorTheme{
|
||||||
Fg: colDefault,
|
Colored: true,
|
||||||
Bg: colDefault,
|
Input: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewFg: colUndefined,
|
Fg: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewBg: colUndefined,
|
Bg: ColorAttr{colDefault, AttrUndefined},
|
||||||
DarkBg: 251,
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Gutter: colUndefined,
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Prompt: 25,
|
DarkBg: ColorAttr{251, AttrUndefined},
|
||||||
Match: 66,
|
Gutter: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Current: 237,
|
Prompt: ColorAttr{25, AttrUndefined},
|
||||||
CurrentMatch: 23,
|
Match: ColorAttr{66, AttrUndefined},
|
||||||
Spinner: 65,
|
Current: ColorAttr{237, AttrUndefined},
|
||||||
Info: 101,
|
CurrentMatch: ColorAttr{23, AttrUndefined},
|
||||||
Cursor: 161,
|
Spinner: ColorAttr{65, AttrUndefined},
|
||||||
Selected: 168,
|
Info: ColorAttr{101, AttrUndefined},
|
||||||
Header: 31,
|
Cursor: ColorAttr{161, AttrUndefined},
|
||||||
Border: 145}
|
Selected: ColorAttr{168, AttrUndefined},
|
||||||
|
Header: ColorAttr{31, AttrUndefined},
|
||||||
|
Border: ColorAttr{145, AttrUndefined}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
|
func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
|
||||||
if theme == nil {
|
|
||||||
initPalette(theme)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if forceBlack {
|
if forceBlack {
|
||||||
theme.Bg = colBlack
|
theme.Bg = ColorAttr{colBlack, AttrUndefined}
|
||||||
}
|
}
|
||||||
|
|
||||||
o := func(a Color, b Color) Color {
|
o := func(a ColorAttr, b ColorAttr) ColorAttr {
|
||||||
if b == colUndefined {
|
c := a
|
||||||
return a
|
if b.Color != colUndefined {
|
||||||
|
c.Color = b.Color
|
||||||
}
|
}
|
||||||
return b
|
if b.Attr != AttrUndefined {
|
||||||
|
c.Attr = b.Attr
|
||||||
}
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
theme.Input = o(baseTheme.Input, theme.Input)
|
||||||
theme.Fg = o(baseTheme.Fg, theme.Fg)
|
theme.Fg = o(baseTheme.Fg, theme.Fg)
|
||||||
theme.Bg = o(baseTheme.Bg, theme.Bg)
|
theme.Bg = o(baseTheme.Bg, theme.Bg)
|
||||||
theme.PreviewFg = o(theme.Fg, o(baseTheme.PreviewFg, theme.PreviewFg))
|
theme.PreviewFg = o(theme.Fg, o(baseTheme.PreviewFg, theme.PreviewFg))
|
||||||
@@ -470,54 +553,32 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initPalette(theme *ColorTheme) {
|
func initPalette(theme *ColorTheme) {
|
||||||
idx := 0
|
pair := func(fg, bg ColorAttr) ColorPair {
|
||||||
pair := func(fg, bg Color) ColorPair {
|
if fg.Color == colDefault && (fg.Attr&Reverse) > 0 {
|
||||||
idx++
|
bg.Color = colDefault
|
||||||
return ColorPair{fg, bg, idx}
|
|
||||||
}
|
}
|
||||||
if theme != nil {
|
return ColorPair{fg.Color, bg.Color, fg.Attr}
|
||||||
|
}
|
||||||
|
blank := theme.Fg
|
||||||
|
blank.Attr = AttrRegular
|
||||||
|
|
||||||
ColPrompt = pair(theme.Prompt, theme.Bg)
|
ColPrompt = pair(theme.Prompt, theme.Bg)
|
||||||
ColNormal = pair(theme.Fg, theme.Bg)
|
ColNormal = pair(theme.Fg, theme.Bg)
|
||||||
|
ColInput = pair(theme.Input, theme.Bg)
|
||||||
ColMatch = pair(theme.Match, theme.Bg)
|
ColMatch = pair(theme.Match, theme.Bg)
|
||||||
ColCursor = pair(theme.Cursor, theme.Gutter)
|
ColCursor = pair(theme.Cursor, theme.Gutter)
|
||||||
|
ColCursorEmpty = pair(blank, theme.Gutter)
|
||||||
ColSelected = pair(theme.Selected, theme.Gutter)
|
ColSelected = pair(theme.Selected, theme.Gutter)
|
||||||
ColCurrent = pair(theme.Current, theme.DarkBg)
|
ColCurrent = pair(theme.Current, theme.DarkBg)
|
||||||
ColCurrentMatch = pair(theme.CurrentMatch, theme.DarkBg)
|
ColCurrentMatch = pair(theme.CurrentMatch, theme.DarkBg)
|
||||||
ColCurrentCursor = pair(theme.Cursor, theme.DarkBg)
|
ColCurrentCursor = pair(theme.Cursor, theme.DarkBg)
|
||||||
|
ColCurrentCursorEmpty = pair(blank, theme.DarkBg)
|
||||||
ColCurrentSelected = pair(theme.Selected, theme.DarkBg)
|
ColCurrentSelected = pair(theme.Selected, theme.DarkBg)
|
||||||
|
ColCurrentSelectedEmpty = pair(blank, theme.DarkBg)
|
||||||
ColSpinner = pair(theme.Spinner, theme.Bg)
|
ColSpinner = pair(theme.Spinner, theme.Bg)
|
||||||
ColInfo = pair(theme.Info, theme.Bg)
|
ColInfo = pair(theme.Info, theme.Bg)
|
||||||
ColHeader = pair(theme.Header, theme.Bg)
|
ColHeader = pair(theme.Header, theme.Bg)
|
||||||
ColBorder = pair(theme.Border, theme.Bg)
|
ColBorder = pair(theme.Border, theme.Bg)
|
||||||
ColPreview = pair(theme.PreviewFg, theme.PreviewBg)
|
ColPreview = pair(theme.PreviewFg, theme.PreviewBg)
|
||||||
ColPreviewBorder = pair(theme.Border, theme.PreviewBg)
|
ColPreviewBorder = pair(theme.Border, theme.PreviewBg)
|
||||||
} else {
|
|
||||||
ColPrompt = pair(colDefault, colDefault)
|
|
||||||
ColNormal = pair(colDefault, colDefault)
|
|
||||||
ColMatch = pair(colDefault, colDefault)
|
|
||||||
ColCursor = pair(colDefault, colDefault)
|
|
||||||
ColSelected = pair(colDefault, colDefault)
|
|
||||||
ColCurrent = pair(colDefault, colDefault)
|
|
||||||
ColCurrentMatch = pair(colDefault, colDefault)
|
|
||||||
ColCurrentCursor = pair(colDefault, colDefault)
|
|
||||||
ColCurrentSelected = pair(colDefault, colDefault)
|
|
||||||
ColSpinner = pair(colDefault, colDefault)
|
|
||||||
ColInfo = pair(colDefault, colDefault)
|
|
||||||
ColHeader = pair(colDefault, colDefault)
|
|
||||||
ColBorder = pair(colDefault, colDefault)
|
|
||||||
ColPreview = pair(colDefault, colDefault)
|
|
||||||
ColPreviewBorder = pair(colDefault, colDefault)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func attrFor(color ColorPair, attr Attr) Attr {
|
|
||||||
switch color {
|
|
||||||
case ColCurrent:
|
|
||||||
return attr | Reverse
|
|
||||||
case ColMatch:
|
|
||||||
return attr | Underline
|
|
||||||
case ColCurrentMatch:
|
|
||||||
return attr | Underline | Reverse
|
|
||||||
}
|
|
||||||
return attr
|
|
||||||
}
|
}
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# http://www.rubydoc.info/github/rest-client/rest-client/RestClient
|
|
||||||
require 'rest_client'
|
|
||||||
require 'json'
|
|
||||||
|
|
||||||
if ARGV.length < 3
|
|
||||||
puts "usage: #{$PROGRAM_NAME} <token> <version> <files...>"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
token, version, *files = ARGV
|
|
||||||
base = 'https://api.github.com/repos/junegunn/fzf-bin/releases'
|
|
||||||
|
|
||||||
# List releases
|
|
||||||
rels = JSON.parse(RestClient.get(base, authorization: "token #{token}"))
|
|
||||||
rel = rels.find { |r| r['tag_name'] == version }
|
|
||||||
unless rel
|
|
||||||
puts "#{version} not found"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# List assets
|
|
||||||
assets = Hash[rel['assets'].map { |a| a.values_at('name', 'id') }]
|
|
||||||
|
|
||||||
files.select { |f| File.exist?(f) }.map do |file|
|
|
||||||
Thread.new do
|
|
||||||
name = File.basename(file)
|
|
||||||
|
|
||||||
if asset_id = assets[name] # rubocop:todo Lint/AssignmentInCondition
|
|
||||||
puts "#{name} found. Deleting asset id #{asset_id}."
|
|
||||||
RestClient.delete("#{base}/assets/#{asset_id}",
|
|
||||||
authorization: "token #{token}")
|
|
||||||
else
|
|
||||||
puts "#{name} not found"
|
|
||||||
end
|
|
||||||
|
|
||||||
puts "Uploading #{name}"
|
|
||||||
RestClient.post(
|
|
||||||
"#{base.sub('api', 'uploads')}/#{rel['id']}/assets?name=#{name}",
|
|
||||||
File.read(file),
|
|
||||||
authorization: "token #{token}",
|
|
||||||
content_type: 'application/octet-stream'
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end.each(&:join)
|
|
@@ -11,12 +11,13 @@ require 'tempfile'
|
|||||||
TEMPLATE = DATA.read
|
TEMPLATE = DATA.read
|
||||||
UNSETS = %w[
|
UNSETS = %w[
|
||||||
FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS
|
FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS
|
||||||
|
FZF_TMUX FZF_TMUX_OPTS
|
||||||
FZF_CTRL_T_COMMAND FZF_CTRL_T_OPTS
|
FZF_CTRL_T_COMMAND FZF_CTRL_T_OPTS
|
||||||
FZF_ALT_C_COMMAND
|
FZF_ALT_C_COMMAND
|
||||||
FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
|
FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
|
||||||
fish_history
|
fish_history
|
||||||
].freeze
|
].freeze
|
||||||
DEFAULT_TIMEOUT = 20
|
DEFAULT_TIMEOUT = 10
|
||||||
|
|
||||||
FILE = File.expand_path(__FILE__)
|
FILE = File.expand_path(__FILE__)
|
||||||
BASE = File.expand_path('..', __dir__)
|
BASE = File.expand_path('..', __dir__)
|
||||||
@@ -26,7 +27,7 @@ FZF = "FZF_DEFAULT_OPTS= FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf"
|
|||||||
def wait
|
def wait
|
||||||
since = Time.now
|
since = Time.now
|
||||||
begin
|
begin
|
||||||
yield
|
yield or raise Minitest::Assertion, 'Assertion failure'
|
||||||
rescue Minitest::Assertion
|
rescue Minitest::Assertion
|
||||||
raise if Time.now - since > DEFAULT_TIMEOUT
|
raise if Time.now - since > DEFAULT_TIMEOUT
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ class Tmux
|
|||||||
return unless shell == :fish
|
return unless shell == :fish
|
||||||
|
|
||||||
send_keys 'function fish_prompt; end; clear', :Enter
|
send_keys 'function fish_prompt; end; clear', :Enter
|
||||||
self.until { |lines| raise Minitest::Assertion unless lines.empty? }
|
self.until(&:empty?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def kill
|
def kill
|
||||||
@@ -108,7 +109,7 @@ class Tmux
|
|||||||
class << lines
|
class << lines
|
||||||
def counts
|
def counts
|
||||||
lazy
|
lazy
|
||||||
.map { |l| l.scan(%r{^. ([0-9]+)\/([0-9]+)( \(([0-9]+)\))?}) }
|
.map { |l| l.scan(%r{^. ([0-9]+)/([0-9]+)( \(([0-9]+)\))?}) }
|
||||||
.reject(&:empty?)
|
.reject(&:empty?)
|
||||||
.first&.first&.map(&:to_i)&.values_at(0, 1, 3) || [0, 0, 0]
|
.first&.first&.map(&:to_i)&.values_at(0, 1, 3) || [0, 0, 0]
|
||||||
end
|
end
|
||||||
@@ -149,7 +150,7 @@ class Tmux
|
|||||||
begin
|
begin
|
||||||
self.until do |lines|
|
self.until do |lines|
|
||||||
send_keys ' ', 'C-u', :Enter, 'hello', :Left, :Right
|
send_keys ' ', 'C-u', :Enter, 'hello', :Left, :Right
|
||||||
raise Minitest::Assertion unless lines[-1] == 'hello'
|
lines[-1] == 'hello'
|
||||||
end
|
end
|
||||||
rescue Minitest::Assertion
|
rescue Minitest::Assertion
|
||||||
(tries += 1) < 5 ? retry : raise
|
(tries += 1) < 5 ? retry : raise
|
||||||
@@ -420,7 +421,7 @@ class TestGoFZF < TestBase
|
|||||||
echo ' first second third/') |
|
echo ' first second third/') |
|
||||||
#{fzf(multi && :multi, :x, :nth, 2, :with_nth, '2,-1,1')}",
|
#{fzf(multi && :multi, :x, :nth, 2, :with_nth, '2,-1,1')}",
|
||||||
:Enter
|
:Enter
|
||||||
tmux.until { |lines| assert_equal ' 2/2', lines[-2] }
|
tmux.until { |lines| assert_equal multi ? ' 2/2 (0)' : ' 2/2', lines[-2] }
|
||||||
|
|
||||||
# Transformed list
|
# Transformed list
|
||||||
lines = tmux.capture
|
lines = tmux.capture
|
||||||
@@ -485,7 +486,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys "seq 1 100 | #{fzf!(:multi)} | awk '{print $1 $1}' | #{fzf(:sync)}", :Enter
|
tmux.send_keys "seq 1 100 | #{fzf!(:multi)} | awk '{print $1 $1}' | #{fzf(:sync)}", :Enter
|
||||||
tmux.until { |lines| assert_equal '>', lines[-1] }
|
tmux.until { |lines| assert_equal '>', lines[-1] }
|
||||||
tmux.send_keys 9
|
tmux.send_keys 9
|
||||||
tmux.until { |lines| assert_equal ' 19/100', lines[-2] }
|
tmux.until { |lines| assert_equal ' 19/100 (0)', lines[-2] }
|
||||||
tmux.send_keys :BTab, :BTab, :BTab
|
tmux.send_keys :BTab, :BTab, :BTab
|
||||||
tmux.until { |lines| assert_equal ' 19/100 (3)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 19/100 (3)', lines[-2] }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
@@ -496,7 +497,7 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_tac
|
def test_tac
|
||||||
tmux.send_keys "seq 1 1000 | #{fzf(:tac, :multi)}", :Enter
|
tmux.send_keys "seq 1 1000 | #{fzf(:tac, :multi)}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys :BTab, :BTab, :BTab
|
tmux.send_keys :BTab, :BTab, :BTab
|
||||||
tmux.until { |lines| assert_equal ' 1000/1000 (3)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1000/1000 (3)', lines[-2] }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
@@ -505,9 +506,9 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_tac_sort
|
def test_tac_sort
|
||||||
tmux.send_keys "seq 1 1000 | #{fzf(:tac, :multi)}", :Enter
|
tmux.send_keys "seq 1 1000 | #{fzf(:tac, :multi)}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys '99'
|
tmux.send_keys '99'
|
||||||
tmux.until { |lines| assert_equal ' 28/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 28/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys :BTab, :BTab, :BTab
|
tmux.send_keys :BTab, :BTab, :BTab
|
||||||
tmux.until { |lines| assert_equal ' 28/1000 (3)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 28/1000 (3)', lines[-2] }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
@@ -516,9 +517,9 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_tac_nosort
|
def test_tac_nosort
|
||||||
tmux.send_keys "seq 1 1000 | #{fzf(:tac, :no_sort, :multi)}", :Enter
|
tmux.send_keys "seq 1 1000 | #{fzf(:tac, :no_sort, :multi)}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys '00'
|
tmux.send_keys '00'
|
||||||
tmux.until { |lines| assert_equal ' 10/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 10/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys :BTab, :BTab, :BTab
|
tmux.send_keys :BTab, :BTab, :BTab
|
||||||
tmux.until { |lines| assert_equal ' 10/1000 (3)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 10/1000 (3)', lines[-2] }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
@@ -800,14 +801,14 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_bind
|
def test_bind
|
||||||
tmux.send_keys "seq 1 1000 | #{fzf('-m --bind=ctrl-j:accept,u:up,T:toggle-up,t:toggle')}", :Enter
|
tmux.send_keys "seq 1 1000 | #{fzf('-m --bind=ctrl-j:accept,u:up,T:toggle-up,t:toggle')}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys 'uuu', 'TTT', 'tt', 'uu', 'ttt', 'C-j'
|
tmux.send_keys 'uuu', 'TTT', 'tt', 'uu', 'ttt', 'C-j'
|
||||||
assert_equal %w[4 5 6 9], readonce.lines(chomp: true)
|
assert_equal %w[4 5 6 9], readonce.lines(chomp: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_bind_print_query
|
def test_bind_print_query
|
||||||
tmux.send_keys "seq 1 1000 | #{fzf('-m --bind=ctrl-j:print-query')}", :Enter
|
tmux.send_keys "seq 1 1000 | #{fzf('-m --bind=ctrl-j:print-query')}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys 'print-my-query', 'C-j'
|
tmux.send_keys 'print-my-query', 'C-j'
|
||||||
assert_equal %w[print-my-query], readonce.lines(chomp: true)
|
assert_equal %w[print-my-query], readonce.lines(chomp: true)
|
||||||
end
|
end
|
||||||
@@ -839,7 +840,7 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_select_all_deselect_all_toggle_all
|
def test_select_all_deselect_all_toggle_all
|
||||||
tmux.send_keys "seq 100 | #{fzf('--bind ctrl-a:select-all,ctrl-d:deselect-all,ctrl-t:toggle-all --multi')}", :Enter
|
tmux.send_keys "seq 100 | #{fzf('--bind ctrl-a:select-all,ctrl-d:deselect-all,ctrl-t:toggle-all --multi')}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
|
tmux.until { |lines| assert_equal ' 100/100 (0)', lines[-2] }
|
||||||
tmux.send_keys :BTab, :BTab, :BTab
|
tmux.send_keys :BTab, :BTab, :BTab
|
||||||
tmux.until { |lines| assert_equal ' 100/100 (3)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 100/100 (3)', lines[-2] }
|
||||||
tmux.send_keys 'C-t'
|
tmux.send_keys 'C-t'
|
||||||
@@ -855,7 +856,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys 'C-u'
|
tmux.send_keys 'C-u'
|
||||||
tmux.until { |lines| assert_equal 100, lines.match_count }
|
tmux.until { |lines| assert_equal 100, lines.match_count }
|
||||||
tmux.send_keys 'C-d'
|
tmux.send_keys 'C-d'
|
||||||
tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
|
tmux.until { |lines| assert_equal ' 100/100 (0)', lines[-2] }
|
||||||
tmux.send_keys :BTab, :BTab
|
tmux.send_keys :BTab, :BTab
|
||||||
tmux.until { |lines| assert_equal ' 100/100 (2)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 100/100 (2)', lines[-2] }
|
||||||
tmux.send_keys 0
|
tmux.send_keys 0
|
||||||
@@ -962,7 +963,7 @@ class TestGoFZF < TestBase
|
|||||||
opts = %[--multi --bind "alt-a:execute-multi(echo {}/{+} >> #{output})"]
|
opts = %[--multi --bind "alt-a:execute-multi(echo {}/{+} >> #{output})"]
|
||||||
writelines(tempname, %w[foo'bar foo"bar foo$bar foobar])
|
writelines(tempname, %w[foo'bar foo"bar foo$bar foobar])
|
||||||
tmux.send_keys "cat #{tempname} | #{fzf(opts)}", :Enter
|
tmux.send_keys "cat #{tempname} | #{fzf(opts)}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 4/4', lines[-2] }
|
tmux.until { |lines| assert_equal ' 4/4 (0)', lines[-2] }
|
||||||
tmux.send_keys :Escape, :a
|
tmux.send_keys :Escape, :a
|
||||||
tmux.send_keys :BTab, :BTab, :BTab
|
tmux.send_keys :BTab, :BTab, :BTab
|
||||||
tmux.until { |lines| assert_equal ' 4/4 (3)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 4/4 (3)', lines[-2] }
|
||||||
@@ -997,11 +998,11 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
tmux.send_keys "cat #{tempname} | #{FZF} --multi --bind 'x:execute-silent(echo {+}/{}/{+2}/{2} >> #{output})'", :Enter
|
tmux.send_keys "cat #{tempname} | #{FZF} --multi --bind 'x:execute-silent(echo {+}/{}/{+2}/{2} >> #{output})'", :Enter
|
||||||
|
|
||||||
tmux.until { |lines| assert_equal ' 2/2', lines[-2] }
|
tmux.until { |lines| assert_equal ' 2/2 (0)', lines[-2] }
|
||||||
tmux.send_keys 'xy'
|
tmux.send_keys 'xy'
|
||||||
tmux.until { |lines| assert_equal ' 0/2', lines[-2] }
|
tmux.until { |lines| assert_equal ' 0/2 (0)', lines[-2] }
|
||||||
tmux.send_keys :BSpace
|
tmux.send_keys :BSpace
|
||||||
tmux.until { |lines| assert_equal ' 2/2', lines[-2] }
|
tmux.until { |lines| assert_equal ' 2/2 (0)', lines[-2] }
|
||||||
|
|
||||||
tmux.send_keys :Up
|
tmux.send_keys :Up
|
||||||
tmux.send_keys :Tab
|
tmux.send_keys :Tab
|
||||||
@@ -1362,7 +1363,7 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_jump
|
def test_jump
|
||||||
tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump'")}", :Enter
|
tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump'")}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys 'C-j'
|
tmux.send_keys 'C-j'
|
||||||
tmux.until { |lines| assert_equal '5 5', lines[-7] }
|
tmux.until { |lines| assert_equal '5 5', lines[-7] }
|
||||||
tmux.until { |lines| assert_equal ' 6', lines[-8] }
|
tmux.until { |lines| assert_equal ' 6', lines[-8] }
|
||||||
@@ -1390,7 +1391,7 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_jump_accept
|
def test_jump_accept
|
||||||
tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump-accept'")}", :Enter
|
tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump-accept'")}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
|
||||||
tmux.send_keys 'C-j'
|
tmux.send_keys 'C-j'
|
||||||
tmux.until { |lines| assert_equal '5 5', lines[-7] }
|
tmux.until { |lines| assert_equal '5 5', lines[-7] }
|
||||||
tmux.send_keys '3'
|
tmux.send_keys '3'
|
||||||
@@ -1405,7 +1406,7 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_pointer_with_jump
|
def test_pointer_with_jump
|
||||||
tmux.send_keys "seq 10 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump' --pointer '>>'")}", :Enter
|
tmux.send_keys "seq 10 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump' --pointer '>>'")}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 10/10', lines[-2] }
|
tmux.until { |lines| assert_equal ' 10/10 (0)', lines[-2] }
|
||||||
tmux.send_keys 'C-j'
|
tmux.send_keys 'C-j'
|
||||||
# Correctly padded jump label should appear
|
# Correctly padded jump label should appear
|
||||||
tmux.until { |lines| assert_equal '5 5', lines[-7] }
|
tmux.until { |lines| assert_equal '5 5', lines[-7] }
|
||||||
@@ -1417,7 +1418,7 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_marker
|
def test_marker
|
||||||
tmux.send_keys "seq 10 | #{fzf("--multi --marker '>>'")}", :Enter
|
tmux.send_keys "seq 10 | #{fzf("--multi --marker '>>'")}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 10/10', lines[-2] }
|
tmux.until { |lines| assert_equal ' 10/10 (0)', lines[-2] }
|
||||||
tmux.send_keys :BTab
|
tmux.send_keys :BTab
|
||||||
# Assert that specified marker is displayed
|
# Assert that specified marker is displayed
|
||||||
tmux.until { |lines| assert_equal ' >>1', lines[-3] }
|
tmux.until { |lines| assert_equal ' >>1', lines[-3] }
|
||||||
@@ -1678,7 +1679,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys :Tab
|
tmux.send_keys :Tab
|
||||||
tmux.until { |lines| assert_equal ' 198/198 (1/2)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 198/198 (1/2)', lines[-2] }
|
||||||
tmux.send_keys '555'
|
tmux.send_keys '555'
|
||||||
tmux.until { |lines| assert_equal ' 1/553', lines[-2] }
|
tmux.until { |lines| assert_equal ' 1/553 (0/2)', lines[-2] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_reload_even_when_theres_no_match
|
def test_reload_even_when_theres_no_match
|
||||||
@@ -1713,7 +1714,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys 'foo'
|
tmux.send_keys 'foo'
|
||||||
tmux.until { |lines| assert_equal ' 0/100 (1)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 0/100 (1)', lines[-2] }
|
||||||
tmux.send_keys :Space
|
tmux.send_keys :Space
|
||||||
tmux.until { |lines| assert_equal ' 0/100', lines[-2] }
|
tmux.until { |lines| assert_equal ' 0/100 (0)', lines[-2] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_backward_delete_char_eof
|
def test_backward_delete_char_eof
|
||||||
@@ -1790,20 +1791,20 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_preview_scroll_begin_constant
|
def test_preview_scroll_begin_constant
|
||||||
tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+123", :Enter
|
tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+123", :Enter
|
||||||
tmux.until { |lines| lines.item_count == 1 }
|
tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
|
||||||
tmux.until { |lines| assert_match %r{123.*123/1000}, lines[1] }
|
tmux.until { |lines| assert_match %r{123.*123/1000}, lines[1] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_preview_scroll_begin_expr
|
def test_preview_scroll_begin_expr
|
||||||
tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+{3}", :Enter
|
tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+{3}", :Enter
|
||||||
tmux.until { |lines| lines.item_count == 1 }
|
tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
|
||||||
tmux.until { |lines| assert_match %r{321.*321/1000}, lines[1] }
|
tmux.until { |lines| assert_match %r{321.*321/1000}, lines[1] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_preview_scroll_begin_and_offset
|
def test_preview_scroll_begin_and_offset
|
||||||
['echo foo 123 321', 'echo foo :123: 321'].each do |input|
|
['echo foo 123 321', 'echo foo :123: 321'].each do |input|
|
||||||
tmux.send_keys "#{input} | #{FZF} --preview 'seq 1000' --preview-window left:+{2}-2", :Enter
|
tmux.send_keys "#{input} | #{FZF} --preview 'seq 1000' --preview-window left:+{2}-2", :Enter
|
||||||
tmux.until { |lines| lines.item_count == 1 }
|
tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
|
||||||
tmux.until { |lines| assert_match %r{121.*121/1000}, lines[1] }
|
tmux.until { |lines| assert_match %r{121.*121/1000}, lines[1] }
|
||||||
tmux.send_keys 'C-c'
|
tmux.send_keys 'C-c'
|
||||||
end
|
end
|
||||||
@@ -1816,6 +1817,12 @@ class TestGoFZF < TestBase
|
|||||||
assert_equal %w[A Á], `#{echoes} | #{FZF} -f A`.lines.map(&:chomp)
|
assert_equal %w[A Á], `#{echoes} | #{FZF} -f A`.lines.map(&:chomp)
|
||||||
assert_equal %w[Á], `#{echoes} | #{FZF} -f Á`.lines.map(&:chomp)
|
assert_equal %w[Á], `#{echoes} | #{FZF} -f Á`.lines.map(&:chomp)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_preview_clear_screen
|
||||||
|
tmux.send_keys %{seq 100 | #{FZF} --preview 'for i in $(seq 300); do (( i % 200 == 0 )) && printf "\\033[2J"; echo "[$i]"; sleep 0.001; done'}, :Enter
|
||||||
|
tmux.until { |lines| lines.item_count == 100 }
|
||||||
|
tmux.until { |lines| lines[1]&.include?('[200]') }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module TestShell
|
module TestShell
|
||||||
@@ -1970,7 +1977,7 @@ module CompletionTest
|
|||||||
FileUtils.mkdir_p('/tmp/fzf-test')
|
FileUtils.mkdir_p('/tmp/fzf-test')
|
||||||
FileUtils.mkdir_p('/tmp/fzf test')
|
FileUtils.mkdir_p('/tmp/fzf test')
|
||||||
(1..100).each { |i| FileUtils.touch("/tmp/fzf-test/#{i}") }
|
(1..100).each { |i| FileUtils.touch("/tmp/fzf-test/#{i}") }
|
||||||
['no~such~user', '/tmp/fzf test/foobar', '~/.fzf-home'].each do |f|
|
['no~such~user', '/tmp/fzf test/foobar'].each do |f|
|
||||||
FileUtils.touch(File.expand_path(f))
|
FileUtils.touch(File.expand_path(f))
|
||||||
end
|
end
|
||||||
tmux.prepare
|
tmux.prepare
|
||||||
@@ -1986,14 +1993,15 @@ module CompletionTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
# ~USERNAME**<TAB>
|
# ~USERNAME**<TAB>
|
||||||
|
user = ENV['USER']
|
||||||
tmux.send_keys 'C-u'
|
tmux.send_keys 'C-u'
|
||||||
tmux.send_keys "cat ~#{ENV['USER']}**", :Tab
|
tmux.send_keys "cat ~#{user}**", :Tab
|
||||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys "'.fzf-home"
|
tmux.send_keys "/#{user}"
|
||||||
tmux.until { |lines| assert(lines.any? { |l| l.end_with?('/.fzf-home') }) }
|
tmux.until { |lines| assert(lines.any? { |l| l.end_with?("/#{user}") }) }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until(true) do |lines|
|
tmux.until(true) do |lines|
|
||||||
assert_match %r{cat .*/\.fzf-home}, lines[-1]
|
assert_match %r{cat .*/#{user}}, lines[-1]
|
||||||
end
|
end
|
||||||
|
|
||||||
# ~INVALID_USERNAME**<TAB>
|
# ~INVALID_USERNAME**<TAB>
|
||||||
@@ -2050,7 +2058,7 @@ module CompletionTest
|
|||||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
|
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
|
||||||
|
|
||||||
# Should not match regular files (bash-only)
|
# Should not match regular files (bash-only)
|
||||||
if self.class == TestBash
|
if instance_of?(TestBash)
|
||||||
tmux.send_keys :Tab
|
tmux.send_keys :Tab
|
||||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
|
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
|
||||||
end
|
end
|
||||||
@@ -2241,6 +2249,11 @@ class TestFish < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
|
||||||
|
unset <%= UNSETS.join(' ') %>
|
||||||
|
unset $(env | sed -n /^_fzf_orig/s/=.*//p)
|
||||||
|
unset $(declare -F | sed -n "/_fzf/s/.*-f //p")
|
||||||
|
|
||||||
# Setup fzf
|
# Setup fzf
|
||||||
# ---------
|
# ---------
|
||||||
if [[ ! "$PATH" == *<%= BASE %>/bin* ]]; then
|
if [[ ! "$PATH" == *<%= BASE %>/bin* ]]; then
|
||||||
@@ -2255,9 +2268,6 @@ fi
|
|||||||
# ------------
|
# ------------
|
||||||
source "<%= BASE %>/shell/key-bindings.<%= __method__ %>"
|
source "<%= BASE %>/shell/key-bindings.<%= __method__ %>"
|
||||||
|
|
||||||
PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
|
|
||||||
unset <%= UNSETS.join(' ') %>
|
|
||||||
|
|
||||||
# Old API
|
# Old API
|
||||||
_fzf_complete_f() {
|
_fzf_complete_f() {
|
||||||
_fzf_complete "+m --multi --prompt \"prompt-f> \"" "$@" < <(
|
_fzf_complete "+m --multi --prompt \"prompt-f> \"" "$@" < <(
|
||||||
|
Reference in New Issue
Block a user