Compare commits

...

26 Commits

Author SHA1 Message Date
Junegunn Choi
d01ae55109 0.35.0 2022-11-12 00:58:06 +09:00
Junegunn Choi
8868d7d188 Add --separator to customize the info separator 2022-11-10 16:23:33 +09:00
Junegunn Choi
2eec9892be [neovim] Use Normal group colors for floating window
Instead of NormalFloat.

https://github.com/junegunn/fzf/issues/3035#issuecomment-1305094043
2022-11-08 00:32:41 +09:00
Junegunn Choi
01ae621f11 Add --border=[bold|double] and --preview-window=border-[bold|double] 2022-11-06 14:38:31 +09:00
Junegunn Choi
f984aa0d2c Fix --border-label and --preview-label on tcell renderer 2022-11-06 14:35:20 +09:00
Junegunn Choi
0881a6bc17 [neovim] Do not use Pmenu group colors for floating window
In Neovim, the foreground and background colors of a floating window
defaults to those of Pmenu highlight group, which yields unexpected
results.

This commit makes the colors of fzf window defaults to those of 'Normal'
group (or 'NormalFloat' if defined), by ignoring Pmenu group.

Then the colors can be configured via --color option of fzf.

NOTE: An error from setwinvar call is ignored because the exact
behavior of &winhighlight with an empty target group is not clearly
documented.

Close #3035
Close https://github.com/junegunn/fzf.vim/issues/1431
See https://github.com/neovim/neovim/pull/9722#discussion_r264777602
2022-11-05 16:52:23 +09:00
Junegunn Choi
2c6a73546d Fix rubocop check 2022-11-01 13:59:17 +09:00
Junegunn Choi
a29944660e Fix typo in CHANGELOG 2022-11-01 13:33:09 +09:00
Junegunn Choi
f6ce624c6f Add tests for --border-label and --preview-label
Also fix failing tests due to info separator

Related #3022 #3029
2022-11-01 13:30:41 +09:00
Junegunn Choi
c09ec8e4d1 Allow putting border label on the bottom line
Related #3022
2022-11-01 13:30:40 +09:00
Junegunn Choi
31bbaad06e Add --preview-label and --preview-label-pos
Close #3022
2022-11-01 13:27:22 +09:00
Junegunn Choi
b9ca1fe830 Add horizontal separator after info panel (counter)
Close #3029
2022-11-01 13:27:11 +09:00
Junegunn Choi
e61585f2f3 Add --border-label and --border-label-pos
Close #3022
2022-11-01 13:27:00 +09:00
Junegunn Choi
0de1aacb0c [vim] Fix version check on Windows when shellslash is set 2022-10-31 10:57:05 +09:00
Junegunn Choi
168829b555 Add 'start' event that is triggered once when fzf finder starts
Close #1622
2022-10-27 00:38:38 +09:00
dependabot[bot]
170fc517d0 Use actions/setup-go v3 (#3021)
* Bump actions/setup-go from 3.3.0 to 3.3.1

Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](268d8c0ca0...c4a742cab1)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Apply suggestions from code review

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2022-10-25 15:54:44 +09:00
Naveen
0fbf1c7c71 Add dependency review (#2817)
* chore(deps): Included dependency review

> Dependency Review GitHub Action in your repository to enforce dependency
> reviews on your pull requests.
> The action scans for vulnerable versions of dependencies introduced by package version
> changes in pull requests,
> and warns you about the associated security vulnerabilities.
> This gives you better visibility of what's changing in a pull request,
> and helps prevent vulnerabilities being added to your repository.

https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com>

* Update .github/workflows/depsreview.yaml

Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com>
Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2022-10-25 09:34:58 +09:00
dependabot[bot]
694be39c71 Use ruby/setup-ruby v1 (#3012)
* Bump ruby/setup-ruby from 1.117.0 to 1.118.0

Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.117.0 to 1.118.0.
- [Release notes](https://github.com/ruby/setup-ruby/releases)
- [Commits](3068fa83f9...eae47962ba)

---
updated-dependencies:
- dependency-name: ruby/setup-ruby
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Apply suggestions from code review

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2022-10-24 20:07:31 +09:00
dependabot[bot]
dad26d81df Use github/codeql-action@v2 (#2998)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2022-10-16 22:36:59 +09:00
dependabot[bot]
bcaea097ea Bump actions/checkout from 61b9e3751b92087fd0b06925ba6dd6314e06f089 to v3 (#2997)
* Bump actions/checkout

Bumps [actions/checkout](https://github.com/actions/checkout) from 61b9e3751b92087fd0b06925ba6dd6314e06f089 to 3.1.0. This release includes the previously tagged commit.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](61b9e3751b...93ea575cb5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Apply suggestions from code review

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2022-10-16 22:33:49 +09:00
Junegunn Choi
d56fe74e24 Add checksums of Darwin binaries
Close #2989
2022-10-16 22:06:40 +09:00
John Fred Fadrigalan
4603d540c3 [shell] Make bash/zsh completion and bindings work with 'set -u' (#2999)
Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2022-10-16 17:15:19 +09:00
Junegunn Choi
f9d53303bb [vim] Remove unnecessary powershell check
&shell is guaranteed to be cmd.exe on windows because we call s:use_sh()
2022-10-13 14:48:32 +09:00
Junegunn Choi
d04faa6505 [vim] Fix escaping of fzf binary path containing spaces on Windows
Fix #2992
2022-10-12 20:07:58 +09:00
Kyle L. Davis
07da058eae [vim] Update fzf#install to handle spaces on Windows (#2993) 2022-10-08 19:20:39 +09:00
Bruno Heridet
cefa6b9878 doc(man): add a hint about which UI element is the finder info (#2991)
While reading the description of the --info flag, it's not
immediately obvious that the "finder info" is in fact the
UI element representing the match counters.
2022-10-04 09:52:28 +09:00
24 changed files with 832 additions and 305 deletions

View File

@@ -27,18 +27,18 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@86f3159a697a097a813ad9bfa0002412d97690a4 uses: github/codeql-action/init@v2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@86f3159a697a097a813ad9bfa0002412d97690a4 uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@86f3159a697a097a813ad9bfa0002412d97690a4 uses: github/codeql-action/analyze@v2

14
.github/workflows/depsreview.yaml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: 'Dependency Review'
uses: actions/dependency-review-action@v2

View File

@@ -15,17 +15,17 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up Go - name: Set up Go
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2 uses: actions/setup-go@v3
with: with:
go-version: 1.19 go-version: 1.19
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@3068fa83f9cbd7ae106cac45483635a2f3a195c9 uses: ruby/setup-ruby@v1
with: with:
ruby-version: 3.0.0 ruby-version: 3.0.0

View File

@@ -15,17 +15,17 @@ jobs:
build: build:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up Go - name: Set up Go
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2 uses: actions/setup-go@v3
with: with:
go-version: 1.18 go-version: 1.18
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@3068fa83f9cbd7ae106cac45483635a2f3a195c9 uses: ruby/setup-ruby@v1
with: with:
ruby-version: 3.0.0 ruby-version: 3.0.0

View File

@@ -72,6 +72,7 @@ builds:
- amd64 - amd64
- arm - arm
- arm64 - arm64
- loong64
goarm: goarm:
- 5 - 5
- 6 - 6
@@ -99,6 +100,10 @@ archives:
files: files:
- non-existent* - non-existent*
checksum:
extra_files:
- glob: ./dist/fzf-*darwin*.zip
release: release:
github: github:
owner: junegunn owner: junegunn

View File

@@ -26,3 +26,5 @@ Style/OptionalBooleanParameter:
Enabled: false Enabled: false
Style/WordArray: Style/WordArray:
MinSize: 1 MinSize: 1
Minitest/AssertEqual:
Enabled: false

View File

@@ -1,6 +1,48 @@
CHANGELOG CHANGELOG
========= =========
0.35.0
------
- Added `start` event that is triggered only once when fzf finder starts.
Since fzf consumes the input stream asynchronously, the input list is not
available unless you use `--sync`.
```sh
seq 100 | fzf --multi --sync --bind 'start:last+select-all+preview(echo welcome)'
```
- Added `--border-label` and `--border-label-pos` for putting label on the border
```sh
# ANSI color codes are supported
# (with https://github.com/busyloop/lolcat)
label=$(curl -s http://metaphorpsum.com/sentences/1 | lolcat -f)
# Border label at the center
fzf --height=10 --border --border-label="╢ $label ╟" --color=label:italic:black
# Left-aligned (positive integer)
fzf --height=10 --border --border-label="╢ $label ╟" --border-label-pos=3 --color=label:italic:black
# Right-aligned (negative integer) on the bottom line (:bottom)
fzf --height=10 --border --border-label="╢ $label ╟" --border-label-pos=-3:bottom --color=label:italic:black
```
- Also added `--preview-label` and `--preview-label-pos` for the border of the
preview window
```sh
fzf --preview 'cat {}' --border --preview-label=' Preview ' --preview-label-pos=2
```
- Info panel (match counter) will be followed by a horizontal separator by
default
- Use `--no-separator` or `--separator=''` to hide the separator
- You can specify an arbitrary string that is repeated to form the
horizontal separator. e.g. `--separator=╸`
- The color of the separator can be customized via `--color=separator:...`
- ANSI color codes are also supported
```sh
fzf --separator=╸ --color=separator:green
fzf --separator=$(lolcat -f -F 1.4 <<< ▁▁▂▃▄▅▆▆▅▄▃▂▁▁) --info=inline
```
- Added `--border=bold` and `--border=double` along with
`--preview-window=border-bold` and `--preview-window=border-double`
0.34.0 0.34.0
------ ------
- Added support for adaptive `--height`. If the `--height` value is prefixed - Added support for adaptive `--height`. If the `--height` value is prefixed

33
install
View File

@@ -2,7 +2,7 @@
set -u set -u
version=0.34.0 version=0.35.0
auto_completion= auto_completion=
key_bindings= key_bindings=
update_config=2 update_config=2
@@ -168,21 +168,22 @@ archi=$(uname -sm)
binary_available=1 binary_available=1
binary_error="" binary_error=""
case "$archi" in case "$archi" in
Darwin\ arm64) download fzf-$version-darwin_arm64.zip ;; Darwin\ arm64) download fzf-$version-darwin_arm64.zip ;;
Darwin\ x86_64) download fzf-$version-darwin_amd64.zip ;; Darwin\ x86_64) download fzf-$version-darwin_amd64.zip ;;
Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;; Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;;
Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;; Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;;
Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;; Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;;
Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;; Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;;
Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;; Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;;
Linux\ *64) download fzf-$version-linux_amd64.tar.gz ;; Linux\ loongarch64) download fzf-$version-linux_loong64.tar.gz ;;
FreeBSD\ *64) download fzf-$version-freebsd_amd64.tar.gz ;; Linux\ *64) download fzf-$version-linux_amd64.tar.gz ;;
OpenBSD\ *64) download fzf-$version-openbsd_amd64.tar.gz ;; FreeBSD\ *64) download fzf-$version-freebsd_amd64.tar.gz ;;
CYGWIN*\ *64) download fzf-$version-windows_amd64.zip ;; OpenBSD\ *64) download fzf-$version-openbsd_amd64.tar.gz ;;
MINGW*\ *64) download fzf-$version-windows_amd64.zip ;; CYGWIN*\ *64) download fzf-$version-windows_amd64.zip ;;
MSYS*\ *64) download fzf-$version-windows_amd64.zip ;; MINGW*\ *64) download fzf-$version-windows_amd64.zip ;;
Windows*\ *64) download fzf-$version-windows_amd64.zip ;; MSYS*\ *64) download fzf-$version-windows_amd64.zip ;;
*) binary_available=0 binary_error=1 ;; Windows*\ *64) download fzf-$version-windows_amd64.zip ;;
*) binary_available=0 binary_error=1 ;;
esac esac
cd "$fzf_base" cd "$fzf_base"

View File

@@ -1,4 +1,4 @@
$version="0.34.0" $version="0.35.0"
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition $fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition

View File

@@ -5,7 +5,7 @@ import (
"github.com/junegunn/fzf/src/protector" "github.com/junegunn/fzf/src/protector"
) )
var version string = "0.34" var version string = "0.35"
var revision string = "devel" var revision string = "devel"
func main() { func main() {

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf-tmux 1 "Sep 2022" "fzf 0.34.0" "fzf-tmux - open fzf in tmux split pane" .TH fzf-tmux 1 "Nov 2022" "fzf 0.35.0" "fzf-tmux - open fzf in tmux split pane"
.SH NAME .SH NAME
fzf-tmux - open fzf in tmux split pane fzf-tmux - open fzf in tmux split pane

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf 1 "Sep 2022" "fzf 0.34.0" "fzf - a command-line fuzzy finder" .TH fzf 1 "Nov 2022" "fzf 0.35.0" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@@ -211,6 +211,10 @@ Draw border around the finder
.br .br
.BR sharp " Border with sharp corners" .BR sharp " Border with sharp corners"
.br .br
.BR bold " Border with bold lines"
.br
.BR double " Border with double lines"
.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 vertical " Vertical lines on each side of the finder"
@@ -226,6 +230,50 @@ Draw border around the finder
.BR none .BR none
.br .br
.TP
.BI "--border-label" [=LABEL]
Label to print on the horizontal border line. Should be used with one of the
following \fB--border\fR options.
.br
.B * rounded
.br
.B * sharp
.br
.B * bold
.br
.B * double
.br
.B * horizontal
.br
.BR "* top" " (up)"
.br
.BR "* bottom" " (down)"
.br
.br
e.g.
\fB# ANSI color codes are supported
# (with https://github.com/busyloop/lolcat)
label=$(curl -s http://metaphorpsum.com/sentences/1 | lolcat -f)
# Border label at the center
fzf --height=10 --border --border-label="╢ $label ╟" --color=label:italic:black
# Left-aligned (positive integer)
fzf --height=10 --border --border-label="╢ $label ╟" --border-label-pos=3 --color=label:italic:black
# Right-aligned (negative integer) on the bottom line (:bottom)
fzf --height=10 --border --border-label="╢ $label ╟" --border-label-pos=-3:bottom --color=label:italic:black\fR
.TP
.BI "--border-label-pos" [=N[:top|bottom]]
Position of the border label on the border line. Specify a positive integer as
the column position from the left. Specify a negative integer to right-align
the label. Label is printed on the top border line by default, add
\fB:bottom\fR to put it on the border line on the bottom. The default value
\fB0 (or \fBcenter\fR) will put the label at the center of the border line.
.TP .TP
.B "--no-unicode" .B "--no-unicode"
Use ASCII characters instead of Unicode box drawing characters to draw border Use ASCII characters instead of Unicode box drawing characters to draw border
@@ -281,7 +329,7 @@ e.g.
.TP .TP
.BI "--info=" "STYLE" .BI "--info=" "STYLE"
Determines the display style of finder info. Determines the display style of finder info (match counters).
.br .br
.BR default " Display on the next line to the prompt" .BR default " Display on the next line to the prompt"
@@ -295,6 +343,18 @@ Determines the display style of finder info.
.B "--no-info" .B "--no-info"
A synonym for \fB--info=hidden\fB A synonym for \fB--info=hidden\fB
.TP
.BI "--separator=" "STR"
The given string will be repeated to form the horizontal separator on the info
line (default: '─' or '-' depending on \fB--no-unicode\fR).
ANSI color codes are supported.
.TP
.B "--no-separator"
Do not display horizontal separator on the info line. A synonym for
\fB--separator=''\fB
.TP .TP
.BI "--prompt=" "STR" .BI "--prompt=" "STR"
Input prompt (default: '> ') Input prompt (default: '> ')
@@ -355,7 +415,9 @@ color mappings.
\fBquery \fRQuery string \fBquery \fRQuery string
\fBdisabled \fRQuery string when search is disabled \fBdisabled \fRQuery string when search is disabled
\fBinfo \fRInfo line (match counters) \fBinfo \fRInfo line (match counters)
\fBseparator \fRHorizontal separator on info line (match counters)
\fBborder \fRBorder around the window (\fB--border\fR and \fB--preview\fR) \fBborder \fRBorder around the window (\fB--border\fR and \fB--preview\fR)
\fBlabel \fRBorder label (\fB--border-label\fR and \fB--preview-label\fR)
\fBprompt \fRPrompt \fBprompt \fRPrompt
\fBpointer \fRPointer to the current line \fBpointer \fRPointer to the current line
\fBmarker \fRMulti-select marker \fBmarker \fRMulti-select marker
@@ -486,6 +548,37 @@ e.g.
sleep 0.01 sleep 0.01
done'\fR done'\fR
.RE .RE
.TP
.BI "--preview-label" [=LABEL]
Label to print on the horizontal border line of the preview window.
Should be used with one of the following \fB--preview-window\fR options.
.br
.B * border-rounded (default)
.br
.B * border-sharp
.br
.B * border-bold
.br
.B * border-double
.br
.B * border-horizontal
.br
.B * border-top
.br
.B * border-bottom
.br
.TP
.BI "--preview-label-pos" [=N[:top|bottom]]
Position of the border label on the border line of the preview window. Specify
a positive integer as the column position from the left. Specify a negative
integer to right-align the label. Label is printed on the top border line by
default, add \fB:bottom\fR to put it on the border line on the bottom. The
default value 0 (or \fBcenter\fR) will put the label at the center of the
border line.
.TP .TP
.BI "--preview-window=" "[POSITION][,SIZE[%]][,border-BORDER_OPT][,[no]wrap][,[no]follow][,[no]cycle][,[no]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]" .BI "--preview-window=" "[POSITION][,SIZE[%]][,border-BORDER_OPT][,[no]wrap][,[no]follow][,[no]cycle][,[no]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]"
@@ -811,6 +904,15 @@ e.g.
or any single character or any single character
.SS AVAILABLE EVENTS: .SS AVAILABLE EVENTS:
\fIstart\fR
.RS
Triggered only once when fzf finder starts. Since fzf consumes the input stream
asynchronously, the input list is not available unless you use \fI--sync\fR.
e.g.
\fB# Move cursor to the last item and select all items
seq 1000 | fzf --multi --sync --bind start:last+select-all\fR
.RE
\fIchange\fR \fIchange\fR
.RS .RS
Triggered whenever the query string is changed Triggered whenever the query string is changed

View File

@@ -143,7 +143,7 @@ function! fzf#install()
if !filereadable(script) if !filereadable(script)
throw script.' not found' throw script.' not found'
endif endif
let script = 'powershell -ExecutionPolicy Bypass -file ' . script let script = 'powershell -ExecutionPolicy Bypass -file ' . shellescape(script)
else else
let script = s:base_dir.'/install' let script = s:base_dir.'/install'
if !executable(script) if !executable(script)
@@ -164,7 +164,7 @@ function s:get_version(bin)
if has_key(s:versions, a:bin) if has_key(s:versions, a:bin)
return s:versions[a:bin] return s:versions[a:bin]
end end
let command = (&shell =~ 'powershell' ? '&' : '') . shellescape(a:bin) . ' --version --no-height' let command = (&shell =~ 'powershell' ? '&' : '') . s:fzf_call('shellescape', a:bin) . ' --version --no-height'
let output = systemlist(command) let output = systemlist(command)
if v:shell_error || empty(output) if v:shell_error || empty(output)
return '' return ''
@@ -464,7 +464,7 @@ try
let temps = { 'result': s:fzf_tempname() } let temps = { 'result': s:fzf_tempname() }
let optstr = s:evaluate_opts(get(dict, 'options', '')) let optstr = s:evaluate_opts(get(dict, 'options', ''))
try try
let fzf_exec = fzf#shellescape(fzf#exec()) let fzf_exec = shellescape(fzf#exec())
catch catch
throw v:exception throw v:exception
endtry endtry
@@ -973,16 +973,16 @@ function! s:callback(dict, lines) abort
endfunction endfunction
if has('nvim') if has('nvim')
function s:create_popup(hl, opts) abort function s:create_popup(opts) abort
let buf = nvim_create_buf(v:false, v:true) let buf = nvim_create_buf(v:false, v:true)
let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts) let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts)
let win = nvim_open_win(buf, v:true, opts) let win = nvim_open_win(buf, v:true, opts)
call setwinvar(win, '&winhighlight', 'NormalFloat:'..a:hl) silent! call setwinvar(win, '&winhighlight', 'Pmenu:,Normal:Normal')
call setwinvar(win, '&colorcolumn', '') call setwinvar(win, '&colorcolumn', '')
return buf return buf
endfunction endfunction
else else
function! s:create_popup(hl, opts) abort function! s:create_popup(opts) abort
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,
@@ -1017,7 +1017,7 @@ function! s:popup(opts) abort
let row += !has('nvim') let row += !has('nvim')
let col += !has('nvim') let col += !has('nvim')
call s:create_popup('Normal', { call s:create_popup({
\ 'row': row, 'col': col, 'width': width, 'height': height \ 'row': row, 'col': col, 'width': width, 'height': height
\ }) \ })
endfunction endfunction

View File

@@ -37,7 +37,7 @@ bind '"\e[0n": redraw-current-line' 2> /dev/null
__fzf_comprun() { __fzf_comprun() {
if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
_fzf_comprun "$@" _fzf_comprun "$@"
elif [[ -n "$TMUX_PANE" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "$FZF_TMUX_OPTS" ]]; }; then elif [[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; }; then
shift shift
fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@" fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@"
else else
@@ -55,8 +55,8 @@ __fzf_orig_completion() {
cmd="${BASH_REMATCH[3]}" cmd="${BASH_REMATCH[3]}"
[[ "$f" = _fzf_* ]] && continue [[ "$f" = _fzf_* ]] && continue
printf -v "_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" "%s" "${comp} %s ${cmd} #${f}" printf -v "_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" "%s" "${comp} %s ${cmd} #${f}"
if [[ "$l" = *" -o nospace "* ]] && [[ ! "$__fzf_nospace_commands" = *" $cmd "* ]]; then if [[ "$l" = *" -o nospace "* ]] && [[ ! "${__fzf_nospace_commands-}" = *" $cmd "* ]]; then
__fzf_nospace_commands="$__fzf_nospace_commands $cmd " __fzf_nospace_commands="${__fzf_nospace_commands-} $cmd "
fi fi
fi fi
done done
@@ -139,17 +139,18 @@ _fzf_handle_dynamic_completion() {
shift shift
orig_cmd="$1" orig_cmd="$1"
orig_var="_fzf_orig_completion_$cmd" orig_var="_fzf_orig_completion_$cmd"
orig="${!orig_var##*#}" orig="${!orig_var-}"
orig="${orig##*#}"
if [[ -n "$orig" ]] && type "$orig" > /dev/null 2>&1; then if [[ -n "$orig" ]] && type "$orig" > /dev/null 2>&1; then
$orig "$@" $orig "$@"
elif [[ -n "$_fzf_completion_loader" ]]; then elif [[ -n "${_fzf_completion_loader-}" ]]; then
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null) orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
_completion_loader "$@" _completion_loader "$@"
ret=$? ret=$?
# _completion_loader may not have updated completion for the command # _completion_loader may not have updated completion for the command
if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
__fzf_orig_completion < <(complete -p "$orig_cmd" 2> /dev/null) __fzf_orig_completion < <(complete -p "$orig_cmd" 2> /dev/null)
if [[ "$__fzf_nospace_commands" = *" $orig_cmd "* ]]; then if [[ "${__fzf_nospace_commands-}" = *" $orig_cmd "* ]]; then
eval "${orig_complete/ -F / -o nospace -F }" eval "${orig_complete/ -F / -o nospace -F }"
else else
eval "$orig_complete" eval "$orig_complete"
@@ -173,6 +174,7 @@ __fzf_generic_path_completion() {
base=${cur:0:${#cur}-${#trigger}} base=${cur:0:${#cur}-${#trigger}}
eval "base=$base" eval "base=$base"
dir=
[[ $base = *"/"* ]] && dir="$base" [[ $base = *"/"* ]] && dir="$base"
while true; do while true; do
if [[ -z "$dir" ]] || [[ -d "$dir" ]]; then if [[ -z "$dir" ]] || [[ -d "$dir" ]]; then
@@ -180,11 +182,11 @@ __fzf_generic_path_completion() {
leftover=${leftover/#\/} leftover=${leftover/#\/}
[[ -z "$dir" ]] && dir='.' [[ -z "$dir" ]] && dir='.'
[[ "$dir" != "/" ]] && dir="${dir/%\//}" [[ "$dir" != "/" ]] && dir="${dir/%\//}"
matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do
printf "%q " "${item%$3}$3" printf "%q " "${item%$3}$3"
done) done)
matches=${matches% } matches=${matches% }
[[ -z "$3" ]] && [[ "$__fzf_nospace_commands" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches " [[ -z "$3" ]] && [[ "${__fzf_nospace_commands-}" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
if [[ -n "$matches" ]]; then if [[ -n "$matches" ]]; then
COMPREPLY=( "$matches" ) COMPREPLY=( "$matches" )
else else
@@ -236,7 +238,7 @@ _fzf_complete() {
if [[ "$cur" == *"$trigger" ]]; then if [[ "$cur" == *"$trigger" ]]; then
cur=${cur:0:${#cur}-${#trigger}} cur=${cur:0:${#cur}-${#trigger}}
selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ') selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $str_arg" __fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
selected=${selected% } # Strip trailing space not to repeat "-o nospace" selected=${selected% } # Strip trailing space not to repeat "-o nospace"
if [[ -n "$selected" ]]; then if [[ -n "$selected" ]]; then
COMPREPLY=("$selected") COMPREPLY=("$selected")
@@ -329,7 +331,7 @@ __fzf_defc() {
func="$2" func="$2"
opts="$3" opts="$3"
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
orig="${!orig_var}" orig="${!orig_var-}"
if [[ -n "$orig" ]]; then if [[ -n "$orig" ]]; then
printf -v def "$orig" "$func" printf -v def "$orig" "$func"
eval "$def" eval "$def"

View File

@@ -99,9 +99,9 @@ fi
__fzf_comprun() { __fzf_comprun() {
if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
_fzf_comprun "$@" _fzf_comprun "$@"
elif [ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; }; then elif [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; }; then
shift shift
if [ -n "$FZF_TMUX_OPTS" ]; then if [ -n "${FZF_TMUX_OPTS-}" ]; then
fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@" fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@"
else else
fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@" fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@"
@@ -145,7 +145,7 @@ __fzf_generic_path_completion() {
leftover=${leftover/#\/} leftover=${leftover/#\/}
[ -z "$dir" ] && dir='.' [ -z "$dir" ] && dir='.'
[ "$dir" != "/" ] && dir="${dir/%\//}" [ "$dir" != "/" ] && dir="${dir/%\//}"
matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-}" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
item="${item%$suffix}$suffix" item="${item%$suffix}$suffix"
echo -n "${(q)item} " echo -n "${(q)item} "
done) done)
@@ -184,7 +184,7 @@ _fzf_complete() {
args=("$@") args=("$@")
sep= sep=
for i in {0..${#args[@]}}; do for i in {0..${#args[@]}}; do
if [[ "${args[$i]}" = -- ]]; then if [[ "${args[$i]-}" = -- ]]; then
sep=$i sep=$i
break break
fi fi
@@ -208,7 +208,7 @@ _fzf_complete() {
type $post > /dev/null 2>&1 || post=cat type $post > /dev/null 2>&1 || post=cat
_fzf_feed_fifo "$fifo" _fzf_feed_fifo "$fifo"
matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ') matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $str_arg" __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
if [ -n "$matches" ]; then if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches" LBUFFER="$lbuf$matches"
fi fi
@@ -279,8 +279,8 @@ fzf-completion() {
[ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("") [ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
# When the trigger starts with ';', it becomes a separate token # When the trigger starts with ';', it becomes a separate token
if [[ ${LBUFFER} = *"${tokens[-2]}${tokens[-1]}" ]]; then if [[ ${LBUFFER} = *"${tokens[-2]-}${tokens[-1]}" ]]; then
tokens[-2]="${tokens[-2]}${tokens[-1]}" tokens[-2]="${tokens[-2]-}${tokens[-1]}"
tokens=(${tokens[0,-2]}) tokens=(${tokens[0,-2]})
fi fi

View File

@@ -19,7 +19,7 @@ __fzf_select__() {
-o -type f -print \ -o -type f -print \
-o -type d -print \ -o -type d -print \
-o -type l -print 2> /dev/null | cut -b3-"}" -o -type l -print 2> /dev/null | cut -b3-"}"
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS -m" opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m"
eval "$cmd" | eval "$cmd" |
FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" |
while read -r item; do while read -r item; do
@@ -30,7 +30,7 @@ __fzf_select__() {
if [[ $- =~ i ]]; then if [[ $- =~ i ]]; then
__fzfcmd() { __fzfcmd() {
[[ -n "$TMUX_PANE" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "$FZF_TMUX_OPTS" ]]; } && [[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; } &&
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf" echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
} }
@@ -44,13 +44,13 @@ __fzf_cd__() {
local cmd opts dir local cmd opts dir
cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \ cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
-o -type d -print 2> /dev/null | cut -b3-"}" -o -type d -print 2> /dev/null | cut -b3-"}"
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS +m" opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-} +m"
dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)) && printf 'builtin cd -- %q' "$dir" dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)) && printf 'builtin cd -- %q' "$dir"
} }
__fzf_history__() { __fzf_history__() {
local output opts script local output opts script
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m --read0" opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort ${FZF_CTRL_R_OPTS-} +m --read0"
script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++' script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++'
output=$( output=$(
builtin fc -lnr -2147483648 | builtin fc -lnr -2147483648 |

View File

@@ -46,7 +46,7 @@ __fsel() {
-o -type l -print 2> /dev/null | cut -b3-"}" -o -type l -print 2> /dev/null | cut -b3-"}"
setopt localoptions pipefail no_aliases 2> /dev/null setopt localoptions pipefail no_aliases 2> /dev/null
local item local item
eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS" $(__fzfcmd) -m "$@" | while read item; do eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-}" $(__fzfcmd) -m "$@" | while read item; do
echo -n "${(q)item} " echo -n "${(q)item} "
done done
local ret=$? local ret=$?
@@ -55,7 +55,7 @@ __fsel() {
} }
__fzfcmd() { __fzfcmd() {
[ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; } && [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; } &&
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf" echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
} }
@@ -75,7 +75,7 @@ fzf-cd-widget() {
local cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \ local cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
-o -type d -print 2> /dev/null | cut -b3-"}" -o -type d -print 2> /dev/null | cut -b3-"}"
setopt localoptions pipefail no_aliases 2> /dev/null setopt localoptions pipefail no_aliases 2> /dev/null
local dir="$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) +m)" local dir="$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-}" $(__fzfcmd) +m)"
if [[ -z "$dir" ]]; then if [[ -z "$dir" ]]; then
zle redisplay zle redisplay
return 0 return 0
@@ -98,7 +98,7 @@ fzf-history-widget() {
local selected num local selected num
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
selected=( $(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' | selected=( $(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) ) FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
local ret=$? local ret=$?
if [ -n "$selected" ]; then if [ -n "$selected" ]; then
num=$selected[1] num=$selected[1]

View File

@@ -18,97 +18,107 @@ import (
const usage = `usage: fzf [options] const usage = `usage: fzf [options]
Search Search
-x, --extended Extended-search mode -x, --extended Extended-search mode
(enabled by default; +x or --no-extended to disable) (enabled by default; +x or --no-extended to disable)
-e, --exact Enable Exact-match -e, --exact Enable Exact-match
-i Case-insensitive match (default: smart-case match) -i Case-insensitive match (default: smart-case match)
+i Case-sensitive match +i Case-sensitive match
--scheme=SCHEME Scoring scheme [default|path|history] --scheme=SCHEME Scoring scheme [default|path|history]
--literal Do not normalize latin script letters before matching --literal Do not normalize latin script letters before matching
-n, --nth=N[,..] Comma-separated list of field index expressions -n, --nth=N[,..] Comma-separated list of field index expressions
for limiting search scope. Each can be a non-zero for limiting search scope. Each can be a non-zero
integer or a range expression ([BEGIN]..[END]). integer or a range expression ([BEGIN]..[END]).
--with-nth=N[,..] Transform the presentation of each line using --with-nth=N[,..] Transform the presentation of each line using
field index expressions field index expressions
-d, --delimiter=STR Field delimiter regex (default: AWK-style) -d, --delimiter=STR Field delimiter regex (default: AWK-style)
+s, --no-sort Do not sort the result +s, --no-sort Do not sort the result
--tac Reverse the order of the input --tac Reverse the order of the input
--disabled Do not perform search --disabled Do not perform search
--tiebreak=CRI[,..] Comma-separated list of sort criteria to apply --tiebreak=CRI[,..] Comma-separated list of sort criteria to apply
when the scores are tied [length|chunk|begin|end|index] when the scores are tied [length|chunk|begin|end|index]
(default: length) (default: length)
Interface Interface
-m, --multi[=MAX] Enable multi-select with tab/shift-tab -m, --multi[=MAX] Enable multi-select with tab/shift-tab
--no-mouse Disable mouse --no-mouse Disable mouse
--bind=KEYBINDS Custom key bindings. Refer to the man page. --bind=KEYBINDS Custom key bindings. Refer to the man page.
--cycle Enable cyclic scroll --cycle Enable cyclic scroll
--keep-right Keep the right end of the line visible on overflow --keep-right Keep the right end of the line visible on overflow
--scroll-off=LINES Number of screen lines to keep above or below when --scroll-off=LINES Number of screen lines to keep above or below when
scrolling to the top or to the bottom (default: 0) scrolling to the top or to the bottom (default: 0)
--no-hscroll Disable horizontal scroll --no-hscroll Disable horizontal scroll
--hscroll-off=COLS Number of screen columns to keep to the right of the --hscroll-off=COLS Number of screen columns to keep to the right of the
highlighted substring (default: 10) highlighted substring (default: 10)
--filepath-word Make word-wise movements respect path separators --filepath-word Make word-wise movements respect path separators
--jump-labels=CHARS Label characters for jump and jump-accept --jump-labels=CHARS Label characters for jump and jump-accept
Layout Layout
--height=[~]HEIGHT[%] Display fzf window below the cursor with the given --height=[~]HEIGHT[%] Display fzf window below the cursor with the given
height instead of using fullscreen. height instead of using fullscreen.
If prefixed with '~', fzf will determine the height If prefixed with '~', fzf will determine the height
according to the input size. according to the input size.
--min-height=HEIGHT Minimum height when --height is given in percent --min-height=HEIGHT Minimum height when --height is given in percent
(default: 10) (default: 10)
--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|vertical| [rounded|sharp|horizontal|vertical|
top|bottom|left|right|none] (default: rounded) top|bottom|left|right|none] (default: rounded)
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L) --border-label=LABEL Label to print on the border
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L) --border-label-pos=COL Position of the border label
--info=STYLE Finder info style [default|inline|hidden] [POSITIVE_INTEGER: columns from left|
--prompt=STR Input prompt (default: '> ') NEGATIVE_INTEGER: columns from right][:bottom]
--pointer=STR Pointer to the current line (default: '>') (default: 0 or center)
--marker=STR Multi-select marker (default: '>') --margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
--header=STR String to print as header --padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
--header-lines=N The first N lines of the input are treated as header --info=STYLE Finder info style [default|inline|hidden]
--header-first Print header before the prompt line --separator=STR String to form horizontal separator on info line
--ellipsis=STR Ellipsis to show when line is truncated (default: '..') --no-separator Hide info line separator
--prompt=STR Input prompt (default: '> ')
--pointer=STR Pointer to the current line (default: '>')
--marker=STR Multi-select marker (default: '>')
--header=STR String to print as header
--header-lines=N The first N lines of the input are treated as header
--header-first Print header before the prompt line
--ellipsis=STR Ellipsis to show when line is truncated (default: '..')
Display Display
--ansi Enable processing of ANSI color codes --ansi Enable processing of ANSI color codes
--tabstop=SPACES Number of spaces for a tab character (default: 8) --tabstop=SPACES Number of spaces for a tab character (default: 8)
--color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors --color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors
--no-bold Do not use bold text --no-bold Do not use bold text
History History
--history=FILE History file --history=FILE History file
--history-size=N Maximum number of history entries (default: 1000) --history-size=N Maximum number of history entries (default: 1000)
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[%]] [up|down|left|right][,SIZE[%]]
[,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden] [,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden]
[,border-BORDER_OPT] [,border-BORDER_OPT]
[,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES] [,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
[,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)] [,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]
--preview-label=LABEL
--preview-label-pos=N Same as --border-label and --border-label-pos,
but for preview window
Scripting Scripting
-q, --query=STR Start the finder with the given query -q, --query=STR Start the finder with the given query
-1, --select-1 Automatically select the only match -1, --select-1 Automatically select the only match
-0, --exit-0 Exit immediately when there's no match -0, --exit-0 Exit immediately when there's no match
-f, --filter=STR Filter mode. Do not start interactive finder. -f, --filter=STR Filter mode. Do not start interactive finder.
--print-query Print query as the first line --print-query Print query as the first line
--expect=KEYS Comma-separated list of keys to complete fzf --expect=KEYS Comma-separated list of keys to complete fzf
--read0 Read input delimited by ASCII NUL characters --read0 Read input delimited by ASCII NUL characters
--print0 Print output delimited by ASCII NUL characters --print0 Print output delimited by ASCII NUL characters
--sync Synchronous search for multi-staged filtering --sync Synchronous search for multi-staged filtering
--version Display version information and exit --version Display version information and exit
Environment variables Environment variables
FZF_DEFAULT_COMMAND Default command to use when input is tty FZF_DEFAULT_COMMAND Default command to use when input is tty
FZF_DEFAULT_OPTS Default options FZF_DEFAULT_OPTS Default options
(e.g. '--layout=reverse --inline-info') (e.g. '--layout=reverse --inline-info')
` `
@@ -173,6 +183,12 @@ const (
infoHidden infoHidden
) )
type labelOpts struct {
label string
column int
bottom bool
}
type previewOpts struct { type previewOpts struct {
command string command string
position windowPosition position windowPosition
@@ -188,6 +204,23 @@ type previewOpts struct {
alternative *previewOpts alternative *previewOpts
} }
func parseLabelPosition(opts *labelOpts, arg string) {
opts.column = 0
opts.bottom = false
for _, token := range splitRegexp.Split(strings.ToLower(arg), -1) {
switch token {
case "center":
opts.column = 0
case "bottom":
opts.bottom = true
case "top":
opts.bottom = false
default:
opts.column = atoi(token)
}
}
}
func (a previewOpts) aboveOrBelow() bool { func (a previewOpts) aboveOrBelow() bool {
return a.size.size > 0 && (a.position == posUp || a.position == posDown) return a.size.size > 0 && (a.position == posUp || a.position == posDown)
} }
@@ -204,64 +237,67 @@ func (a previewOpts) sameContentLayout(b previewOpts) bool {
// Options stores the values of command-line options // Options stores the values of command-line options
type Options struct { type Options struct {
Fuzzy bool Fuzzy bool
FuzzyAlgo algo.Algo FuzzyAlgo algo.Algo
Scheme string Scheme string
Extended bool Extended bool
Phony bool Phony bool
Case Case Case Case
Normalize bool Normalize bool
Nth []Range Nth []Range
WithNth []Range WithNth []Range
Delimiter Delimiter Delimiter Delimiter
Sort int Sort int
Tac bool Tac bool
Criteria []criterion Criteria []criterion
Multi int Multi int
Ansi bool Ansi bool
Mouse bool Mouse bool
Theme *tui.ColorTheme Theme *tui.ColorTheme
Black bool Black bool
Bold bool Bold bool
Height heightSpec Height heightSpec
MinHeight int MinHeight int
Layout layoutType Layout layoutType
Cycle bool Cycle bool
KeepRight bool KeepRight bool
Hscroll bool Hscroll bool
HscrollOff int HscrollOff int
ScrollOff int ScrollOff int
FileWord bool FileWord bool
InfoStyle infoStyle InfoStyle infoStyle
JumpLabels string Separator *string
Prompt string JumpLabels string
Pointer string Prompt string
Marker string Pointer string
Query string Marker string
Select1 bool Query string
Exit0 bool Select1 bool
Filter *string Exit0 bool
ToggleSort bool Filter *string
Expect map[tui.Event]string ToggleSort bool
Keymap map[tui.Event][]*action Expect map[tui.Event]string
Preview previewOpts Keymap map[tui.Event][]*action
PrintQuery bool Preview previewOpts
ReadZero bool PrintQuery bool
Printer func(string) ReadZero bool
PrintSep string Printer func(string)
Sync bool PrintSep string
History *History Sync bool
Header []string History *History
HeaderLines int Header []string
HeaderFirst bool HeaderLines int
Ellipsis string HeaderFirst bool
Margin [4]sizeSpec Ellipsis string
Padding [4]sizeSpec Margin [4]sizeSpec
BorderShape tui.BorderShape Padding [4]sizeSpec
Unicode bool BorderShape tui.BorderShape
Tabstop int BorderLabel labelOpts
ClearOnExit bool PreviewLabel labelOpts
Version bool Unicode bool
Tabstop int
ClearOnExit bool
Version bool
} }
func defaultPreviewOpts(command string) previewOpts { func defaultPreviewOpts(command string) previewOpts {
@@ -270,62 +306,65 @@ func defaultPreviewOpts(command string) previewOpts {
func defaultOptions() *Options { func defaultOptions() *Options {
return &Options{ return &Options{
Fuzzy: true, Fuzzy: true,
FuzzyAlgo: algo.FuzzyMatchV2, FuzzyAlgo: algo.FuzzyMatchV2,
Scheme: "default", Scheme: "default",
Extended: true, Extended: true,
Phony: false, Phony: false,
Case: CaseSmart, Case: CaseSmart,
Normalize: true, Normalize: true,
Nth: make([]Range, 0), Nth: make([]Range, 0),
WithNth: make([]Range, 0), WithNth: make([]Range, 0),
Delimiter: Delimiter{}, Delimiter: Delimiter{},
Sort: 1000, Sort: 1000,
Tac: false, Tac: false,
Criteria: []criterion{byScore, byLength}, Criteria: []criterion{byScore, byLength},
Multi: 0, Multi: 0,
Ansi: false, Ansi: false,
Mouse: true, Mouse: true,
Theme: tui.EmptyTheme(), Theme: tui.EmptyTheme(),
Black: false, Black: false,
Bold: true, Bold: true,
MinHeight: 10, MinHeight: 10,
Layout: layoutDefault, Layout: layoutDefault,
Cycle: false, Cycle: false,
KeepRight: false, KeepRight: false,
Hscroll: true, Hscroll: true,
HscrollOff: 10, HscrollOff: 10,
ScrollOff: 0, ScrollOff: 0,
FileWord: false, FileWord: false,
InfoStyle: infoDefault, InfoStyle: infoDefault,
JumpLabels: defaultJumpLabels, Separator: nil,
Prompt: "> ", JumpLabels: defaultJumpLabels,
Pointer: ">", Prompt: "> ",
Marker: ">", Pointer: ">",
Query: "", Marker: ">",
Select1: false, Query: "",
Exit0: false, Select1: false,
Filter: nil, Exit0: false,
ToggleSort: false, Filter: nil,
Expect: make(map[tui.Event]string), ToggleSort: false,
Keymap: make(map[tui.Event][]*action), Expect: make(map[tui.Event]string),
Preview: defaultPreviewOpts(""), Keymap: make(map[tui.Event][]*action),
PrintQuery: false, Preview: defaultPreviewOpts(""),
ReadZero: false, PrintQuery: false,
Printer: func(str string) { fmt.Println(str) }, ReadZero: false,
PrintSep: "\n", Printer: func(str string) { fmt.Println(str) },
Sync: false, PrintSep: "\n",
History: nil, Sync: false,
Header: make([]string, 0), History: nil,
HeaderLines: 0, Header: make([]string, 0),
HeaderFirst: false, HeaderLines: 0,
Ellipsis: "..", HeaderFirst: false,
Margin: defaultMargin(), Ellipsis: "..",
Padding: defaultMargin(), Margin: defaultMargin(),
Unicode: true, Padding: defaultMargin(),
Tabstop: 8, Unicode: true,
ClearOnExit: true, Tabstop: 8,
Version: false} BorderLabel: labelOpts{},
PreviewLabel: labelOpts{},
ClearOnExit: true,
Version: false}
} }
func help(code int) { func help(code int) {
@@ -470,6 +509,10 @@ func parseBorder(str string, optional bool) tui.BorderShape {
return tui.BorderRounded return tui.BorderRounded
case "sharp": case "sharp":
return tui.BorderSharp return tui.BorderSharp
case "bold":
return tui.BorderBold
case "double":
return tui.BorderDouble
case "horizontal": case "horizontal":
return tui.BorderHorizontal return tui.BorderHorizontal
case "vertical": case "vertical":
@@ -488,7 +531,7 @@ func parseBorder(str string, optional bool) tui.BorderShape {
if optional && str == "" { if optional && str == "" {
return tui.BorderRounded return tui.BorderRounded
} }
errorExit("invalid border style (expected: rounded|sharp|horizontal|vertical|top|bottom|left|right|none)") errorExit("invalid border style (expected: rounded|sharp|bold|double|horizontal|vertical|top|bottom|left|right|none)")
} }
return tui.BorderNone return tui.BorderNone
} }
@@ -543,6 +586,8 @@ func parseKeyChords(str string, message string) map[tui.Event]string {
add(tui.Change) add(tui.Change)
case "backward-eof": case "backward-eof":
add(tui.BackwardEOF) add(tui.BackwardEOF)
case "start":
add(tui.Start)
case "alt-enter", "alt-return": case "alt-enter", "alt-return":
chords[tui.CtrlAltKey('m')] = key chords[tui.CtrlAltKey('m')] = key
case "alt-space": case "alt-space":
@@ -796,6 +841,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
mergeAttr(&theme.CurrentMatch) mergeAttr(&theme.CurrentMatch)
case "border": case "border":
mergeAttr(&theme.Border) mergeAttr(&theme.Border)
case "separator":
mergeAttr(&theme.Separator)
case "label":
mergeAttr(&theme.BorderLabel)
case "prompt": case "prompt":
mergeAttr(&theme.Prompt) mergeAttr(&theme.Prompt)
case "spinner": case "spinner":
@@ -816,7 +865,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
return theme return theme
} }
var executeRegexp *regexp.Regexp var (
executeRegexp *regexp.Regexp
splitRegexp *regexp.Regexp
)
func firstKey(keymap map[tui.Event]string) tui.Event { func firstKey(keymap map[tui.Event]string) tui.Event {
for k := range keymap { for k := range keymap {
@@ -836,6 +888,7 @@ func init() {
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|') // "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`) `(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
splitRegexp = regexp.MustCompile("[,:]+")
} }
func parseKeymap(keymap map[tui.Event][]*action, str string) { func parseKeymap(keymap map[tui.Event][]*action, str string) {
@@ -1203,7 +1256,7 @@ func parseInfoStyle(str string) infoStyle {
case "hidden": case "hidden":
return infoHidden return infoHidden
default: default:
errorExit("invalid info style (expected: default / inline / hidden)") errorExit("invalid info style (expected: default|inline|hidden)")
} }
return infoDefault return infoDefault
} }
@@ -1250,6 +1303,10 @@ func parsePreviewWindow(opts *previewOpts, input string) {
opts.border = tui.BorderRounded opts.border = tui.BorderRounded
case "sharp", "border-sharp": case "sharp", "border-sharp":
opts.border = tui.BorderSharp opts.border = tui.BorderSharp
case "border-bold":
opts.border = tui.BorderBold
case "border-double":
opts.border = tui.BorderDouble
case "noborder", "border-none": case "noborder", "border-none":
opts.border = tui.BorderNone opts.border = tui.BorderNone
case "border-horizontal": case "border-horizontal":
@@ -1472,6 +1529,12 @@ func parseOptions(opts *Options, allArgs []string) {
opts.InfoStyle = infoInline opts.InfoStyle = infoInline
case "--no-inline-info": case "--no-inline-info":
opts.InfoStyle = infoDefault opts.InfoStyle = infoDefault
case "--separator":
separator := nextString(allArgs, &i, "separator character required")
opts.Separator = &separator
case "--no-separator":
nosep := ""
opts.Separator = &nosep
case "--jump-labels": case "--jump-labels":
opts.JumpLabels = nextString(allArgs, &i, "label characters required") opts.JumpLabels = nextString(allArgs, &i, "label characters required")
validateJumpLabels = true validateJumpLabels = true
@@ -1554,6 +1617,20 @@ func parseOptions(opts *Options, allArgs []string) {
case "--border": case "--border":
hasArg, arg := optionalNextString(allArgs, &i) hasArg, arg := optionalNextString(allArgs, &i)
opts.BorderShape = parseBorder(arg, !hasArg) opts.BorderShape = parseBorder(arg, !hasArg)
case "--no-border-label":
opts.BorderLabel.label = ""
case "--border-label":
opts.BorderLabel.label = nextString(allArgs, &i, "label required")
case "--border-label-pos":
pos := nextString(allArgs, &i, "label position required (positive or negative integer or 'center')")
parseLabelPosition(&opts.BorderLabel, pos)
case "--no-preview-label":
opts.PreviewLabel.label = ""
case "--preview-label":
opts.PreviewLabel.label = nextString(allArgs, &i, "preview label required")
case "--preview-label-pos":
pos := nextString(allArgs, &i, "preview label position required (positive or negative integer or 'center')")
parseLabelPosition(&opts.PreviewLabel, pos)
case "--no-unicode": case "--no-unicode":
opts.Unicode = false opts.Unicode = false
case "--unicode": case "--unicode":
@@ -1589,6 +1666,14 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Delimiter = delimiterRegexp(value) opts.Delimiter = delimiterRegexp(value)
} else if match, value := optString(arg, "--border="); match { } else if match, value := optString(arg, "--border="); match {
opts.BorderShape = parseBorder(value, false) opts.BorderShape = parseBorder(value, false)
} else if match, value := optString(arg, "--border-label="); match {
opts.BorderLabel.label = value
} else if match, value := optString(arg, "--border-label-pos="); match {
parseLabelPosition(&opts.BorderLabel, value)
} else if match, value := optString(arg, "--preview-label="); match {
opts.PreviewLabel.label = value
} else if match, value := optString(arg, "--preview-label-pos="); match {
parseLabelPosition(&opts.PreviewLabel, value)
} else if match, value := optString(arg, "--prompt="); match { } else if match, value := optString(arg, "--prompt="); match {
opts.Prompt = value opts.Prompt = value
} else if match, value := optString(arg, "--pointer="); match { } else if match, value := optString(arg, "--pointer="); match {
@@ -1613,6 +1698,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Layout = parseLayout(value) opts.Layout = parseLayout(value)
} else if match, value := optString(arg, "--info="); match { } else if match, value := optString(arg, "--info="); match {
opts.InfoStyle = parseInfoStyle(value) opts.InfoStyle = parseInfoStyle(value)
} else if match, value := optString(arg, "--separator="); match {
opts.Separator = &value
} else if match, value := optString(arg, "--toggle-sort="); match { } else if match, value := optString(arg, "--toggle-sort="); match {
parseToggleSort(opts.Keymap, value) parseToggleSort(opts.Keymap, value)
} else if match, value := optString(arg, "--expect="); match { } else if match, value := optString(arg, "--expect="); match {

View File

@@ -107,13 +107,23 @@ type fitpad struct {
var emptyLine = itemLine{} var emptyLine = itemLine{}
type labelPrinter func(tui.Window, int)
// Terminal represents terminal input/output // Terminal represents terminal input/output
type Terminal struct { type Terminal struct {
initDelay time.Duration initDelay time.Duration
infoStyle infoStyle infoStyle infoStyle
separator labelPrinter
separatorLen int
spinner []string spinner []string
prompt func() prompt func()
promptLen int promptLen int
borderLabel labelPrinter
borderLabelLen int
borderLabelOpts labelOpts
previewLabel labelPrinter
previewLabelLen int
previewLabelOpts labelOpts
pointer string pointer string
pointerLen int pointerLen int
pointerEmpty string pointerEmpty string
@@ -514,6 +524,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
t := Terminal{ t := Terminal{
initDelay: delay, initDelay: delay,
infoStyle: opts.InfoStyle, infoStyle: opts.InfoStyle,
separator: nil,
spinner: makeSpinner(opts.Unicode), spinner: makeSpinner(opts.Unicode),
queryLen: [2]int{0, 0}, queryLen: [2]int{0, 0},
layout: opts.Layout, layout: opts.Layout,
@@ -544,6 +555,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
padding: opts.Padding, padding: opts.Padding,
unicode: opts.Unicode, unicode: opts.Unicode,
borderShape: opts.BorderShape, borderShape: opts.BorderShape,
borderLabel: nil,
borderLabelOpts: opts.BorderLabel,
previewLabel: nil,
previewLabelOpts: opts.PreviewLabel,
cleanExit: opts.ClearOnExit, cleanExit: opts.ClearOnExit,
paused: opts.Phony, paused: opts.Phony,
strong: strongAttr, strong: strongAttr,
@@ -587,13 +602,24 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
// Pre-calculated empty pointer and marker signs // Pre-calculated empty pointer and marker signs
t.pointerEmpty = strings.Repeat(" ", t.pointerLen) t.pointerEmpty = strings.Repeat(" ", t.pointerLen)
t.markerEmpty = strings.Repeat(" ", t.markerLen) t.markerEmpty = strings.Repeat(" ", t.markerLen)
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(opts.BorderLabel.label, &tui.ColBorderLabel, false)
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColBorderLabel, false)
if opts.Separator == nil || len(*opts.Separator) > 0 {
bar := "─"
if opts.Separator != nil {
bar = *opts.Separator
} else if !t.unicode {
bar = "-"
}
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
}
return &t return &t
} }
func borderLines(shape tui.BorderShape) int { func borderLines(shape tui.BorderShape) int {
switch shape { switch shape {
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp: case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
return 2 return 2
case tui.BorderTop, tui.BorderBottom: case tui.BorderTop, tui.BorderBottom:
return 1 return 1
@@ -617,6 +643,65 @@ func (t *Terminal) MaxFitAndPad(opts *Options) (int, int) {
return fit, padHeight return fit, padHeight
} }
func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool) (labelPrinter, int) {
// Nothing to do
if len(str) == 0 {
return nil, 0
}
// Extract ANSI color codes
text, colors, _ := extractColor(str, nil, nil)
runes := []rune(text)
// Simpler printer for strings without ANSI colors or tab characters
if colors == nil && strings.IndexRune(str, '\t') < 0 {
length := runewidth.StringWidth(str)
if length == 0 {
return nil, 0
}
printFn := func(window tui.Window, limit int) {
if length > limit {
trimmedRunes, _ := t.trimRight(runes, limit)
window.CPrint(*color, string(trimmedRunes))
} else if fill {
window.CPrint(*color, util.RepeatToFill(str, length, limit))
} else {
window.CPrint(*color, str)
}
}
return printFn, len(text)
}
// Printer that correctly handles ANSI color codes and tab characters
item := &Item{text: util.RunesToChars(runes), colors: colors}
length := t.displayWidth(runes)
if length == 0 {
return nil, 0
}
result := Result{item: item}
var offsets []colorOffset
printFn := func(window tui.Window, limit int) {
if offsets == nil {
// tui.Col* are not initialized until renderer.Init()
offsets = result.colorOffsets(nil, t.theme, *color, *color, false)
}
for limit > 0 {
if length > limit {
trimmedRunes, _ := t.trimRight(runes, limit)
t.printColoredString(window, trimmedRunes, offsets, *color)
break
} else if fill {
t.printColoredString(window, runes, offsets, *color)
limit -= length
} else {
t.printColoredString(window, runes, offsets, *color)
break
}
}
}
return printFn, length
}
func (t *Terminal) parsePrompt(prompt string) (func(), int) { 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)
@@ -813,7 +898,7 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
if idx == 3 { if idx == 3 {
extraMargin[idx] += 2 extraMargin[idx] += 2
} }
case tui.BorderRounded, tui.BorderSharp: case tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
extraMargin[idx] += 1 + idx%2 extraMargin[idx] += 1 + idx%2
} }
marginInt[idx] = sizeSpecToInt(idx, sizeSpec) + extraMargin[idx] marginInt[idx] = sizeSpecToInt(idx, sizeSpec) + extraMargin[idx]
@@ -905,7 +990,7 @@ func (t *Terminal) resizeWindows() {
t.border = t.tui.NewWindow( t.border = t.tui.NewWindow(
marginInt[0], marginInt[3], width+2, height, marginInt[0], marginInt[3], width+2, height,
false, tui.MakeBorderStyle(tui.BorderRight, t.unicode)) false, tui.MakeBorderStyle(tui.BorderRight, t.unicode))
case tui.BorderRounded, tui.BorderSharp: case tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
t.border = t.tui.NewWindow( t.border = t.tui.NewWindow(
marginInt[0]-1, marginInt[3]-2, width+4, height+2, marginInt[0]-1, marginInt[3]-2, width+4, height+2,
false, tui.MakeBorderStyle(t.borderShape, t.unicode)) false, tui.MakeBorderStyle(t.borderShape, t.unicode))
@@ -935,7 +1020,7 @@ func (t *Terminal) resizeWindows() {
} }
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder) t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
switch previewOpts.border { switch previewOpts.border {
case tui.BorderSharp, tui.BorderRounded: case tui.BorderSharp, tui.BorderRounded, tui.BorderBold, tui.BorderDouble:
pwidth -= 4 pwidth -= 4
pheight -= 2 pheight -= 2
x += 2 x += 2
@@ -1015,6 +1100,34 @@ func (t *Terminal) resizeWindows() {
width, width,
height, false, noBorder) height, false, noBorder)
} }
// Print border label
printLabel := func(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape) {
if window == nil || render == nil {
return
}
switch borderShape {
case tui.BorderHorizontal, tui.BorderTop, tui.BorderBottom, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
var col int
if opts.column == 0 {
col = util.Max(0, (window.Width()-length)/2)
} else if opts.column < 0 {
col = util.Max(0, window.Width()+opts.column+1-length)
} else {
col = util.Min(opts.column-1, window.Width()-length)
}
row := 0
if borderShape == tui.BorderBottom || opts.bottom {
row = window.Height() - 1
}
window.Move(row, col)
render(window, window.Width())
}
}
printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape)
printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.previewOpts.border)
for i := 0; i < t.window.Height(); i++ { for i := 0; i < t.window.Height(); i++ {
t.window.MoveAndClear(i, 0) t.window.MoveAndClear(i, 0)
} }
@@ -1154,8 +1267,15 @@ 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)
} }
output = t.trimMessage(output, t.window.Width()-pos) maxWidth := t.window.Width() - pos
output = t.trimMessage(output, maxWidth)
t.window.CPrint(tui.ColInfo, output) t.window.CPrint(tui.ColInfo, output)
fillLength := maxWidth - len(output) - 2
if t.separatorLen > 0 && fillLength > 0 {
t.window.CPrint(tui.ColSeparator, " ")
t.separator(t.window, fillLength)
}
} }
func (t *Terminal) printHeader() { func (t *Terminal) printHeader() {
@@ -1394,6 +1514,11 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
displayWidth = t.displayWidthWithLimit(text, 0, displayWidth) displayWidth = t.displayWidthWithLimit(text, 0, displayWidth)
} }
t.printColoredString(t.window, text, offsets, colBase)
return displayWidth
}
func (t *Terminal) printColoredString(window tui.Window, text []rune, offsets []colorOffset, colBase tui.ColorPair) {
var index int32 var index int32
var substr string var substr string
var prefixWidth int var prefixWidth int
@@ -1403,11 +1528,11 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
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(colBase, substr) 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, substr) window.CPrint(offset.color, substr)
} }
index = e index = e
@@ -1417,9 +1542,8 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
} }
if index < maxOffset { if index < maxOffset {
substr, _ = t.processTabs(text[index:], prefixWidth) substr, _ = t.processTabs(text[index:], prefixWidth)
t.window.CPrint(colBase, substr) window.CPrint(colBase, substr)
} }
return displayWidth
} }
func (t *Terminal) renderPreviewSpinner() { func (t *Terminal) renderPreviewSpinner() {
@@ -2362,13 +2486,21 @@ func (t *Terminal) Loop() {
}() }()
looping := true looping := true
_, startEvent := t.keymap[tui.Start.AsEvent()]
for looping { for looping {
var newCommand *string var newCommand *string
changed := false changed := false
beof := false beof := false
queryChanged := false queryChanged := false
event := t.tui.GetChar() var event tui.Event
if startEvent {
event = tui.Start.AsEvent()
startEvent = false
} else {
event = t.tui.GetChar()
}
t.mutex.Lock() t.mutex.Lock()
previousInput := t.input previousInput := t.input

View File

@@ -712,7 +712,7 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
func (w *LightWindow) drawBorder() { func (w *LightWindow) drawBorder() {
switch w.border.shape { switch w.border.shape {
case BorderRounded, BorderSharp: case BorderRounded, BorderSharp, BorderBold, BorderDouble:
w.drawBorderAround() w.drawBorderAround()
case BorderHorizontal: case BorderHorizontal:
w.drawBorderHorizontal(true, true) w.drawBorderHorizontal(true, true)

View File

@@ -75,8 +75,6 @@ func (w *TcellWindow) Refresh() {
} }
w.lastX = 0 w.lastX = 0
w.lastY = 0 w.lastY = 0
w.drawBorder()
} }
func (w *TcellWindow) FinishFill() { func (w *TcellWindow) FinishFill() {
@@ -517,7 +515,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
if preview { if preview {
normal = ColPreview normal = ColPreview
} }
return &TcellWindow{ w := &TcellWindow{
color: r.theme.Colored, color: r.theme.Colored,
preview: preview, preview: preview,
top: top, top: top,
@@ -526,6 +524,8 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
height: height, height: height,
normal: normal, normal: normal,
borderStyle: borderStyle} borderStyle: borderStyle}
w.drawBorder()
return w
} }
func (w *TcellWindow) Close() { func (w *TcellWindow) Close() {
@@ -705,31 +705,31 @@ func (w *TcellWindow) drawBorder() {
} }
switch shape { switch shape {
case BorderRounded, BorderSharp, BorderHorizontal, BorderTop: case BorderRounded, BorderSharp, BorderBold, BorderDouble, 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 { switch shape {
case BorderRounded, BorderSharp, BorderHorizontal, BorderBottom: case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderHorizontal, BorderBottom:
for x := left; x < right; x++ { 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)
} }
} }
switch shape { switch shape {
case BorderRounded, BorderSharp, BorderVertical, BorderLeft: case BorderRounded, BorderSharp, BorderBold, BorderDouble, 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 { switch shape {
case BorderRounded, BorderSharp, BorderVertical, BorderRight: case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderRight:
for y := top; y < bot; y++ { 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 { switch shape {
case BorderRounded, BorderSharp: case BorderRounded, BorderSharp, BorderBold, BorderDouble:
_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)

View File

@@ -90,6 +90,7 @@ const (
Change Change
BackwardEOF BackwardEOF
Start
AltBS AltBS
@@ -266,7 +267,9 @@ type ColorTheme struct {
Cursor ColorAttr Cursor ColorAttr
Selected ColorAttr Selected ColorAttr
Header ColorAttr Header ColorAttr
Separator ColorAttr
Border ColorAttr Border ColorAttr
BorderLabel ColorAttr
} }
type Event struct { type Event struct {
@@ -291,6 +294,8 @@ const (
BorderNone BorderShape = iota BorderNone BorderShape = iota
BorderRounded BorderRounded
BorderSharp BorderSharp
BorderBold
BorderDouble
BorderHorizontal BorderHorizontal
BorderVertical BorderVertical
BorderTop BorderTop
@@ -312,18 +317,19 @@ type BorderStyle struct {
type BorderCharacter int type BorderCharacter int
func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle { func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
if unicode { if !unicode {
if shape == BorderRounded { return BorderStyle{
return BorderStyle{ shape: shape,
shape: shape, horizontal: '-',
horizontal: '', vertical: '|',
vertical: '', topLeft: '+',
topLeft: '', topRight: '+',
topRight: '', bottomLeft: '+',
bottomLeft: '', bottomRight: '+',
bottomRight: '╯',
}
} }
}
switch shape {
case BorderSharp:
return BorderStyle{ return BorderStyle{
shape: shape, shape: shape,
horizontal: '─', horizontal: '─',
@@ -333,15 +339,35 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
bottomLeft: '└', bottomLeft: '└',
bottomRight: '┘', bottomRight: '┘',
} }
case BorderBold:
return BorderStyle{
shape: shape,
horizontal: '━',
vertical: '┃',
topLeft: '┏',
topRight: '┓',
bottomLeft: '┗',
bottomRight: '┛',
}
case BorderDouble:
return BorderStyle{
shape: shape,
horizontal: '═',
vertical: '║',
topLeft: '╔',
topRight: '╗',
bottomLeft: '╚',
bottomRight: '╝',
}
} }
return BorderStyle{ return BorderStyle{
shape: shape, shape: shape,
horizontal: '-', horizontal: '',
vertical: '|', vertical: '',
topLeft: '+', topLeft: '',
topRight: '+', topRight: '',
bottomLeft: '+', bottomLeft: '',
bottomRight: '+', bottomRight: '',
} }
} }
@@ -437,9 +463,11 @@ var (
ColSpinner ColorPair ColSpinner ColorPair
ColInfo ColorPair ColInfo ColorPair
ColHeader ColorPair ColHeader ColorPair
ColSeparator ColorPair
ColBorder ColorPair ColBorder ColorPair
ColPreview ColorPair ColPreview ColorPair
ColPreviewBorder ColorPair ColPreviewBorder ColorPair
ColBorderLabel ColorPair
) )
func EmptyTheme() *ColorTheme { func EmptyTheme() *ColorTheme {
@@ -462,7 +490,10 @@ func EmptyTheme() *ColorTheme {
Cursor: ColorAttr{colUndefined, AttrUndefined}, Cursor: ColorAttr{colUndefined, AttrUndefined},
Selected: ColorAttr{colUndefined, AttrUndefined}, Selected: ColorAttr{colUndefined, AttrUndefined},
Header: ColorAttr{colUndefined, AttrUndefined}, Header: ColorAttr{colUndefined, AttrUndefined},
Border: ColorAttr{colUndefined, AttrUndefined}} Separator: ColorAttr{colUndefined, AttrUndefined},
Border: ColorAttr{colUndefined, AttrUndefined},
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
}
} }
func NoColorTheme() *ColorTheme { func NoColorTheme() *ColorTheme {
@@ -485,7 +516,10 @@ func NoColorTheme() *ColorTheme {
Cursor: ColorAttr{colDefault, AttrRegular}, Cursor: ColorAttr{colDefault, AttrRegular},
Selected: ColorAttr{colDefault, AttrRegular}, Selected: ColorAttr{colDefault, AttrRegular},
Header: ColorAttr{colDefault, AttrRegular}, Header: ColorAttr{colDefault, AttrRegular},
Border: ColorAttr{colDefault, AttrRegular}} Separator: ColorAttr{colDefault, AttrRegular},
Border: ColorAttr{colDefault, AttrRegular},
BorderLabel: ColorAttr{colDefault, AttrRegular},
}
} }
func errorExit(message string) { func errorExit(message string) {
@@ -513,7 +547,10 @@ func init() {
Cursor: ColorAttr{colRed, AttrUndefined}, Cursor: ColorAttr{colRed, AttrUndefined},
Selected: ColorAttr{colMagenta, AttrUndefined}, Selected: ColorAttr{colMagenta, AttrUndefined},
Header: ColorAttr{colCyan, AttrUndefined}, Header: ColorAttr{colCyan, AttrUndefined},
Border: ColorAttr{colBlack, AttrUndefined}} Separator: ColorAttr{colBlack, AttrUndefined},
Border: ColorAttr{colBlack, AttrUndefined},
BorderLabel: ColorAttr{colWhite, AttrUndefined},
}
Dark256 = &ColorTheme{ Dark256 = &ColorTheme{
Colored: true, Colored: true,
Input: ColorAttr{colDefault, AttrUndefined}, Input: ColorAttr{colDefault, AttrUndefined},
@@ -533,7 +570,10 @@ func init() {
Cursor: ColorAttr{161, AttrUndefined}, Cursor: ColorAttr{161, AttrUndefined},
Selected: ColorAttr{168, AttrUndefined}, Selected: ColorAttr{168, AttrUndefined},
Header: ColorAttr{109, AttrUndefined}, Header: ColorAttr{109, AttrUndefined},
Border: ColorAttr{59, AttrUndefined}} Separator: ColorAttr{59, AttrUndefined},
Border: ColorAttr{59, AttrUndefined},
BorderLabel: ColorAttr{145, AttrUndefined},
}
Light256 = &ColorTheme{ Light256 = &ColorTheme{
Colored: true, Colored: true,
Input: ColorAttr{colDefault, AttrUndefined}, Input: ColorAttr{colDefault, AttrUndefined},
@@ -553,7 +593,10 @@ func init() {
Cursor: ColorAttr{161, AttrUndefined}, Cursor: ColorAttr{161, AttrUndefined},
Selected: ColorAttr{168, AttrUndefined}, Selected: ColorAttr{168, AttrUndefined},
Header: ColorAttr{31, AttrUndefined}, Header: ColorAttr{31, AttrUndefined},
Border: ColorAttr{145, AttrUndefined}} Separator: ColorAttr{145, AttrUndefined},
Border: ColorAttr{145, AttrUndefined},
BorderLabel: ColorAttr{59, AttrUndefined},
}
} }
func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) { func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
@@ -588,7 +631,9 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
theme.Cursor = o(baseTheme.Cursor, theme.Cursor) theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
theme.Selected = o(baseTheme.Selected, theme.Selected) theme.Selected = o(baseTheme.Selected, theme.Selected)
theme.Header = o(baseTheme.Header, theme.Header) theme.Header = o(baseTheme.Header, theme.Header)
theme.Separator = o(baseTheme.Separator, theme.Separator)
theme.Border = o(baseTheme.Border, theme.Border) theme.Border = o(baseTheme.Border, theme.Border)
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
initPalette(theme) initPalette(theme)
} }
@@ -620,7 +665,9 @@ func initPalette(theme *ColorTheme) {
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)
ColSeparator = pair(theme.Separator, theme.Bg)
ColBorder = pair(theme.Border, theme.Bg) ColBorder = pair(theme.Border, theme.Bg)
ColBorderLabel = pair(theme.BorderLabel, 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)
} }

View File

@@ -153,3 +153,23 @@ func Once(nextResponse bool) func() bool {
return prevState return prevState
} }
} }
// RepeatToFill repeats the given string to fill the given width
func RepeatToFill(str string, length int, limit int) string {
times := limit / length
rest := limit % length
output := strings.Repeat(str, times)
if rest > 0 {
for _, r := range str {
rest -= runewidth.RuneWidth(r)
if rest < 0 {
break
}
output += string(r)
if rest == 0 {
break
}
}
}
return output
}

View File

@@ -192,6 +192,13 @@ class TestBase < Minitest::Test
tmux.prepare tmux.prepare
end end
alias assert_equal_org assert_equal
def assert_equal(expected, actual)
# Ignore info separator
actual = actual&.sub(/\s*─+$/, '') if actual.is_a?(String) && actual&.match?(%r{\d+/\d+})
assert_equal_org(expected, actual)
end
def fzf(*opts) def fzf(*opts)
fzf!(*opts) + " > #{tempname}.tmp; mv #{tempname}.tmp #{tempname}" fzf!(*opts) + " > #{tempname}.tmp; mv #{tempname}.tmp #{tempname}"
end end
@@ -255,7 +262,7 @@ class TestGoFZF < TestBase
def test_fzf_default_command_failure def test_fzf_default_command_failure
tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', 'FZF_DEFAULT_COMMAND=false'), :Enter tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', 'FZF_DEFAULT_COMMAND=false'), :Enter
tmux.until { |lines| assert_equal ' [Command failed: false]', lines[-2] } tmux.until { |lines| assert_includes lines[-2], ' [Command failed: false]' }
tmux.send_keys :Enter tmux.send_keys :Enter
end end
@@ -447,7 +454,7 @@ class TestGoFZF < TestBase
def test_scroll def test_scroll
[true, false].each do |rev| [true, false].each do |rev|
tmux.send_keys "seq 1 100 | #{fzf(rev && :reverse)}", :Enter tmux.send_keys "seq 1 100 | #{fzf(rev && :reverse)}", :Enter
tmux.until { |lines| assert_includes lines, ' 100/100' } tmux.until { |lines| assert_equal ' 100/100', lines[rev ? 1 : -2] }
tmux.send_keys(*Array.new(110) { rev ? :Down : :Up }) tmux.send_keys(*Array.new(110) { rev ? :Down : :Up })
tmux.until { |lines| assert_includes lines, '> 100' } tmux.until { |lines| assert_includes lines, '> 100' }
tmux.send_keys :Enter tmux.send_keys :Enter
@@ -2249,7 +2256,7 @@ class TestGoFZF < TestBase
def assert_block(expected, lines) def assert_block(expected, lines)
cols = expected.lines.map(&:chomp).map(&:length).max cols = expected.lines.map(&:chomp).map(&:length).max
actual = lines.reverse.take(expected.lines.length).reverse.map { _1[0, cols].rstrip + "\n" }.join actual = lines.reverse.take(expected.lines.length).reverse.map { _1[0, cols].rstrip + "\n" }.join
assert_equal expected, actual assert_equal_org expected, actual
end end
def test_height_range_fit def test_height_range_fit
@@ -2297,7 +2304,7 @@ class TestGoFZF < TestBase
2 2
1 1
hello hello
1/1 1/1
> >
@@ -2314,7 +2321,7 @@ class TestGoFZF < TestBase
3 1 3 1
hello hello
world world
1/1 1/1
> >
OUTPUT OUTPUT
@@ -2332,6 +2339,71 @@ class TestGoFZF < TestBase
OUTPUT OUTPUT
tmux.until { assert_block(expected, _1) } tmux.until { assert_block(expected, _1) }
end end
def test_start_event
tmux.send_keys 'seq 100 | fzf --multi --sync --preview-window border-none --bind "start:select-all+last+preview(echo welcome)"', :Enter
tmux.until do |lines|
assert_match(/>100.*welcome/, lines[0])
assert_includes(lines[-2], '100/100 (100)')
end
end
def test_labels_center
tmux.send_keys ': | fzf --border --border-label foobar --preview : --preview-label barfoo', :Enter
tmux.until do
assert_includes(_1[0], '─foobar─')
assert_includes(_1[1], '─barfoo─')
end
end
def test_labels_left
tmux.send_keys ': | fzf --border --border-label foobar --border-label-pos 2 --preview : --preview-label barfoo --preview-label-pos 2', :Enter
tmux.until do
assert_includes(_1[0], '╭foobar─')
assert_includes(_1[1], '╭barfoo─')
end
end
def test_labels_right
tmux.send_keys ': | fzf --border --border-label foobar --border-label-pos -2 --preview : --preview-label barfoo --preview-label-pos -2', :Enter
tmux.until do
assert_includes(_1[0], '─foobar╮')
assert_includes(_1[1], '─barfoo╮')
end
end
def test_labels_bottom
tmux.send_keys ': | fzf --border --border-label foobar --border-label-pos 2:bottom --preview : --preview-label barfoo --preview-label-pos -2:bottom', :Enter
tmux.until do
assert_includes(_1[-1], '╰foobar─')
assert_includes(_1[-2], '─barfoo╯')
end
end
def test_info_separator_unicode
tmux.send_keys 'seq 100 | fzf -q55', :Enter
tmux.until { assert_includes(_1[-2], ' 1/100 ─') }
end
def test_info_separator_no_unicode
tmux.send_keys 'seq 100 | fzf -q55 --no-unicode', :Enter
tmux.until { assert_includes(_1[-2], ' 1/100 -') }
end
def test_info_separator_repeat
tmux.send_keys 'seq 100 | fzf -q55 --separator _-', :Enter
tmux.until { assert_includes(_1[-2], ' 1/100 _-_-') }
end
def test_info_separator_ansi_colors_and_tabs
tmux.send_keys "seq 100 | fzf -q55 --tabstop 4 --separator $'\\x1b[33ma\\tb'", :Enter
tmux.until { assert_includes(_1[-2], ' 1/100 a ba ba') }
end
def test_info_no_separator
tmux.send_keys 'seq 100 | fzf -q55 --no-separator', :Enter
tmux.until { assert(_1[-2] == ' 1/100') }
end
end end
module TestShell module TestShell
@@ -2758,6 +2830,7 @@ class TestFish < TestBase
end end
__END__ __END__
set -u
PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100 PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
unset <%= UNSETS.join(' ') %> unset <%= UNSETS.join(' ') %>
unset $(env | sed -n /^_fzf_orig/s/=.*//p) unset $(env | sed -n /^_fzf_orig/s/=.*//p)
@@ -2801,8 +2874,8 @@ _fzf_complete_g_post() {
awk '{print "g" $0 $0}' awk '{print "g" $0 $0}'
} }
[ -n "$BASH" ] && complete -F _fzf_complete_f -o default -o bashdefault f [ -n "${BASH-}" ] && complete -F _fzf_complete_f -o default -o bashdefault f
[ -n "$BASH" ] && complete -F _fzf_complete_g -o default -o bashdefault g [ -n "${BASH-}" ] && complete -F _fzf_complete_g -o default -o bashdefault g
_comprun() { _comprun() {
local command=$1 local command=$1