mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-22 16:03:49 -07:00
Compare commits
91 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
62963dcefd | ||
|
68a35e4735 | ||
|
9b9ad77e1c | ||
|
118b4d4a01 | ||
|
da14ab6f16 | ||
|
09a4ca6ab5 | ||
|
8a2df79711 | ||
|
c30e486b64 | ||
|
a575c0c54b | ||
|
77fe96ac0d | ||
|
5234c3759a | ||
|
41b3511ad9 | ||
|
128e4a2e8d | ||
|
07ac90d798 | ||
|
7de87a9b2c | ||
|
dff865239a | ||
|
07f8f70c5b | ||
|
f625c5aabe | ||
|
8a74976c1f | ||
|
b6bfd4a5cb | ||
|
008fb9d258 | ||
|
db6db49ed6 | ||
|
05453881c3 | ||
|
5e47ab9431 | ||
|
ec70acd0b9 | ||
|
25e61056b6 | ||
|
d579e335b5 | ||
|
7e344ceb85 | ||
|
0145b82ea0 | ||
|
b4efe7aab7 | ||
|
9ffe951f6d | ||
|
a5ea4f57bd | ||
|
88f4c16755 | ||
|
c7ee071efa | ||
|
0740ef7ceb | ||
|
b29bd809ac | ||
|
8977c9257a | ||
|
091b7eacba | ||
|
e74b1251c0 | ||
|
d282a1649d | ||
|
6ce8d49d1b | ||
|
c5b197078a | ||
|
0494f20d62 | ||
|
73aff476dd | ||
|
98ee5e651a | ||
|
01871ea383 | ||
|
1dbdb9438f | ||
|
c70f0eadb8 | ||
|
26244ad8c2 | ||
|
fa0aa5510d | ||
|
eec557b6aa | ||
|
0cc27c3cc1 | ||
|
507089d7b2 | ||
|
a6b3517b75 | ||
|
2d6beb7813 | ||
|
61bc129e1d | ||
|
52210a57f0 | ||
|
8061a2f108 | ||
|
7444eff6d4 | ||
|
f35a9da99a | ||
|
c3098e9ab2 | ||
|
686f9288fc | ||
|
1833670fb9 | ||
|
3dd42f5aa2 | ||
|
99a7beba57 | ||
|
edee2b753c | ||
|
545d5770be | ||
|
ca747a2b54 | ||
|
17da165cfe | ||
|
5e6788c679 | ||
|
425deadca9 | ||
|
2c8e9dd3a5 | ||
|
7a72f1a253 | ||
|
208e556332 | ||
|
c65d11bfb5 | ||
|
3b5b52d89a | ||
|
a4f6c8f990 | ||
|
670c329852 | ||
|
f3551c8422 | ||
|
90b8187882 | ||
|
1a43259989 | ||
|
3c0a630475 | ||
|
2a1e5a9729 | ||
|
413c66beba | ||
|
1416e696b1 | ||
|
d373cf89c7 | ||
|
dd886d22f0 | ||
|
472569a27c | ||
|
76cf6559cc | ||
|
a34e8dcdc9 | ||
|
da752fc9a4 |
22
.github/ISSUE_TEMPLATE.md
vendored
22
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,22 +0,0 @@
|
|||||||
<!-- ISSUES NOT FOLLOWING THIS TEMPLATE WILL BE CLOSED AND DELETED -->
|
|
||||||
|
|
||||||
<!-- Check all that apply [x] -->
|
|
||||||
|
|
||||||
- [ ] I have read through the manual page (`man fzf`)
|
|
||||||
- [ ] I have the latest version of fzf
|
|
||||||
- [ ] I have searched through the existing issues
|
|
||||||
|
|
||||||
## Info
|
|
||||||
|
|
||||||
- OS
|
|
||||||
- [ ] Linux
|
|
||||||
- [ ] Mac OS X
|
|
||||||
- [ ] Windows
|
|
||||||
- [ ] Etc.
|
|
||||||
- Shell
|
|
||||||
- [ ] bash
|
|
||||||
- [ ] zsh
|
|
||||||
- [ ] fish
|
|
||||||
|
|
||||||
## Problem / Steps to reproduce
|
|
||||||
|
|
47
.github/ISSUE_TEMPLATE/issue_template.yml
vendored
Normal file
47
.github/ISSUE_TEMPLATE/issue_template.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
name: Issue Template
|
||||||
|
description: Report a problem or bug related to fzf to help us improve
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: ISSUES NOT FOLLOWING THIS TEMPLATE WILL BE CLOSED AND DELETED
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
options:
|
||||||
|
- label: I have read through the manual page (`man fzf`)
|
||||||
|
required: true
|
||||||
|
- label: I have searched through the existing issues
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Output of `fzf --version`
|
||||||
|
placeholder: e.g. 0.48.1 (d579e33)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: OS
|
||||||
|
options:
|
||||||
|
- label: Linux
|
||||||
|
- label: macOS
|
||||||
|
- label: Windows
|
||||||
|
- label: Etc.
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Shell
|
||||||
|
options:
|
||||||
|
- label: bash
|
||||||
|
- label: zsh
|
||||||
|
- label: fish
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Problem / Steps to reproduce
|
||||||
|
validations:
|
||||||
|
required: true
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
2
.github/workflows/depsreview.yaml
vendored
2
.github/workflows/depsreview.yaml
vendored
@@ -9,6 +9,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 'Checkout Repository'
|
- name: 'Checkout Repository'
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: 'Dependency Review'
|
- name: 'Dependency Review'
|
||||||
uses: actions/dependency-review-action@v4
|
uses: actions/dependency-review-action@v4
|
||||||
|
12
.github/workflows/linux.yml
vendored
12
.github/workflows/linux.yml
vendored
@@ -11,18 +11,21 @@ on:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
LANG: C.UTF-8
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: "1.20"
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
@@ -42,4 +45,7 @@ jobs:
|
|||||||
run: make test
|
run: make test
|
||||||
|
|
||||||
- name: Integration test
|
- name: Integration test
|
||||||
run: make install && ./install --all && LC_ALL=C tmux new-session -d && ruby test/test_go.rb --verbose
|
run: make install && ./install --all && tmux new-session -d && ruby test/test_go.rb --verbose
|
||||||
|
|
||||||
|
- name: Integration test (tcell)
|
||||||
|
run: TAGS=tcell make clean install && ruby test/test_go.rb --verbose
|
||||||
|
4
.github/workflows/macos.yml
vendored
4
.github/workflows/macos.yml
vendored
@@ -15,14 +15,14 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.18
|
go-version: "1.20"
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
|
2
.github/workflows/sponsors.yml
vendored
2
.github/workflows/sponsors.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout 🛎️
|
- name: Checkout 🛎️
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Generate Sponsors 💖
|
- name: Generate Sponsors 💖
|
||||||
uses: JamesIves/github-sponsors-readme-action@v1
|
uses: JamesIves/github-sponsors-readme-action@v1
|
||||||
|
4
.github/workflows/typos.yml
vendored
4
.github/workflows/typos.yml
vendored
@@ -6,5 +6,5 @@ jobs:
|
|||||||
name: Spell Check with Typos
|
name: Spell Check with Typos
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: crate-ci/typos@v1.17.2
|
- uses: crate-ci/typos@v1.19.0
|
||||||
|
@@ -20,10 +20,6 @@ builds:
|
|||||||
cat > /tmp/fzf-gon-amd64.hcl << EOF
|
cat > /tmp/fzf-gon-amd64.hcl << EOF
|
||||||
source = ["./dist/fzf-macos_darwin_amd64_v1/fzf"]
|
source = ["./dist/fzf-macos_darwin_amd64_v1/fzf"]
|
||||||
bundle_id = "kr.junegunn.fzf"
|
bundle_id = "kr.junegunn.fzf"
|
||||||
apple_id {
|
|
||||||
username = "junegunn.c@gmail.com"
|
|
||||||
password = "@env:AC_PASSWORD"
|
|
||||||
}
|
|
||||||
sign {
|
sign {
|
||||||
application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
|
application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
|
||||||
}
|
}
|
||||||
@@ -48,10 +44,6 @@ builds:
|
|||||||
cat > /tmp/fzf-gon-arm64.hcl << EOF
|
cat > /tmp/fzf-gon-arm64.hcl << EOF
|
||||||
source = ["./dist/fzf-macos-arm_darwin_arm64/fzf"]
|
source = ["./dist/fzf-macos-arm_darwin_arm64/fzf"]
|
||||||
bundle_id = "kr.junegunn.fzf"
|
bundle_id = "kr.junegunn.fzf"
|
||||||
apple_id {
|
|
||||||
username = "junegunn.c@gmail.com"
|
|
||||||
password = "@env:AC_PASSWORD"
|
|
||||||
}
|
|
||||||
sign {
|
sign {
|
||||||
application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
|
application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
|
||||||
}
|
}
|
||||||
|
@@ -1 +1 @@
|
|||||||
golang 1.21.6
|
golang 1.20.13
|
||||||
|
@@ -552,7 +552,7 @@ pods() {
|
|||||||
- Press enter key on a pod to `kubectl exec` into it
|
- Press enter key on a pod to `kubectl exec` into it
|
||||||
- Press CTRL-O to open the log in your editor
|
- Press CTRL-O to open the log in your editor
|
||||||
- Press CTRL-R to reload the pod list
|
- Press CTRL-R to reload the pod list
|
||||||
- Press CTRL-/ repeatedly to to rotate through a different sets of preview
|
- Press CTRL-/ repeatedly to rotate through a different sets of preview
|
||||||
window options
|
window options
|
||||||
1. `80%,border-bottom`
|
1. `80%,border-bottom`
|
||||||
1. `hidden`
|
1. `hidden`
|
||||||
|
4
BUILD.md
4
BUILD.md
@@ -6,7 +6,7 @@ Build instructions
|
|||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Go 1.18 or above
|
- Go 1.20 or above
|
||||||
|
|
||||||
### Using Makefile
|
### Using Makefile
|
||||||
|
|
||||||
@@ -42,6 +42,8 @@ Third-party libraries used
|
|||||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||||
- [tcell](https://github.com/gdamore/tcell)
|
- [tcell](https://github.com/gdamore/tcell)
|
||||||
- Licensed under [Apache License 2.0](https://github.com/gdamore/tcell/blob/master/LICENSE)
|
- Licensed under [Apache License 2.0](https://github.com/gdamore/tcell/blob/master/LICENSE)
|
||||||
|
- [fastwalk](https://github.com/charlievieth/fastwalk)
|
||||||
|
- Licensed under [MIT](https://raw.githubusercontent.com/charlievieth/fastwalk/master/LICENSE)
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
122
CHANGELOG.md
122
CHANGELOG.md
@@ -1,6 +1,128 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.49.0
|
||||||
|
------
|
||||||
|
- Ingestion performance improved by around 40% (more or less depending on options)
|
||||||
|
- `--info=hidden` and `--info=inline-right` will no longer hide the horizontal separator by default. This gives you more flexibility in customizing the layout.
|
||||||
|
```sh
|
||||||
|
fzf --border --info=inline-right
|
||||||
|
fzf --border --info=inline-right --separator ═
|
||||||
|
fzf --border --info=inline-right --no-separator
|
||||||
|
fzf --border --info=hidden
|
||||||
|
fzf --border --info=hidden --separator ━
|
||||||
|
fzf --border --info=hidden --no-separator
|
||||||
|
```
|
||||||
|
- Added two environment variables exported to the child processes
|
||||||
|
- `FZF_PREVIEW_LABEL`
|
||||||
|
- `FZF_BORDER_LABEL`
|
||||||
|
```sh
|
||||||
|
# Use the current value of $FZF_PREVIEW_LABEL to determine which actions to perform
|
||||||
|
git ls-files |
|
||||||
|
fzf --header 'Press CTRL-P to change preview mode' \
|
||||||
|
--bind='ctrl-p:transform:[[ $FZF_PREVIEW_LABEL =~ cat ]] \
|
||||||
|
&& echo "change-preview(git log --color=always \{})+change-preview-label([[ log ]])" \
|
||||||
|
|| echo "change-preview(bat --color=always \{})+change-preview-label([[ cat ]])"'
|
||||||
|
```
|
||||||
|
- Renamed `track` action to `track-current` to highlight the difference between the global tracking state set by `--track` and a one-off tracking action
|
||||||
|
- `track` is still available as an alias
|
||||||
|
- Added `untrack-current` and `toggle-track-current` actions
|
||||||
|
- `*-current` actions are no-op when the global tracking state is set
|
||||||
|
- Bug fixes and minor improvements
|
||||||
|
|
||||||
|
0.48.1
|
||||||
|
------
|
||||||
|
- CTRL-T and ALT-C bindings can be disabled by setting `FZF_CTRL_T_COMMAND` and `FZF_ALT_C_COMMAND` to empty strings respectively when sourcing the script
|
||||||
|
```sh
|
||||||
|
# bash
|
||||||
|
FZF_CTRL_T_COMMAND= FZF_ALT_C_COMMAND= eval "$(fzf --bash)"
|
||||||
|
|
||||||
|
# zsh
|
||||||
|
FZF_CTRL_T_COMMAND= FZF_ALT_C_COMMAND= eval "$(fzf --zsh)"
|
||||||
|
|
||||||
|
# fish
|
||||||
|
fzf --fish | FZF_CTRL_T_COMMAND= FZF_ALT_C_COMMAND= source
|
||||||
|
```
|
||||||
|
- Setting the variables after sourcing the script will have no effect
|
||||||
|
- Bug fixes
|
||||||
|
|
||||||
|
0.48.0
|
||||||
|
------
|
||||||
|
- Shell integration scripts are now embedded in the fzf binary. This simplifies the distribution, and the users are less likely to have problems caused by using incompatible scripts and binaries.
|
||||||
|
- bash
|
||||||
|
```sh
|
||||||
|
# Set up fzf key bindings and fuzzy completion
|
||||||
|
eval "$(fzf --bash)"
|
||||||
|
```
|
||||||
|
- zsh
|
||||||
|
```sh
|
||||||
|
# Set up fzf key bindings and fuzzy completion
|
||||||
|
eval "$(fzf --zsh)"
|
||||||
|
```
|
||||||
|
- fish
|
||||||
|
```fish
|
||||||
|
# Set up fzf key bindings
|
||||||
|
fzf --fish | source
|
||||||
|
```
|
||||||
|
- Added options for customizing the behavior of the built-in walker
|
||||||
|
| Option | Description | Default |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `--walker=OPTS` | Walker options (`[file][,dir][,follow][,hidden]`) | `file,follow,hidden` |
|
||||||
|
| `--walker-root=DIR` | Root directory from which to start walker | `.` |
|
||||||
|
| `--walker-skip=DIRS` | Comma-separated list of directory names to skip | `.git,node_modules` |
|
||||||
|
- Examples
|
||||||
|
```sh
|
||||||
|
# Built-in walker is only used by standalone fzf when $FZF_DEFAULT_COMMAND is not set
|
||||||
|
unset FZF_DEFAULT_COMMAND
|
||||||
|
|
||||||
|
fzf # default: --walker=file,follow,hidden --walker-root=. --walker-skip=.git,node_modules
|
||||||
|
fzf --walker=file,dir,hidden,follow --walker-skip=.git,node_modules,target
|
||||||
|
|
||||||
|
# Walker options in $FZF_DEFAULT_OPTS
|
||||||
|
export FZF_DEFAULT_OPTS="--walker=file,dir,hidden,follow --walker-skip=.git,node_modules,target"
|
||||||
|
fzf
|
||||||
|
|
||||||
|
# Reading from STDIN; --walker is ignored
|
||||||
|
seq 100 | fzf --walker=dir
|
||||||
|
|
||||||
|
# Reading from $FZF_DEFAULT_COMMAND; --walker is ignored
|
||||||
|
export FZF_DEFAULT_COMMAND='seq 100'
|
||||||
|
fzf --walker=dir
|
||||||
|
```
|
||||||
|
- Shell integration scripts have been updated to use the built-in walker with these new options and they are now much faster out of the box.
|
||||||
|
|
||||||
|
0.47.0
|
||||||
|
------
|
||||||
|
- Replaced ["the default find command"][find] with a built-in directory walker to simplify the code and to achieve better performance and consistent behavior across platforms.
|
||||||
|
This doesn't affect you if you have `$FZF_DEFAULT_COMMAND` set.
|
||||||
|
- Breaking changes:
|
||||||
|
- Unlike [the previous "find" command][find], the new traversal code will list hidden files, but hidden directories will still be ignored
|
||||||
|
- No filtering of `devtmpfs` or `proc` types
|
||||||
|
- Traversal is parallelized, so the order of the entries will be different each time
|
||||||
|
- You may wonder why fzf implements directory walker anyway when it's a filter program following the [Unix philosophy][unix].
|
||||||
|
But fzf has had [the walker code for years][walker] to tackle the performance problem on Windows. And I decided to use the same approach on different platforms as well for the benefits listed above.
|
||||||
|
- Built-in walker is using the excellent [charlievieth/fastwalk][fastwalk] library, which easily outperforms its competitors and supports safely following symlinks.
|
||||||
|
- Added `$FZF_DEFAULT_OPTS_FILE` to allow managing default options in a file
|
||||||
|
- See [#3618](https://github.com/junegunn/fzf/pull/3618)
|
||||||
|
- Option precedence from lower to higher
|
||||||
|
1. Options read from `$FZF_DEFAULT_OPTS_FILE`
|
||||||
|
1. Options from `$FZF_DEFAULT_OPTS`
|
||||||
|
1. Options from command-line arguments
|
||||||
|
- Bug fixes and improvements
|
||||||
|
|
||||||
|
[find]: https://github.com/junegunn/fzf/blob/0.46.1/src/constants.go#L60-L64
|
||||||
|
[walker]: https://github.com/junegunn/fzf/pull/1847
|
||||||
|
[fastwalk]: https://github.com/charlievieth/fastwalk
|
||||||
|
[unix]: https://en.wikipedia.org/wiki/Unix_philosophy
|
||||||
|
|
||||||
|
0.46.1
|
||||||
|
------
|
||||||
|
- Bug fixes and improvements
|
||||||
|
- Fixed Windows binaries
|
||||||
|
- Downgraded Go version to 1.20 to support older versions of Windows
|
||||||
|
- https://tip.golang.org/doc/go1.21#windows
|
||||||
|
- Updated [rivo/uniseg](https://github.com/rivo/uniseg) dependency to v0.4.6
|
||||||
|
|
||||||
0.46.0
|
0.46.0
|
||||||
------
|
------
|
||||||
- Added two new events
|
- Added two new events
|
||||||
|
2
Makefile
2
Makefile
@@ -4,7 +4,7 @@ GOOS ?= $(word 1, $(subst /, " ", $(word 4, $(shell go version))))
|
|||||||
|
|
||||||
MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
|
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 shell/*sh) $(MAKEFILE)
|
||||||
|
|
||||||
ifdef FZF_VERSION
|
ifdef FZF_VERSION
|
||||||
VERSION := $(FZF_VERSION)
|
VERSION := $(FZF_VERSION)
|
||||||
|
@@ -238,19 +238,20 @@ call fzf#run({'sink': 'e'})
|
|||||||
```
|
```
|
||||||
|
|
||||||
We haven't specified the `source`, so this is equivalent to starting fzf on
|
We haven't specified the `source`, so this is equivalent to starting fzf on
|
||||||
command line without standard input pipe; fzf will use find command (or
|
command line without standard input pipe; fzf will traverse the file system
|
||||||
`$FZF_DEFAULT_COMMAND` if defined) to list the files under the current
|
under the current directory to get the list of files. (If
|
||||||
directory. When you select one, it will open it with the sink, `:e` command.
|
`$FZF_DEFAULT_COMMAND` is set, fzf will use the output of the command
|
||||||
If you want to open it in a new tab, you can pass `:tabedit` command instead
|
instead.) When you select one, it will open it with the sink, `:e` command. If
|
||||||
as the sink.
|
you want to open it in a new tab, you can pass `:tabedit` command instead as
|
||||||
|
the sink.
|
||||||
|
|
||||||
```vim
|
```vim
|
||||||
call fzf#run({'sink': 'tabedit'})
|
call fzf#run({'sink': 'tabedit'})
|
||||||
```
|
```
|
||||||
|
|
||||||
Instead of using the default find command, you can use any shell command as
|
You can use any shell command as the source to generate the list. The
|
||||||
the source. The following example will list the files managed by git. It's
|
following example will list the files managed by git. It's equivalent to
|
||||||
equivalent to running `git ls-files | fzf` on shell.
|
running `git ls-files | fzf` on shell.
|
||||||
|
|
||||||
```vim
|
```vim
|
||||||
call fzf#run({'source': 'git ls-files', 'sink': 'e'})
|
call fzf#run({'source': 'git ls-files', 'sink': 'e'})
|
||||||
|
@@ -95,9 +95,9 @@ while [[ $# -gt 0 ]]; do
|
|||||||
elif [[ "$size" =~ %$ ]]; then
|
elif [[ "$size" =~ %$ ]]; then
|
||||||
size=${size:0:((${#size}-1))}
|
size=${size:0:((${#size}-1))}
|
||||||
if [[ -n "$swap" ]]; then
|
if [[ -n "$swap" ]]; then
|
||||||
opt="$opt -p $(( 100 - size ))"
|
opt="$opt -l $(( 100 - size ))%"
|
||||||
else
|
else
|
||||||
opt="$opt -p $size"
|
opt="$opt -l $size%"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [[ -n "$swap" ]]; then
|
if [[ -n "$swap" ]]; then
|
||||||
@@ -196,8 +196,9 @@ if [[ "$opt" =~ "-E" ]]; then
|
|||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
[[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
|
envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
|
||||||
[[ -n "$FZF_DEFAULT_COMMAND" ]] && envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
|
envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
|
||||||
|
envs="$envs FZF_DEFAULT_OPTS_FILE=$(printf %q "$FZF_DEFAULT_OPTS_FILE")"
|
||||||
[[ -n "$RUNEWIDTH_EASTASIAN" ]] && envs="$envs RUNEWIDTH_EASTASIAN=$(printf %q "$RUNEWIDTH_EASTASIAN")"
|
[[ -n "$RUNEWIDTH_EASTASIAN" ]] && envs="$envs RUNEWIDTH_EASTASIAN=$(printf %q "$RUNEWIDTH_EASTASIAN")"
|
||||||
[[ -n "$BAT_THEME" ]] && envs="$envs BAT_THEME=$(printf %q "$BAT_THEME")"
|
[[ -n "$BAT_THEME" ]] && envs="$envs BAT_THEME=$(printf %q "$BAT_THEME")"
|
||||||
echo "$envs;" > "$argsf"
|
echo "$envs;" > "$argsf"
|
||||||
|
31
doc/fzf.txt
31
doc/fzf.txt
@@ -1,4 +1,4 @@
|
|||||||
fzf.txt fzf Last change: January 1 2024
|
fzf.txt fzf Last change: February 15 2024
|
||||||
FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
|
FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
@@ -264,17 +264,18 @@ entry.
|
|||||||
call fzf#run({'sink': 'e'})
|
call fzf#run({'sink': 'e'})
|
||||||
<
|
<
|
||||||
We haven't specified the `source`, so this is equivalent to starting fzf on
|
We haven't specified the `source`, so this is equivalent to starting fzf on
|
||||||
command line without standard input pipe; fzf will use find command (or
|
command line without standard input pipe; fzf will traverse the file system
|
||||||
`$FZF_DEFAULT_COMMAND` if defined) to list the files under the current
|
under the current directory to get the list of files. (If
|
||||||
directory. When you select one, it will open it with the sink, `:e` command.
|
`$FZF_DEFAULT_COMMAND` is set, fzf will use the output of the command
|
||||||
If you want to open it in a new tab, you can pass `:tabedit` command instead
|
instead.) When you select one, it will open it with the sink, `:e` command. If
|
||||||
as the sink.
|
you want to open it in a new tab, you can pass `:tabedit` command instead as
|
||||||
|
the sink.
|
||||||
>
|
>
|
||||||
call fzf#run({'sink': 'tabedit'})
|
call fzf#run({'sink': 'tabedit'})
|
||||||
<
|
<
|
||||||
Instead of using the default find command, you can use any shell command as
|
You can use any shell command as the source to generate the list. The
|
||||||
the source. The following example will list the files managed by git. It's
|
following example will list the files managed by git. It's equivalent to
|
||||||
equivalent to running `git ls-files | fzf` on shell.
|
running `git ls-files | fzf` on shell.
|
||||||
>
|
>
|
||||||
call fzf#run({'source': 'git ls-files', 'sink': 'e'})
|
call fzf#run({'source': 'git ls-files', 'sink': 'e'})
|
||||||
<
|
<
|
||||||
@@ -417,24 +418,12 @@ TIPS *fzf-tips*
|
|||||||
< fzf inside terminal buffer >________________________________________________~
|
< fzf inside terminal buffer >________________________________________________~
|
||||||
*fzf-inside-terminal-buffer*
|
*fzf-inside-terminal-buffer*
|
||||||
|
|
||||||
The latest versions of Vim and Neovim include builtin terminal emulator
|
|
||||||
(`:terminal`) and fzf will start in a terminal buffer in the following cases:
|
|
||||||
|
|
||||||
- On Neovim
|
|
||||||
- On GVim
|
|
||||||
- On Terminal Vim with a non-default layout
|
|
||||||
- `call fzf#run({'left': '30%'})` or `let g:fzf_layout = {'left': '30%'}`
|
|
||||||
|
|
||||||
On the latest versions of Vim and Neovim, fzf will start in a terminal buffer.
|
On the latest versions of Vim and Neovim, fzf will start in a terminal buffer.
|
||||||
If you find the default ANSI colors to be different, consider configuring the
|
If you find the default ANSI colors to be different, consider configuring the
|
||||||
colors using `g:terminal_ansi_colors` in regular Vim or `g:terminal_color_x`
|
colors using `g:terminal_ansi_colors` in regular Vim or `g:terminal_color_x`
|
||||||
in Neovim.
|
in Neovim.
|
||||||
|
|
||||||
*g:terminal_color_15* *g:terminal_color_14* *g:terminal_color_13*
|
|
||||||
*g:terminal_color_12* *g:terminal_color_11* *g:terminal_color_10* *g:terminal_color_9*
|
|
||||||
*g:terminal_color_8* *g:terminal_color_7* *g:terminal_color_6* *g:terminal_color_5*
|
|
||||||
*g:terminal_color_4* *g:terminal_color_3* *g:terminal_color_2* *g:terminal_color_1*
|
|
||||||
*g:terminal_color_0*
|
|
||||||
>
|
>
|
||||||
" Terminal colors for seoul256 color scheme
|
" Terminal colors for seoul256 color scheme
|
||||||
if has('nvim')
|
if has('nvim')
|
||||||
|
20
go.mod
20
go.mod
@@ -1,22 +1,20 @@
|
|||||||
module github.com/junegunn/fzf
|
module github.com/junegunn/fzf
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/tcell/v2 v2.5.4
|
github.com/charlievieth/fastwalk v1.0.3
|
||||||
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2
|
github.com/gdamore/tcell/v2 v2.7.4
|
||||||
github.com/mattn/go-isatty v0.0.17
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/mattn/go-shellwords v1.0.12
|
github.com/mattn/go-shellwords v1.0.12
|
||||||
github.com/saracen/walker v0.1.3
|
github.com/rivo/uniseg v0.4.7
|
||||||
golang.org/x/sys v0.16.0
|
golang.org/x/sys v0.18.0
|
||||||
golang.org/x/term v0.16.0
|
golang.org/x/term v0.18.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/encoding v1.0.0 // indirect
|
github.com/gdamore/encoding v1.0.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/sync v0.5.0 // indirect
|
|
||||||
golang.org/x/text v0.5.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.17
|
go 1.20
|
||||||
|
49
go.sum
49
go.sum
@@ -1,52 +1,57 @@
|
|||||||
|
github.com/charlievieth/fastwalk v1.0.3 h1:eNWFaNPe5srPqQ5yyDbhAf11paeZaHWcihRhpuYFfSg=
|
||||||
|
github.com/charlievieth/fastwalk v1.0.3/go.mod h1:JSfglY/gmL/rqsUS1NCsJTocB5n6sSl9ApAqif4CUbs=
|
||||||
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/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k=
|
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
|
||||||
github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw=
|
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
|
||||||
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2 h1:oEwPBh29BPu1MaTsz2dM9bDrkOgKBoYFC0u6uY2izWo=
|
|
||||||
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2/go.mod h1:ywqF55XaSE3/uS2tkJqVFKiE0oIYAXRvU2N7DU4y3XQ=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/saracen/walker v0.1.3 h1:YtcKKmpRPy6XJTHJ75J2QYXXZYWnZNQxPCVqZSHVV/g=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/saracen/walker v0.1.3/go.mod h1:FU+7qU8DeQQgSZDmmThMJi93kPkLFgy0oVAcLxurjIk=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||||
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
36
install
36
install
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
version=0.46.0
|
version=0.49.0
|
||||||
auto_completion=
|
auto_completion=
|
||||||
key_bindings=
|
key_bindings=
|
||||||
update_config=2
|
update_config=2
|
||||||
@@ -262,6 +262,12 @@ if [[ ! "\$PATH" == *$fzf_base_esc/bin* ]]; then
|
|||||||
PATH="\${PATH:+\${PATH}:}$fzf_base/bin"
|
PATH="\${PATH:+\${PATH}:}$fzf_base/bin"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [[ $auto_completion -eq 1 ]] && [[ $key_bindings -eq 1 ]]; then
|
||||||
|
echo "eval \"\$(fzf --$shell)\"" >> "$src"
|
||||||
|
else
|
||||||
|
cat >> "$src" << EOF
|
||||||
# Auto-completion
|
# Auto-completion
|
||||||
# ---------------
|
# ---------------
|
||||||
$fzf_completion
|
$fzf_completion
|
||||||
@@ -270,6 +276,7 @@ $fzf_completion
|
|||||||
# ------------
|
# ------------
|
||||||
$fzf_key_bindings
|
$fzf_key_bindings
|
||||||
EOF
|
EOF
|
||||||
|
fi
|
||||||
echo "OK"
|
echo "OK"
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -281,18 +288,6 @@ if [[ "$shells" =~ fish ]]; then
|
|||||||
or set --universal fish_user_paths \$fish_user_paths "$fzf_base"/bin
|
or set --universal fish_user_paths \$fish_user_paths "$fzf_base"/bin
|
||||||
EOF
|
EOF
|
||||||
[ $? -eq 0 ] && echo "OK" || echo "Failed"
|
[ $? -eq 0 ] && echo "OK" || echo "Failed"
|
||||||
|
|
||||||
mkdir -p "${fish_dir}/functions"
|
|
||||||
fish_binding="${fish_dir}/functions/fzf_key_bindings.fish"
|
|
||||||
if [ $key_bindings -ne 0 ]; then
|
|
||||||
echo -n "Symlink $fish_binding ... "
|
|
||||||
ln -sf "$fzf_base/shell/key-bindings.fish" \
|
|
||||||
"$fish_binding" && echo "OK" || echo "Failed"
|
|
||||||
else
|
|
||||||
echo -n "Removing $fish_binding ... "
|
|
||||||
rm -f "$fish_binding"
|
|
||||||
echo "OK"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
append_line() {
|
append_line() {
|
||||||
@@ -355,12 +350,23 @@ done
|
|||||||
if [ $key_bindings -eq 1 ] && [[ "$shells" =~ fish ]]; then
|
if [ $key_bindings -eq 1 ] && [[ "$shells" =~ fish ]]; then
|
||||||
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
||||||
if [ ! -e "$bind_file" ]; then
|
if [ ! -e "$bind_file" ]; then
|
||||||
|
mkdir -p "${fish_dir}/functions"
|
||||||
create_file "$bind_file" \
|
create_file "$bind_file" \
|
||||||
'function fish_user_key_bindings' \
|
'function fish_user_key_bindings' \
|
||||||
' fzf_key_bindings' \
|
' fzf --fish | source' \
|
||||||
'end'
|
'end'
|
||||||
else
|
else
|
||||||
append_line $update_config "fzf_key_bindings" "$bind_file"
|
echo "Check $bind_file:"
|
||||||
|
lno=$(\grep -nF "fzf_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
|
||||||
|
if [[ -n $lno ]]; then
|
||||||
|
echo " ** Found 'fzf_key_bindings' in line #$lno"
|
||||||
|
echo " ** You have to replace the line to 'fzf --fish | source'"
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
echo " - Clear"
|
||||||
|
echo
|
||||||
|
append_line $update_config "fzf --fish | source" "$bind_file"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
$version="0.46.0"
|
$version="0.49.0"
|
||||||
|
|
||||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
45
main.go
45
main.go
@@ -1,14 +1,55 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
fzf "github.com/junegunn/fzf/src"
|
fzf "github.com/junegunn/fzf/src"
|
||||||
"github.com/junegunn/fzf/src/protector"
|
"github.com/junegunn/fzf/src/protector"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.46"
|
var version string = "0.49"
|
||||||
var revision string = "devel"
|
var revision string = "devel"
|
||||||
|
|
||||||
|
//go:embed shell/key-bindings.bash
|
||||||
|
var bashKeyBindings []byte
|
||||||
|
|
||||||
|
//go:embed shell/completion.bash
|
||||||
|
var bashCompletion []byte
|
||||||
|
|
||||||
|
//go:embed shell/key-bindings.zsh
|
||||||
|
var zshKeyBindings []byte
|
||||||
|
|
||||||
|
//go:embed shell/completion.zsh
|
||||||
|
var zshCompletion []byte
|
||||||
|
|
||||||
|
//go:embed shell/key-bindings.fish
|
||||||
|
var fishKeyBindings []byte
|
||||||
|
|
||||||
|
func printScript(label string, content []byte) {
|
||||||
|
fmt.Println("### " + label + " ###")
|
||||||
|
fmt.Println(strings.TrimSpace(string(content)))
|
||||||
|
fmt.Println("### end: " + label + " ###")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
protector.Protect()
|
protector.Protect()
|
||||||
fzf.Run(fzf.ParseOptions(), version, revision)
|
options := fzf.ParseOptions()
|
||||||
|
if options.Bash {
|
||||||
|
printScript("key-bindings.bash", bashKeyBindings)
|
||||||
|
printScript("completion.bash", bashCompletion)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if options.Zsh {
|
||||||
|
printScript("key-bindings.zsh", zshKeyBindings)
|
||||||
|
printScript("completion.zsh", zshCompletion)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if options.Fish {
|
||||||
|
printScript("key-bindings.fish", fishKeyBindings)
|
||||||
|
fmt.Println("fzf_key_bindings")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fzf.Run(options, 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 "Jan 2024" "fzf 0.46.0" "fzf-tmux - open fzf in tmux split pane"
|
.TH fzf-tmux 1 "Apr 2024" "fzf 0.49.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
|
||||||
|
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
..
|
..
|
||||||
.TH fzf 1 "Jan 2024" "fzf 0.46.0" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Apr 2024" "fzf 0.49.0" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@@ -33,6 +33,10 @@ fzf [options]
|
|||||||
fzf is a general-purpose command-line fuzzy finder.
|
fzf is a general-purpose command-line fuzzy finder.
|
||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
|
.SS Note
|
||||||
|
.TP
|
||||||
|
Most long options have the opposite version with \fB--no-\fR prefix.
|
||||||
|
|
||||||
.SS Search mode
|
.SS Search mode
|
||||||
.TP
|
.TP
|
||||||
.B "-x, --extended"
|
.B "-x, --extended"
|
||||||
@@ -368,20 +372,21 @@ e.g.
|
|||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "--info=" "STYLE"
|
.BI "--info=" "STYLE"
|
||||||
Determines the display style of finder info (match counters).
|
Determines the display style of the finder info. (e.g. match counter, loading indicator, etc.)
|
||||||
|
|
||||||
|
.BR default " On the left end of the horizontal separator"
|
||||||
.br
|
.br
|
||||||
.BR default " Display on the next line to the prompt"
|
.BR right " On the right end of the horizontal separator"
|
||||||
.br
|
.br
|
||||||
.BR right " Display on the right end of the next line to the prompt"
|
.BR hidden " Do not display finder info"
|
||||||
.br
|
.br
|
||||||
.BR inline " Display on the same line with the default separator ' < '"
|
.BR inline " After the prompt with the default prefix ' < '"
|
||||||
.br
|
.br
|
||||||
.BR inline:SEPARATOR " Display on the same line with a non-default separator"
|
.BR inline:PREFIX " After the prompt with a non-default prefix"
|
||||||
.br
|
.br
|
||||||
.BR inline-right " Display on the right end of the same line
|
.BR inline-right " On the right end of the prompt line"
|
||||||
.br
|
.br
|
||||||
.BR hidden " Do not display finder info"
|
.BR inline-right:PREFIX " On the right end of the prompt line with a custom prefix"
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
@@ -620,7 +625,7 @@ The following example uses https://github.com/junegunn/fzf/blob/master/bin/fzf-p
|
|||||||
script to render an image using either of the protocols inside the preview window.
|
script to render an image using either of the protocols inside the preview window.
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
\fBfzf --preview='fzf-preview.sh {}'
|
\fBfzf --preview='fzf-preview.sh {}'\fR
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
@@ -854,8 +859,49 @@ e.g.
|
|||||||
.B "--version"
|
.B "--version"
|
||||||
Display version information and exit
|
Display version information and exit
|
||||||
|
|
||||||
|
.SS Directory traversal
|
||||||
.TP
|
.TP
|
||||||
Note that most options have the opposite versions with \fB--no-\fR prefix.
|
.B "--walker=[file][,dir][,follow][,hidden]"
|
||||||
|
Determines the behavior of the built-in directory walker that is used when
|
||||||
|
\fB$FZF_DEFAULT_COMMAND\fR is not set. The default value is \fBfile,follow,hidden\fR.
|
||||||
|
|
||||||
|
* \fBfile\fR: Include files in the search result
|
||||||
|
.br
|
||||||
|
* \fBdir\fR: Include directories in the search result
|
||||||
|
.br
|
||||||
|
* \fBhidden\fR: Include and follow hidden directories
|
||||||
|
.br
|
||||||
|
* \fBfollow\fR: Follow symbolic links
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B "--walker-root=DIR"
|
||||||
|
The root directory from which to start the built-in directory walker.
|
||||||
|
The default value is the current working directory.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B "--walker-skip=DIRS"
|
||||||
|
Comma-separated list of directory names to skip during the directory walk.
|
||||||
|
The default value is \fB.git,node_modules\fR.
|
||||||
|
|
||||||
|
.SS Shell integration
|
||||||
|
.TP
|
||||||
|
.B "--bash"
|
||||||
|
Print script to set up Bash shell integration
|
||||||
|
|
||||||
|
e.g. \fBeval "$(fzf --bash)"\fR
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B "--zsh"
|
||||||
|
Print script to set up Zsh shell integration
|
||||||
|
|
||||||
|
e.g. \fBeval "$(fzf --zsh)"\fR
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B "--fish"
|
||||||
|
Print script to set up Fish shell integration
|
||||||
|
|
||||||
|
e.g. \fBfzf --fish | source\fR
|
||||||
|
|
||||||
.SH ENVIRONMENT VARIABLES
|
.SH ENVIRONMENT VARIABLES
|
||||||
.TP
|
.TP
|
||||||
@@ -865,7 +911,14 @@ with \fB$SHELL -c\fR if \fBSHELL\fR is set, otherwise with \fBsh -c\fR, so in
|
|||||||
this case make sure that the command is POSIX-compliant.
|
this case make sure that the command is POSIX-compliant.
|
||||||
.TP
|
.TP
|
||||||
.B FZF_DEFAULT_OPTS
|
.B FZF_DEFAULT_OPTS
|
||||||
Default options. e.g. \fBexport FZF_DEFAULT_OPTS="--extended --cycle"\fR
|
Default options.
|
||||||
|
.br
|
||||||
|
e.g. \fBexport FZF_DEFAULT_OPTS="--layout=reverse --border --cycle"\fR
|
||||||
|
.TP
|
||||||
|
.B FZF_DEFAULT_OPTS_FILE
|
||||||
|
The location of the file that contains the default options.
|
||||||
|
.br
|
||||||
|
e.g. \fBexport FZF_DEFAULT_OPTS_FILE=~/.fzfrc\fR
|
||||||
.TP
|
.TP
|
||||||
.B FZF_API_KEY
|
.B FZF_API_KEY
|
||||||
Can be used to require an API key when using \fB--listen\fR option. If not set,
|
Can be used to require an API key when using \fB--listen\fR option. If not set,
|
||||||
@@ -923,6 +976,10 @@ fzf exports the following environment variables to its child processes.
|
|||||||
.br
|
.br
|
||||||
.BR FZF_PROMPT " Prompt string"
|
.BR FZF_PROMPT " Prompt string"
|
||||||
.br
|
.br
|
||||||
|
.BR FZF_PREVIEW_LABEL " Preview label string"
|
||||||
|
.br
|
||||||
|
.BR FZF_BORDER_LABEL " Border label string"
|
||||||
|
.br
|
||||||
.BR FZF_ACTION " The name of the last action performed"
|
.BR FZF_ACTION " The name of the last action performed"
|
||||||
.br
|
.br
|
||||||
.BR FZF_PORT " Port number when --listen option is used"
|
.BR FZF_PORT " Port number when --listen option is used"
|
||||||
@@ -1274,9 +1331,10 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBtoggle-preview-wrap\fR
|
\fBtoggle-preview-wrap\fR
|
||||||
\fBtoggle-search\fR (toggle search functionality)
|
\fBtoggle-search\fR (toggle search functionality)
|
||||||
\fBtoggle-sort\fR
|
\fBtoggle-sort\fR
|
||||||
\fBtoggle-track\fR
|
\fBtoggle-track\fR (toggle global tracking option (\fB--track\fR))
|
||||||
|
\fBtoggle-track-current\fR (toggle tracking of the current item)
|
||||||
\fBtoggle+up\fR \fIbtab (shift-tab)\fR
|
\fBtoggle+up\fR \fIbtab (shift-tab)\fR
|
||||||
\fBtrack\fR (track the current item; automatically disabled if focus changes)
|
\fBtrack-current\fR (track the current item; automatically disabled if focus changes)
|
||||||
\fBtransform(...)\fR (transform states using the output of an external command)
|
\fBtransform(...)\fR (transform states using the output of an external command)
|
||||||
\fBtransform-border-label(...)\fR (transform border label using an external command)
|
\fBtransform-border-label(...)\fR (transform border label using an external command)
|
||||||
\fBtransform-header(...)\fR (transform header using an external command)
|
\fBtransform-header(...)\fR (transform header using an external command)
|
||||||
@@ -1286,6 +1344,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBunbind(...)\fR (unbind bindings)
|
\fBunbind(...)\fR (unbind bindings)
|
||||||
\fBunix-line-discard\fR \fIctrl-u\fR
|
\fBunix-line-discard\fR \fIctrl-u\fR
|
||||||
\fBunix-word-rubout\fR \fIctrl-w\fR
|
\fBunix-word-rubout\fR \fIctrl-w\fR
|
||||||
|
\fBuntrack-current\fR (stop tracking the current item; no-op if global tracking is enabled)
|
||||||
\fBup\fR \fIctrl-k ctrl-p up\fR
|
\fBup\fR \fIctrl-k ctrl-p up\fR
|
||||||
\fByank\fR \fIctrl-y\fR
|
\fByank\fR \fIctrl-y\fR
|
||||||
|
|
||||||
|
@@ -13,22 +13,19 @@
|
|||||||
|
|
||||||
|
|
||||||
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
||||||
if ! declare -F _fzf_compgen_path > /dev/null; then
|
#
|
||||||
_fzf_compgen_path() {
|
# _fzf_compgen_path() {
|
||||||
echo "$1"
|
# echo "$1"
|
||||||
command find -L "$1" \
|
# command find -L "$1" \
|
||||||
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
|
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
|
||||||
-a -not -path "$1" -print 2> /dev/null | command sed 's@^\./@@'
|
# -a -not -path "$1" -print 2> /dev/null | command sed 's@^\./@@'
|
||||||
}
|
# }
|
||||||
fi
|
#
|
||||||
|
# _fzf_compgen_dir() {
|
||||||
if ! declare -F _fzf_compgen_dir > /dev/null; then
|
# command find -L "$1" \
|
||||||
_fzf_compgen_dir() {
|
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
|
||||||
command find -L "$1" \
|
# -a -not -path "$1" -print 2> /dev/null | command sed 's@^\./@@'
|
||||||
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
|
# }
|
||||||
-a -not -path "$1" -print 2> /dev/null | command sed 's@^\./@@'
|
|
||||||
}
|
|
||||||
fi
|
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
@@ -63,6 +60,31 @@ __fzf_orig_completion() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# @param $1 cmd - Command name for which the original completion is searched
|
||||||
|
# @var[out] REPLY - Original function name is returned
|
||||||
|
__fzf_orig_completion_get_orig_func() {
|
||||||
|
local cmd orig_var orig
|
||||||
|
cmd=$1
|
||||||
|
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
|
||||||
|
orig="${!orig_var-}"
|
||||||
|
REPLY="${orig##*#}"
|
||||||
|
[[ $REPLY ]] && type "$REPLY" &> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# @param $1 cmd - Command name for which the original completion is searched
|
||||||
|
# @param $2 func - Fzf's completion function to replace the original function
|
||||||
|
# @var[out] REPLY - Completion setting is returned as a string to "eval"
|
||||||
|
__fzf_orig_completion_instantiate() {
|
||||||
|
local cmd func orig_var orig
|
||||||
|
cmd=$1
|
||||||
|
func=$2
|
||||||
|
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
|
||||||
|
orig="${!orig_var-}"
|
||||||
|
orig="${orig%#*}"
|
||||||
|
[[ $orig == *' %s '* ]] || return 1
|
||||||
|
printf -v REPLY "$orig" "$func"
|
||||||
|
}
|
||||||
|
|
||||||
_fzf_opts_completion() {
|
_fzf_opts_completion() {
|
||||||
local cur prev opts
|
local cur prev opts
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
@@ -261,28 +283,32 @@ _fzf_opts_completion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_handle_dynamic_completion() {
|
_fzf_handle_dynamic_completion() {
|
||||||
local cmd orig_var orig ret orig_cmd orig_complete
|
local cmd ret REPLY orig_cmd orig_complete
|
||||||
cmd="$1"
|
cmd="$1"
|
||||||
shift
|
shift
|
||||||
orig_cmd="$1"
|
orig_cmd="$1"
|
||||||
orig_var="_fzf_orig_completion_$cmd"
|
if __fzf_orig_completion_get_orig_func "$cmd"; then
|
||||||
orig="${!orig_var-}"
|
"$REPLY" "$@"
|
||||||
orig="${orig##*#}"
|
|
||||||
if [[ -n "$orig" ]] && type "$orig" > /dev/null 2>&1; then
|
|
||||||
$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 "$@"
|
$_fzf_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)
|
||||||
|
|
||||||
|
# Update orig_complete by _fzf_orig_completion entry
|
||||||
|
[[ $orig_complete =~ ' -F '(_fzf_[^ ]+)' ' ]] &&
|
||||||
|
__fzf_orig_completion_instantiate "$cmd" "${BASH_REMATCH[1]}" &&
|
||||||
|
orig_complete=$REPLY
|
||||||
|
|
||||||
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"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
[[ $ret -eq 0 ]] && return 124
|
||||||
return $ret
|
return $ret
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -293,7 +319,6 @@ __fzf_generic_path_completion() {
|
|||||||
if [[ $cmd == \\* ]]; then
|
if [[ $cmd == \\* ]]; then
|
||||||
cmd="${cmd:1}"
|
cmd="${cmd:1}"
|
||||||
fi
|
fi
|
||||||
cmd="${cmd//[^A-Za-z0-9_=]/_}"
|
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
@@ -309,9 +334,18 @@ __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 --scheme=path --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do
|
matches=$(
|
||||||
printf "%q " "${item%$3}$3"
|
unset FZF_DEFAULT_COMMAND
|
||||||
done)
|
export FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --scheme=path --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $2"
|
||||||
|
if declare -F "$1" > /dev/null; then
|
||||||
|
eval "$1 $(printf %q "$dir")" | __fzf_comprun "$4" -q "$leftover"
|
||||||
|
else
|
||||||
|
[[ $1 =~ dir ]] && walker=dir,follow || walker=file,dir,follow,hidden
|
||||||
|
__fzf_comprun "$4" -q "$leftover" --walker "$walker" --walker-root="$dir"
|
||||||
|
fi | while read -r item; do
|
||||||
|
printf "%q " "${item%$3}$3"
|
||||||
|
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
|
||||||
@@ -359,8 +393,8 @@ _fzf_complete() {
|
|||||||
post="$(caller 0 | command awk '{print $2}')_post"
|
post="$(caller 0 | command awk '{print $2}')_post"
|
||||||
type -t "$post" > /dev/null 2>&1 || post='command cat'
|
type -t "$post" > /dev/null 2>&1 || post='command cat'
|
||||||
|
|
||||||
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
|
|
||||||
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
|
cmd="${COMP_WORDS[0]}"
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
if [[ "$cur" == *"$trigger" ]] && [[ $cur != *'$('* ]] && [[ $cur != *':='* ]] && [[ $cur != *'`'* ]]; then
|
if [[ "$cur" == *"$trigger" ]] && [[ $cur != *'$('* ]] && [[ $cur != *':='* ]] && [[ $cur != *'`'* ]]; then
|
||||||
cur=${cur:0:${#cur}-${#trigger}}
|
cur=${cur:0:${#cur}-${#trigger}}
|
||||||
@@ -483,20 +517,25 @@ a_cmds="
|
|||||||
# Preserve existing completion
|
# Preserve existing completion
|
||||||
__fzf_orig_completion < <(complete -p $d_cmds $a_cmds ssh 2> /dev/null)
|
__fzf_orig_completion < <(complete -p $d_cmds $a_cmds ssh 2> /dev/null)
|
||||||
|
|
||||||
if type _completion_loader > /dev/null 2>&1; then
|
if type _comp_load > /dev/null 2>&1; then
|
||||||
_fzf_completion_loader=1
|
# _comp_load was added in bash-completion 2.12 to replace _completion_loader.
|
||||||
|
# We use it without -D option so that it does not use _comp_complete_minimal as the fallback.
|
||||||
|
_fzf_completion_loader=_comp_load
|
||||||
|
elif type __load_completion > /dev/null 2>&1; then
|
||||||
|
# In bash-completion 2.11, _completion_loader internally calls __load_completion
|
||||||
|
# and if it returns a non-zero status, it sets the default 'minimal' completion.
|
||||||
|
_fzf_completion_loader=__load_completion
|
||||||
|
elif type _completion_loader > /dev/null 2>&1; then
|
||||||
|
_fzf_completion_loader=_completion_loader
|
||||||
fi
|
fi
|
||||||
|
|
||||||
__fzf_defc() {
|
__fzf_defc() {
|
||||||
local cmd func opts orig_var orig def
|
local cmd func opts REPLY
|
||||||
cmd="$1"
|
cmd="$1"
|
||||||
func="$2"
|
func="$2"
|
||||||
opts="$3"
|
opts="$3"
|
||||||
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
|
if __fzf_orig_completion_instantiate "$cmd" "$func"; then
|
||||||
orig="${!orig_var-}"
|
eval "$REPLY"
|
||||||
if [[ -n "$orig" ]]; then
|
|
||||||
printf -v def "$orig" "$func"
|
|
||||||
eval "$def"
|
|
||||||
else
|
else
|
||||||
complete -F "$func" $opts "$cmd"
|
complete -F "$func" $opts "$cmd"
|
||||||
fi
|
fi
|
||||||
@@ -509,7 +548,7 @@ done
|
|||||||
|
|
||||||
# Directory
|
# Directory
|
||||||
for cmd in $d_cmds; do
|
for cmd in $d_cmds; do
|
||||||
__fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o dirnames"
|
__fzf_defc "$cmd" _fzf_dir_completion "-o bashdefault -o nospace -o dirnames"
|
||||||
done
|
done
|
||||||
|
|
||||||
# ssh
|
# ssh
|
||||||
|
@@ -77,22 +77,19 @@ fi
|
|||||||
{
|
{
|
||||||
|
|
||||||
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
||||||
if ! declare -f _fzf_compgen_path > /dev/null; then
|
#
|
||||||
_fzf_compgen_path() {
|
# _fzf_compgen_path() {
|
||||||
echo "$1"
|
# echo "$1"
|
||||||
command find -L "$1" \
|
# command find -L "$1" \
|
||||||
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
|
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
|
||||||
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
# -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
||||||
}
|
# }
|
||||||
fi
|
#
|
||||||
|
# _fzf_compgen_dir() {
|
||||||
if ! declare -f _fzf_compgen_dir > /dev/null; then
|
# command find -L "$1" \
|
||||||
_fzf_compgen_dir() {
|
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
|
||||||
command find -L "$1" \
|
# -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
||||||
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
|
# }
|
||||||
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
|
||||||
}
|
|
||||||
fi
|
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
@@ -148,10 +145,19 @@ __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 --scheme=path --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=$(
|
||||||
item="${item%$suffix}$suffix"
|
unset FZF_DEFAULT_COMMAND
|
||||||
echo -n "${(q)item} "
|
export FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --scheme=path --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-}"
|
||||||
done)
|
if declare -f "$compgen" > /dev/null; then
|
||||||
|
eval "$compgen $(printf %q "$dir")" | __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover"
|
||||||
|
else
|
||||||
|
[[ $compgen =~ dir ]] && walker=dir,follow || walker=file,dir,follow,hidden
|
||||||
|
__fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" --walker "$walker" --walker-root="$dir" < /dev/tty
|
||||||
|
fi | while read item; do
|
||||||
|
item="${item%$suffix}$suffix"
|
||||||
|
echo -n "${(q)item} "
|
||||||
|
done
|
||||||
|
)
|
||||||
matches=${matches% }
|
matches=${matches% }
|
||||||
if [ -n "$matches" ]; then
|
if [ -n "$matches" ]; then
|
||||||
LBUFFER="$lbuf$matches$tail"
|
LBUFFER="$lbuf$matches$tail"
|
||||||
|
@@ -17,14 +17,9 @@
|
|||||||
# Key bindings
|
# Key bindings
|
||||||
# ------------
|
# ------------
|
||||||
__fzf_select__() {
|
__fzf_select__() {
|
||||||
local cmd opts
|
local opts
|
||||||
cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse --walker=file,dir,follow,hidden --scheme=path ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m"
|
||||||
-o -type f -print \
|
FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" |
|
||||||
-o -type d -print \
|
|
||||||
-o -type l -print 2> /dev/null | command cut -b3-"}"
|
|
||||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse --scheme=path ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m"
|
|
||||||
eval "$cmd" |
|
|
||||||
FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" |
|
|
||||||
while read -r item; do
|
while read -r item; do
|
||||||
printf '%q ' "$item" # escape special chars
|
printf '%q ' "$item" # escape special chars
|
||||||
done
|
done
|
||||||
@@ -42,11 +37,11 @@ fzf-file-widget() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
__fzf_cd__() {
|
__fzf_cd__() {
|
||||||
local cmd opts dir
|
local 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 \
|
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse --walker=dir,follow,hidden --scheme=path ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-} +m"
|
||||||
-o -type d -print 2> /dev/null | command cut -b3-"}"
|
dir=$(
|
||||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse --scheme=path ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-} +m"
|
FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)
|
||||||
dir=$(set +o pipefail; eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)) && printf 'builtin cd -- %q' "$dir"
|
) && printf 'builtin cd -- %q' "$(builtin unset CDPATH && builtin cd -- "$dir" && builtin pwd)"
|
||||||
}
|
}
|
||||||
|
|
||||||
if command -v perl > /dev/null; then
|
if command -v perl > /dev/null; then
|
||||||
@@ -107,9 +102,11 @@ bind -m emacs-standard '"\C-z": vi-editing-mode'
|
|||||||
|
|
||||||
if (( BASH_VERSINFO[0] < 4 )); then
|
if (( BASH_VERSINFO[0] < 4 )); then
|
||||||
# CTRL-T - Paste the selected file path into the command line
|
# CTRL-T - Paste the selected file path into the command line
|
||||||
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
|
if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
|
||||||
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
|
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
|
||||||
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
|
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
|
||||||
|
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
|
||||||
|
fi
|
||||||
|
|
||||||
# CTRL-R - Paste the selected command from history into the command line
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u`__fzf_history__`\e\C-e\er"'
|
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u`__fzf_history__`\e\C-e\er"'
|
||||||
@@ -117,9 +114,11 @@ if (( BASH_VERSINFO[0] < 4 )); then
|
|||||||
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
|
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
|
||||||
else
|
else
|
||||||
# CTRL-T - Paste the selected file path into the command line
|
# CTRL-T - Paste the selected file path into the command line
|
||||||
bind -m emacs-standard -x '"\C-t": fzf-file-widget'
|
if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
|
||||||
bind -m vi-command -x '"\C-t": fzf-file-widget'
|
bind -m emacs-standard -x '"\C-t": fzf-file-widget'
|
||||||
bind -m vi-insert -x '"\C-t": fzf-file-widget'
|
bind -m vi-command -x '"\C-t": fzf-file-widget'
|
||||||
|
bind -m vi-insert -x '"\C-t": fzf-file-widget'
|
||||||
|
fi
|
||||||
|
|
||||||
# CTRL-R - Paste the selected command from history into the command line
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
bind -m emacs-standard -x '"\C-r": __fzf_history__'
|
bind -m emacs-standard -x '"\C-r": __fzf_history__'
|
||||||
@@ -128,6 +127,8 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ALT-C - cd into the selected directory
|
# ALT-C - cd into the selected directory
|
||||||
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
|
if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then
|
||||||
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
|
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
|
||||||
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
|
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
|
||||||
|
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
|
||||||
|
fi
|
||||||
|
@@ -21,22 +21,15 @@ function fzf_key_bindings
|
|||||||
# Store current token in $dir as root for the 'find' command
|
# Store current token in $dir as root for the 'find' command
|
||||||
function fzf-file-widget -d "List files and folders"
|
function fzf-file-widget -d "List files and folders"
|
||||||
set -l commandline (__fzf_parse_commandline)
|
set -l commandline (__fzf_parse_commandline)
|
||||||
set -l dir $commandline[1]
|
set -lx dir $commandline[1]
|
||||||
set -l fzf_query $commandline[2]
|
set -l fzf_query $commandline[2]
|
||||||
set -l prefix $commandline[3]
|
set -l prefix $commandline[3]
|
||||||
|
|
||||||
# "-path \$dir'*/.*'" matches hidden files/folders inside $dir but not
|
|
||||||
# $dir itself, even if hidden.
|
|
||||||
test -n "$FZF_CTRL_T_COMMAND"; or set -l FZF_CTRL_T_COMMAND "
|
|
||||||
command find -L \$dir -mindepth 1 \\( -path \$dir'*/.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
|
|
||||||
-o -type f -print \
|
|
||||||
-o -type d -print \
|
|
||||||
-o -type l -print 2> /dev/null | sed 's@^\./@@'"
|
|
||||||
|
|
||||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||||
begin
|
begin
|
||||||
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --scheme=path --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
|
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --walker=file,dir,follow,hidden --walker-root='$dir' --scheme=path --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
|
||||||
eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)' -m --query "'$fzf_query'"' | while read -l r; set result $result $r; end
|
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
|
||||||
|
eval (__fzfcmd)' -m --query "'$fzf_query'"' | while read -l r; set result $result $r; end
|
||||||
end
|
end
|
||||||
if [ -z "$result" ]
|
if [ -z "$result" ]
|
||||||
commandline -f repaint
|
commandline -f repaint
|
||||||
@@ -77,17 +70,15 @@ function fzf_key_bindings
|
|||||||
|
|
||||||
function fzf-cd-widget -d "Change directory"
|
function fzf-cd-widget -d "Change directory"
|
||||||
set -l commandline (__fzf_parse_commandline)
|
set -l commandline (__fzf_parse_commandline)
|
||||||
set -l dir $commandline[1]
|
set -lx dir $commandline[1]
|
||||||
set -l fzf_query $commandline[2]
|
set -l fzf_query $commandline[2]
|
||||||
set -l prefix $commandline[3]
|
set -l prefix $commandline[3]
|
||||||
|
|
||||||
test -n "$FZF_ALT_C_COMMAND"; or set -l FZF_ALT_C_COMMAND "
|
|
||||||
command find -L \$dir -mindepth 1 \\( -path \$dir'*/.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
|
|
||||||
-o -type d -print 2> /dev/null | sed 's@^\./@@'"
|
|
||||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||||
begin
|
begin
|
||||||
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --scheme=path --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS"
|
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --walker=dir,follow,hidden --walker-root='$dir' --scheme=path --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS"
|
||||||
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)' +m --query "'$fzf_query'"' | read -l result
|
set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND"
|
||||||
|
eval (__fzfcmd)' +m --query "'$fzf_query'"' | read -l result
|
||||||
|
|
||||||
if [ -n "$result" ]
|
if [ -n "$result" ]
|
||||||
cd -- $result
|
cd -- $result
|
||||||
@@ -113,14 +104,22 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
bind \ct fzf-file-widget
|
|
||||||
bind \cr fzf-history-widget
|
bind \cr fzf-history-widget
|
||||||
bind \ec fzf-cd-widget
|
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
|
||||||
|
bind \ct fzf-file-widget
|
||||||
|
end
|
||||||
|
if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND"
|
||||||
|
bind \ec fzf-cd-widget
|
||||||
|
end
|
||||||
|
|
||||||
if bind -M insert > /dev/null 2>&1
|
if bind -M insert > /dev/null 2>&1
|
||||||
bind -M insert \ct fzf-file-widget
|
|
||||||
bind -M insert \cr fzf-history-widget
|
bind -M insert \cr fzf-history-widget
|
||||||
bind -M insert \ec fzf-cd-widget
|
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
|
||||||
|
bind -M insert \ct fzf-file-widget
|
||||||
|
end
|
||||||
|
if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND"
|
||||||
|
bind -M insert \ec fzf-cd-widget
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
||||||
|
@@ -41,13 +41,9 @@ fi
|
|||||||
|
|
||||||
# CTRL-T - Paste the selected file path(s) into the command line
|
# CTRL-T - Paste the selected file path(s) into the command line
|
||||||
__fsel() {
|
__fsel() {
|
||||||
local cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
|
||||||
-o -type f -print \
|
|
||||||
-o -type d -print \
|
|
||||||
-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 --scheme=path --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-}" $(__fzfcmd) -m "$@" | while read item; do
|
FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --walker=file,dir,follow,hidden --scheme=path --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-}" $(__fzfcmd) -m "$@" < /dev/tty | while read item; do
|
||||||
echo -n "${(q)item} "
|
echo -n "${(q)item} "
|
||||||
done
|
done
|
||||||
local ret=$?
|
local ret=$?
|
||||||
@@ -66,33 +62,35 @@ fzf-file-widget() {
|
|||||||
zle reset-prompt
|
zle reset-prompt
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
zle -N fzf-file-widget
|
if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
|
||||||
bindkey -M emacs '^T' fzf-file-widget
|
zle -N fzf-file-widget
|
||||||
bindkey -M vicmd '^T' fzf-file-widget
|
bindkey -M emacs '^T' fzf-file-widget
|
||||||
bindkey -M viins '^T' fzf-file-widget
|
bindkey -M vicmd '^T' fzf-file-widget
|
||||||
|
bindkey -M viins '^T' fzf-file-widget
|
||||||
|
fi
|
||||||
|
|
||||||
# ALT-C - cd into the selected directory
|
# ALT-C - cd into the selected directory
|
||||||
fzf-cd-widget() {
|
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 \
|
|
||||||
-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 --scheme=path --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-}" $(__fzfcmd) +m)"
|
local dir="$(FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --walker=dir,follow,hidden --scheme=path --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-}" $(__fzfcmd) +m < /dev/tty)"
|
||||||
if [[ -z "$dir" ]]; then
|
if [[ -z "$dir" ]]; then
|
||||||
zle redisplay
|
zle redisplay
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
zle push-line # Clear buffer. Auto-restored on next prompt.
|
zle push-line # Clear buffer. Auto-restored on next prompt.
|
||||||
BUFFER="builtin cd -- ${(q)dir}"
|
BUFFER="builtin cd -- ${(q)dir:a}"
|
||||||
zle accept-line
|
zle accept-line
|
||||||
local ret=$?
|
local ret=$?
|
||||||
unset dir # ensure this doesn't end up appearing in prompt expansion
|
unset dir # ensure this doesn't end up appearing in prompt expansion
|
||||||
zle reset-prompt
|
zle reset-prompt
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
zle -N fzf-cd-widget
|
if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then
|
||||||
bindkey -M emacs '\ec' fzf-cd-widget
|
zle -N fzf-cd-widget
|
||||||
bindkey -M vicmd '\ec' fzf-cd-widget
|
bindkey -M emacs '\ec' fzf-cd-widget
|
||||||
bindkey -M viins '\ec' fzf-cd-widget
|
bindkey -M vicmd '\ec' fzf-cd-widget
|
||||||
|
bindkey -M viins '\ec' fzf-cd-widget
|
||||||
|
fi
|
||||||
|
|
||||||
# CTRL-R - Paste the selected command from history into the command line
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
fzf-history-widget() {
|
fzf-history-widget() {
|
||||||
|
@@ -54,72 +54,74 @@ func _() {
|
|||||||
_ = x[actToggleIn-43]
|
_ = x[actToggleIn-43]
|
||||||
_ = x[actToggleOut-44]
|
_ = x[actToggleOut-44]
|
||||||
_ = x[actToggleTrack-45]
|
_ = x[actToggleTrack-45]
|
||||||
_ = x[actToggleHeader-46]
|
_ = x[actToggleTrackCurrent-46]
|
||||||
_ = x[actTrack-47]
|
_ = x[actToggleHeader-47]
|
||||||
_ = x[actDown-48]
|
_ = x[actTrackCurrent-48]
|
||||||
_ = x[actUp-49]
|
_ = x[actUntrackCurrent-49]
|
||||||
_ = x[actPageUp-50]
|
_ = x[actDown-50]
|
||||||
_ = x[actPageDown-51]
|
_ = x[actUp-51]
|
||||||
_ = x[actPosition-52]
|
_ = x[actPageUp-52]
|
||||||
_ = x[actHalfPageUp-53]
|
_ = x[actPageDown-53]
|
||||||
_ = x[actHalfPageDown-54]
|
_ = x[actPosition-54]
|
||||||
_ = x[actOffsetUp-55]
|
_ = x[actHalfPageUp-55]
|
||||||
_ = x[actOffsetDown-56]
|
_ = x[actHalfPageDown-56]
|
||||||
_ = x[actJump-57]
|
_ = x[actOffsetUp-57]
|
||||||
_ = x[actJumpAccept-58]
|
_ = x[actOffsetDown-58]
|
||||||
_ = x[actPrintQuery-59]
|
_ = x[actJump-59]
|
||||||
_ = x[actRefreshPreview-60]
|
_ = x[actJumpAccept-60]
|
||||||
_ = x[actReplaceQuery-61]
|
_ = x[actPrintQuery-61]
|
||||||
_ = x[actToggleSort-62]
|
_ = x[actRefreshPreview-62]
|
||||||
_ = x[actShowPreview-63]
|
_ = x[actReplaceQuery-63]
|
||||||
_ = x[actHidePreview-64]
|
_ = x[actToggleSort-64]
|
||||||
_ = x[actTogglePreview-65]
|
_ = x[actShowPreview-65]
|
||||||
_ = x[actTogglePreviewWrap-66]
|
_ = x[actHidePreview-66]
|
||||||
_ = x[actTransform-67]
|
_ = x[actTogglePreview-67]
|
||||||
_ = x[actTransformBorderLabel-68]
|
_ = x[actTogglePreviewWrap-68]
|
||||||
_ = x[actTransformHeader-69]
|
_ = x[actTransform-69]
|
||||||
_ = x[actTransformPreviewLabel-70]
|
_ = x[actTransformBorderLabel-70]
|
||||||
_ = x[actTransformPrompt-71]
|
_ = x[actTransformHeader-71]
|
||||||
_ = x[actTransformQuery-72]
|
_ = x[actTransformPreviewLabel-72]
|
||||||
_ = x[actPreview-73]
|
_ = x[actTransformPrompt-73]
|
||||||
_ = x[actChangePreview-74]
|
_ = x[actTransformQuery-74]
|
||||||
_ = x[actChangePreviewWindow-75]
|
_ = x[actPreview-75]
|
||||||
_ = x[actPreviewTop-76]
|
_ = x[actChangePreview-76]
|
||||||
_ = x[actPreviewBottom-77]
|
_ = x[actChangePreviewWindow-77]
|
||||||
_ = x[actPreviewUp-78]
|
_ = x[actPreviewTop-78]
|
||||||
_ = x[actPreviewDown-79]
|
_ = x[actPreviewBottom-79]
|
||||||
_ = x[actPreviewPageUp-80]
|
_ = x[actPreviewUp-80]
|
||||||
_ = x[actPreviewPageDown-81]
|
_ = x[actPreviewDown-81]
|
||||||
_ = x[actPreviewHalfPageUp-82]
|
_ = x[actPreviewPageUp-82]
|
||||||
_ = x[actPreviewHalfPageDown-83]
|
_ = x[actPreviewPageDown-83]
|
||||||
_ = x[actPrevHistory-84]
|
_ = x[actPreviewHalfPageUp-84]
|
||||||
_ = x[actPrevSelected-85]
|
_ = x[actPreviewHalfPageDown-85]
|
||||||
_ = x[actPut-86]
|
_ = x[actPrevHistory-86]
|
||||||
_ = x[actNextHistory-87]
|
_ = x[actPrevSelected-87]
|
||||||
_ = x[actNextSelected-88]
|
_ = x[actPut-88]
|
||||||
_ = x[actExecute-89]
|
_ = x[actNextHistory-89]
|
||||||
_ = x[actExecuteSilent-90]
|
_ = x[actNextSelected-90]
|
||||||
_ = x[actExecuteMulti-91]
|
_ = x[actExecute-91]
|
||||||
_ = x[actSigStop-92]
|
_ = x[actExecuteSilent-92]
|
||||||
_ = x[actFirst-93]
|
_ = x[actExecuteMulti-93]
|
||||||
_ = x[actLast-94]
|
_ = x[actSigStop-94]
|
||||||
_ = x[actReload-95]
|
_ = x[actFirst-95]
|
||||||
_ = x[actReloadSync-96]
|
_ = x[actLast-96]
|
||||||
_ = x[actDisableSearch-97]
|
_ = x[actReload-97]
|
||||||
_ = x[actEnableSearch-98]
|
_ = x[actReloadSync-98]
|
||||||
_ = x[actSelect-99]
|
_ = x[actDisableSearch-99]
|
||||||
_ = x[actDeselect-100]
|
_ = x[actEnableSearch-100]
|
||||||
_ = x[actUnbind-101]
|
_ = x[actSelect-101]
|
||||||
_ = x[actRebind-102]
|
_ = x[actDeselect-102]
|
||||||
_ = x[actBecome-103]
|
_ = x[actUnbind-103]
|
||||||
_ = x[actResponse-104]
|
_ = x[actRebind-104]
|
||||||
_ = x[actShowHeader-105]
|
_ = x[actBecome-105]
|
||||||
_ = x[actHideHeader-106]
|
_ = x[actResponse-106]
|
||||||
|
_ = x[actShowHeader-107]
|
||||||
|
_ = x[actHideHeader-108]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleHeaderactTrackactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader"
|
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader"
|
||||||
|
|
||||||
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 263, 278, 292, 306, 319, 336, 344, 357, 373, 385, 399, 413, 424, 435, 453, 470, 477, 496, 508, 522, 531, 546, 558, 571, 582, 593, 605, 619, 634, 642, 649, 654, 663, 674, 685, 698, 713, 724, 737, 744, 757, 770, 787, 802, 815, 829, 843, 859, 879, 891, 914, 932, 956, 974, 991, 1001, 1017, 1039, 1052, 1068, 1080, 1094, 1110, 1128, 1148, 1170, 1184, 1199, 1205, 1219, 1234, 1244, 1260, 1275, 1285, 1293, 1300, 1309, 1322, 1338, 1353, 1362, 1373, 1382, 1391, 1400, 1411, 1424, 1437}
|
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 263, 278, 292, 306, 319, 336, 344, 357, 373, 385, 399, 413, 424, 435, 453, 470, 477, 496, 508, 522, 531, 546, 558, 571, 582, 593, 605, 619, 640, 655, 670, 687, 694, 699, 708, 719, 730, 743, 758, 769, 782, 789, 802, 815, 832, 847, 860, 874, 888, 904, 924, 936, 959, 977, 1001, 1019, 1036, 1046, 1062, 1084, 1097, 1113, 1125, 1139, 1155, 1173, 1193, 1215, 1229, 1244, 1250, 1264, 1279, 1289, 1305, 1320, 1330, 1338, 1345, 1354, 1367, 1383, 1398, 1407, 1418, 1427, 1436, 1445, 1456, 1469, 1482}
|
||||||
|
|
||||||
func (i actionType) String() string {
|
func (i actionType) String() string {
|
||||||
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
||||||
|
@@ -255,24 +255,29 @@ func charClassOf(char rune) charClass {
|
|||||||
|
|
||||||
func bonusFor(prevClass charClass, class charClass) int16 {
|
func bonusFor(prevClass charClass, class charClass) int16 {
|
||||||
if class > charNonWord {
|
if class > charNonWord {
|
||||||
if prevClass == charWhite {
|
switch prevClass {
|
||||||
|
case charWhite:
|
||||||
// Word boundary after whitespace
|
// Word boundary after whitespace
|
||||||
return bonusBoundaryWhite
|
return bonusBoundaryWhite
|
||||||
} else if prevClass == charDelimiter {
|
case charDelimiter:
|
||||||
// Word boundary after a delimiter character
|
// Word boundary after a delimiter character
|
||||||
return bonusBoundaryDelimiter
|
return bonusBoundaryDelimiter
|
||||||
} else if prevClass == charNonWord {
|
case charNonWord:
|
||||||
// Word boundary
|
// Word boundary
|
||||||
return bonusBoundary
|
return bonusBoundary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if prevClass == charLower && class == charUpper ||
|
if prevClass == charLower && class == charUpper ||
|
||||||
prevClass != charNumber && class == charNumber {
|
prevClass != charNumber && class == charNumber {
|
||||||
// camelCase letter123
|
// camelCase letter123
|
||||||
return bonusCamel123
|
return bonusCamel123
|
||||||
} else if class == charNonWord {
|
}
|
||||||
|
|
||||||
|
switch class {
|
||||||
|
case charNonWord, charDelimiter:
|
||||||
return bonusNonWord
|
return bonusNonWord
|
||||||
} else if class == charWhite {
|
case charWhite:
|
||||||
return bonusBoundaryWhite
|
return bonusBoundaryWhite
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
@@ -312,7 +312,7 @@ func parseAnsiCode(s string, delimiter byte) (int, byte, string) {
|
|||||||
// Inlined version of strconv.Atoi() that only handles positive
|
// Inlined version of strconv.Atoi() that only handles positive
|
||||||
// integers and does not allocate on error.
|
// integers and does not allocate on error.
|
||||||
code := 0
|
code := 0
|
||||||
for _, ch := range []byte(s) {
|
for _, ch := range sbytes(s) {
|
||||||
ch -= '0'
|
ch -= '0'
|
||||||
if ch > 9 {
|
if ch > 9 {
|
||||||
return -1, delimiter, remaining
|
return -1, delimiter, remaining
|
||||||
|
@@ -2,7 +2,6 @@ package fzf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
@@ -15,6 +14,7 @@ const (
|
|||||||
|
|
||||||
// Reader
|
// Reader
|
||||||
readerBufferSize = 64 * 1024
|
readerBufferSize = 64 * 1024
|
||||||
|
readerSlabSize = 128 * 1024
|
||||||
readerPollIntervalMin = 10 * time.Millisecond
|
readerPollIntervalMin = 10 * time.Millisecond
|
||||||
readerPollIntervalStep = 5 * time.Millisecond
|
readerPollIntervalStep = 5 * time.Millisecond
|
||||||
readerPollIntervalMax = 50 * time.Millisecond
|
readerPollIntervalMax = 50 * time.Millisecond
|
||||||
@@ -54,16 +54,6 @@ const (
|
|||||||
defaultJumpLabels string = "asdfghjklqwertyuiopzxcvbnm1234567890ASDFGHJKLQWERTYUIOPZXCVBNM`~;:,<.>/?'\"!@#$%^&*()[{]}-_=+"
|
defaultJumpLabels string = "asdfghjklqwertyuiopzxcvbnm1234567890ASDFGHJKLQWERTYUIOPZXCVBNM`~;:,<.>/?'\"!@#$%^&*()[{]}-_=+"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultCommand string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if !util.IsWindows() {
|
|
||||||
defaultCommand = `set -o pipefail; command find -L . -mindepth 1 \( -path '*/.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \) -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-`
|
|
||||||
} else if os.Getenv("TERM") == "cygwin" {
|
|
||||||
defaultCommand = `sh -c "command find -L . -mindepth 1 -path '*/.*' -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fzf events
|
// fzf events
|
||||||
const (
|
const (
|
||||||
EvtReadNew util.EventType = iota
|
EvtReadNew util.EventType = iota
|
||||||
|
55
src/core.go
55
src/core.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
)
|
)
|
||||||
@@ -18,6 +19,14 @@ Matcher -> EvtSearchFin -> Terminal (update list)
|
|||||||
Matcher -> EvtHeader -> Terminal (update header)
|
Matcher -> EvtHeader -> Terminal (update header)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
func ustring(data []byte) string {
|
||||||
|
return unsafe.String(unsafe.SliceData(data), len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sbytes(data string) []byte {
|
||||||
|
return unsafe.Slice(unsafe.StringData(data), len(data))
|
||||||
|
}
|
||||||
|
|
||||||
// Run starts fzf
|
// Run starts fzf
|
||||||
func Run(opts *Options, version string, revision string) {
|
func Run(opts *Options, version string, revision string) {
|
||||||
sort := opts.Sort > 0
|
sort := opts.Sort > 0
|
||||||
@@ -45,16 +54,16 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
if opts.Theme.Colored {
|
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(ustring(data), lineAnsiState, nil)
|
||||||
lineAnsiState = newState
|
lineAnsiState = newState
|
||||||
return util.ToChars([]byte(trimmed)), offsets
|
return util.ToChars(sbytes(trimmed)), offsets
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// When color is disabled but ansi option is given,
|
// When color is disabled but ansi option is given,
|
||||||
// we simply strip out ANSI codes from the input
|
// we simply strip out ANSI codes from the input
|
||||||
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
||||||
trimmed, _, _ := extractColor(string(data), nil, nil)
|
trimmed, _, _ := extractColor(ustring(data), nil, nil)
|
||||||
return util.ToChars([]byte(trimmed)), nil
|
return util.ToChars(sbytes(trimmed)), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +75,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
if len(opts.WithNth) == 0 {
|
if len(opts.WithNth) == 0 {
|
||||||
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
||||||
if len(header) < opts.HeaderLines {
|
if len(header) < opts.HeaderLines {
|
||||||
header = append(header, string(data))
|
header = append(header, ustring(data))
|
||||||
eventBox.Set(EvtHeader, header)
|
eventBox.Set(EvtHeader, header)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -77,7 +86,7 @@ func Run(opts *Options, version string, 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(ustring(data), opts.Delimiter)
|
||||||
if opts.Ansi && opts.Theme.Colored && len(tokens) > 1 {
|
if opts.Ansi && opts.Theme.Colored && len(tokens) > 1 {
|
||||||
var ansiState *ansiState
|
var ansiState *ansiState
|
||||||
if prevLineAnsiState != nil {
|
if prevLineAnsiState != nil {
|
||||||
@@ -101,7 +110,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
eventBox.Set(EvtHeader, header)
|
eventBox.Set(EvtHeader, header)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
item.text, item.colors = ansiProcessor([]byte(transformed))
|
item.text, item.colors = ansiProcessor(sbytes(transformed))
|
||||||
item.text.TrimTrailingWhitespaces()
|
item.text.TrimTrailingWhitespaces()
|
||||||
item.text.Index = itemIndex
|
item.text.Index = itemIndex
|
||||||
item.origText = &data
|
item.origText = &data
|
||||||
@@ -117,7 +126,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
reader = NewReader(func(data []byte) bool {
|
reader = NewReader(func(data []byte) bool {
|
||||||
return chunkList.Push(data)
|
return chunkList.Push(data)
|
||||||
}, eventBox, opts.ReadZero, opts.Filter == nil)
|
}, eventBox, opts.ReadZero, opts.Filter == nil)
|
||||||
go reader.ReadSource()
|
go reader.ReadSource(opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matcher
|
// Matcher
|
||||||
@@ -165,7 +174,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}, eventBox, opts.ReadZero, false)
|
}, eventBox, opts.ReadZero, false)
|
||||||
reader.ReadSource()
|
reader.ReadSource(opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip)
|
||||||
} else {
|
} else {
|
||||||
eventBox.Unwatch(EvtReadNew)
|
eventBox.Unwatch(EvtReadNew)
|
||||||
eventBox.WaitFor(EvtReadFin)
|
eventBox.WaitFor(EvtReadFin)
|
||||||
@@ -213,6 +222,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
reading := true
|
reading := true
|
||||||
ticks := 0
|
ticks := 0
|
||||||
var nextCommand *string
|
var nextCommand *string
|
||||||
|
var nextEnviron []string
|
||||||
eventBox.Watch(EvtReadNew)
|
eventBox.Watch(EvtReadNew)
|
||||||
total := 0
|
total := 0
|
||||||
query := []rune{}
|
query := []rune{}
|
||||||
@@ -232,23 +242,20 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
useSnapshot := false
|
useSnapshot := false
|
||||||
var snapshot []*Chunk
|
var snapshot []*Chunk
|
||||||
var count int
|
var count int
|
||||||
restart := func(command string) {
|
restart := func(command string, environ []string) {
|
||||||
reading = true
|
reading = true
|
||||||
chunkList.Clear()
|
chunkList.Clear()
|
||||||
itemIndex = 0
|
itemIndex = 0
|
||||||
inputRevision++
|
inputRevision++
|
||||||
header = make([]string, 0, opts.HeaderLines)
|
header = make([]string, 0, opts.HeaderLines)
|
||||||
go reader.restart(command)
|
go reader.restart(command, environ)
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
delay := true
|
delay := true
|
||||||
ticks++
|
ticks++
|
||||||
input := func() []rune {
|
input := func() []rune {
|
||||||
reloaded := snapshotRevision != inputRevision
|
|
||||||
paused, input := terminal.Input()
|
paused, input := terminal.Input()
|
||||||
if reloaded && paused {
|
if !paused {
|
||||||
query = []rune{}
|
|
||||||
} else if !paused {
|
|
||||||
query = input
|
query = input
|
||||||
}
|
}
|
||||||
return query
|
return query
|
||||||
@@ -266,8 +273,9 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
os.Exit(value.(int))
|
os.Exit(value.(int))
|
||||||
case EvtReadNew, EvtReadFin:
|
case EvtReadNew, EvtReadFin:
|
||||||
if evt == EvtReadFin && nextCommand != nil {
|
if evt == EvtReadFin && nextCommand != nil {
|
||||||
restart(*nextCommand)
|
restart(*nextCommand, nextEnviron)
|
||||||
nextCommand = nil
|
nextCommand = nil
|
||||||
|
nextEnviron = nil
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
reading = reading && evt == EvtReadNew
|
reading = reading && evt == EvtReadNew
|
||||||
@@ -276,6 +284,9 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
useSnapshot = false
|
useSnapshot = false
|
||||||
}
|
}
|
||||||
if !useSnapshot {
|
if !useSnapshot {
|
||||||
|
if snapshotRevision != inputRevision {
|
||||||
|
query = []rune{}
|
||||||
|
}
|
||||||
snapshot, count = chunkList.Snapshot()
|
snapshot, count = chunkList.Snapshot()
|
||||||
snapshotRevision = inputRevision
|
snapshotRevision = inputRevision
|
||||||
}
|
}
|
||||||
@@ -292,11 +303,13 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
|
|
||||||
case EvtSearchNew:
|
case EvtSearchNew:
|
||||||
var command *string
|
var command *string
|
||||||
|
var environ []string
|
||||||
var changed bool
|
var changed bool
|
||||||
switch val := value.(type) {
|
switch val := value.(type) {
|
||||||
case searchRequest:
|
case searchRequest:
|
||||||
sort = val.sort
|
sort = val.sort
|
||||||
command = val.command
|
command = val.command
|
||||||
|
environ = val.environ
|
||||||
changed = val.changed
|
changed = val.changed
|
||||||
if command != nil {
|
if command != nil {
|
||||||
useSnapshot = val.sync
|
useSnapshot = val.sync
|
||||||
@@ -306,18 +319,22 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
if reading {
|
if reading {
|
||||||
reader.terminate()
|
reader.terminate()
|
||||||
nextCommand = command
|
nextCommand = command
|
||||||
|
nextEnviron = environ
|
||||||
} else {
|
} else {
|
||||||
restart(*command)
|
restart(*command, environ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !changed {
|
if !changed {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !useSnapshot {
|
if !useSnapshot {
|
||||||
newSnapshot, _ := chunkList.Snapshot()
|
newSnapshot, newCount := chunkList.Snapshot()
|
||||||
// We want to avoid showing empty list when reload is triggered
|
// We want to avoid showing empty list when reload is triggered
|
||||||
// and the query string is changed at the same time i.e. command != nil && changed
|
// and the query string is changed at the same time i.e. command != nil && changed
|
||||||
if command == nil || len(newSnapshot) > 0 {
|
if command == nil || newCount > 0 {
|
||||||
|
if snapshotRevision != inputRevision {
|
||||||
|
query = []rune{}
|
||||||
|
}
|
||||||
snapshot = newSnapshot
|
snapshot = newSnapshot
|
||||||
snapshotRevision = inputRevision
|
snapshotRevision = inputRevision
|
||||||
}
|
}
|
||||||
|
177
src/options.go
177
src/options.go
@@ -11,9 +11,9 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/algo"
|
"github.com/junegunn/fzf/src/algo"
|
||||||
"github.com/junegunn/fzf/src/tui"
|
"github.com/junegunn/fzf/src/tui"
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
"github.com/junegunn/uniseg"
|
|
||||||
|
|
||||||
"github.com/mattn/go-shellwords"
|
"github.com/mattn/go-shellwords"
|
||||||
|
"github.com/rivo/uniseg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `usage: fzf [options]
|
const usage = `usage: fzf [options]
|
||||||
@@ -75,7 +75,7 @@ const usage = `usage: fzf [options]
|
|||||||
--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)
|
||||||
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||||
--info=STYLE Finder info style
|
--info=STYLE Finder info style
|
||||||
[default|right|hidden|inline[:SEPARATOR]|inline-right]
|
[default|right|hidden|inline[-right][:PREFIX]]
|
||||||
--separator=STR String to form horizontal separator on info line
|
--separator=STR String to form horizontal separator on info line
|
||||||
--no-separator Hide info line separator
|
--no-separator Hide info line separator
|
||||||
--scrollbar[=C1[C2]] Scrollbar character(s) (each for main and preview window)
|
--scrollbar[=C1[C2]] Scrollbar character(s) (each for main and preview window)
|
||||||
@@ -124,15 +124,26 @@ const usage = `usage: fzf [options]
|
|||||||
(To allow remote process execution, use --listen-unsafe)
|
(To allow remote process execution, use --listen-unsafe)
|
||||||
--version Display version information and exit
|
--version Display version information and exit
|
||||||
|
|
||||||
|
Directory traversal (Only used when $FZF_DEFAULT_COMMAND is not set)
|
||||||
|
--walker=OPTS [file][,dir][,follow][,hidden] (default: file,follow,hidden)
|
||||||
|
--walker-root=DIR Root directory from which to start walker (default: .)
|
||||||
|
--walker-skip=DIRS Comma-separated list of directory names to skip
|
||||||
|
(default: .git,node_modules)
|
||||||
|
|
||||||
|
Shell integration
|
||||||
|
--bash Print script to set up Bash shell integration
|
||||||
|
--zsh Print script to set up Zsh shell integration
|
||||||
|
--fish Print script to set up Fish shell integration
|
||||||
|
|
||||||
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 --info=inline')
|
||||||
(e.g. '--layout=reverse --inline-info')
|
FZF_DEFAULT_OPTS_FILE Location of the file to read default options from
|
||||||
FZF_API_KEY X-API-Key header for HTTP server (--listen)
|
FZF_API_KEY X-API-Key header for HTTP server (--listen)
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const defaultInfoSep = " < "
|
const defaultInfoPrefix = " < "
|
||||||
|
|
||||||
// Case denotes case-sensitivity of search
|
// Case denotes case-sensitivity of search
|
||||||
type Case int
|
type Case int
|
||||||
@@ -206,10 +217,6 @@ const (
|
|||||||
infoHidden
|
infoHidden
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s infoStyle) noExtraLine() bool {
|
|
||||||
return s == infoInline || s == infoInlineRight || s == infoHidden
|
|
||||||
}
|
|
||||||
|
|
||||||
type labelOpts struct {
|
type labelOpts struct {
|
||||||
label string
|
label string
|
||||||
column int
|
column int
|
||||||
@@ -274,8 +281,18 @@ func firstLine(s string) string {
|
|||||||
return strings.SplitN(s, "\n", 2)[0]
|
return strings.SplitN(s, "\n", 2)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type walkerOpts struct {
|
||||||
|
file bool
|
||||||
|
dir bool
|
||||||
|
hidden bool
|
||||||
|
follow bool
|
||||||
|
}
|
||||||
|
|
||||||
// Options stores the values of command-line options
|
// Options stores the values of command-line options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
Bash bool
|
||||||
|
Zsh bool
|
||||||
|
Fish bool
|
||||||
Fuzzy bool
|
Fuzzy bool
|
||||||
FuzzyAlgo algo.Algo
|
FuzzyAlgo algo.Algo
|
||||||
Scheme string
|
Scheme string
|
||||||
@@ -306,7 +323,7 @@ type Options struct {
|
|||||||
ScrollOff int
|
ScrollOff int
|
||||||
FileWord bool
|
FileWord bool
|
||||||
InfoStyle infoStyle
|
InfoStyle infoStyle
|
||||||
InfoSep string
|
InfoPrefix string
|
||||||
Separator *string
|
Separator *string
|
||||||
JumpLabels string
|
JumpLabels string
|
||||||
Prompt string
|
Prompt string
|
||||||
@@ -342,15 +359,31 @@ type Options struct {
|
|||||||
ListenAddr *listenAddress
|
ListenAddr *listenAddress
|
||||||
Unsafe bool
|
Unsafe bool
|
||||||
ClearOnExit bool
|
ClearOnExit bool
|
||||||
|
WalkerOpts walkerOpts
|
||||||
|
WalkerRoot string
|
||||||
|
WalkerSkip []string
|
||||||
Version bool
|
Version bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterNonEmpty(input []string) []string {
|
||||||
|
output := make([]string, 0, len(input))
|
||||||
|
for _, str := range input {
|
||||||
|
if len(str) > 0 {
|
||||||
|
output = append(output, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
func defaultPreviewOpts(command string) previewOpts {
|
func defaultPreviewOpts(command string) previewOpts {
|
||||||
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.DefaultBorderShape, 0, 0, nil}
|
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.DefaultBorderShape, 0, 0, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultOptions() *Options {
|
func defaultOptions() *Options {
|
||||||
return &Options{
|
return &Options{
|
||||||
|
Bash: false,
|
||||||
|
Zsh: false,
|
||||||
|
Fish: false,
|
||||||
Fuzzy: true,
|
Fuzzy: true,
|
||||||
FuzzyAlgo: algo.FuzzyMatchV2,
|
FuzzyAlgo: algo.FuzzyMatchV2,
|
||||||
Scheme: "default",
|
Scheme: "default",
|
||||||
@@ -413,6 +446,9 @@ func defaultOptions() *Options {
|
|||||||
PreviewLabel: labelOpts{},
|
PreviewLabel: labelOpts{},
|
||||||
Unsafe: false,
|
Unsafe: false,
|
||||||
ClearOnExit: true,
|
ClearOnExit: true,
|
||||||
|
WalkerOpts: walkerOpts{file: true, hidden: true, follow: true},
|
||||||
|
WalkerRoot: ".",
|
||||||
|
WalkerSkip: []string{".git", "node_modules"},
|
||||||
Version: false}
|
Version: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,8 +457,10 @@ func help(code int) {
|
|||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errorContext = ""
|
||||||
|
|
||||||
func errorExit(msg string) {
|
func errorExit(msg string) {
|
||||||
os.Stderr.WriteString(msg + "\n")
|
os.Stderr.WriteString(errorContext + msg + "\n")
|
||||||
os.Exit(exitError)
|
os.Exit(exitError)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -964,6 +1002,30 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
|||||||
return theme
|
return theme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseWalkerOpts(str string) walkerOpts {
|
||||||
|
opts := walkerOpts{}
|
||||||
|
for _, str := range strings.Split(strings.ToLower(str), ",") {
|
||||||
|
switch str {
|
||||||
|
case "file":
|
||||||
|
opts.file = true
|
||||||
|
case "dir":
|
||||||
|
opts.dir = true
|
||||||
|
case "hidden":
|
||||||
|
opts.hidden = true
|
||||||
|
case "follow":
|
||||||
|
opts.follow = true
|
||||||
|
case "":
|
||||||
|
// Ignored
|
||||||
|
default:
|
||||||
|
errorExit("invalid walker option: " + str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !opts.file && !opts.dir {
|
||||||
|
errorExit("at least one of 'file' or 'dir' should be specified")
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
executeRegexp *regexp.Regexp
|
executeRegexp *regexp.Regexp
|
||||||
splitRegexp *regexp.Regexp
|
splitRegexp *regexp.Regexp
|
||||||
@@ -1145,14 +1207,18 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
|||||||
appendAction(actToggleSearch)
|
appendAction(actToggleSearch)
|
||||||
case "toggle-track":
|
case "toggle-track":
|
||||||
appendAction(actToggleTrack)
|
appendAction(actToggleTrack)
|
||||||
|
case "toggle-track-current":
|
||||||
|
appendAction(actToggleTrackCurrent)
|
||||||
case "toggle-header":
|
case "toggle-header":
|
||||||
appendAction(actToggleHeader)
|
appendAction(actToggleHeader)
|
||||||
case "show-header":
|
case "show-header":
|
||||||
appendAction(actShowHeader)
|
appendAction(actShowHeader)
|
||||||
case "hide-header":
|
case "hide-header":
|
||||||
appendAction(actHideHeader)
|
appendAction(actHideHeader)
|
||||||
case "track":
|
case "track", "track-current":
|
||||||
appendAction(actTrack)
|
appendAction(actTrackCurrent)
|
||||||
|
case "untrack-current":
|
||||||
|
appendAction(actUntrackCurrent)
|
||||||
case "select":
|
case "select":
|
||||||
appendAction(actSelect)
|
appendAction(actSelect)
|
||||||
case "select-all":
|
case "select-all":
|
||||||
@@ -1436,17 +1502,24 @@ func parseInfoStyle(str string) (infoStyle, string) {
|
|||||||
case "right":
|
case "right":
|
||||||
return infoRight, ""
|
return infoRight, ""
|
||||||
case "inline":
|
case "inline":
|
||||||
return infoInline, defaultInfoSep
|
return infoInline, defaultInfoPrefix
|
||||||
case "inline-right":
|
case "inline-right":
|
||||||
return infoInlineRight, ""
|
return infoInlineRight, ""
|
||||||
case "hidden":
|
case "hidden":
|
||||||
return infoHidden, ""
|
return infoHidden, ""
|
||||||
default:
|
default:
|
||||||
prefix := "inline:"
|
type infoSpec struct {
|
||||||
if strings.HasPrefix(str, prefix) {
|
name string
|
||||||
return infoInline, strings.ReplaceAll(str[len(prefix):], "\n", " ")
|
style infoStyle
|
||||||
}
|
}
|
||||||
errorExit("invalid info style (expected: default|right|hidden|inline[:SEPARATOR]|inline-right)")
|
for _, spec := range []infoSpec{
|
||||||
|
{"inline", infoInline},
|
||||||
|
{"inline-right", infoInlineRight}} {
|
||||||
|
if strings.HasPrefix(str, spec.name+":") {
|
||||||
|
return spec.style, strings.ReplaceAll(str[len(spec.name)+1:], "\n", " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errorExit("invalid info style (expected: default|right|hidden|inline[-right][:PREFIX])")
|
||||||
}
|
}
|
||||||
return infoDefault, ""
|
return infoDefault, ""
|
||||||
}
|
}
|
||||||
@@ -1600,6 +1673,21 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
for i := 0; i < len(allArgs); i++ {
|
for i := 0; i < len(allArgs); i++ {
|
||||||
arg := allArgs[i]
|
arg := allArgs[i]
|
||||||
switch arg {
|
switch arg {
|
||||||
|
case "--bash":
|
||||||
|
opts.Bash = true
|
||||||
|
if opts.Zsh || opts.Fish {
|
||||||
|
errorExit("cannot specify --bash with --zsh or --fish")
|
||||||
|
}
|
||||||
|
case "--zsh":
|
||||||
|
opts.Zsh = true
|
||||||
|
if opts.Bash || opts.Fish {
|
||||||
|
errorExit("cannot specify --zsh with --bash or --fish")
|
||||||
|
}
|
||||||
|
case "--fish":
|
||||||
|
opts.Fish = true
|
||||||
|
if opts.Bash || opts.Zsh {
|
||||||
|
errorExit("cannot specify --fish with --bash or --zsh")
|
||||||
|
}
|
||||||
case "-h", "--help":
|
case "-h", "--help":
|
||||||
help(exitOk)
|
help(exitOk)
|
||||||
case "-x", "--extended":
|
case "-x", "--extended":
|
||||||
@@ -1722,13 +1810,13 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
case "--no-filepath-word":
|
case "--no-filepath-word":
|
||||||
opts.FileWord = false
|
opts.FileWord = false
|
||||||
case "--info":
|
case "--info":
|
||||||
opts.InfoStyle, opts.InfoSep = parseInfoStyle(
|
opts.InfoStyle, opts.InfoPrefix = parseInfoStyle(
|
||||||
nextString(allArgs, &i, "info style required"))
|
nextString(allArgs, &i, "info style required"))
|
||||||
case "--no-info":
|
case "--no-info":
|
||||||
opts.InfoStyle = infoHidden
|
opts.InfoStyle = infoHidden
|
||||||
case "--inline-info":
|
case "--inline-info":
|
||||||
opts.InfoStyle = infoInline
|
opts.InfoStyle = infoInline
|
||||||
opts.InfoSep = defaultInfoSep
|
opts.InfoPrefix = defaultInfoPrefix
|
||||||
case "--no-inline-info":
|
case "--no-inline-info":
|
||||||
opts.InfoStyle = infoDefault
|
opts.InfoStyle = infoDefault
|
||||||
case "--separator":
|
case "--separator":
|
||||||
@@ -1864,7 +1952,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
addr := defaultListenAddr
|
addr := defaultListenAddr
|
||||||
if given {
|
if given {
|
||||||
var err error
|
var err error
|
||||||
err, addr = parseListenAddress(str)
|
addr, err = parseListenAddress(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorExit(err.Error())
|
errorExit(err.Error())
|
||||||
}
|
}
|
||||||
@@ -1878,6 +1966,12 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.ClearOnExit = true
|
opts.ClearOnExit = true
|
||||||
case "--no-clear":
|
case "--no-clear":
|
||||||
opts.ClearOnExit = false
|
opts.ClearOnExit = false
|
||||||
|
case "--walker":
|
||||||
|
opts.WalkerOpts = parseWalkerOpts(nextString(allArgs, &i, "walker options required [file][,dir][,follow][,hidden]"))
|
||||||
|
case "--walker-root":
|
||||||
|
opts.WalkerRoot = nextString(allArgs, &i, "directory required")
|
||||||
|
case "--walker-skip":
|
||||||
|
opts.WalkerSkip = filterNonEmpty(strings.Split(nextString(allArgs, &i, "directory names to ignore required"), ","))
|
||||||
case "--version":
|
case "--version":
|
||||||
opts.Version = true
|
opts.Version = true
|
||||||
case "--":
|
case "--":
|
||||||
@@ -1924,7 +2018,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
} else if match, value := optString(arg, "--layout="); match {
|
} else if match, value := optString(arg, "--layout="); match {
|
||||||
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, opts.InfoSep = parseInfoStyle(value)
|
opts.InfoStyle, opts.InfoPrefix = parseInfoStyle(value)
|
||||||
} else if match, value := optString(arg, "--separator="); match {
|
} else if match, value := optString(arg, "--separator="); match {
|
||||||
opts.Separator = &value
|
opts.Separator = &value
|
||||||
} else if match, value := optString(arg, "--scrollbar="); match {
|
} else if match, value := optString(arg, "--scrollbar="); match {
|
||||||
@@ -1962,19 +2056,25 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
} else if match, value := optString(arg, "--tabstop="); match {
|
} else if match, value := optString(arg, "--tabstop="); match {
|
||||||
opts.Tabstop = atoi(value)
|
opts.Tabstop = atoi(value)
|
||||||
} else if match, value := optString(arg, "--listen="); match {
|
} else if match, value := optString(arg, "--listen="); match {
|
||||||
err, addr := parseListenAddress(value)
|
addr, err := parseListenAddress(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorExit(err.Error())
|
errorExit(err.Error())
|
||||||
}
|
}
|
||||||
opts.ListenAddr = &addr
|
opts.ListenAddr = &addr
|
||||||
opts.Unsafe = false
|
opts.Unsafe = false
|
||||||
} else if match, value := optString(arg, "--listen-unsafe="); match {
|
} else if match, value := optString(arg, "--listen-unsafe="); match {
|
||||||
err, addr := parseListenAddress(value)
|
addr, err := parseListenAddress(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorExit(err.Error())
|
errorExit(err.Error())
|
||||||
}
|
}
|
||||||
opts.ListenAddr = &addr
|
opts.ListenAddr = &addr
|
||||||
opts.Unsafe = true
|
opts.Unsafe = true
|
||||||
|
} else if match, value := optString(arg, "--walker="); match {
|
||||||
|
opts.WalkerOpts = parseWalkerOpts(value)
|
||||||
|
} else if match, value := optString(arg, "--walker-root="); match {
|
||||||
|
opts.WalkerRoot = value
|
||||||
|
} else if match, value := optString(arg, "--walker-skip="); match {
|
||||||
|
opts.WalkerSkip = filterNonEmpty(strings.Split(value, ","))
|
||||||
} else if match, value := optString(arg, "--hscroll-off="); match {
|
} else if match, value := optString(arg, "--hscroll-off="); match {
|
||||||
opts.HscrollOff = atoi(value)
|
opts.HscrollOff = atoi(value)
|
||||||
} else if match, value := optString(arg, "--scroll-off="); match {
|
} else if match, value := optString(arg, "--scroll-off="); match {
|
||||||
@@ -2167,13 +2267,36 @@ func ParseOptions() *Options {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options from Env var
|
// 1. Options from $FZF_DEFAULT_OPTS_FILE
|
||||||
words, _ := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS"))
|
if path := os.Getenv("FZF_DEFAULT_OPTS_FILE"); path != "" {
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
errorContext = "$FZF_DEFAULT_OPTS_FILE: "
|
||||||
|
errorExit(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
words, parseErr := shellwords.Parse(string(bytes))
|
||||||
|
if parseErr != nil {
|
||||||
|
errorContext = path + ": "
|
||||||
|
errorExit(parseErr.Error())
|
||||||
|
}
|
||||||
|
if len(words) > 0 {
|
||||||
|
parseOptions(opts, words)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Options from $FZF_DEFAULT_OPTS string
|
||||||
|
words, parseErr := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS"))
|
||||||
|
errorContext = "$FZF_DEFAULT_OPTS: "
|
||||||
|
if parseErr != nil {
|
||||||
|
errorExit(parseErr.Error())
|
||||||
|
}
|
||||||
if len(words) > 0 {
|
if len(words) > 0 {
|
||||||
parseOptions(opts, words)
|
parseOptions(opts, words)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options from command-line arguments
|
// 3. Options from command-line arguments
|
||||||
|
errorContext = ""
|
||||||
parseOptions(opts, os.Args[1:])
|
parseOptions(opts, os.Args[1:])
|
||||||
|
|
||||||
postProcessOptions(opts)
|
postProcessOptions(opts)
|
||||||
|
@@ -209,11 +209,10 @@ func parseTerms(fuzzy bool, caseMode Case, normalize bool, str string) []termSet
|
|||||||
// Flip exactness
|
// Flip exactness
|
||||||
if fuzzy && !inv {
|
if fuzzy && !inv {
|
||||||
typ = termExact
|
typ = termExact
|
||||||
text = text[1:]
|
|
||||||
} else {
|
} else {
|
||||||
typ = termFuzzy
|
typ = termFuzzy
|
||||||
text = text[1:]
|
|
||||||
}
|
}
|
||||||
|
text = text[1:]
|
||||||
} else if strings.HasPrefix(text, "^") {
|
} else if strings.HasPrefix(text, "^") {
|
||||||
if typ == termSuffix {
|
if typ == termSuffix {
|
||||||
typ = termEqual
|
typ = termEqual
|
||||||
|
160
src/reader.go
160
src/reader.go
@@ -1,19 +1,18 @@
|
|||||||
package fzf
|
package fzf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/charlievieth/fastwalk"
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
"github.com/saracen/walker"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reader reads from command or standard input
|
// Reader reads from command or standard input
|
||||||
@@ -77,48 +76,33 @@ func (r *Reader) fin(success bool) {
|
|||||||
|
|
||||||
func (r *Reader) terminate() {
|
func (r *Reader) terminate() {
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
defer func() { r.mutex.Unlock() }()
|
|
||||||
|
|
||||||
r.killed = true
|
r.killed = true
|
||||||
if r.exec != nil && r.exec.Process != nil {
|
if r.exec != nil && r.exec.Process != nil {
|
||||||
util.KillCommand(r.exec)
|
util.KillCommand(r.exec)
|
||||||
} else if defaultCommand != "" {
|
} else {
|
||||||
os.Stdin.Close()
|
os.Stdin.Close()
|
||||||
}
|
}
|
||||||
|
r.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) restart(command string) {
|
func (r *Reader) restart(command string, environ []string) {
|
||||||
r.event = int32(EvtReady)
|
r.event = int32(EvtReady)
|
||||||
r.startEventPoller()
|
r.startEventPoller()
|
||||||
success := r.readFromCommand(nil, command)
|
success := r.readFromCommand(command, environ)
|
||||||
r.fin(success)
|
r.fin(success)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadSource reads data from the default command or from standard input
|
// ReadSource reads data from the default command or from standard input
|
||||||
func (r *Reader) ReadSource() {
|
func (r *Reader) ReadSource(root string, opts walkerOpts, ignores []string) {
|
||||||
r.startEventPoller()
|
r.startEventPoller()
|
||||||
var success bool
|
var success bool
|
||||||
if util.IsTty() {
|
if util.IsTty() {
|
||||||
// The default command for *nix requires a shell that supports "pipefail"
|
|
||||||
// https://unix.stackexchange.com/a/654932/62171
|
|
||||||
shell := "bash"
|
|
||||||
currentShell := os.Getenv("SHELL")
|
|
||||||
currentShellName := path.Base(currentShell)
|
|
||||||
for _, shellName := range []string{"bash", "zsh", "ksh", "ash", "hush", "mksh", "yash"} {
|
|
||||||
if currentShellName == shellName {
|
|
||||||
shell = currentShell
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
|
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
|
||||||
if len(cmd) == 0 {
|
if len(cmd) == 0 {
|
||||||
if defaultCommand != "" {
|
success = r.readFiles(root, opts, ignores)
|
||||||
success = r.readFromCommand(&shell, defaultCommand)
|
|
||||||
} else {
|
|
||||||
success = r.readFiles()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
success = r.readFromCommand(nil, cmd)
|
// We can't export FZF_* environment variables to the default command
|
||||||
|
success = r.readFromCommand(cmd, nil)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
success = r.readFromStdin()
|
success = r.readFromStdin()
|
||||||
@@ -127,32 +111,84 @@ func (r *Reader) ReadSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) feed(src io.Reader) {
|
func (r *Reader) feed(src io.Reader) {
|
||||||
|
/*
|
||||||
|
readerSlabSize, ae := strconv.Atoi(os.Getenv("SLAB_KB"))
|
||||||
|
if ae != nil {
|
||||||
|
readerSlabSize = 128 * 1024
|
||||||
|
} else {
|
||||||
|
readerSlabSize *= 1024
|
||||||
|
}
|
||||||
|
readerBufferSize, be := strconv.Atoi(os.Getenv("BUF_KB"))
|
||||||
|
if be != nil {
|
||||||
|
readerBufferSize = 64 * 1024
|
||||||
|
} else {
|
||||||
|
readerBufferSize *= 1024
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
delim := byte('\n')
|
delim := byte('\n')
|
||||||
|
trimCR := util.IsWindows()
|
||||||
if r.delimNil {
|
if r.delimNil {
|
||||||
delim = '\000'
|
delim = '\000'
|
||||||
|
trimCR = false
|
||||||
}
|
}
|
||||||
reader := bufio.NewReaderSize(src, readerBufferSize)
|
|
||||||
|
slab := make([]byte, readerSlabSize)
|
||||||
|
leftover := []byte{}
|
||||||
|
var err error
|
||||||
for {
|
for {
|
||||||
// ReadBytes returns err != nil if and only if the returned data does not
|
n := 0
|
||||||
// end in delim.
|
scope := slab[:util.Min(len(slab), readerBufferSize)]
|
||||||
bytea, err := reader.ReadBytes(delim)
|
for i := 0; i < 100; i++ {
|
||||||
byteaLen := len(bytea)
|
n, err = src.Read(scope)
|
||||||
if byteaLen > 0 {
|
if n > 0 || err != nil {
|
||||||
if err == nil {
|
break
|
||||||
// get rid of carriage return if under Windows:
|
|
||||||
if util.IsWindows() && byteaLen >= 2 && bytea[byteaLen-2] == byte('\r') {
|
|
||||||
bytea = bytea[:byteaLen-2]
|
|
||||||
} else {
|
|
||||||
bytea = bytea[:byteaLen-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.pusher(bytea) {
|
|
||||||
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
|
// We're not making any progress after 100 tries. Stop.
|
||||||
|
if n == 0 && err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf := slab[:n]
|
||||||
|
slab = slab[n:]
|
||||||
|
|
||||||
|
for len(buf) > 0 {
|
||||||
|
if i := bytes.IndexByte(buf, delim); i >= 0 {
|
||||||
|
// Found the delimiter
|
||||||
|
slice := buf[:i+1]
|
||||||
|
buf = buf[i+1:]
|
||||||
|
if trimCR && len(slice) >= 2 && slice[len(slice)-2] == byte('\r') {
|
||||||
|
slice = slice[:len(slice)-2]
|
||||||
|
} else {
|
||||||
|
slice = slice[:len(slice)-1]
|
||||||
|
}
|
||||||
|
if len(leftover) > 0 {
|
||||||
|
slice = append(leftover, slice...)
|
||||||
|
leftover = []byte{}
|
||||||
|
}
|
||||||
|
if (err == nil || len(slice) > 0) && r.pusher(slice) {
|
||||||
|
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Could not find the delimiter in the buffer
|
||||||
|
leftover = append(leftover, buf...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
leftover = append(leftover, buf...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(slab) == 0 {
|
||||||
|
slab = make([]byte, readerSlabSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(leftover) > 0 && r.pusher(leftover) {
|
||||||
|
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,16 +197,28 @@ func (r *Reader) readFromStdin() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) readFiles() bool {
|
func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool {
|
||||||
r.killed = false
|
r.killed = false
|
||||||
fn := func(path string, mode os.FileInfo) error {
|
conf := fastwalk.Config{Follow: opts.follow}
|
||||||
|
fn := func(path string, de os.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
path = filepath.Clean(path)
|
path = filepath.Clean(path)
|
||||||
if path != "." {
|
if path != "." {
|
||||||
isDir := mode.Mode().IsDir()
|
isDir := de.IsDir()
|
||||||
if isDir && filepath.Base(path)[0] == '.' {
|
if isDir {
|
||||||
return filepath.SkipDir
|
base := filepath.Base(path)
|
||||||
|
if !opts.hidden && base[0] == '.' {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
for _, ignore := range ignores {
|
||||||
|
if ignore == base {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !isDir && r.pusher([]byte(path)) {
|
if ((opts.file && !isDir) || (opts.dir && isDir)) && r.pusher([]byte(path)) {
|
||||||
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,20 +229,16 @@ func (r *Reader) readFiles() bool {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cb := walker.WithErrorCallback(func(pathname string, err error) error {
|
return fastwalk.Walk(&conf, root, fn) == nil
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return walker.Walk(".", fn, cb) == nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) readFromCommand(shell *string, command string) bool {
|
func (r *Reader) readFromCommand(command string, environ []string) bool {
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
r.killed = false
|
r.killed = false
|
||||||
r.command = &command
|
r.command = &command
|
||||||
if shell != nil {
|
r.exec = util.ExecCommand(command, true)
|
||||||
r.exec = util.ExecCommandWith(*shell, command, true)
|
if environ != nil {
|
||||||
} else {
|
r.exec.Env = environ
|
||||||
r.exec = util.ExecCommand(command, true)
|
|
||||||
}
|
}
|
||||||
out, err := r.exec.StdoutPipe()
|
out, err := r.exec.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -22,7 +22,7 @@ func TestReadFromCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal command
|
// Normal command
|
||||||
reader.fin(reader.readFromCommand(nil, `echo abc&&echo def`))
|
reader.fin(reader.readFromCommand(`echo abc&&echo def`, nil))
|
||||||
if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" {
|
if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" {
|
||||||
t.Errorf("%s", strs)
|
t.Errorf("%s", strs)
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ func TestReadFromCommand(t *testing.T) {
|
|||||||
reader.startEventPoller()
|
reader.startEventPoller()
|
||||||
|
|
||||||
// Failing command
|
// Failing command
|
||||||
reader.fin(reader.readFromCommand(nil, `no-such-command`))
|
reader.fin(reader.readFromCommand(`no-such-command`, nil))
|
||||||
strs = []string{}
|
strs = []string{}
|
||||||
if len(strs) > 0 {
|
if len(strs) > 0 {
|
||||||
t.Errorf("%s", strs)
|
t.Errorf("%s", strs)
|
||||||
|
@@ -32,6 +32,7 @@ const (
|
|||||||
httpUnauthorized = "HTTP/1.1 401 Unauthorized" + crlf
|
httpUnauthorized = "HTTP/1.1 401 Unauthorized" + crlf
|
||||||
httpUnavailable = "HTTP/1.1 503 Service Unavailable" + crlf
|
httpUnavailable = "HTTP/1.1 503 Service Unavailable" + crlf
|
||||||
httpReadTimeout = 10 * time.Second
|
httpReadTimeout = 10 * time.Second
|
||||||
|
channelTimeout = 2 * time.Second
|
||||||
jsonContentType = "Content-Type: application/json" + crlf
|
jsonContentType = "Content-Type: application/json" + crlf
|
||||||
maxContentLength = 1024 * 1024
|
maxContentLength = 1024 * 1024
|
||||||
)
|
)
|
||||||
@@ -53,47 +54,47 @@ func (addr listenAddress) IsLocal() bool {
|
|||||||
|
|
||||||
var defaultListenAddr = listenAddress{"localhost", 0}
|
var defaultListenAddr = listenAddress{"localhost", 0}
|
||||||
|
|
||||||
func parseListenAddress(address string) (error, listenAddress) {
|
func parseListenAddress(address string) (listenAddress, error) {
|
||||||
parts := strings.SplitN(address, ":", 3)
|
parts := strings.SplitN(address, ":", 3)
|
||||||
if len(parts) == 1 {
|
if len(parts) == 1 {
|
||||||
parts = []string{"localhost", parts[0]}
|
parts = []string{"localhost", parts[0]}
|
||||||
}
|
}
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return fmt.Errorf("invalid listen address: %s", address), defaultListenAddr
|
return defaultListenAddr, fmt.Errorf("invalid listen address: %s", address)
|
||||||
}
|
}
|
||||||
portStr := parts[len(parts)-1]
|
portStr := parts[len(parts)-1]
|
||||||
port, err := strconv.Atoi(portStr)
|
port, err := strconv.Atoi(portStr)
|
||||||
if err != nil || port < 0 || port > 65535 {
|
if err != nil || port < 0 || port > 65535 {
|
||||||
return fmt.Errorf("invalid listen port: %s", portStr), defaultListenAddr
|
return defaultListenAddr, fmt.Errorf("invalid listen port: %s", portStr)
|
||||||
}
|
}
|
||||||
if len(parts[0]) == 0 {
|
if len(parts[0]) == 0 {
|
||||||
parts[0] = "localhost"
|
parts[0] = "localhost"
|
||||||
}
|
}
|
||||||
return nil, listenAddress{parts[0], port}
|
return listenAddress{parts[0], port}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startHttpServer(address listenAddress, actionChannel chan []*action, responseChannel chan string) (error, int) {
|
func startHttpServer(address listenAddress, actionChannel chan []*action, responseChannel chan string) (int, error) {
|
||||||
host := address.host
|
host := address.host
|
||||||
port := address.port
|
port := address.port
|
||||||
apiKey := os.Getenv("FZF_API_KEY")
|
apiKey := os.Getenv("FZF_API_KEY")
|
||||||
if !address.IsLocal() && len(apiKey) == 0 {
|
if !address.IsLocal() && len(apiKey) == 0 {
|
||||||
return fmt.Errorf("FZF_API_KEY is required to allow remote access"), port
|
return port, fmt.Errorf("FZF_API_KEY is required to allow remote access")
|
||||||
}
|
}
|
||||||
addrStr := fmt.Sprintf("%s:%d", host, port)
|
addrStr := fmt.Sprintf("%s:%d", host, port)
|
||||||
listener, err := net.Listen("tcp", addrStr)
|
listener, err := net.Listen("tcp", addrStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to listen on %s", addrStr), port
|
return port, fmt.Errorf("failed to listen on %s", addrStr)
|
||||||
}
|
}
|
||||||
if port == 0 {
|
if port == 0 {
|
||||||
addr := listener.Addr().String()
|
addr := listener.Addr().String()
|
||||||
parts := strings.Split(addr, ":")
|
parts := strings.Split(addr, ":")
|
||||||
if len(parts) < 2 {
|
if len(parts) < 2 {
|
||||||
return fmt.Errorf("cannot extract port: %s", addr), port
|
return port, fmt.Errorf("cannot extract port: %s", addr)
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
port, err = strconv.Atoi(parts[len(parts)-1])
|
port, err = strconv.Atoi(parts[len(parts)-1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, port
|
return port, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +120,7 @@ func startHttpServer(address listenAddress, actionChannel chan []*action, respon
|
|||||||
listener.Close()
|
listener.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil, port
|
return port, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we are writing a simplistic HTTP server without using net/http
|
// Here we are writing a simplistic HTTP server without using net/http
|
||||||
@@ -170,7 +171,7 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
|
|||||||
select {
|
select {
|
||||||
case response := <-server.responseChannel:
|
case response := <-server.responseChannel:
|
||||||
return good(response)
|
return good(response)
|
||||||
case <-time.After(2 * time.Second):
|
case <-time.After(channelTimeout):
|
||||||
go func() {
|
go func() {
|
||||||
// Drain the channel
|
// Drain the channel
|
||||||
<-server.responseChannel
|
<-server.responseChannel
|
||||||
@@ -227,7 +228,11 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
|
|||||||
return bad("no action specified")
|
return bad("no action specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
server.actionChannel <- actions
|
select {
|
||||||
|
case server.actionChannel <- actions:
|
||||||
|
case <-time.After(channelTimeout):
|
||||||
|
return httpUnavailable + crlf
|
||||||
|
}
|
||||||
return httpOk + crlf
|
return httpOk + crlf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,15 +242,13 @@ func parseGetParams(query string) getParams {
|
|||||||
parts := strings.SplitN(pair, "=", 2)
|
parts := strings.SplitN(pair, "=", 2)
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
switch parts[0] {
|
switch parts[0] {
|
||||||
case "limit":
|
case "limit", "offset":
|
||||||
val, err := strconv.Atoi(parts[1])
|
if val, err := strconv.Atoi(parts[1]); err == nil {
|
||||||
if err == nil {
|
if parts[0] == "limit" {
|
||||||
params.limit = val
|
params.limit = val
|
||||||
}
|
} else {
|
||||||
case "offset":
|
params.offset = val
|
||||||
val, err := strconv.Atoi(parts[1])
|
}
|
||||||
if err == nil {
|
|
||||||
params.offset = val
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
257
src/terminal.go
257
src/terminal.go
@@ -17,7 +17,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/junegunn/uniseg"
|
"github.com/rivo/uniseg"
|
||||||
|
|
||||||
"github.com/junegunn/fzf/src/tui"
|
"github.com/junegunn/fzf/src/tui"
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
@@ -55,6 +55,9 @@ var actionTypeRegex *regexp.Regexp
|
|||||||
|
|
||||||
const clearCode string = "\x1b[2J"
|
const clearCode string = "\x1b[2J"
|
||||||
|
|
||||||
|
// Number of maximum focus events to process synchronously
|
||||||
|
const maxFocusEvents = 10000
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{fzf:(?:query|action|prompt)}|{\+?f?nf?})`)
|
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{fzf:(?:query|action|prompt)}|{\+?f?nf?})`)
|
||||||
whiteSuffix = regexp.MustCompile(`\s*$`)
|
whiteSuffix = regexp.MustCompile(`\s*$`)
|
||||||
@@ -67,7 +70,7 @@ func init() {
|
|||||||
// * https://sw.kovidgoyal.net/kitty/graphics-protocol
|
// * https://sw.kovidgoyal.net/kitty/graphics-protocol
|
||||||
// * https://en.wikipedia.org/wiki/Sixel
|
// * https://en.wikipedia.org/wiki/Sixel
|
||||||
// * https://iterm2.com/documentation-images.html
|
// * https://iterm2.com/documentation-images.html
|
||||||
passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b(_G|P[0-9;]*q).*?\x1b\\\r?|\x1b]1337;.*?\a`)
|
passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b(_G|P[0-9;]*q).*?\x1b\\\r?|\x1b]1337;.*?(\a|\x1b\\)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
type jumpMode int
|
type jumpMode int
|
||||||
@@ -178,7 +181,7 @@ type Status struct {
|
|||||||
type Terminal struct {
|
type Terminal struct {
|
||||||
initDelay time.Duration
|
initDelay time.Duration
|
||||||
infoStyle infoStyle
|
infoStyle infoStyle
|
||||||
infoSep string
|
infoPrefix string
|
||||||
separator labelPrinter
|
separator labelPrinter
|
||||||
separatorLen int
|
separatorLen int
|
||||||
spinner []string
|
spinner []string
|
||||||
@@ -293,6 +296,7 @@ type Terminal struct {
|
|||||||
lastFocus int32
|
lastFocus int32
|
||||||
areaLines int
|
areaLines int
|
||||||
areaColumns int
|
areaColumns int
|
||||||
|
forcePreview bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type selectedItem struct {
|
type selectedItem struct {
|
||||||
@@ -390,8 +394,10 @@ const (
|
|||||||
actToggleIn
|
actToggleIn
|
||||||
actToggleOut
|
actToggleOut
|
||||||
actToggleTrack
|
actToggleTrack
|
||||||
|
actToggleTrackCurrent
|
||||||
actToggleHeader
|
actToggleHeader
|
||||||
actTrack
|
actTrackCurrent
|
||||||
|
actUntrackCurrent
|
||||||
actDown
|
actDown
|
||||||
actUp
|
actUp
|
||||||
actPageUp
|
actPageUp
|
||||||
@@ -498,6 +504,7 @@ type searchRequest struct {
|
|||||||
sort bool
|
sort bool
|
||||||
sync bool
|
sync bool
|
||||||
command *string
|
command *string
|
||||||
|
environ []string
|
||||||
changed bool
|
changed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,7 +677,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
if previewBox != nil && opts.Preview.aboveOrBelow() {
|
if previewBox != nil && opts.Preview.aboveOrBelow() {
|
||||||
effectiveMinHeight += 1 + borderLines(opts.Preview.border)
|
effectiveMinHeight += 1 + borderLines(opts.Preview.border)
|
||||||
}
|
}
|
||||||
if opts.InfoStyle.noExtraLine() {
|
if noSeparatorLine(opts.InfoStyle, opts.Separator == nil || uniseg.StringWidth(*opts.Separator) > 0) {
|
||||||
effectiveMinHeight--
|
effectiveMinHeight--
|
||||||
}
|
}
|
||||||
effectiveMinHeight += borderLines(opts.BorderShape)
|
effectiveMinHeight += borderLines(opts.BorderShape)
|
||||||
@@ -692,7 +699,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
t := Terminal{
|
t := Terminal{
|
||||||
initDelay: delay,
|
initDelay: delay,
|
||||||
infoStyle: opts.InfoStyle,
|
infoStyle: opts.InfoStyle,
|
||||||
infoSep: opts.InfoSep,
|
infoPrefix: opts.InfoPrefix,
|
||||||
separator: nil,
|
separator: nil,
|
||||||
spinner: makeSpinner(opts.Unicode),
|
spinner: makeSpinner(opts.Unicode),
|
||||||
promptString: opts.Prompt,
|
promptString: opts.Prompt,
|
||||||
@@ -771,7 +778,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
theme: opts.Theme,
|
theme: opts.Theme,
|
||||||
startChan: make(chan fitpad, 1),
|
startChan: make(chan fitpad, 1),
|
||||||
killChan: make(chan int),
|
killChan: make(chan int),
|
||||||
serverInputChan: make(chan []*action, 10),
|
serverInputChan: make(chan []*action, 100),
|
||||||
serverOutputChan: make(chan string),
|
serverOutputChan: make(chan string),
|
||||||
eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar)
|
eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar)
|
||||||
tui: renderer,
|
tui: renderer,
|
||||||
@@ -817,13 +824,17 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, t.hasResizeActions = t.keymap[tui.Resize.AsEvent()]
|
var resizeActions []*action
|
||||||
|
resizeActions, t.hasResizeActions = t.keymap[tui.Resize.AsEvent()]
|
||||||
|
if t.tui.ShouldEmitResizeEvent() {
|
||||||
|
t.keymap[tui.Resize.AsEvent()] = append(toActions(actClearScreen), resizeActions...)
|
||||||
|
}
|
||||||
_, t.hasResultActions = t.keymap[tui.Result.AsEvent()]
|
_, t.hasResultActions = t.keymap[tui.Result.AsEvent()]
|
||||||
_, t.hasFocusActions = t.keymap[tui.Focus.AsEvent()]
|
_, t.hasFocusActions = t.keymap[tui.Focus.AsEvent()]
|
||||||
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
|
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
|
||||||
|
|
||||||
if t.listenAddr != nil {
|
if t.listenAddr != nil {
|
||||||
err, port := startHttpServer(*t.listenAddr, t.serverInputChan, t.serverOutputChan)
|
port, err := startHttpServer(*t.listenAddr, t.serverInputChan, t.serverOutputChan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorExit(err.Error())
|
errorExit(err.Error())
|
||||||
}
|
}
|
||||||
@@ -841,6 +852,8 @@ func (t *Terminal) environ() []string {
|
|||||||
env = append(env, "FZF_QUERY="+string(t.input))
|
env = append(env, "FZF_QUERY="+string(t.input))
|
||||||
env = append(env, "FZF_ACTION="+t.lastAction.Name())
|
env = append(env, "FZF_ACTION="+t.lastAction.Name())
|
||||||
env = append(env, "FZF_PROMPT="+string(t.promptString))
|
env = append(env, "FZF_PROMPT="+string(t.promptString))
|
||||||
|
env = append(env, "FZF_PREVIEW_LABEL="+t.previewLabelOpts.label)
|
||||||
|
env = append(env, "FZF_BORDER_LABEL="+t.borderLabelOpts.label)
|
||||||
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
|
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
|
||||||
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length()))
|
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length()))
|
||||||
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
|
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
|
||||||
@@ -869,7 +882,7 @@ func (t *Terminal) visibleHeaderLines() int {
|
|||||||
// Extra number of lines needed to display fzf
|
// Extra number of lines needed to display fzf
|
||||||
func (t *Terminal) extraLines() int {
|
func (t *Terminal) extraLines() int {
|
||||||
extra := t.visibleHeaderLines() + 1
|
extra := t.visibleHeaderLines() + 1
|
||||||
if !t.noInfoLine() {
|
if !t.noSeparatorLine() {
|
||||||
extra++
|
extra++
|
||||||
}
|
}
|
||||||
return extra
|
return extra
|
||||||
@@ -975,8 +988,18 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
|
|||||||
return output, promptLen
|
return output, promptLen
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) noInfoLine() bool {
|
func noSeparatorLine(style infoStyle, separator bool) bool {
|
||||||
return t.infoStyle.noExtraLine()
|
switch style {
|
||||||
|
case infoInline:
|
||||||
|
return true
|
||||||
|
case infoHidden, infoInlineRight:
|
||||||
|
return !separator
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) noSeparatorLine() bool {
|
||||||
|
return noSeparatorLine(t.infoStyle, t.separatorLen > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getScrollbar(total int, height int, offset int) (int, int) {
|
func getScrollbar(total int, height int, offset int) (int, int) {
|
||||||
@@ -1051,11 +1074,11 @@ func (t *Terminal) UpdateProgress(progress float32) {
|
|||||||
// UpdateList updates Merger to display the list
|
// UpdateList updates Merger to display the list
|
||||||
func (t *Terminal) UpdateList(merger *Merger) {
|
func (t *Terminal) UpdateList(merger *Merger) {
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
var prevIndex int32 = -1
|
prevIndex := minItem.Index()
|
||||||
reset := t.revision != merger.Revision()
|
reset := t.revision != merger.Revision()
|
||||||
if !reset && t.track != trackDisabled {
|
if !reset && t.track != trackDisabled {
|
||||||
if t.merger.Length() > 0 {
|
if t.merger.Length() > 0 {
|
||||||
prevIndex = t.merger.Get(t.cy).item.Index()
|
prevIndex = t.currentIndex()
|
||||||
} else if merger.Length() > 0 {
|
} else if merger.Length() > 0 {
|
||||||
prevIndex = merger.First().item.Index()
|
prevIndex = merger.First().item.Index()
|
||||||
}
|
}
|
||||||
@@ -1228,7 +1251,7 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
|
|||||||
|
|
||||||
minAreaWidth := minWidth
|
minAreaWidth := minWidth
|
||||||
minAreaHeight := minHeight
|
minAreaHeight := minHeight
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
minAreaHeight -= 1
|
minAreaHeight -= 1
|
||||||
}
|
}
|
||||||
if t.needPreviewWindow() {
|
if t.needPreviewWindow() {
|
||||||
@@ -1250,6 +1273,7 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) resizeWindows(forcePreview bool) {
|
func (t *Terminal) resizeWindows(forcePreview bool) {
|
||||||
|
t.forcePreview = forcePreview
|
||||||
screenWidth, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding()
|
screenWidth, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding()
|
||||||
width := screenWidth - marginInt[1] - marginInt[3]
|
width := screenWidth - marginInt[1] - marginInt[3]
|
||||||
height := screenHeight - marginInt[0] - marginInt[2]
|
height := screenHeight - marginInt[0] - marginInt[2]
|
||||||
@@ -1508,7 +1532,7 @@ func (t *Terminal) move(y int, x int, clear bool) {
|
|||||||
y = h - y - 1
|
y = h - y - 1
|
||||||
case layoutReverseList:
|
case layoutReverseList:
|
||||||
n := 2 + t.visibleHeaderLines()
|
n := 2 + t.visibleHeaderLines()
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
n--
|
n--
|
||||||
}
|
}
|
||||||
if y < n {
|
if y < n {
|
||||||
@@ -1548,7 +1572,7 @@ func (t *Terminal) updatePromptOffset() ([]rune, []rune) {
|
|||||||
func (t *Terminal) promptLine() int {
|
func (t *Terminal) promptLine() int {
|
||||||
if t.headerFirst {
|
if t.headerFirst {
|
||||||
max := t.window.Height() - 1
|
max := t.window.Height() - 1
|
||||||
if !t.noInfoLine() {
|
if !t.noSeparatorLine() {
|
||||||
max--
|
max--
|
||||||
}
|
}
|
||||||
return util.Min(t.visibleHeaderLines(), max)
|
return util.Min(t.visibleHeaderLines(), max)
|
||||||
@@ -1593,20 +1617,8 @@ func (t *Terminal) printInfo() {
|
|||||||
t.window.Print(" ") // Clear spinner
|
t.window.Print(" ") // Clear spinner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch t.infoStyle {
|
printInfoPrefix := func() {
|
||||||
case infoDefault:
|
str := t.infoPrefix
|
||||||
t.move(line+1, 0, t.separatorLen == 0)
|
|
||||||
printSpinner()
|
|
||||||
t.move(line+1, 2, false)
|
|
||||||
pos = 2
|
|
||||||
case infoRight:
|
|
||||||
t.move(line+1, 0, false)
|
|
||||||
case infoInlineRight:
|
|
||||||
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
|
||||||
t.move(line, pos, true)
|
|
||||||
case infoInline:
|
|
||||||
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
|
||||||
str := t.infoSep
|
|
||||||
maxWidth := t.window.Width() - pos
|
maxWidth := t.window.Width() - pos
|
||||||
width := util.StringWidth(str)
|
width := util.StringWidth(str)
|
||||||
if width > maxWidth {
|
if width > maxWidth {
|
||||||
@@ -1621,7 +1633,34 @@ func (t *Terminal) printInfo() {
|
|||||||
t.window.CPrint(tui.ColPrompt, str)
|
t.window.CPrint(tui.ColPrompt, str)
|
||||||
}
|
}
|
||||||
pos += width
|
pos += width
|
||||||
|
}
|
||||||
|
printSeparator := func(fillLength int, pad bool) {
|
||||||
|
// --------_
|
||||||
|
if t.separatorLen > 0 {
|
||||||
|
t.separator(t.window, fillLength)
|
||||||
|
t.window.Print(" ")
|
||||||
|
} else if pad {
|
||||||
|
t.window.Print(strings.Repeat(" ", fillLength+1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch t.infoStyle {
|
||||||
|
case infoDefault:
|
||||||
|
t.move(line+1, 0, t.separatorLen == 0)
|
||||||
|
printSpinner()
|
||||||
|
t.move(line+1, 2, false)
|
||||||
|
pos = 2
|
||||||
|
case infoRight:
|
||||||
|
t.move(line+1, 0, false)
|
||||||
|
case infoInlineRight:
|
||||||
|
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
||||||
|
case infoInline:
|
||||||
|
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
||||||
|
printInfoPrefix()
|
||||||
case infoHidden:
|
case infoHidden:
|
||||||
|
if t.separatorLen > 0 {
|
||||||
|
t.move(line+1, 0, false)
|
||||||
|
printSeparator(t.window.Width()-1, false)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1635,8 +1674,11 @@ func (t *Terminal) printInfo() {
|
|||||||
output += " -S"
|
output += " -S"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.track != trackDisabled {
|
switch t.track {
|
||||||
|
case trackEnabled:
|
||||||
output += " +T"
|
output += " +T"
|
||||||
|
case trackCurrent:
|
||||||
|
output += " +t"
|
||||||
}
|
}
|
||||||
if t.multi > 0 {
|
if t.multi > 0 {
|
||||||
if t.multi == maxMulti {
|
if t.multi == maxMulti {
|
||||||
@@ -1652,15 +1694,6 @@ func (t *Terminal) printInfo() {
|
|||||||
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
printSeparator := func(fillLength int, pad bool) {
|
|
||||||
// --------_
|
|
||||||
if t.separatorLen > 0 {
|
|
||||||
t.separator(t.window, fillLength)
|
|
||||||
t.window.Print(" ")
|
|
||||||
} else if pad {
|
|
||||||
t.window.Print(strings.Repeat(" ", fillLength+1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t.infoStyle == infoRight {
|
if t.infoStyle == infoRight {
|
||||||
maxWidth := t.window.Width()
|
maxWidth := t.window.Width()
|
||||||
if t.reading {
|
if t.reading {
|
||||||
@@ -1683,19 +1716,35 @@ func (t *Terminal) printInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if t.infoStyle == infoInlineRight {
|
if t.infoStyle == infoInlineRight {
|
||||||
pos = util.Max(pos, t.window.Width()-util.StringWidth(output)-3)
|
if len(t.infoPrefix) == 0 {
|
||||||
if pos >= t.window.Width() {
|
pos = util.Max(pos, t.window.Width()-util.StringWidth(output)-3)
|
||||||
return
|
if pos < t.window.Width() {
|
||||||
|
t.move(line, pos, false)
|
||||||
|
printSpinner()
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
if pos < t.window.Width()-1 {
|
||||||
|
t.window.Print(" ")
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pos = util.Max(pos, t.window.Width()-util.StringWidth(output)-util.StringWidth(t.infoPrefix)-1)
|
||||||
|
printInfoPrefix()
|
||||||
}
|
}
|
||||||
t.move(line, pos, false)
|
|
||||||
printSpinner()
|
|
||||||
t.window.Print(" ")
|
|
||||||
pos += 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
maxWidth := t.window.Width() - pos
|
maxWidth := t.window.Width() - pos
|
||||||
output = t.trimMessage(output, maxWidth)
|
output = t.trimMessage(output, maxWidth)
|
||||||
t.window.CPrint(tui.ColInfo, output)
|
t.window.CPrint(tui.ColInfo, output)
|
||||||
|
|
||||||
|
if t.infoStyle == infoInlineRight {
|
||||||
|
if t.separatorLen > 0 {
|
||||||
|
t.move(line+1, 0, false)
|
||||||
|
printSeparator(t.window.Width()-1, false)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
fillLength := maxWidth - len(output) - 2
|
fillLength := maxWidth - len(output) - 2
|
||||||
if fillLength > 0 {
|
if fillLength > 0 {
|
||||||
t.window.CPrint(tui.ColSeparator, " ")
|
t.window.CPrint(tui.ColSeparator, " ")
|
||||||
@@ -1710,7 +1759,7 @@ func (t *Terminal) printHeader() {
|
|||||||
max := t.window.Height()
|
max := t.window.Height()
|
||||||
if t.headerFirst {
|
if t.headerFirst {
|
||||||
max--
|
max--
|
||||||
if !t.noInfoLine() {
|
if !t.noSeparatorLine() {
|
||||||
max--
|
max--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1727,7 +1776,7 @@ func (t *Terminal) printHeader() {
|
|||||||
}
|
}
|
||||||
if !t.headerFirst {
|
if !t.headerFirst {
|
||||||
line++
|
line++
|
||||||
if !t.noInfoLine() {
|
if !t.noSeparatorLine() {
|
||||||
line++
|
line++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1759,7 +1808,7 @@ func (t *Terminal) printList() {
|
|||||||
i = maxy - 1 - j
|
i = maxy - 1 - j
|
||||||
}
|
}
|
||||||
line := i + 2 + t.visibleHeaderLines()
|
line := i + 2 + t.visibleHeaderLines()
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
line--
|
line--
|
||||||
}
|
}
|
||||||
if i < count {
|
if i < count {
|
||||||
@@ -2312,7 +2361,7 @@ func (t *Terminal) processTabs(runes []rune, prefixWidth int) (string, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printAll() {
|
func (t *Terminal) printAll() {
|
||||||
t.resizeWindows(false)
|
t.resizeWindows(t.forcePreview)
|
||||||
t.printList()
|
t.printList()
|
||||||
t.printPrompt()
|
t.printPrompt()
|
||||||
t.printInfo()
|
t.printInfo()
|
||||||
@@ -2852,14 +2901,16 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
resizeChan := make(chan os.Signal, 1)
|
if !t.tui.ShouldEmitResizeEvent() {
|
||||||
notifyOnResize(resizeChan) // Non-portable
|
resizeChan := make(chan os.Signal, 1)
|
||||||
go func() {
|
notifyOnResize(resizeChan) // Non-portable
|
||||||
for {
|
go func() {
|
||||||
<-resizeChan
|
for {
|
||||||
t.reqBox.Set(reqResize, nil)
|
<-resizeChan
|
||||||
}
|
t.reqBox.Set(reqResize, nil)
|
||||||
}()
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
t.initFunc()
|
t.initFunc()
|
||||||
@@ -3083,7 +3134,7 @@ func (t *Terminal) Loop() {
|
|||||||
switch req {
|
switch req {
|
||||||
case reqPrompt:
|
case reqPrompt:
|
||||||
t.printPrompt()
|
t.printPrompt()
|
||||||
if t.noInfoLine() {
|
if t.infoStyle == infoInline || t.infoStyle == infoInlineRight {
|
||||||
t.printInfo()
|
t.printInfo()
|
||||||
}
|
}
|
||||||
case reqInfo:
|
case reqInfo:
|
||||||
@@ -3152,7 +3203,7 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
t.previewer.lines = result.lines
|
t.previewer.lines = result.lines
|
||||||
t.previewer.spinner = result.spinner
|
t.previewer.spinner = result.spinner
|
||||||
if t.previewer.following.Enabled() {
|
if t.hasPreviewWindow() && t.previewer.following.Enabled() {
|
||||||
t.previewer.offset = util.Max(t.previewer.offset, len(t.previewer.lines)-(t.pwindow.Height()-t.previewOpts.headerLines))
|
t.previewer.offset = util.Max(t.previewer.offset, len(t.previewer.lines)-(t.pwindow.Height()-t.previewOpts.headerLines))
|
||||||
} else if result.offset >= 0 {
|
} else if result.offset >= 0 {
|
||||||
t.previewer.offset = util.Constrain(result.offset, t.previewOpts.headerLines, len(t.previewer.lines)-1)
|
t.previewer.offset = util.Constrain(result.offset, t.previewOpts.headerLines, len(t.previewer.lines)-1)
|
||||||
@@ -3215,7 +3266,11 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case event = <-t.eventChan:
|
case event = <-t.eventChan:
|
||||||
needBarrier = !event.Is(tui.Load, tui.Result, tui.Focus, tui.One, tui.Zero, tui.Resize)
|
if t.tui.ShouldEmitResizeEvent() {
|
||||||
|
needBarrier = !event.Is(tui.Load, tui.Result, tui.Focus, tui.One, tui.Zero)
|
||||||
|
} else {
|
||||||
|
needBarrier = !event.Is(tui.Load, tui.Result, tui.Focus, tui.One, tui.Zero, tui.Resize)
|
||||||
|
}
|
||||||
case serverActions := <-t.serverInputChan:
|
case serverActions := <-t.serverInputChan:
|
||||||
event = tui.Invalid.AsEvent()
|
event = tui.Invalid.AsEvent()
|
||||||
if t.listenAddr == nil || t.listenAddr.IsLocal() || t.listenUnsafe {
|
if t.listenAddr == nil || t.listenAddr.IsLocal() || t.listenUnsafe {
|
||||||
@@ -3292,18 +3347,22 @@ func (t *Terminal) Loop() {
|
|||||||
var doAction func(*action) bool
|
var doAction func(*action) bool
|
||||||
var doActions func(actions []*action) bool
|
var doActions func(actions []*action) bool
|
||||||
doActions = func(actions []*action) bool {
|
doActions = func(actions []*action) bool {
|
||||||
currentIndex := t.currentIndex()
|
for iter := 0; iter <= maxFocusEvents; iter++ {
|
||||||
for _, action := range actions {
|
currentIndex := t.currentIndex()
|
||||||
if !doAction(action) {
|
for _, action := range actions {
|
||||||
return false
|
if !doAction(action) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if onFocus, prs := t.keymap[tui.Focus.AsEvent()]; prs {
|
if onFocus, prs := t.keymap[tui.Focus.AsEvent()]; prs && iter < maxFocusEvents {
|
||||||
if newIndex := t.currentIndex(); newIndex != currentIndex {
|
if newIndex := t.currentIndex(); newIndex != currentIndex {
|
||||||
t.lastFocus = newIndex
|
t.lastFocus = newIndex
|
||||||
return doActions(onFocus)
|
actions = onFocus
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -3369,6 +3428,9 @@ func (t *Terminal) Loop() {
|
|||||||
// Discard the preview content so that it won't accidentally appear
|
// Discard the preview content so that it won't accidentally appear
|
||||||
// when preview window is re-enabled and previewDelay is triggered
|
// when preview window is re-enabled and previewDelay is triggered
|
||||||
t.previewer.lines = nil
|
t.previewer.lines = nil
|
||||||
|
|
||||||
|
// Also kill the preview process if it's still running
|
||||||
|
t.cancelPreview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case actTogglePreviewWrap:
|
case actTogglePreviewWrap:
|
||||||
@@ -3448,11 +3510,13 @@ func (t *Terminal) Loop() {
|
|||||||
req(reqHeader)
|
req(reqHeader)
|
||||||
}
|
}
|
||||||
case actChangeBorderLabel:
|
case actChangeBorderLabel:
|
||||||
|
t.borderLabelOpts.label = a.a
|
||||||
if t.border != nil {
|
if t.border != nil {
|
||||||
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(a.a, &tui.ColBorderLabel, false)
|
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(a.a, &tui.ColBorderLabel, false)
|
||||||
req(reqRedrawBorderLabel)
|
req(reqRedrawBorderLabel)
|
||||||
}
|
}
|
||||||
case actChangePreviewLabel:
|
case actChangePreviewLabel:
|
||||||
|
t.previewLabelOpts.label = a.a
|
||||||
if t.pborder != nil {
|
if t.pborder != nil {
|
||||||
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(a.a, &tui.ColPreviewLabel, false)
|
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(a.a, &tui.ColPreviewLabel, false)
|
||||||
req(reqRedrawPreviewLabel)
|
req(reqRedrawPreviewLabel)
|
||||||
@@ -3462,14 +3526,16 @@ func (t *Terminal) Loop() {
|
|||||||
actions := parseSingleActionList(strings.Trim(body, "\r\n"), func(message string) {})
|
actions := parseSingleActionList(strings.Trim(body, "\r\n"), func(message string) {})
|
||||||
return doActions(actions)
|
return doActions(actions)
|
||||||
case actTransformBorderLabel:
|
case actTransformBorderLabel:
|
||||||
|
label := t.executeCommand(a.a, false, true, true, true)
|
||||||
|
t.borderLabelOpts.label = label
|
||||||
if t.border != nil {
|
if t.border != nil {
|
||||||
label := t.executeCommand(a.a, false, true, true, true)
|
|
||||||
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false)
|
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false)
|
||||||
req(reqRedrawBorderLabel)
|
req(reqRedrawBorderLabel)
|
||||||
}
|
}
|
||||||
case actTransformPreviewLabel:
|
case actTransformPreviewLabel:
|
||||||
|
label := t.executeCommand(a.a, false, true, true, true)
|
||||||
|
t.previewLabelOpts.label = label
|
||||||
if t.pborder != nil {
|
if t.pborder != nil {
|
||||||
label := t.executeCommand(a.a, false, true, true, true)
|
|
||||||
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false)
|
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false)
|
||||||
req(reqRedrawPreviewLabel)
|
req(reqRedrawPreviewLabel)
|
||||||
}
|
}
|
||||||
@@ -3478,7 +3544,9 @@ func (t *Terminal) Loop() {
|
|||||||
t.prompt, t.promptLen = t.parsePrompt(a.a)
|
t.prompt, t.promptLen = t.parsePrompt(a.a)
|
||||||
req(reqPrompt)
|
req(reqPrompt)
|
||||||
case actPreview:
|
case actPreview:
|
||||||
updatePreviewWindow(true)
|
if !t.hasPreviewWindow() {
|
||||||
|
updatePreviewWindow(true)
|
||||||
|
}
|
||||||
refreshPreview(a.a)
|
refreshPreview(a.a)
|
||||||
case actRefreshPreview:
|
case actRefreshPreview:
|
||||||
refreshPreview(t.previewOpts.command)
|
refreshPreview(t.previewOpts.command)
|
||||||
@@ -3753,6 +3821,14 @@ func (t *Terminal) Loop() {
|
|||||||
t.track = trackEnabled
|
t.track = trackEnabled
|
||||||
}
|
}
|
||||||
req(reqInfo)
|
req(reqInfo)
|
||||||
|
case actToggleTrackCurrent:
|
||||||
|
switch t.track {
|
||||||
|
case trackCurrent:
|
||||||
|
t.track = trackDisabled
|
||||||
|
case trackDisabled:
|
||||||
|
t.track = trackCurrent
|
||||||
|
}
|
||||||
|
req(reqInfo)
|
||||||
case actShowHeader:
|
case actShowHeader:
|
||||||
t.headerVisible = true
|
t.headerVisible = true
|
||||||
req(reqList, reqInfo, reqPrompt, reqHeader)
|
req(reqList, reqInfo, reqPrompt, reqHeader)
|
||||||
@@ -3762,11 +3838,16 @@ func (t *Terminal) Loop() {
|
|||||||
case actToggleHeader:
|
case actToggleHeader:
|
||||||
t.headerVisible = !t.headerVisible
|
t.headerVisible = !t.headerVisible
|
||||||
req(reqList, reqInfo, reqPrompt, reqHeader)
|
req(reqList, reqInfo, reqPrompt, reqHeader)
|
||||||
case actTrack:
|
case actTrackCurrent:
|
||||||
if t.track == trackDisabled {
|
if t.track == trackDisabled {
|
||||||
t.track = trackCurrent
|
t.track = trackCurrent
|
||||||
}
|
}
|
||||||
req(reqInfo)
|
req(reqInfo)
|
||||||
|
case actUntrackCurrent:
|
||||||
|
if t.track == trackCurrent {
|
||||||
|
t.track = trackDisabled
|
||||||
|
}
|
||||||
|
req(reqInfo)
|
||||||
case actEnableSearch:
|
case actEnableSearch:
|
||||||
t.paused = false
|
t.paused = false
|
||||||
changed = true
|
changed = true
|
||||||
@@ -3855,7 +3936,7 @@ func (t *Terminal) Loop() {
|
|||||||
mx -= t.window.Left()
|
mx -= t.window.Left()
|
||||||
my -= t.window.Top()
|
my -= t.window.Top()
|
||||||
min := 2 + t.visibleHeaderLines()
|
min := 2 + t.visibleHeaderLines()
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
min--
|
min--
|
||||||
}
|
}
|
||||||
h := t.window.Height()
|
h := t.window.Height()
|
||||||
@@ -3973,11 +4054,18 @@ func (t *Terminal) Loop() {
|
|||||||
|
|
||||||
// Full redraw
|
// Full redraw
|
||||||
if !currentPreviewOpts.sameLayout(t.previewOpts) {
|
if !currentPreviewOpts.sameLayout(t.previewOpts) {
|
||||||
wasHidden := t.pwindow == nil
|
// Preview command can be running in the background if the size of
|
||||||
|
// the preview window is 0 but not 'hidden'
|
||||||
|
wasHidden := currentPreviewOpts.hidden
|
||||||
updatePreviewWindow(false)
|
updatePreviewWindow(false)
|
||||||
if wasHidden && t.hasPreviewWindow() {
|
if wasHidden && t.hasPreviewWindow() {
|
||||||
|
// Restart
|
||||||
refreshPreview(t.previewOpts.command)
|
refreshPreview(t.previewOpts.command)
|
||||||
|
} else if t.previewOpts.hidden {
|
||||||
|
// Cancel
|
||||||
|
t.cancelPreview()
|
||||||
} else {
|
} else {
|
||||||
|
// Refresh
|
||||||
req(reqPreviewRefresh)
|
req(reqPreviewRefresh)
|
||||||
}
|
}
|
||||||
} else if !currentPreviewOpts.sameContentLayout(t.previewOpts) {
|
} else if !currentPreviewOpts.sameContentLayout(t.previewOpts) {
|
||||||
@@ -4067,10 +4155,15 @@ func (t *Terminal) Loop() {
|
|||||||
req(reqPrompt)
|
req(reqPrompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reload := changed || newCommand != nil
|
||||||
|
var reloadRequest *searchRequest
|
||||||
|
if reload {
|
||||||
|
reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, command: newCommand, environ: t.environ(), changed: changed}
|
||||||
|
}
|
||||||
t.mutex.Unlock() // Must be unlocked before touching reqBox
|
t.mutex.Unlock() // Must be unlocked before touching reqBox
|
||||||
|
|
||||||
if changed || newCommand != nil {
|
if reload {
|
||||||
t.eventBox.Set(EvtSearchNew, searchRequest{sort: t.sort, sync: reloadSync, command: newCommand, changed: changed})
|
t.eventBox.Set(EvtSearchNew, *reloadRequest)
|
||||||
}
|
}
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
t.reqBox.Set(event, nil)
|
t.reqBox.Set(event, nil)
|
||||||
@@ -4084,7 +4177,7 @@ func (t *Terminal) constrain() {
|
|||||||
// count of lines can be displayed
|
// count of lines can be displayed
|
||||||
height := t.maxItems()
|
height := t.maxItems()
|
||||||
|
|
||||||
t.cy = util.Constrain(t.cy, 0, count-1)
|
t.cy = util.Constrain(t.cy, 0, util.Max(0, count-1))
|
||||||
|
|
||||||
minOffset := util.Max(t.cy-height+1, 0)
|
minOffset := util.Max(t.cy-height+1, 0)
|
||||||
maxOffset := util.Max(util.Min(count-height, t.cy), 0)
|
maxOffset := util.Max(util.Min(count-height, t.cy), 0)
|
||||||
@@ -4135,7 +4228,7 @@ func (t *Terminal) vset(o int) bool {
|
|||||||
|
|
||||||
func (t *Terminal) maxItems() int {
|
func (t *Terminal) maxItems() int {
|
||||||
max := t.window.Height() - 2 - t.visibleHeaderLines()
|
max := t.window.Height() - 2 - t.visibleHeaderLines()
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
max++
|
max++
|
||||||
}
|
}
|
||||||
return util.Max(max, 0)
|
return util.Max(max, 0)
|
||||||
|
@@ -91,7 +91,7 @@ func withPrefixLengths(tokens []string, begin int) []Token {
|
|||||||
|
|
||||||
prefixLength := begin
|
prefixLength := begin
|
||||||
for idx := range tokens {
|
for idx := range tokens {
|
||||||
chars := util.ToChars([]byte(tokens[idx]))
|
chars := util.ToChars(sbytes(tokens[idx]))
|
||||||
ret[idx] = Token{&chars, int32(prefixLength)}
|
ret[idx] = Token{&chars, int32(prefixLength)}
|
||||||
prefixLength += chars.Length()
|
prefixLength += chars.Length()
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,7 @@ func Transform(tokens []Token, withNth []Range) []Token {
|
|||||||
if r.begin == r.end {
|
if r.begin == r.end {
|
||||||
idx := r.begin
|
idx := r.begin
|
||||||
if idx == rangeEllipsis {
|
if idx == rangeEllipsis {
|
||||||
chars := util.ToChars([]byte(joinTokens(tokens)))
|
chars := util.ToChars(sbytes(joinTokens(tokens)))
|
||||||
parts = append(parts, &chars)
|
parts = append(parts, &chars)
|
||||||
} else {
|
} else {
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
|
@@ -36,6 +36,7 @@ func (r *FullscreenRenderer) Resume(bool, bool) {}
|
|||||||
func (r *FullscreenRenderer) PassThrough(string) {}
|
func (r *FullscreenRenderer) PassThrough(string) {}
|
||||||
func (r *FullscreenRenderer) Clear() {}
|
func (r *FullscreenRenderer) Clear() {}
|
||||||
func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false }
|
func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false }
|
||||||
|
func (r *FullscreenRenderer) ShouldEmitResizeEvent() bool { return false }
|
||||||
func (r *FullscreenRenderer) Refresh() {}
|
func (r *FullscreenRenderer) Refresh() {}
|
||||||
func (r *FullscreenRenderer) Close() {}
|
func (r *FullscreenRenderer) Close() {}
|
||||||
func (r *FullscreenRenderer) Size() TermSize { return TermSize{} }
|
func (r *FullscreenRenderer) Size() TermSize { return TermSize{} }
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/junegunn/uniseg"
|
"github.com/rivo/uniseg"
|
||||||
|
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
@@ -694,6 +694,10 @@ func (r *LightRenderer) NeedScrollbarRedraw() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) ShouldEmitResizeEvent() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (r *LightRenderer) RefreshWindows(windows []Window) {
|
func (r *LightRenderer) RefreshWindows(windows []Window) {
|
||||||
r.flush()
|
r.flush()
|
||||||
}
|
}
|
||||||
@@ -804,13 +808,25 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
|
|||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
hw := runeWidth(w.border.top)
|
hw := runeWidth(w.border.top)
|
||||||
|
pad := repeat(' ', w.width/hw)
|
||||||
|
|
||||||
|
w.Move(0, 0)
|
||||||
if top {
|
if top {
|
||||||
w.Move(0, 0)
|
|
||||||
w.CPrint(color, repeat(w.border.top, w.width/hw))
|
w.CPrint(color, repeat(w.border.top, w.width/hw))
|
||||||
|
} else {
|
||||||
|
w.CPrint(color, pad)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for y := 1; y < w.height-1; y++ {
|
||||||
|
w.Move(y, 0)
|
||||||
|
w.CPrint(color, pad)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Move(w.height-1, 0)
|
||||||
if bottom {
|
if bottom {
|
||||||
w.Move(w.height-1, 0)
|
|
||||||
w.CPrint(color, repeat(w.border.bottom, w.width/hw))
|
w.CPrint(color, repeat(w.border.bottom, w.width/hw))
|
||||||
|
} else {
|
||||||
|
w.CPrint(color, pad)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/gdamore/tcell/v2/encoding"
|
"github.com/gdamore/tcell/v2/encoding"
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
|
||||||
"github.com/junegunn/uniseg"
|
"github.com/rivo/uniseg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HasFullscreenRenderer() bool {
|
func HasFullscreenRenderer() bool {
|
||||||
@@ -143,6 +143,7 @@ func (a Attr) Merge(b Attr) Attr {
|
|||||||
var (
|
var (
|
||||||
_screen tcell.Screen
|
_screen tcell.Screen
|
||||||
_prevMouseButton tcell.ButtonMask
|
_prevMouseButton tcell.ButtonMask
|
||||||
|
_initialResize bool = true
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *FullscreenRenderer) initScreen() {
|
func (r *FullscreenRenderer) initScreen() {
|
||||||
@@ -202,6 +203,10 @@ func (r *FullscreenRenderer) NeedScrollbarRedraw() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *FullscreenRenderer) ShouldEmitResizeEvent() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (r *FullscreenRenderer) Refresh() {
|
func (r *FullscreenRenderer) Refresh() {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
@@ -216,6 +221,12 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
ev := _screen.PollEvent()
|
ev := _screen.PollEvent()
|
||||||
switch ev := ev.(type) {
|
switch ev := ev.(type) {
|
||||||
case *tcell.EventResize:
|
case *tcell.EventResize:
|
||||||
|
// Ignore the first resize event
|
||||||
|
// https://github.com/gdamore/tcell/blob/v2.7.0/TUTORIAL.md?plain=1#L18
|
||||||
|
if _initialResize {
|
||||||
|
_initialResize = false
|
||||||
|
return Event{Invalid, 0, nil}
|
||||||
|
}
|
||||||
return Event{Resize, 0, nil}
|
return Event{Resize, 0, nil}
|
||||||
|
|
||||||
// process mouse events:
|
// process mouse events:
|
||||||
@@ -533,7 +544,7 @@ 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(false)
|
w.Erase()
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,8 +561,8 @@ func fill(x, y, w, h int, n ColorPair, r rune) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) Erase() {
|
func (w *TcellWindow) Erase() {
|
||||||
w.drawBorder(false)
|
|
||||||
fill(w.left-1, w.top, w.width+1, w.height-1, w.normal, ' ')
|
fill(w.left-1, w.top, w.width+1, w.height-1, w.normal, ' ')
|
||||||
|
w.drawBorder(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) EraseMaybe() bool {
|
func (w *TcellWindow) EraseMaybe() bool {
|
||||||
|
@@ -182,6 +182,7 @@ func TestGetCharEventKey(t *testing.T) {
|
|||||||
r.Init()
|
r.Init()
|
||||||
|
|
||||||
// run and evaluate the tests
|
// run and evaluate the tests
|
||||||
|
initialResizeAsInvalid := true
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
// generate key event
|
// generate key event
|
||||||
giveEvent := tcell.NewEventKey(test.giveKey.Type, test.giveKey.Char, test.giveKey.Mods)
|
giveEvent := tcell.NewEventKey(test.giveKey.Type, test.giveKey.Char, test.giveKey.Mods)
|
||||||
@@ -191,8 +192,9 @@ func TestGetCharEventKey(t *testing.T) {
|
|||||||
// process the event in fzf and evaluate the test
|
// process the event in fzf and evaluate the test
|
||||||
gotEvent := r.GetChar()
|
gotEvent := r.GetChar()
|
||||||
// skip Resize events, those are sometimes put in the buffer outside of this test
|
// skip Resize events, those are sometimes put in the buffer outside of this test
|
||||||
for gotEvent.Type == Resize {
|
if initialResizeAsInvalid && gotEvent.Type == Invalid {
|
||||||
t.Logf("Resize swallowed")
|
t.Logf("Resize as Invalid swallowed")
|
||||||
|
initialResizeAsInvalid = false
|
||||||
gotEvent = r.GetChar()
|
gotEvent = r.GetChar()
|
||||||
}
|
}
|
||||||
t.Logf("wantEvent = %T{Type: %v, Char: %q (%[3]v)}\n", test.wantKey, test.wantKey.Type, test.wantKey.Char)
|
t.Logf("wantEvent = %T{Type: %v, Char: %q (%[3]v)}\n", test.wantKey, test.wantKey.Type, test.wantKey.Char)
|
||||||
|
@@ -6,7 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/junegunn/uniseg"
|
"github.com/rivo/uniseg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Types of user action
|
// Types of user action
|
||||||
@@ -494,6 +494,7 @@ type Renderer interface {
|
|||||||
Close()
|
Close()
|
||||||
PassThrough(string)
|
PassThrough(string)
|
||||||
NeedScrollbarRedraw() bool
|
NeedScrollbarRedraw() bool
|
||||||
|
ShouldEmitResizeEvent() bool
|
||||||
|
|
||||||
GetChar() Event
|
GetChar() Event
|
||||||
|
|
||||||
|
@@ -163,7 +163,7 @@ func (chars *Chars) ToString() string {
|
|||||||
if runes := chars.optionalRunes(); runes != nil {
|
if runes := chars.optionalRunes(); runes != nil {
|
||||||
return string(runes)
|
return string(runes)
|
||||||
}
|
}
|
||||||
return string(chars.slice)
|
return unsafe.String(unsafe.SliceData(chars.slice), len(chars.slice))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (chars *Chars) ToRunes() []rune {
|
func (chars *Chars) ToRunes() []rune {
|
||||||
|
@@ -6,8 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/junegunn/uniseg"
|
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
|
"github.com/rivo/uniseg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StringWidth returns string width where each CR/LF character takes 1 column
|
// StringWidth returns string width where each CR/LF character takes 1 column
|
||||||
|
195
test/test_go.rb
195
test/test_go.rb
@@ -18,7 +18,6 @@ UNSETS = %w[
|
|||||||
FZF_ALT_C_COMMAND
|
FZF_ALT_C_COMMAND
|
||||||
FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
|
FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
|
||||||
FZF_API_KEY
|
FZF_API_KEY
|
||||||
fish_history
|
|
||||||
].freeze
|
].freeze
|
||||||
DEFAULT_TIMEOUT = 10
|
DEFAULT_TIMEOUT = 10
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@ class Shell
|
|||||||
end
|
end
|
||||||
|
|
||||||
def fish
|
def fish
|
||||||
UNSETS.map { |v| v + '= ' }.join + ' FZF_DEFAULT_OPTS=--no-scrollbar fish'
|
"unset #{UNSETS.join(' ')}; FZF_DEFAULT_OPTS=--no-scrollbar fish_history= fish"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -964,26 +963,40 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_execute
|
def test_execute
|
||||||
output = '/tmp/fzf-test-execute'
|
output = '/tmp/fzf-test-execute'
|
||||||
opts = %[--bind "alt-a:execute(echo /{}/ >> #{output}),alt-b:execute[echo /{}{}/ >> #{output}],C:execute:echo /{}{}{}/ >> #{output}"]
|
opts = %[--bind "alt-a:execute(echo /{}/ >> #{output})+change-header(alt-a),alt-b:execute[echo /{}{}/ >> #{output}]+change-header(alt-b),C:execute(echo /{}{}{}/ >> #{output})+change-header(C)"]
|
||||||
writelines(tempname, %w[foo'bar foo"bar foo$bar])
|
writelines(tempname, %w[foo'bar foo"bar foo$bar])
|
||||||
tmux.send_keys "cat #{tempname} | #{fzf(opts)}", :Enter
|
tmux.send_keys "cat #{tempname} | #{fzf(opts)}", :Enter
|
||||||
tmux.until { |lines| assert_equal ' 3/3', lines[-2] }
|
tmux.until { |lines| assert_equal 3, lines.item_count }
|
||||||
tmux.send_keys :Escape, :a
|
|
||||||
|
ready = ->(s) { tmux.until { |lines| assert_includes lines[-3], s } }
|
||||||
tmux.send_keys :Escape, :a
|
tmux.send_keys :Escape, :a
|
||||||
|
ready.call('alt-a')
|
||||||
|
tmux.send_keys :Escape, :b
|
||||||
|
ready.call('alt-b')
|
||||||
|
|
||||||
tmux.send_keys :Up
|
tmux.send_keys :Up
|
||||||
|
tmux.send_keys :Escape, :a
|
||||||
|
ready.call('alt-a')
|
||||||
tmux.send_keys :Escape, :b
|
tmux.send_keys :Escape, :b
|
||||||
tmux.send_keys :Escape, :b
|
ready.call('alt-b')
|
||||||
|
|
||||||
tmux.send_keys :Up
|
tmux.send_keys :Up
|
||||||
tmux.send_keys :C
|
tmux.send_keys :C
|
||||||
|
ready.call('C')
|
||||||
|
|
||||||
tmux.send_keys 'barfoo'
|
tmux.send_keys 'barfoo'
|
||||||
tmux.until { |lines| assert_equal ' 0/3', lines[-2] }
|
tmux.until { |lines| assert_equal ' 0/3', lines[-2] }
|
||||||
|
|
||||||
tmux.send_keys :Escape, :a
|
tmux.send_keys :Escape, :a
|
||||||
|
ready.call('alt-a')
|
||||||
tmux.send_keys :Escape, :b
|
tmux.send_keys :Escape, :b
|
||||||
|
ready.call('alt-b')
|
||||||
|
|
||||||
wait do
|
wait do
|
||||||
assert_path_exists output
|
assert_path_exists output
|
||||||
assert_equal %w[
|
assert_equal %w[
|
||||||
/foo'bar/ /foo'bar/
|
/foo'bar/ /foo'barfoo'bar/
|
||||||
/foo"barfoo"bar/ /foo"barfoo"bar/
|
/foo"bar/ /foo"barfoo"bar/
|
||||||
/foo$barfoo$barfoo$bar/
|
/foo$barfoo$barfoo$bar/
|
||||||
], File.readlines(output, chomp: true)
|
], File.readlines(output, chomp: true)
|
||||||
end
|
end
|
||||||
@@ -993,17 +1006,28 @@ class TestGoFZF < TestBase
|
|||||||
|
|
||||||
def test_execute_multi
|
def test_execute_multi
|
||||||
output = '/tmp/fzf-test-execute-multi'
|
output = '/tmp/fzf-test-execute-multi'
|
||||||
opts = %[--multi --bind "alt-a:execute-multi(echo {}/{+} >> #{output})"]
|
opts = %[--multi --bind "alt-a:execute-multi(echo {}/{+} >> #{output})+change-header(alt-a),alt-b:change-header(alt-b)"]
|
||||||
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
|
||||||
|
ready = ->(s) { tmux.until { |lines| assert_includes lines[-3], s } }
|
||||||
|
|
||||||
tmux.until { |lines| assert_equal ' 4/4 (0)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 4/4 (0)', lines[-2] }
|
||||||
tmux.send_keys :Escape, :a
|
tmux.send_keys :Escape, :a
|
||||||
|
ready.call('alt-a')
|
||||||
|
tmux.send_keys :Escape, :b
|
||||||
|
ready.call('alt-b')
|
||||||
|
|
||||||
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] }
|
||||||
tmux.send_keys :Escape, :a
|
tmux.send_keys :Escape, :a
|
||||||
|
ready.call('alt-a')
|
||||||
|
tmux.send_keys :Escape, :b
|
||||||
|
ready.call('alt-b')
|
||||||
|
|
||||||
tmux.send_keys :Tab, :Tab
|
tmux.send_keys :Tab, :Tab
|
||||||
tmux.until { |lines| assert_equal ' 4/4 (3)', lines[-2] }
|
tmux.until { |lines| assert_equal ' 4/4 (3)', lines[-2] }
|
||||||
tmux.send_keys :Escape, :a
|
tmux.send_keys :Escape, :a
|
||||||
|
ready.call('alt-a')
|
||||||
wait do
|
wait do
|
||||||
assert_path_exists output
|
assert_path_exists output
|
||||||
assert_equal [
|
assert_equal [
|
||||||
@@ -1221,7 +1245,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_toggle_header
|
def test_toggle_header
|
||||||
tmux.send_keys "seq 4 | #{FZF} --header-lines 2 --header foo --bind space:toggle-header --header-first --height 10 --border", :Enter
|
tmux.send_keys "seq 4 | #{FZF} --header-lines 2 --header foo --bind space:toggle-header --header-first --height 10 --border rounded", :Enter
|
||||||
before = <<~OUTPUT
|
before = <<~OUTPUT
|
||||||
╭───────
|
╭───────
|
||||||
│
|
│
|
||||||
@@ -1695,7 +1719,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_info_hidden
|
def test_info_hidden
|
||||||
tmux.send_keys 'seq 10 | fzf --info=hidden', :Enter
|
tmux.send_keys 'seq 10 | fzf --info=hidden --no-separator', :Enter
|
||||||
tmux.until { |lines| assert_equal '> 1', lines[-2] }
|
tmux.until { |lines| assert_equal '> 1', lines[-2] }
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1893,7 +1917,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_reload
|
def test_reload
|
||||||
tmux.send_keys %(seq 1000 | #{FZF} --bind 'change:reload(seq {q}),a:reload(seq 100),b:reload:seq 200' --header-lines 2 --multi 2), :Enter
|
tmux.send_keys %(seq 1000 | #{FZF} --bind 'change:reload(seq $FZF_QUERY),a:reload(seq 100),b:reload:seq 200' --header-lines 2 --multi 2), :Enter
|
||||||
tmux.until { |lines| assert_equal 998, lines.match_count }
|
tmux.until { |lines| assert_equal 998, lines.match_count }
|
||||||
tmux.send_keys 'a'
|
tmux.send_keys 'a'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
@@ -2160,14 +2184,15 @@ class TestGoFZF < TestBase
|
|||||||
file = Tempfile.new('fzf-follow')
|
file = Tempfile.new('fzf-follow')
|
||||||
file.sync = true
|
file.sync = true
|
||||||
|
|
||||||
tmux.send_keys %(seq 100 | #{FZF} --preview 'tail -f "#{file.path}"' --preview-window follow --bind 'up:preview-up,down:preview-down,space:change-preview-window:follow|nofollow' --preview-window '~3'), :Enter
|
tmux.send_keys %(seq 100 | #{FZF} --preview 'echo start; tail -f "#{file.path}"' --preview-window follow --bind 'up:preview-up,down:preview-down,space:change-preview-window:follow|nofollow' --preview-window '~4'), :Enter
|
||||||
tmux.until { |lines| lines.item_count == 100 }
|
tmux.until { |lines| lines.item_count == 100 }
|
||||||
|
|
||||||
# Write to the temporary file, and check if the preview window is showing
|
# Write to the temporary file, and check if the preview window is showing
|
||||||
# the last line of the file
|
# the last line of the file
|
||||||
|
tmux.until { |lines| assert_includes lines[1], 'start' }
|
||||||
3.times { file.puts _1 } # header lines
|
3.times { file.puts _1 } # header lines
|
||||||
1000.times { file.puts _1 }
|
1000.times { file.puts _1 }
|
||||||
tmux.until { |lines| assert_includes lines[1], '/1003' }
|
tmux.until { |lines| assert_includes lines[1], '/1004' }
|
||||||
tmux.until { |lines| assert_includes lines[-2], '999' }
|
tmux.until { |lines| assert_includes lines[-2], '999' }
|
||||||
|
|
||||||
# Scroll the preview window and fzf should stop following the file content
|
# Scroll the preview window and fzf should stop following the file content
|
||||||
@@ -2175,7 +2200,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_includes lines[-2], '998' }
|
tmux.until { |lines| assert_includes lines[-2], '998' }
|
||||||
file.puts 'foo', 'bar'
|
file.puts 'foo', 'bar'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[1], '/1005'
|
assert_includes lines[1], '/1006'
|
||||||
assert_includes lines[-2], '998'
|
assert_includes lines[-2], '998'
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2188,7 +2213,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
file.puts 'baz'
|
file.puts 'baz'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[1], '/1006'
|
assert_includes lines[1], '/1007'
|
||||||
assert_includes lines[-2], 'baz'
|
assert_includes lines[-2], 'baz'
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2197,7 +2222,7 @@ class TestGoFZF < TestBase
|
|||||||
wait { assert_includes lines[-2], 'bar' }
|
wait { assert_includes lines[-2], 'bar' }
|
||||||
file.puts 'aaa'
|
file.puts 'aaa'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[1], '/1007'
|
assert_includes lines[1], '/1008'
|
||||||
assert_includes lines[-2], 'bar'
|
assert_includes lines[-2], 'bar'
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2206,7 +2231,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_includes lines[-2], 'aaa' }
|
tmux.until { |lines| assert_includes lines[-2], 'aaa' }
|
||||||
file.puts 'bbb'
|
file.puts 'bbb'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[1], '/1008'
|
assert_includes lines[1], '/1009'
|
||||||
assert_includes lines[-2], 'bbb'
|
assert_includes lines[-2], 'bbb'
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2214,7 +2239,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys :Space
|
tmux.send_keys :Space
|
||||||
file.puts 'ccc', 'ddd'
|
file.puts 'ccc', 'ddd'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[1], '/1010'
|
assert_includes lines[1], '/1011'
|
||||||
assert_includes lines[-2], 'bbb'
|
assert_includes lines[-2], 'bbb'
|
||||||
end
|
end
|
||||||
rescue StandardError
|
rescue StandardError
|
||||||
@@ -2438,6 +2463,84 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_equal 10, lines.match_count }
|
tmux.until { |lines| assert_equal 10, lines.match_count }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case1
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(sleep 2; seq 1000)'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 0, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case2
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload-sync(sleep 2; seq 1000)'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case3
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(sleep 2; seq 1000)+backward-delete-char'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 0, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case4
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload-sync(sleep 2; seq 1000)+backward-delete-char'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case5
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(echo xx; sleep 2; seq 1000)'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 1, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 1001, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case6
|
||||||
|
tmux.send_keys "seq 1000 | #{FZF} --disabled --bind 'change:reload:sleep 0.5; seq {q}'", :Enter
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
tmux.send_keys '9'
|
||||||
|
tmux.until { |lines| assert_equal 9, lines.match_count }
|
||||||
|
tmux.send_keys '9'
|
||||||
|
tmux.until { |lines| assert_equal 99, lines.match_count }
|
||||||
|
|
||||||
|
# TODO: How do we verify if an intermediate empty list is not shown?
|
||||||
|
end
|
||||||
|
|
||||||
def test_scroll_off
|
def test_scroll_off
|
||||||
tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
|
tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
|
||||||
tmux.until { |lines| assert_equal 1000, lines.item_count }
|
tmux.until { |lines| assert_equal 1000, lines.item_count }
|
||||||
@@ -2556,6 +2659,7 @@ class TestGoFZF < TestBase
|
|||||||
def test_change_preview_window_rotate
|
def test_change_preview_window_rotate
|
||||||
tmux.send_keys "seq 100 | #{FZF} --preview-window left,border-none --preview 'echo hello' --bind '" \
|
tmux.send_keys "seq 100 | #{FZF} --preview-window left,border-none --preview 'echo hello' --bind '" \
|
||||||
"a:change-preview-window(right|down|up|hidden|)'", :Enter
|
"a:change-preview-window(right|down|up|hidden|)'", :Enter
|
||||||
|
tmux.until { |lines| assert(lines.any? { _1.include?('100/100') }) }
|
||||||
3.times do
|
3.times do
|
||||||
tmux.until { |lines| lines[0].start_with?('hello') }
|
tmux.until { |lines| lines[0].start_with?('hello') }
|
||||||
tmux.send_keys 'a'
|
tmux.send_keys 'a'
|
||||||
@@ -2616,7 +2720,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_height_range_fit
|
def test_height_range_fit
|
||||||
tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border', :Enter
|
tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border rounded', :Enter
|
||||||
expected = <<~OUTPUT
|
expected = <<~OUTPUT
|
||||||
╭──────────
|
╭──────────
|
||||||
│ 3
|
│ 3
|
||||||
@@ -2629,7 +2733,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_height_range_fit_preview_above
|
def test_height_range_fit_preview_above
|
||||||
tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border --preview "seq {}" --preview-window up,60%', :Enter
|
tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border rounded --preview-window border-rounded --preview "seq {}" --preview-window up,60%', :Enter
|
||||||
expected = <<~OUTPUT
|
expected = <<~OUTPUT
|
||||||
╭──────────
|
╭──────────
|
||||||
│ ╭────────
|
│ ╭────────
|
||||||
@@ -2685,7 +2789,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_height_range_overflow
|
def test_height_range_overflow
|
||||||
tmux.send_keys 'seq 100 | fzf --height ~5 --info=inline --border', :Enter
|
tmux.send_keys 'seq 100 | fzf --height ~5 --info=inline --border rounded', :Enter
|
||||||
expected = <<~OUTPUT
|
expected = <<~OUTPUT
|
||||||
╭──────────────
|
╭──────────────
|
||||||
│ 2
|
│ 2
|
||||||
@@ -2750,7 +2854,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_labels_left
|
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.send_keys ': | fzf --border rounded --preview-window border-rounded --border-label foobar --border-label-pos 2 --preview : --preview-label barfoo --preview-label-pos 2', :Enter
|
||||||
tmux.until do
|
tmux.until do
|
||||||
assert_includes(_1[0], '╭foobar─')
|
assert_includes(_1[0], '╭foobar─')
|
||||||
assert_includes(_1[1], '╭barfoo─')
|
assert_includes(_1[1], '╭barfoo─')
|
||||||
@@ -2758,7 +2862,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_labels_right
|
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.send_keys ': | fzf --border rounded --preview-window border-rounded --border-label foobar --border-label-pos -2 --preview : --preview-label barfoo --preview-label-pos -2', :Enter
|
||||||
tmux.until do
|
tmux.until do
|
||||||
assert_includes(_1[0], '─foobar╮')
|
assert_includes(_1[0], '─foobar╮')
|
||||||
assert_includes(_1[1], '─barfoo╮')
|
assert_includes(_1[1], '─barfoo╮')
|
||||||
@@ -2766,13 +2870,34 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_labels_bottom
|
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.send_keys ': | fzf --border rounded --preview-window border-rounded --border-label foobar --border-label-pos 2:bottom --preview : --preview-label barfoo --preview-label-pos -2:bottom', :Enter
|
||||||
tmux.until do
|
tmux.until do
|
||||||
assert_includes(_1[-1], '╰foobar─')
|
assert_includes(_1[-1], '╰foobar─')
|
||||||
assert_includes(_1[-2], '─barfoo╯')
|
assert_includes(_1[-2], '─barfoo╯')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_labels_variables
|
||||||
|
tmux.send_keys ': | fzf --border --border-label foobar --preview "echo \$FZF_BORDER_LABEL // \$FZF_PREVIEW_LABEL" --preview-label barfoo --bind "space:change-border-label(barbaz)+change-preview-label(bazbar)+refresh-preview,enter:transform-border-label(echo 123)+transform-preview-label(echo 456)+refresh-preview"', :Enter
|
||||||
|
tmux.until do
|
||||||
|
assert_includes(_1[0], '─foobar─')
|
||||||
|
assert_includes(_1[1], '─barfoo─')
|
||||||
|
assert_includes(_1[2], ' foobar // barfoo ')
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until do
|
||||||
|
assert_includes(_1[0], '─barbaz─')
|
||||||
|
assert_includes(_1[1], '─bazbar─')
|
||||||
|
assert_includes(_1[2], ' barbaz // bazbar ')
|
||||||
|
end
|
||||||
|
tmux.send_keys :Enter
|
||||||
|
tmux.until do
|
||||||
|
assert_includes(_1[0], '─123─')
|
||||||
|
assert_includes(_1[1], '─456─')
|
||||||
|
assert_includes(_1[2], ' 123 // 456 ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_info_separator_unicode
|
def test_info_separator_unicode
|
||||||
tmux.send_keys 'seq 100 | fzf -q55', :Enter
|
tmux.send_keys 'seq 100 | fzf -q55', :Enter
|
||||||
tmux.until { assert_includes(_1[-2], ' 1/100 ─') }
|
tmux.until { assert_includes(_1[-2], ' 1/100 ─') }
|
||||||
@@ -2895,7 +3020,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_no_extra_newline_issue_3209
|
def test_no_extra_newline_issue_3209
|
||||||
tmux.send_keys(%(seq 100 | #{FZF} --height 10 --preview-window up,wrap --preview 'printf "─%.0s" $(seq 1 "$((FZF_PREVIEW_COLUMNS - 5))"); printf $"\\e[7m%s\\e[0m" title; echo; echo something'), :Enter)
|
tmux.send_keys(%(seq 100 | #{FZF} --height 10 --preview-window up,wrap,border-rounded --preview 'printf "─%.0s" $(seq 1 "$((FZF_PREVIEW_COLUMNS - 5))"); printf $"\\e[7m%s\\e[0m" title; echo; echo something'), :Enter)
|
||||||
expected = <<~OUTPUT
|
expected = <<~OUTPUT
|
||||||
╭──────────
|
╭──────────
|
||||||
│ ─────────
|
│ ─────────
|
||||||
@@ -2976,7 +3101,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
tmux.send_keys :t
|
tmux.send_keys :t
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[-2], '+T'
|
assert_includes lines[-2], '+t'
|
||||||
end
|
end
|
||||||
tmux.send_keys :BSpace
|
tmux.send_keys :BSpace
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
@@ -2988,7 +3113,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys '4'
|
tmux.send_keys '4'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_equal 28, lines.match_count
|
assert_equal 28, lines.match_count
|
||||||
refute_includes lines[-2], '+T'
|
refute_includes lines[-2], '+t'
|
||||||
end
|
end
|
||||||
tmux.send_keys :BSpace
|
tmux.send_keys :BSpace
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
@@ -2997,11 +3122,11 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
tmux.send_keys :t
|
tmux.send_keys :t
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[-2], '+T'
|
assert_includes lines[-2], '+t'
|
||||||
end
|
end
|
||||||
tmux.send_keys :Up
|
tmux.send_keys :Up
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
refute_includes lines[-2], '+T'
|
refute_includes lines[-2], '+t'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3056,6 +3181,11 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_delete_with_modifiers
|
def test_delete_with_modifiers
|
||||||
|
if ENV['GITHUB_ACTION']
|
||||||
|
# Expected: "[3]"
|
||||||
|
# Actual: "[]3;5~"
|
||||||
|
skip('CTRL-DELETE is not properly handled in GitHub Actions environment')
|
||||||
|
end
|
||||||
tmux.send_keys "seq 100 | #{FZF} --bind 'ctrl-delete:up+up,shift-delete:down,focus:transform-prompt:echo [{}]'", :Enter
|
tmux.send_keys "seq 100 | #{FZF} --bind 'ctrl-delete:up+up,shift-delete:down,focus:transform-prompt:echo [{}]'", :Enter
|
||||||
tmux.until { |lines| assert_equal 100, lines.item_count }
|
tmux.until { |lines| assert_equal 100, lines.item_count }
|
||||||
tmux.send_keys 'C-Delete'
|
tmux.send_keys 'C-Delete'
|
||||||
@@ -3276,7 +3406,10 @@ module CompletionTest
|
|||||||
tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab
|
tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab
|
||||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys 'foobar$'
|
tmux.send_keys 'foobar$'
|
||||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
tmux.until do |lines|
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
assert lines.any_include?('> /tmp/fzf test/foobar')
|
||||||
|
end
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until(true) { |lines| assert_equal 'cat /tmp/fzf\ test/foobar', lines[-1] }
|
tmux.until(true) { |lines| assert_equal 'cat /tmp/fzf\ test/foobar', lines[-1] }
|
||||||
|
|
||||||
|
@@ -94,6 +94,7 @@ done
|
|||||||
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
||||||
if [ -f "$bind_file" ]; then
|
if [ -f "$bind_file" ]; then
|
||||||
remove_line "$bind_file" "fzf_key_bindings"
|
remove_line "$bind_file" "fzf_key_bindings"
|
||||||
|
remove_line "$bind_file" "fzf --fish | source"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -d "${fish_dir}/functions" ]; then
|
if [ -d "${fish_dir}/functions" ]; then
|
||||||
|
Reference in New Issue
Block a user