mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-12 13:23:48 -05:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2ae1b249c | ||
|
|
92b7efafca | ||
|
|
f092e4038f | ||
|
|
aa5dae391b | ||
|
|
08a6fd4ad4 | ||
|
|
a61150a96c | ||
|
|
0f9cb5590e | ||
|
|
c0a83b27eb | ||
|
|
f79b1f71b8 | ||
|
|
8e027c445f | ||
|
|
dda3e3c39a | ||
|
|
fd5157998c | ||
|
|
e0217e8c79 | ||
|
|
3ab1c42266 | ||
|
|
d1676776aa | ||
|
|
bdde69d011 | ||
|
|
6dec42a33a | ||
|
|
199bc3f0ad | ||
|
|
17dd833925 | ||
|
|
4ec144c969 | ||
|
|
3e36f2b0ac | ||
|
|
07a03b3e73 | ||
|
|
c33258832e | ||
|
|
a7aa08ce07 | ||
|
|
06d63a862e | ||
|
|
43d1c4c4b5 | ||
|
|
f81feb1e69 | ||
|
|
01cf01e084 | ||
|
|
97a725fbd0 | ||
|
|
ace92ba281 | ||
|
|
d631c76e8d | ||
|
|
e6d33f77da | ||
|
|
a6d3e3687b | ||
|
|
08c2bcb952 | ||
|
|
98ca4bdede | ||
|
|
3f8e741562 | ||
|
|
6e464ebd9b | ||
|
|
c329279339 | ||
|
|
cf04753ad7 | ||
|
|
69e7eab11f | ||
|
|
dea206b023 | ||
|
|
5deaf58928 | ||
|
|
15e2952a2b | ||
|
|
a9fba1c849 | ||
|
|
71e573d082 |
24
.rubocop.yml
Normal file
24
.rubocop.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
Layout/LineLength:
|
||||
Enabled: false
|
||||
Metrics:
|
||||
Enabled: false
|
||||
Lint/ShadowingOuterLocalVariable:
|
||||
Enabled: false
|
||||
Style/MethodCallWithArgsParentheses:
|
||||
Enabled: true
|
||||
IgnoredMethods:
|
||||
- assert
|
||||
- exit
|
||||
- paste
|
||||
- puts
|
||||
- raise
|
||||
- refute
|
||||
- require
|
||||
- send_keys
|
||||
IgnoredPatterns:
|
||||
- ^assert_
|
||||
- ^refute_
|
||||
Style/NumericPredicate:
|
||||
Enabled: false
|
||||
Style/WordArray:
|
||||
MinSize: 1
|
||||
@@ -1,22 +1,28 @@
|
||||
language: go
|
||||
go:
|
||||
- "1.14"
|
||||
env: GO111MODULE=on
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
dist: bionic # For fish >= 2.3.0 string builtin
|
||||
dist: bionic
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- fish
|
||||
- zsh
|
||||
sources:
|
||||
sourceline: ppa:fish-shell/release-3
|
||||
homebrew:
|
||||
packages:
|
||||
- fish
|
||||
- tmux
|
||||
update: true
|
||||
install: gem install minitest rubocop rubocop-minitest rubocop-performance
|
||||
script:
|
||||
- make test
|
||||
# LC_ALL=C to avoid escape codes in
|
||||
# printf %q $'\355\205\214\354\212\244\355\212\270' on macOS. Bash on
|
||||
# macOS is built without HANDLE_MULTIBYTE?
|
||||
- make install && ./install --all && LC_ALL=C tmux new-session -d && ruby test/test_go.rb --verbose
|
||||
- rubocop --require rubocop-minitest --require rubocop-performance
|
||||
|
||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -1,6 +1,49 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.22.0
|
||||
------
|
||||
- Added more options for `--bind`
|
||||
- `backward-eof` event
|
||||
```sh
|
||||
# Aborts when you delete backward when the query prompt is already empty
|
||||
fzf --bind backward-eof:abort
|
||||
```
|
||||
- `refresh-preview` action
|
||||
```sh
|
||||
# Rerun preview command when you hit '?'
|
||||
fzf --preview 'echo $RANDOM' --bind '?:refresh-preview'
|
||||
```
|
||||
- `preview` action
|
||||
```sh
|
||||
# Default preview command with an extra preview binding
|
||||
fzf --preview 'file {}' --bind '?:preview:cat {}'
|
||||
|
||||
# A preview binding with no default preview command
|
||||
# (Preview window is initially empty)
|
||||
fzf --bind '?:preview:cat {}'
|
||||
|
||||
# Preview window hidden by default, it appears when you first hit '?'
|
||||
fzf --bind '?:preview:cat {}' --preview-window hidden
|
||||
```
|
||||
- Added preview window option for setting the initial scroll offset
|
||||
```sh
|
||||
# Initial scroll offset is set to the line number of each line of
|
||||
# git grep output *minus* 5 lines
|
||||
git grep --line-number '' |
|
||||
fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5
|
||||
```
|
||||
- Added support for ANSI colors in `--prompt` string
|
||||
- Smart match of accented characters
|
||||
- An unaccented character in the query string will match both accented and
|
||||
unaccented characters, while an accented character will only match
|
||||
accented characters. This is similar to how "smart-case" match works.
|
||||
- Vim plugin
|
||||
- `tmux` layout option for using fzf-tmux
|
||||
```vim
|
||||
let g:fzf_layout = { 'tmux': '-p90%,60%' }
|
||||
```
|
||||
|
||||
0.21.1
|
||||
------
|
||||
- Shell extension
|
||||
|
||||
2
Makefile
2
Makefile
@@ -6,7 +6,7 @@ ROOT_DIR := $(shell dirname $(MAKEFILE))
|
||||
SOURCES := $(wildcard *.go src/*.go src/*/*.go) $(MAKEFILE)
|
||||
|
||||
REVISION := $(shell git log -n 1 --pretty=format:%h -- $(SOURCES))
|
||||
BUILD_FLAGS := -a -ldflags "-X main.revision=$(REVISION) -w -extldflags=$(LDFLAGS)" -tags "$(TAGS)"
|
||||
BUILD_FLAGS := -a -ldflags "-X main.revision=$(REVISION) -w '-extldflags=$(LDFLAGS)'" -tags "$(TAGS)"
|
||||
|
||||
BINARY32 := fzf-$(GOOS)_386
|
||||
BINARY64 := fzf-$(GOOS)_amd64
|
||||
|
||||
@@ -128,7 +128,7 @@ let g:fzf_action = {
|
||||
|
||||
" Default fzf layout
|
||||
" - down / up / left / right
|
||||
let g:fzf_layout = { 'down': '~40%' }
|
||||
let g:fzf_layout = { 'down': '40%' }
|
||||
|
||||
" You can set up fzf window using a Vim command (Neovim or latest Vim 8 required)
|
||||
let g:fzf_layout = { 'window': 'enew' }
|
||||
@@ -274,6 +274,7 @@ The following table summarizes the available options.
|
||||
| `options` | string/list | Options to fzf |
|
||||
| `dir` | string | Working directory |
|
||||
| `up`/`down`/`left`/`right` | number/string | (Layout) Window position and size (e.g. `20`, `50%`) |
|
||||
| `tmux` | string | (Layout) fzf-tmux options (e.g. `-p90%,60%`) |
|
||||
| `window` (Vim 8 / Neovim) | string | (Layout) Command to open fzf window (e.g. `vertical aboveleft 30new`) |
|
||||
| `window` (Vim 8 / Neovim) | dict | (Layout) Popup window settings (e.g. `{'width': 0.9, 'height': 0.6}`) |
|
||||
|
||||
@@ -397,13 +398,25 @@ The latest versions of Vim and Neovim include builtin terminal emulator
|
||||
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||
```
|
||||
|
||||
Alternatively, you can make fzf open in a tmux popup window (requires tmux 3.2
|
||||
or above) by putting fzf-tmux options in `tmux` key.
|
||||
|
||||
```vim
|
||||
" See `man fzf-tmux` for available options
|
||||
if exists('$TMUX')
|
||||
let g:fzf_layout = { 'tmux': '-p90%,60%' }
|
||||
else
|
||||
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||
endif
|
||||
```
|
||||
|
||||
#### Hide statusline
|
||||
|
||||
When fzf starts in a terminal buffer, the file type of the buffer is set to
|
||||
`fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
|
||||
the window.
|
||||
|
||||
For example, if you use the default layout (`{'down': '~40%'}`) on Neovim, you
|
||||
For example, if you use the default layout (`{'down': '40%'}`) on Neovim, you
|
||||
might want to temporarily disable the statusline for a cleaner look.
|
||||
|
||||
```vim
|
||||
|
||||
40
README.md
40
README.md
@@ -104,16 +104,18 @@ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
|
||||
|
||||
### Using Linux package managers
|
||||
|
||||
| Distro | Command |
|
||||
| --- | --- |
|
||||
| Alpine Linux | `sudo apk add fzf` |
|
||||
| Arch Linux | `sudo pacman -S fzf` |
|
||||
| Debian | `sudo apt-get install fzf` |
|
||||
| Fedora | `sudo dnf install fzf` |
|
||||
| FreeBSD | `pkg install fzf` |
|
||||
| NixOS | `nix-env -iA nixpkgs.fzf` |
|
||||
| openSUSE | `sudo zypper install fzf` |
|
||||
| OpenBSD | `pkg_add fzf` |
|
||||
| Package Manager | Linux Distribution | Command |
|
||||
| --- | --- | --- |
|
||||
| APK | Alpine Linux | `sudo apk add fzf` |
|
||||
| APT | Debian 9+/Ubuntu 19.10+ | `sudo apt-get install fzf` |
|
||||
| Conda | | `conda install -c conda-forge fzf` |
|
||||
| DNF | Fedora | `sudo dnf install fzf` |
|
||||
| Nix | NixOS, etc. | `nix-env -iA nixpkgs.fzf` |
|
||||
| Pacman | Arch Linux | `sudo pacman -S fzf` |
|
||||
| pkg | FreeBSD | `pkg install fzf` |
|
||||
| pkg_add | OpenBSD | `pkg_add fzf` |
|
||||
| XBPS | Void Linux | `sudo xbps-install -S fzf` |
|
||||
| Zypper | openSUSE | `sudo zypper install fzf` |
|
||||
|
||||
Shell extensions (key bindings and fuzzy auto-completion) and Vim/Neovim
|
||||
plugin may or may not be enabled by default depending on the package manager.
|
||||
@@ -528,9 +530,10 @@ See *KEY BINDINGS* section of the man page for details.
|
||||
|
||||
### Preview window
|
||||
|
||||
When `--preview` option is set, fzf automatically starts an external process with
|
||||
the current line as the argument and shows the result in the split window. Your
|
||||
`$SHELL` is used to execute the command with `$SHELL -c COMMAND`.
|
||||
When the `--preview` option is set, fzf automatically starts an external process
|
||||
with the current line as the argument and shows the result in the split window.
|
||||
Your `$SHELL` is used to execute the command with `$SHELL -c COMMAND`.
|
||||
The window can be scrolled using the mouse or custom key bindings.
|
||||
|
||||
```bash
|
||||
# {} is replaced to the single-quoted string of the focused line
|
||||
@@ -546,13 +549,12 @@ fzf --preview 'head -100 {}'
|
||||
```
|
||||
|
||||
Preview window supports ANSI colors, so you can use any program that
|
||||
syntax-highlights the content of a file.
|
||||
|
||||
- Bat: https://github.com/sharkdp/bat
|
||||
- Highlight: http://www.andre-simon.de/doku/highlight/en/highlight.php
|
||||
syntax-highlights the content of a file, such as
|
||||
[Bat](https://github.com/sharkdp/bat) or
|
||||
[Highlight](http://www.andre-simon.de/doku/highlight/en/highlight.php):
|
||||
|
||||
```bash
|
||||
fzf --preview 'bat --style=numbers --color=always {} | head -500'
|
||||
fzf --preview 'bat --style=numbers --color=always --line-range :500 {}'
|
||||
```
|
||||
|
||||
You can customize the size, position, and border of the preview window using
|
||||
@@ -581,7 +583,7 @@ not a good idea to add `--preview` option to your `$FZF_DEFAULT_OPTS`**.
|
||||
# *********************
|
||||
# ** DO NOT DO THIS! **
|
||||
# *********************
|
||||
export FZF_DEFAULT_OPTS='--preview "bat --style=numbers --color=always {} | head -500"'
|
||||
export FZF_DEFAULT_OPTS='--preview "bat --style=numbers --color=always --line-range :500 {}"'
|
||||
|
||||
# bat doesn't work with any input other than the list of files
|
||||
ps -ef | fzf
|
||||
|
||||
12
bin/fzf-tmux
12
bin/fzf-tmux
@@ -77,7 +77,7 @@ while [[ $# -gt 0 ]]; do
|
||||
if [[ ${#arg} -gt 2 ]]; then
|
||||
size="${arg:2}"
|
||||
else
|
||||
if [[ "$1" =~ ^[0-9%,C]+$ ]]; then
|
||||
if [[ "$1" =~ ^[0-9%,]+$ ]] || [[ "$1" =~ ^[A-Z]$ ]]; then
|
||||
size="$1"
|
||||
shift
|
||||
else
|
||||
@@ -138,9 +138,9 @@ fi
|
||||
# --height option is not allowed
|
||||
args=("--no-height" "${args[@]}")
|
||||
|
||||
# Handle zoomed tmux pane by moving it to a temp window
|
||||
if tmux list-panes -F '#F' | grep -q Z; then
|
||||
zoomed=1
|
||||
# Handle zoomed tmux pane without popup options by moving it to a temp window
|
||||
if [[ ! "$opt" =~ "-K -E" ]] && tmux list-panes -F '#F' | grep -q Z; then
|
||||
zoomed_without_popup=1
|
||||
original_window=$(tmux display-message -p "#{window_id}")
|
||||
tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c fzf-tmux is running\\r\"; done; done'")
|
||||
tmux swap-pane -t $tmp_window \; select-window -t $tmp_window
|
||||
@@ -162,8 +162,8 @@ cleanup() {
|
||||
eval "tmux ${tmux_win_opts[@]}"
|
||||
fi
|
||||
|
||||
# Remove temp window if we were zoomed
|
||||
if [[ -n "$zoomed" ]]; then
|
||||
# Remove temp window if we were zoomed without popup options
|
||||
if [[ -n "$zoomed_without_popup" ]]; then
|
||||
tmux display-message -p "#{window_id}" > /dev/null
|
||||
tmux swap-pane -t $original_window \; \
|
||||
select-window -t $original_window \; \
|
||||
|
||||
104
doc/fzf.txt
104
doc/fzf.txt
@@ -1,12 +1,14 @@
|
||||
fzf.txt fzf Last change: February 14 2020
|
||||
fzf.txt fzf Last change: April 4 2020
|
||||
FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
|
||||
==============================================================================
|
||||
|
||||
FZF Vim integration
|
||||
Installation
|
||||
Summary
|
||||
:FZF[!]
|
||||
Configuration
|
||||
Examples
|
||||
Explanation of g:fzf_colors
|
||||
fzf#run
|
||||
fzf#wrap
|
||||
Tips
|
||||
@@ -19,6 +21,46 @@ FZF VIM INTEGRATION *fzf-vim-integration*
|
||||
==============================================================================
|
||||
|
||||
|
||||
INSTALLATION *fzf-installation*
|
||||
==============================================================================
|
||||
|
||||
Once you have fzf installed, you can enable it inside Vim simply by adding the
|
||||
directory to 'runtimepath' in your Vim configuration file. The path may differ
|
||||
depending on the package manager.
|
||||
>
|
||||
" If installed using Homebrew
|
||||
set rtp+=/usr/local/opt/fzf
|
||||
|
||||
" If installed using git
|
||||
set rtp+=~/.fzf
|
||||
<
|
||||
If you use {vim-plug}{1}, the same can be written as:
|
||||
>
|
||||
" If installed using Homebrew
|
||||
Plug '/usr/local/opt/fzf'
|
||||
|
||||
" If installed using git
|
||||
Plug '~/.fzf'
|
||||
<
|
||||
But if you want the latest Vim plugin file from GitHub rather than the one
|
||||
included in the package, write:
|
||||
>
|
||||
Plug 'junegunn/fzf'
|
||||
<
|
||||
The Vim plugin will pick up fzf binary available on the system. If fzf is not
|
||||
found on `$PATH`, it will ask you if it should download the latest binary for
|
||||
you.
|
||||
|
||||
To make sure that you have the latest version of the binary, set up
|
||||
post-update hook like so:
|
||||
|
||||
*fzf#install*
|
||||
>
|
||||
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
|
||||
<
|
||||
{1} https://github.com/junegunn/vim-plug
|
||||
|
||||
|
||||
SUMMARY *fzf-summary*
|
||||
==============================================================================
|
||||
|
||||
@@ -38,12 +80,12 @@ the basic file selector command built on top of them.
|
||||
- Basic fuzzy file selector
|
||||
- A reference implementation for those who don't want to write VimScript to
|
||||
implement custom commands
|
||||
- If you're looking for more such commands, check out {fzf.vim}{1} project.
|
||||
- If you're looking for more such commands, check out {fzf.vim}{2} project.
|
||||
|
||||
The most important of all is `fzf#run`, but it would be easier to understand
|
||||
the whole if we start off with `:FZF` command.
|
||||
|
||||
{1} https://github.com/junegunn/fzf.vim
|
||||
{2} https://github.com/junegunn/fzf.vim
|
||||
|
||||
|
||||
:FZF[!]
|
||||
@@ -144,6 +186,51 @@ Examples~
|
||||
let g:fzf_history_dir = '~/.local/share/fzf-history'
|
||||
<
|
||||
|
||||
Explanation of g:fzf_colors~
|
||||
*fzf-explanation-of-gfzfcolors*
|
||||
|
||||
`g:fzf_colors` is a dictionary mapping fzf elements to a color specification
|
||||
list:
|
||||
>
|
||||
element: [ component, group1 [, group2, ...] ]
|
||||
<
|
||||
- `element` is an fzf element to apply a color to:
|
||||
|
||||
----------------------+------------------------------------------------------
|
||||
Element | Description ~
|
||||
----------------------+------------------------------------------------------
|
||||
`fg` / `bg` / `hl` | Item (foreground / background / highlight)
|
||||
`fg+` / `bg+` / `hl+` | Current item (foreground / background / highlight)
|
||||
`hl` / `hl+` | Highlighted substrings (normal / current)
|
||||
`gutter` | Background of the gutter on the left
|
||||
`pointer` | Pointer to the current line ( `>` )
|
||||
`marker` | Multi-select marker ( `>` )
|
||||
`border` | Border around the window ( `--border` and `--preview` )
|
||||
`header` | Header ( `--header` or `--header-lines` )
|
||||
`info` | Info line (match counters)
|
||||
`spinner` | Streaming input indicator
|
||||
`prompt` | Prompt before query ( `> ` )
|
||||
----------------------+------------------------------------------------------
|
||||
- `component` specifies the component (`fg` / `bg`) from which to extract the
|
||||
color when considering each of the following highlight groups
|
||||
- `group1 [, group2, ...]` is a list of highlight groups that are searched (in
|
||||
order) for a matching color definition
|
||||
|
||||
For example, consider the following specification:
|
||||
>
|
||||
'prompt': ['fg', 'Conditional', 'Comment'],
|
||||
<
|
||||
This means we color the prompt - using the `fg` attribute of the `Conditional`
|
||||
if it exists, - otherwise use the `fg` attribute of the `Comment` highlight
|
||||
group if it exists, - otherwise fall back to the default color settings for
|
||||
the prompt.
|
||||
|
||||
You can examine the color option generated according the setting by printing
|
||||
the result of `fzf#wrap()` function like so:
|
||||
>
|
||||
:echo fzf#wrap()
|
||||
<
|
||||
|
||||
FZF#RUN
|
||||
==============================================================================
|
||||
|
||||
@@ -203,6 +290,7 @@ The following table summarizes the available options.
|
||||
`options` | string/list | Options to fzf
|
||||
`dir` | string | Working directory
|
||||
`up` / `down` / `left` / `right` | number/string | (Layout) Window position and size (e.g. `20` , `50%` )
|
||||
`tmux` | string | (Layout) fzf-tmux options (e.g. `-p90%,60%` )
|
||||
`window` (Vim 8 / Neovim) | string | (Layout) Command to open fzf window (e.g. `vertical aboveleft 30new` )
|
||||
`window` (Vim 8 / Neovim) | dict | (Layout) Popup window settings (e.g. `{'width': 0.9, 'height': 0.6}` )
|
||||
---------------------------+---------------+----------------------------------------------------------------------
|
||||
@@ -320,6 +408,16 @@ Starting fzf in a popup window~
|
||||
" - 'rounded' / 'sharp' / 'horizontal' / 'vertical' / 'top' / 'bottom' / 'left' / 'right'
|
||||
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||
<
|
||||
Alternatively, you can make fzf open in a tmux popup window (requires tmux 3.2
|
||||
or above) by putting fzf-tmux options in `tmux` key.
|
||||
>
|
||||
" See `man fzf-tmux` for available options
|
||||
if exists('$TMUX')
|
||||
let g:fzf_layout = { 'tmux': '-p90%,60%' }
|
||||
else
|
||||
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||
endif
|
||||
<
|
||||
|
||||
Hide statusline~
|
||||
*fzf-hide-statusline*
|
||||
|
||||
8
install
8
install
@@ -2,7 +2,7 @@
|
||||
|
||||
set -u
|
||||
|
||||
version=0.21.1
|
||||
version=0.22.0
|
||||
auto_completion=
|
||||
key_bindings=
|
||||
update_config=2
|
||||
@@ -384,7 +384,11 @@ fi
|
||||
|
||||
if [ $update_config -eq 1 ]; then
|
||||
echo 'Finished. Restart your shell or reload config file.'
|
||||
[[ "$shells" =~ bash ]] && echo ' source ~/.bashrc # bash'
|
||||
if [[ "$shells" =~ bash ]]; then
|
||||
echo -n ' source ~/.bashrc # bash'
|
||||
[[ "$archi" =~ Darwin ]] && echo -n ' (.bashrc should be loaded from .bash_profile)'
|
||||
echo
|
||||
fi
|
||||
[[ "$shells" =~ zsh ]] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh"
|
||||
[[ "$shells" =~ fish ]] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish'
|
||||
echo
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
$version="0.21.1"
|
||||
$version="0.22.0"
|
||||
|
||||
if ([Environment]::Is64BitProcess) {
|
||||
$binary_arch="amd64"
|
||||
@@ -57,7 +57,7 @@ function download {
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
(New-Object Net.WebClient).DownloadFile($url, $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$temp"))
|
||||
if ($?) {
|
||||
(Expand-Archive -Path $temp -DestinationPath .); (Remove-Item $temp)
|
||||
(Microsoft.PowerShell.Archive\Expand-Archive -Path $temp -DestinationPath .); (Remove-Item $temp)
|
||||
} else {
|
||||
$binary_error="Failed to download with powershell"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf-tmux 1 "Apr 2020" "fzf 0.21.1" "fzf-tmux - open fzf in tmux split pane"
|
||||
.TH fzf-tmux 1 "Aug 2020" "fzf 0.22.0" "fzf-tmux - open fzf in tmux split pane"
|
||||
|
||||
.SH NAME
|
||||
fzf-tmux - open fzf in tmux split pane
|
||||
|
||||
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf 1 "Apr 2020" "fzf 0.21.1" "fzf - a command-line fuzzy finder"
|
||||
.TH fzf 1 "Aug 2020" "fzf 0.22.0" "fzf - a command-line fuzzy finder"
|
||||
|
||||
.SH NAME
|
||||
fzf - a command-line fuzzy finder
|
||||
@@ -381,7 +381,7 @@ Preview window will be updated even when there is no match for the current
|
||||
query if any of the placeholder expressions evaluates to a non-empty string.
|
||||
.RE
|
||||
.TP
|
||||
.BI "--preview-window=" "[POSITION][:SIZE[%]][:noborder][:wrap][:hidden]"
|
||||
.BI "--preview-window=" "[POSITION][:SIZE[%]][:noborder][:wrap][:hidden][:+SCROLL[-OFFSET]]"
|
||||
Determines the layout of the preview window. If the argument contains
|
||||
\fB:hidden\fR, the preview window will be hidden by default until
|
||||
\fBtoggle-preview\fR action is triggered. Long lines are truncated by default.
|
||||
@@ -390,6 +390,12 @@ Line wrap can be enabled with \fB:wrap\fR flag.
|
||||
If size is given as 0, preview window will not be visible, but fzf will still
|
||||
execute the command in the background.
|
||||
|
||||
\fB+SCROLL[-OFFSET]\fR determines the initial scroll offset of the preview
|
||||
window. \fBSCROLL\fR can be either a numeric integer or a single-field index
|
||||
expression that refers to a numeric integer. The optional \fB-OFFSET\fR part is
|
||||
for adjusting the base offset so that you can see the text above it. It should
|
||||
be given as a numeric integer.
|
||||
|
||||
.RS
|
||||
.B POSITION: (default: right)
|
||||
\fBup
|
||||
@@ -400,8 +406,15 @@ execute the command in the background.
|
||||
|
||||
.RS
|
||||
e.g.
|
||||
\fBfzf --preview="head {}" --preview-window=up:30%
|
||||
fzf --preview="file {}" --preview-window=down:1\fR
|
||||
\fB# Non-default scroll window positions and sizes
|
||||
fzf --preview="head {}" --preview-window=up:30%
|
||||
fzf --preview="file {}" --preview-window=down:1
|
||||
|
||||
# Initial scroll offset is set to the line number of each line of
|
||||
# git grep output *minus* 5 lines
|
||||
git grep --line-number '' |
|
||||
fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5\fR
|
||||
|
||||
.RE
|
||||
.SS Scripting
|
||||
.TP
|
||||
@@ -635,12 +648,23 @@ e.g.
|
||||
or any single character
|
||||
|
||||
.SS AVAILABLE EVENTS:
|
||||
\fIchange\fR (triggered whenever the query string is changed)
|
||||
.br
|
||||
\fIchange\fR
|
||||
.RS
|
||||
Triggered whenever the query string is changed
|
||||
|
||||
e.g.
|
||||
\fB# Moves cursor to the top (or bottom depending on --layout) whenever the query is changed
|
||||
fzf --bind change:top\fR
|
||||
e.g.
|
||||
\fB# Moves cursor to the top (or bottom depending on --layout) whenever the query is changed
|
||||
fzf --bind change:top\fR
|
||||
.RE
|
||||
|
||||
\fIbackward-eof\fR
|
||||
.RS
|
||||
Triggered when the query string is already empty and you try to delete it
|
||||
backward.
|
||||
|
||||
e.g.
|
||||
\fBfzf --bind backward-eof:abort\fR
|
||||
.RE
|
||||
|
||||
.SS AVAILABLE ACTIONS:
|
||||
A key or an event can be bound to one or more of the following actions.
|
||||
@@ -679,12 +703,14 @@ A key or an event can be bound to one or more of the following actions.
|
||||
\fBpage-up\fR \fIpgup\fR
|
||||
\fBhalf-page-down\fR
|
||||
\fBhalf-page-up\fR
|
||||
\fBpreview(...)\fR (see below for the details)
|
||||
\fBpreview-down\fR \fIshift-down\fR
|
||||
\fBpreview-up\fR \fIshift-up\fR
|
||||
\fBpreview-page-down\fR
|
||||
\fBpreview-page-up\fR
|
||||
\fBprevious-history\fR (\fIctrl-p\fR on \fB--history\fR)
|
||||
\fBprint-query\fR (print query and exit)
|
||||
\fBrefresh-preview\fR
|
||||
\fBreload(...)\fR (see below for the details)
|
||||
\fBreplace-query\fR (replace query string with the current selection)
|
||||
\fBselect-all\fR (select all matches)
|
||||
@@ -771,6 +797,24 @@ e.g.
|
||||
fzf --bind "change:reload:$RG_PREFIX {q} || true" \\
|
||||
--ansi --phony --query "$INITIAL_QUERY"\fR
|
||||
|
||||
.SS PREVIEW BINDING
|
||||
|
||||
With \fBpreview(...)\fR action, you can specify multiple different preview
|
||||
commands in addition to the default preview command given by \fB--preview\fR
|
||||
option.
|
||||
|
||||
e.g.
|
||||
|
||||
# Default preview command with an extra preview binding
|
||||
fzf --preview 'file {}' --bind '?:preview:cat {}'
|
||||
|
||||
# A preview binding with no default preview command
|
||||
# (Preview window is initially empty)
|
||||
fzf --bind '?:preview:cat {}'
|
||||
|
||||
# Preview window hidden by default, it appears when you first hit '?'
|
||||
fzf --bind '?:preview:cat {}' --preview-window hidden
|
||||
|
||||
.SH AUTHOR
|
||||
Junegunn Choi (\fIjunegunn.c@gmail.com\fR)
|
||||
|
||||
|
||||
106
plugin/fzf.vim
106
plugin/fzf.vim
@@ -116,7 +116,7 @@ function! s:fzf_tempname()
|
||||
endfunction
|
||||
|
||||
let s:default_layout = { 'down': '~40%' }
|
||||
let s:layout_keys = ['window', 'up', 'down', 'left', 'right']
|
||||
let s:layout_keys = ['window', 'tmux', 'up', 'down', 'left', 'right']
|
||||
let s:fzf_go = s:base_dir.'/bin/fzf'
|
||||
let s:fzf_tmux = s:base_dir.'/bin/fzf-tmux'
|
||||
|
||||
@@ -191,21 +191,6 @@ function! s:escape(path)
|
||||
return s:is_win ? escape(path, '$') : path
|
||||
endfunction
|
||||
|
||||
" Upgrade legacy options
|
||||
function! s:upgrade(dict)
|
||||
let copy = copy(a:dict)
|
||||
if has_key(copy, 'tmux')
|
||||
let copy.down = remove(copy, 'tmux')
|
||||
endif
|
||||
if has_key(copy, 'tmux_height')
|
||||
let copy.down = remove(copy, 'tmux_height')
|
||||
endif
|
||||
if has_key(copy, 'tmux_width')
|
||||
let copy.right = remove(copy, 'tmux_width')
|
||||
endif
|
||||
return copy
|
||||
endfunction
|
||||
|
||||
function! s:error(msg)
|
||||
echohl ErrorMsg
|
||||
echom a:msg
|
||||
@@ -251,9 +236,13 @@ function! s:common_sink(action, lines) abort
|
||||
endif
|
||||
try
|
||||
let empty = empty(s:fzf_expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified
|
||||
let autochdir = &autochdir
|
||||
set noautochdir
|
||||
" Preserve the current working directory in case it's changed during
|
||||
" the execution (e.g. `set autochdir` or `autocmd BufEnter * lcd ...`)
|
||||
let cwd = exists('w:fzf_pushd') ? w:fzf_pushd.dir : expand('%:p:h')
|
||||
for item in a:lines
|
||||
if item[0] != '~' && item !~ (s:is_win ? '^[A-Z]:\' : '^/')
|
||||
let item = join([cwd, item], (s:is_win ? '\' : '/'))
|
||||
endif
|
||||
if empty
|
||||
execute 'e' s:escape(item)
|
||||
let empty = 0
|
||||
@@ -267,7 +256,6 @@ function! s:common_sink(action, lines) abort
|
||||
endfor
|
||||
catch /^Vim:Interrupt$/
|
||||
finally
|
||||
let &autochdir = autochdir
|
||||
silent! autocmd! fzf_swap
|
||||
endtry
|
||||
endfunction
|
||||
@@ -312,7 +300,7 @@ function! fzf#wrap(...)
|
||||
let expects = map(copy(args), 'type(v:val)')
|
||||
let tidx = 0
|
||||
for arg in copy(a:000)
|
||||
let tidx = index(expects, type(arg), tidx)
|
||||
let tidx = index(expects, type(arg) == 6 ? type(0) : type(arg), tidx)
|
||||
if tidx < 0
|
||||
throw 'Invalid arguments (expected: [name string] [opts dict] [fullscreen boolean])'
|
||||
endif
|
||||
@@ -384,7 +372,7 @@ function! fzf#run(...) abort
|
||||
try
|
||||
let [shell, shellslash, shellcmdflag, shellxquote] = s:use_sh()
|
||||
|
||||
let dict = exists('a:1') ? s:upgrade(a:1) : {}
|
||||
let dict = exists('a:1') ? copy(a:1) : {}
|
||||
let temps = { 'result': s:fzf_tempname() }
|
||||
let optstr = s:evaluate_opts(get(dict, 'options', ''))
|
||||
try
|
||||
@@ -416,7 +404,7 @@ try
|
||||
let prefix = ''
|
||||
endif
|
||||
|
||||
let prefer_tmux = get(g:, 'fzf_prefer_tmux', 0)
|
||||
let prefer_tmux = get(g:, 'fzf_prefer_tmux', 0) || has_key(dict, 'tmux')
|
||||
let use_height = has_key(dict, 'down') && !has('gui_running') &&
|
||||
\ !(has('nvim') || s:is_win || has('win32unix') || s:present(dict, 'up', 'left', 'right', 'window')) &&
|
||||
\ executable('tput') && filereadable('/dev/tty')
|
||||
@@ -424,7 +412,7 @@ try
|
||||
let has_nvim_term = has('nvim-0.2.1') || has('nvim') && !s:is_win
|
||||
let use_term = has_nvim_term ||
|
||||
\ has_vim8_term && !has('win32unix') && (has('gui_running') || s:is_win || !use_height && s:present(dict, 'down', 'up', 'left', 'right', 'window'))
|
||||
let use_tmux = (!use_height && !use_term || prefer_tmux) && !has('win32unix') && s:tmux_enabled() && s:splittable(dict)
|
||||
let use_tmux = (has_key(dict, 'tmux') || (!use_height && !use_term || prefer_tmux) && !has('win32unix') && s:splittable(dict)) && s:tmux_enabled()
|
||||
if prefer_tmux && use_tmux
|
||||
let use_height = 0
|
||||
let use_term = 0
|
||||
@@ -460,19 +448,21 @@ function! s:present(dict, ...)
|
||||
endfunction
|
||||
|
||||
function! s:fzf_tmux(dict)
|
||||
let size = ''
|
||||
for o in ['up', 'down', 'left', 'right']
|
||||
if s:present(a:dict, o)
|
||||
let spec = a:dict[o]
|
||||
if (o == 'up' || o == 'down') && spec[0] == '~'
|
||||
let size = '-'.o[0].s:calc_size(&lines, spec, a:dict)
|
||||
else
|
||||
" Legacy boolean option
|
||||
let size = '-'.o[0].(spec == 1 ? '' : substitute(spec, '^\~', '', ''))
|
||||
let size = get(a:dict, 'tmux', '')
|
||||
if empty(size)
|
||||
for o in ['up', 'down', 'left', 'right']
|
||||
if s:present(a:dict, o)
|
||||
let spec = a:dict[o]
|
||||
if (o == 'up' || o == 'down') && spec[0] == '~'
|
||||
let size = '-'.o[0].s:calc_size(&lines, spec, a:dict)
|
||||
else
|
||||
" Legacy boolean option
|
||||
let size = '-'.o[0].(spec == 1 ? '' : substitute(spec, '^\~', '', ''))
|
||||
endif
|
||||
break
|
||||
endif
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
endif
|
||||
return printf('LINES=%d COLUMNS=%d %s %s %s --',
|
||||
\ &lines, &columns, fzf#shellescape(s:fzf_tmux), size, (has_key(a:dict, 'source') ? '' : '-'))
|
||||
endfunction
|
||||
@@ -527,7 +517,7 @@ function! s:dopopd()
|
||||
if s:fzf_getcwd() ==# w:fzf_pushd.dir && (!&autochdir || w:fzf_pushd.bufname ==# bufname(''))
|
||||
execute w:fzf_pushd.command s:escape(w:fzf_pushd.origin)
|
||||
endif
|
||||
unlet w:fzf_pushd
|
||||
unlet! w:fzf_pushd
|
||||
endfunction
|
||||
|
||||
function! s:xterm_launcher()
|
||||
@@ -642,6 +632,9 @@ function! s:calc_size(max, val, dict)
|
||||
endif
|
||||
|
||||
let opts = $FZF_DEFAULT_OPTS.' '.s:evaluate_opts(get(a:dict, 'options', ''))
|
||||
if opts =~ 'preview'
|
||||
return size
|
||||
endif
|
||||
let margin = match(opts, '--inline-info\|--info[^-]\{-}inline') > match(opts, '--no-inline-info\|--info[^-]\{-}\(default\|hidden\)') ? 1 : 2
|
||||
let margin += stridx(opts, '--border') > stridx(opts, '--no-border') ? 2 : 0
|
||||
if stridx(opts, '--header') > stridx(opts, '--no-header')
|
||||
@@ -661,13 +654,15 @@ function! s:split(dict)
|
||||
\ 'left': ['vertical topleft', 'vertical resize', &columns],
|
||||
\ 'right': ['vertical botright', 'vertical resize', &columns] }
|
||||
let ppos = s:getpos()
|
||||
let is_popup = 0
|
||||
try
|
||||
if s:present(a:dict, 'window')
|
||||
if type(a:dict.window) == type({})
|
||||
if !has('nvim') && !has('patch-8.2.191')
|
||||
throw 'Vim 8.2.191 or later is required for pop-up window'
|
||||
if !(has('nvim') ? has('nvim-0.4') : has('popupwin') && has('patch-8.2.191'))
|
||||
throw 'Nvim 0.4+ or Vim 8.2.191+ with popupwin feature is required for pop-up window'
|
||||
end
|
||||
call s:popup(a:dict.window)
|
||||
let is_popup = 1
|
||||
else
|
||||
execute 'keepalt' a:dict.window
|
||||
endif
|
||||
@@ -685,20 +680,22 @@ function! s:split(dict)
|
||||
endif
|
||||
execute cmd sz.'new'
|
||||
execute resz sz
|
||||
return [ppos, {}]
|
||||
return [ppos, {}, is_popup]
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
return [ppos, { '&l:wfw': &l:wfw, '&l:wfh': &l:wfh }]
|
||||
return [ppos, is_popup ? {} : { '&l:wfw': &l:wfw, '&l:wfh': &l:wfh }, is_popup]
|
||||
finally
|
||||
setlocal winfixwidth winfixheight
|
||||
if !is_popup
|
||||
setlocal winfixwidth winfixheight
|
||||
endif
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:execute_term(dict, command, temps) abort
|
||||
let winrest = winrestcmd()
|
||||
let pbuf = bufnr('')
|
||||
let [ppos, winopts] = s:split(a:dict)
|
||||
let [ppos, winopts, is_popup] = s:split(a:dict)
|
||||
call s:use_sh()
|
||||
let b:fzf = a:dict
|
||||
let fzf = { 'buf': bufnr(''), 'pbuf': pbuf, 'ppos': ppos, 'dict': a:dict, 'temps': a:temps,
|
||||
@@ -762,11 +759,17 @@ function! s:execute_term(dict, command, temps) abort
|
||||
if has('nvim')
|
||||
call termopen(command, fzf)
|
||||
else
|
||||
if !len(&bufhidden)
|
||||
setlocal bufhidden=hide
|
||||
let term_opts = {'exit_cb': function(fzf.on_exit)}
|
||||
if is_popup
|
||||
let term_opts.hidden = 1
|
||||
else
|
||||
let term_opts.curwin = 1
|
||||
endif
|
||||
let fzf.buf = term_start([&shell, &shellcmdflag, command], {'curwin': 1, 'exit_cb': function(fzf.on_exit)})
|
||||
if !has('patch-8.0.1261') && !has('nvim') && !s:is_win
|
||||
let fzf.buf = term_start([&shell, &shellcmdflag, command], term_opts)
|
||||
if is_popup && exists('#TerminalWinOpen')
|
||||
doautocmd <nomodeline> TerminalWinOpen
|
||||
endif
|
||||
if !has('patch-8.0.1261') && !s:is_win
|
||||
call term_wait(fzf.buf, 20)
|
||||
endif
|
||||
endif
|
||||
@@ -837,23 +840,22 @@ if has('nvim')
|
||||
else
|
||||
function! s:create_popup(hl, opts) abort
|
||||
let is_frame = has_key(a:opts, 'border')
|
||||
let buf = is_frame ? '' : term_start(&shell, #{hidden: 1, term_finish: 'close'})
|
||||
let id = popup_create(buf, #{
|
||||
let s:popup_create = {buf -> popup_create(buf, #{
|
||||
\ line: a:opts.row,
|
||||
\ col: a:opts.col,
|
||||
\ minwidth: a:opts.width,
|
||||
\ minheight: a:opts.height,
|
||||
\ zindex: 50 - is_frame,
|
||||
\ })
|
||||
|
||||
\ })}
|
||||
if is_frame
|
||||
let id = s:popup_create('')
|
||||
call setwinvar(id, '&wincolor', a:hl)
|
||||
call setbufline(winbufnr(id), 1, a:opts.border)
|
||||
execute 'autocmd BufWipeout * ++once call popup_close('..id..')'
|
||||
return winbufnr(id)
|
||||
else
|
||||
execute 'autocmd BufWipeout * ++once call term_sendkeys('..buf..', "exit\<CR>")'
|
||||
autocmd TerminalOpen * ++once call s:popup_create(str2nr(expand('<abuf>')))
|
||||
endif
|
||||
return winbufnr(id)
|
||||
endfunction
|
||||
endif
|
||||
|
||||
|
||||
@@ -249,17 +249,25 @@ _fzf_dir_completion() {
|
||||
}
|
||||
|
||||
_fzf_complete_kill() {
|
||||
[ -n "${COMP_WORDS[COMP_CWORD]}" ] && return 1
|
||||
|
||||
local selected
|
||||
selected=$(command ps -ef | sed 1d | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-50%} --min-height 15 --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS --preview 'echo {}' --preview-window down:3:wrap" __fzf_comprun "kill" -m | awk '{print $2}' | tr '\n' ' ')
|
||||
selected=${selected% }
|
||||
printf '\e[5n'
|
||||
|
||||
if [ -n "$selected" ]; then
|
||||
COMPREPLY=( "$selected" )
|
||||
return 0
|
||||
local trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
if [[ -z "$cur" ]]; then
|
||||
COMP_WORDS[$COMP_CWORD]=$trigger
|
||||
elif [[ "$cur" != *"$trigger" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_fzf_proc_completion "$@"
|
||||
}
|
||||
|
||||
_fzf_proc_completion() {
|
||||
_fzf_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
|
||||
command ps -ef | sed 1d
|
||||
)
|
||||
}
|
||||
|
||||
_fzf_proc_completion_post() {
|
||||
awk '{print $2}'
|
||||
}
|
||||
|
||||
_fzf_host_completion() {
|
||||
@@ -296,11 +304,10 @@ a_cmds="
|
||||
find git grep gunzip gzip hg jar
|
||||
ln ls mv open rm rsync scp
|
||||
svn tar unzip zip"
|
||||
x_cmds="kill"
|
||||
|
||||
# Preserve existing completion
|
||||
eval "$(complete |
|
||||
sed -E '/-F/!d; / _fzf/d; '"/ ($(echo $d_cmds $a_cmds $x_cmds | sed 's/ /|/g; s/+/\\+/g'))$/"'!d' |
|
||||
sed -E '/-F/!d; / _fzf/d; '"/ ($(echo $d_cmds $a_cmds | sed 's/ /|/g; s/+/\\+/g'))$/"'!d' |
|
||||
__fzf_orig_completion_filter)"
|
||||
|
||||
if type _completion_loader > /dev/null 2>&1; then
|
||||
@@ -332,17 +339,17 @@ for cmd in $d_cmds; do
|
||||
__fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o dirnames"
|
||||
done
|
||||
|
||||
# Kill completion
|
||||
# Kill completion (supports empty completion trigger)
|
||||
complete -F _fzf_complete_kill -o default -o bashdefault kill
|
||||
|
||||
unset cmd d_cmds a_cmds x_cmds
|
||||
unset cmd d_cmds a_cmds
|
||||
|
||||
_fzf_setup_completion() {
|
||||
local kind fn cmd
|
||||
kind=$1
|
||||
fn=_fzf_${1}_completion
|
||||
if [[ $# -lt 2 ]] || ! type -t "$fn" > /dev/null; then
|
||||
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host COMMANDS..."
|
||||
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..."
|
||||
return 1
|
||||
fi
|
||||
shift
|
||||
|
||||
@@ -117,6 +117,7 @@ __fzf_extract_command() {
|
||||
local token tokens
|
||||
tokens=(${(z)1})
|
||||
for token in $tokens; do
|
||||
token=${(Q)token}
|
||||
if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then
|
||||
echo "$token"
|
||||
return
|
||||
@@ -249,6 +250,16 @@ _fzf_complete_unalias() {
|
||||
)
|
||||
}
|
||||
|
||||
_fzf_complete_kill() {
|
||||
_fzf_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
|
||||
command ps -ef | sed 1d
|
||||
)
|
||||
}
|
||||
|
||||
_fzf_complete_kill_post() {
|
||||
awk '{print $2}'
|
||||
}
|
||||
|
||||
fzf-completion() {
|
||||
local tokens cmd prefix trigger tail matches lbuf d_cmds
|
||||
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
|
||||
@@ -273,20 +284,21 @@ fzf-completion() {
|
||||
tokens=(${tokens[0,-2]})
|
||||
fi
|
||||
|
||||
lbuf=$LBUFFER
|
||||
tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
|
||||
# Kill completion (do not require trigger sequence)
|
||||
if [ $cmd = kill -a ${LBUFFER[-1]} = ' ' ]; then
|
||||
matches=$(command ps -ef | sed 1d | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-50%} --min-height 15 --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS --preview 'echo {}' --preview-window down:3:wrap" __fzf_comprun "$cmd" -m | awk '{print $2}' | tr '\n' ' ')
|
||||
if [ -n "$matches" ]; then
|
||||
LBUFFER="$LBUFFER$matches"
|
||||
fi
|
||||
zle reset-prompt
|
||||
if [ "$cmd" = kill -a ${LBUFFER[-1]} = ' ' ]; then
|
||||
tail=$trigger
|
||||
tokens+=$trigger
|
||||
lbuf="$lbuf$trigger"
|
||||
fi
|
||||
|
||||
# Trigger sequence given
|
||||
elif [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
|
||||
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
|
||||
d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir})
|
||||
|
||||
[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
|
||||
[ -z "${tokens[-1]}" ] && lbuf=$LBUFFER || lbuf=${LBUFFER:0:-${#tokens[-1]}}
|
||||
[ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}
|
||||
|
||||
if eval "type _fzf_complete_${cmd} > /dev/null"; then
|
||||
prefix="$prefix" eval _fzf_complete_${cmd} ${(q)lbuf}
|
||||
|
||||
@@ -100,7 +100,7 @@ bindkey '\ec' fzf-cd-widget
|
||||
fzf-history-widget() {
|
||||
local selected num
|
||||
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
|
||||
selected=( $(fc -rl 1 | perl -ne 'print if !$seen{($_ =~ s/^\s*[0-9]+\s+//r)}++' |
|
||||
selected=( $(fc -rl 1 | perl -ne 'print if !$seen{(/^\s*[0-9]+\s+(.*)/, $1)}++' |
|
||||
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
|
||||
local ret=$?
|
||||
if [ -n "$selected" ]; then
|
||||
|
||||
@@ -405,6 +405,74 @@ var normalized map[rune]rune = map[rune]rune{
|
||||
0x024E: 'Y', // WITH STROKE, LATIN CAPITAL LETTER
|
||||
0x028F: 'Y', // , LATIN LETTER SMALL CAPITAL
|
||||
0x1D22: 'Z', // , LATIN LETTER SMALL CAPITAL
|
||||
|
||||
'Ắ': 'A',
|
||||
'Ấ': 'A',
|
||||
'Ằ': 'A',
|
||||
'Ầ': 'A',
|
||||
'Ẳ': 'A',
|
||||
'Ẩ': 'A',
|
||||
'Ẵ': 'A',
|
||||
'Ẫ': 'A',
|
||||
'Ặ': 'A',
|
||||
'Ậ': 'A',
|
||||
|
||||
'ắ': 'a',
|
||||
'ấ': 'a',
|
||||
'ằ': 'a',
|
||||
'ầ': 'a',
|
||||
'ẳ': 'a',
|
||||
'ẩ': 'a',
|
||||
'ẵ': 'a',
|
||||
'ẫ': 'a',
|
||||
'ặ': 'a',
|
||||
'ậ': 'a',
|
||||
|
||||
'Ế': 'E',
|
||||
'Ề': 'E',
|
||||
'Ể': 'E',
|
||||
'Ễ': 'E',
|
||||
'Ệ': 'E',
|
||||
|
||||
'ế': 'e',
|
||||
'ề': 'e',
|
||||
'ể': 'e',
|
||||
'ễ': 'e',
|
||||
'ệ': 'e',
|
||||
|
||||
'Ố': 'O',
|
||||
'Ớ': 'O',
|
||||
'Ồ': 'O',
|
||||
'Ờ': 'O',
|
||||
'Ổ': 'O',
|
||||
'Ở': 'O',
|
||||
'Ỗ': 'O',
|
||||
'Ỡ': 'O',
|
||||
'Ộ': 'O',
|
||||
'Ợ': 'O',
|
||||
|
||||
'ố': 'o',
|
||||
'ớ': 'o',
|
||||
'ồ': 'o',
|
||||
'ờ': 'o',
|
||||
'ổ': 'o',
|
||||
'ở': 'o',
|
||||
'ỗ': 'o',
|
||||
'ỡ': 'o',
|
||||
'ộ': 'o',
|
||||
'ợ': 'o',
|
||||
|
||||
'Ứ': 'U',
|
||||
'Ừ': 'U',
|
||||
'Ử': 'U',
|
||||
'Ữ': 'U',
|
||||
'Ự': 'U',
|
||||
|
||||
'ứ': 'u',
|
||||
'ừ': 'u',
|
||||
'ử': 'u',
|
||||
'ữ': 'u',
|
||||
'ự': 'u',
|
||||
}
|
||||
|
||||
// NormalizeRunes normalizes latin script letters
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
const (
|
||||
// Current version
|
||||
version = "0.21.1"
|
||||
version = "0.22.0"
|
||||
|
||||
// Core
|
||||
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
||||
|
||||
@@ -80,7 +80,7 @@ const usage = `usage: fzf [options]
|
||||
Preview
|
||||
--preview=COMMAND Command to preview highlighted line ({})
|
||||
--preview-window=OPT Preview window layout (default: right:50%)
|
||||
[up|down|left|right][:SIZE[%]][:wrap][:hidden]
|
||||
[up|down|left|right][:SIZE[%]][:wrap][:hidden][:+SCROLL[-OFFSET]]
|
||||
|
||||
Scripting
|
||||
-q, --query=STR Start the finder with the given query
|
||||
@@ -159,6 +159,7 @@ type previewOpts struct {
|
||||
command string
|
||||
position windowPosition
|
||||
size sizeSpec
|
||||
scroll string
|
||||
hidden bool
|
||||
wrap bool
|
||||
border bool
|
||||
@@ -260,7 +261,7 @@ func defaultOptions() *Options {
|
||||
ToggleSort: false,
|
||||
Expect: make(map[int]string),
|
||||
Keymap: make(map[int][]action),
|
||||
Preview: previewOpts{"", posRight, sizeSpec{50, true}, false, false, true},
|
||||
Preview: previewOpts{"", posRight, sizeSpec{50, true}, "", false, false, true},
|
||||
PrintQuery: false,
|
||||
ReadZero: false,
|
||||
Printer: func(str string) { fmt.Println(str) },
|
||||
@@ -464,6 +465,8 @@ func parseKeyChords(str string, message string) map[int]string {
|
||||
chord = tui.CtrlRightBracket
|
||||
case "change":
|
||||
chord = tui.Change
|
||||
case "backward-eof":
|
||||
chord = tui.BackwardEOF
|
||||
case "alt-enter", "alt-return":
|
||||
chord = tui.CtrlAltM
|
||||
case "alt-space":
|
||||
@@ -682,7 +685,7 @@ func init() {
|
||||
// Backreferences are not supported.
|
||||
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
||||
executeRegexp = regexp.MustCompile(
|
||||
`(?si)[:+](execute(?:-multi|-silent)?|reload):.+|[:+](execute(?:-multi|-silent)?|reload)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
||||
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview):.+|[:+](execute(?:-multi|-silent)?|reload|preview)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
||||
}
|
||||
|
||||
func parseKeymap(keymap map[int][]action, str string) {
|
||||
@@ -694,6 +697,8 @@ func parseKeymap(keymap map[int][]action, str string) {
|
||||
prefix := symbol + "execute"
|
||||
if strings.HasPrefix(src[1:], "reload") {
|
||||
prefix = symbol + "reload"
|
||||
} else if strings.HasPrefix(src[1:], "preview") {
|
||||
prefix = symbol + "preview"
|
||||
} else if src[len(prefix)] == '-' {
|
||||
c := src[len(prefix)+1]
|
||||
if c == 's' || c == 'S' {
|
||||
@@ -754,6 +759,8 @@ func parseKeymap(keymap map[int][]action, str string) {
|
||||
appendAction(actAcceptNonEmpty)
|
||||
case "print-query":
|
||||
appendAction(actPrintQuery)
|
||||
case "refresh-preview":
|
||||
appendAction(actRefreshPreview)
|
||||
case "replace-query":
|
||||
appendAction(actReplaceQuery)
|
||||
case "backward-char":
|
||||
@@ -859,6 +866,8 @@ func parseKeymap(keymap map[int][]action, str string) {
|
||||
switch t {
|
||||
case actReload:
|
||||
offset = len("reload")
|
||||
case actPreview:
|
||||
offset = len("preview")
|
||||
case actExecuteSilent:
|
||||
offset = len("execute-silent")
|
||||
case actExecuteMulti:
|
||||
@@ -896,6 +905,8 @@ func isExecuteAction(str string) actionType {
|
||||
switch prefix {
|
||||
case "reload":
|
||||
return actReload
|
||||
case "preview":
|
||||
return actPreview
|
||||
case "execute":
|
||||
return actExecute
|
||||
case "execute-silent":
|
||||
@@ -984,6 +995,7 @@ func parsePreviewWindow(opts *previewOpts, input string) {
|
||||
|
||||
tokens := strings.Split(input, ":")
|
||||
sizeRegex := regexp.MustCompile("^[0-9]+%?$")
|
||||
offsetRegex := regexp.MustCompile("^\\+([0-9]+|{-?[0-9]+})(-[0-9]+)?$")
|
||||
for _, token := range tokens {
|
||||
switch token {
|
||||
case "":
|
||||
@@ -1006,8 +1018,10 @@ func parsePreviewWindow(opts *previewOpts, input string) {
|
||||
default:
|
||||
if sizeRegex.MatchString(token) {
|
||||
opts.size = parseSize(token, 99, "window size")
|
||||
} else if offsetRegex.MatchString(token) {
|
||||
opts.scroll = token[1:]
|
||||
} else {
|
||||
errorExit("invalid preview window layout: " + input)
|
||||
errorExit("invalid preview window option: " + token)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1260,7 +1274,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
opts.Preview.command = ""
|
||||
case "--preview-window":
|
||||
parsePreviewWindow(&opts.Preview,
|
||||
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:noborder][:wrap][:hidden]"))
|
||||
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:noborder][:wrap][:hidden][:+SCROLL[-OFFSET]]"))
|
||||
case "--height":
|
||||
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
|
||||
case "--min-height":
|
||||
|
||||
@@ -33,6 +33,7 @@ type term struct {
|
||||
inv bool
|
||||
text []rune
|
||||
caseSensitive bool
|
||||
normalize bool
|
||||
}
|
||||
|
||||
// String returns the string representation of a term.
|
||||
@@ -128,6 +129,8 @@ func BuildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case,
|
||||
}
|
||||
} else {
|
||||
lowerString := strings.ToLower(asString)
|
||||
normalize = normalize &&
|
||||
lowerString == string(algo.NormalizeRunes([]rune(lowerString)))
|
||||
caseSensitive = caseMode == CaseRespect ||
|
||||
caseMode == CaseSmart && lowerString != asString
|
||||
if !caseSensitive {
|
||||
@@ -173,6 +176,8 @@ func parseTerms(fuzzy bool, caseMode Case, normalize bool, str string) []termSet
|
||||
lowerText := strings.ToLower(text)
|
||||
caseSensitive := caseMode == CaseRespect ||
|
||||
caseMode == CaseSmart && text != lowerText
|
||||
normalizeTerm := normalize &&
|
||||
lowerText == string(algo.NormalizeRunes([]rune(lowerText)))
|
||||
if !caseSensitive {
|
||||
text = lowerText
|
||||
}
|
||||
@@ -222,14 +227,15 @@ func parseTerms(fuzzy bool, caseMode Case, normalize bool, str string) []termSet
|
||||
set = termSet{}
|
||||
}
|
||||
textRunes := []rune(text)
|
||||
if normalize {
|
||||
if normalizeTerm {
|
||||
textRunes = algo.NormalizeRunes(textRunes)
|
||||
}
|
||||
set = append(set, term{
|
||||
typ: typ,
|
||||
inv: inv,
|
||||
text: textRunes,
|
||||
caseSensitive: caseSensitive})
|
||||
caseSensitive: caseSensitive,
|
||||
normalize: normalizeTerm})
|
||||
switchSet = true
|
||||
}
|
||||
}
|
||||
@@ -360,7 +366,7 @@ func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Of
|
||||
matched := false
|
||||
for _, term := range termSet {
|
||||
pfun := p.procFun[term.typ]
|
||||
off, score, pos := p.iter(pfun, input, term.caseSensitive, p.normalize, p.forward, term.text, withPos, slab)
|
||||
off, score, pos := p.iter(pfun, input, term.caseSensitive, term.normalize, p.forward, term.text, withPos, slab)
|
||||
if sidx := off[0]; sidx >= 0 {
|
||||
if term.inv {
|
||||
continue
|
||||
|
||||
171
src/terminal.go
171
src/terminal.go
@@ -23,12 +23,14 @@ import (
|
||||
// import "github.com/pkg/profile"
|
||||
|
||||
var placeholder *regexp.Regexp
|
||||
var numericPrefix *regexp.Regexp
|
||||
var activeTempFiles []string
|
||||
|
||||
const ellipsis string = ".."
|
||||
|
||||
func init() {
|
||||
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
|
||||
numericPrefix = regexp.MustCompile(`^[[:punct:]]*([0-9]+)`)
|
||||
activeTempFiles = []string{}
|
||||
}
|
||||
|
||||
@@ -64,7 +66,7 @@ type Terminal struct {
|
||||
initDelay time.Duration
|
||||
infoStyle infoStyle
|
||||
spinner []string
|
||||
prompt string
|
||||
prompt func()
|
||||
promptLen int
|
||||
pointer string
|
||||
pointerLen int
|
||||
@@ -224,10 +226,12 @@ const (
|
||||
actJump
|
||||
actJumpAccept
|
||||
actPrintQuery
|
||||
actRefreshPreview
|
||||
actReplaceQuery
|
||||
actToggleSort
|
||||
actTogglePreview
|
||||
actTogglePreviewWrap
|
||||
actPreview
|
||||
actPreviewUp
|
||||
actPreviewDown
|
||||
actPreviewPageUp
|
||||
@@ -255,6 +259,16 @@ type searchRequest struct {
|
||||
command *string
|
||||
}
|
||||
|
||||
type previewRequest struct {
|
||||
template string
|
||||
list []*Item
|
||||
}
|
||||
|
||||
type previewResult struct {
|
||||
content string
|
||||
offset int
|
||||
}
|
||||
|
||||
func toActions(types ...actionType) []action {
|
||||
actions := make([]action, len(types))
|
||||
for idx, t := range types {
|
||||
@@ -326,6 +340,17 @@ func trimQuery(query string) []rune {
|
||||
return []rune(strings.Replace(query, "\t", " ", -1))
|
||||
}
|
||||
|
||||
func hasPreviewAction(opts *Options) bool {
|
||||
for _, actions := range opts.Keymap {
|
||||
for _, action := range actions {
|
||||
if action.t == actPreview {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewTerminal returns new Terminal object
|
||||
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
input := trimQuery(opts.Query)
|
||||
@@ -343,7 +368,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
delay = initialDelay
|
||||
}
|
||||
var previewBox *util.EventBox
|
||||
if len(opts.Preview.command) > 0 {
|
||||
if len(opts.Preview.command) > 0 || hasPreviewAction(opts) {
|
||||
previewBox = util.NewEventBox()
|
||||
}
|
||||
strongAttr := tui.Bold
|
||||
@@ -382,8 +407,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
}
|
||||
renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit, false, maxHeightFunc)
|
||||
}
|
||||
wordRubout := "[^[:alnum:]][[:alnum:]]"
|
||||
wordNext := "[[:alnum:]][^[:alnum:]]|(.$)"
|
||||
wordRubout := "[^\\pL\\pN][\\pL\\pN]"
|
||||
wordNext := "[\\pL\\pN][^\\pL\\pN]|(.$)"
|
||||
if opts.FileWord {
|
||||
sep := regexp.QuoteMeta(string(os.PathSeparator))
|
||||
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
|
||||
@@ -451,7 +476,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
killChan: make(chan int),
|
||||
tui: renderer,
|
||||
initFunc: func() { renderer.Init() }}
|
||||
t.prompt, t.promptLen = t.processTabs([]rune(opts.Prompt), 0)
|
||||
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
|
||||
t.pointer, t.pointerLen = t.processTabs([]rune(opts.Pointer), 0)
|
||||
t.marker, t.markerLen = t.processTabs([]rune(opts.Marker), 0)
|
||||
// Pre-calculated empty pointer and marker signs
|
||||
@@ -461,6 +486,19 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
return &t
|
||||
}
|
||||
|
||||
func (t *Terminal) parsePrompt(prompt string) (func(), int) {
|
||||
var state *ansiState
|
||||
trimmed, colors, _ := extractColor(prompt, state, nil)
|
||||
item := &Item{text: util.ToChars([]byte(trimmed)), colors: colors}
|
||||
output := func() {
|
||||
t.printHighlighted(
|
||||
Result{item: item}, t.strong, tui.ColPrompt, tui.ColPrompt, false, false)
|
||||
}
|
||||
_, promptLen := t.processTabs([]rune(trimmed), 0)
|
||||
|
||||
return output, promptLen
|
||||
}
|
||||
|
||||
func (t *Terminal) noInfoLine() bool {
|
||||
return t.infoStyle != infoDefault
|
||||
}
|
||||
@@ -762,7 +800,7 @@ func (t *Terminal) placeCursor() {
|
||||
|
||||
func (t *Terminal) printPrompt() {
|
||||
t.move(0, 0, true)
|
||||
t.window.CPrint(tui.ColPrompt, t.strong, t.prompt)
|
||||
t.prompt()
|
||||
|
||||
before, after := t.updatePromptOffset()
|
||||
t.window.CPrint(tui.ColNormal, t.strong, string(before))
|
||||
@@ -1196,7 +1234,8 @@ func findLastMatch(pattern string, str string) int {
|
||||
if locs == nil {
|
||||
return -1
|
||||
}
|
||||
return locs[len(locs)-1][0]
|
||||
prefix := []rune(str[:locs[len(locs)-1][0]])
|
||||
return len(prefix)
|
||||
}
|
||||
|
||||
func findFirstMatch(pattern string, str string) int {
|
||||
@@ -1208,7 +1247,8 @@ func findFirstMatch(pattern string, str string) int {
|
||||
if loc == nil {
|
||||
return -1
|
||||
}
|
||||
return loc[0]
|
||||
prefix := []rune(str[:loc[0]])
|
||||
return len(prefix)
|
||||
}
|
||||
|
||||
func copySlice(slice []rune) []rune {
|
||||
@@ -1316,6 +1356,43 @@ func cleanTemporaryFiles() {
|
||||
activeTempFiles = []string{}
|
||||
}
|
||||
|
||||
func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list []*Item) string {
|
||||
return replacePlaceholder(
|
||||
template, t.ansi, t.delimiter, t.printsep, forcePlus, input, list)
|
||||
}
|
||||
|
||||
// Ascii to positive integer
|
||||
func atopi(s string) int {
|
||||
matches := numericPrefix.FindStringSubmatch(s)
|
||||
if len(matches) < 2 {
|
||||
return 0
|
||||
}
|
||||
n, e := strconv.Atoi(matches[1])
|
||||
if e != nil || n < 1 {
|
||||
return 0
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (t *Terminal) evaluateScrollOffset(list []*Item) int {
|
||||
offsetExpr := t.replacePlaceholder(t.preview.scroll, false, "", list)
|
||||
nums := strings.Split(offsetExpr, "-")
|
||||
switch len(nums) {
|
||||
case 0:
|
||||
return 0
|
||||
case 1, 2:
|
||||
base := atopi(nums[0])
|
||||
if base == 0 {
|
||||
return 0
|
||||
} else if len(nums) == 1 {
|
||||
return base - 1
|
||||
}
|
||||
return base - atopi(nums[1]) - 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems []*Item) string {
|
||||
current := allItems[:1]
|
||||
selected := allItems[1:]
|
||||
@@ -1414,7 +1491,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
command := replacePlaceholder(template, t.ansi, t.delimiter, t.printsep, forcePlus, string(t.input), list)
|
||||
command := t.replacePlaceholder(template, forcePlus, string(t.input), list)
|
||||
cmd := util.ExecCommand(command, false)
|
||||
if !background {
|
||||
cmd.Stdin = os.Stdin
|
||||
@@ -1422,13 +1499,13 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
||||
cmd.Stderr = os.Stderr
|
||||
t.tui.Pause(true)
|
||||
cmd.Run()
|
||||
t.tui.Resume(true)
|
||||
t.tui.Resume(true, false)
|
||||
t.redraw()
|
||||
t.refresh()
|
||||
} else {
|
||||
t.tui.Pause(false)
|
||||
cmd.Run()
|
||||
t.tui.Resume(false)
|
||||
t.tui.Resume(false, false)
|
||||
}
|
||||
cleanTemporaryFiles()
|
||||
}
|
||||
@@ -1583,20 +1660,23 @@ func (t *Terminal) Loop() {
|
||||
if t.hasPreviewer() {
|
||||
go func() {
|
||||
for {
|
||||
var request []*Item
|
||||
var items []*Item
|
||||
var commandTemplate string
|
||||
t.previewBox.Wait(func(events *util.Events) {
|
||||
for req, value := range *events {
|
||||
switch req {
|
||||
case reqPreviewEnqueue:
|
||||
request = value.([]*Item)
|
||||
request := value.(previewRequest)
|
||||
commandTemplate = request.template
|
||||
items = request.list
|
||||
}
|
||||
}
|
||||
events.Clear()
|
||||
})
|
||||
// We don't display preview window if no match
|
||||
if request[0] != nil {
|
||||
command := replacePlaceholder(t.preview.command,
|
||||
t.ansi, t.delimiter, t.printsep, false, string(t.Input()), request)
|
||||
if items[0] != nil {
|
||||
command := t.replacePlaceholder(commandTemplate, false, string(t.Input()), items)
|
||||
offset := t.evaluateScrollOffset(items)
|
||||
cmd := util.ExecCommand(command, true)
|
||||
if t.pwindow != nil {
|
||||
env := os.Environ()
|
||||
@@ -1639,11 +1719,11 @@ func (t *Terminal) Loop() {
|
||||
cmd.Wait()
|
||||
finishChan <- true
|
||||
if out.Len() > 0 || !<-updateChan {
|
||||
t.reqBox.Set(reqPreviewDisplay, out.String())
|
||||
t.reqBox.Set(reqPreviewDisplay, previewResult{out.String(), offset})
|
||||
}
|
||||
cleanTemporaryFiles()
|
||||
} else {
|
||||
t.reqBox.Set(reqPreviewDisplay, "")
|
||||
t.reqBox.Set(reqPreviewDisplay, previewResult{"", 0})
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -1659,6 +1739,14 @@ func (t *Terminal) Loop() {
|
||||
t.killPreview(code)
|
||||
}
|
||||
|
||||
refreshPreview := func(command string) {
|
||||
if len(command) > 0 && t.isPreviewEnabled() {
|
||||
_, list := t.buildPlusList(command, false)
|
||||
t.cancelPreview()
|
||||
t.previewBox.Set(reqPreviewEnqueue, previewRequest{command, list})
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
var focusedIndex int32 = minItem.Index()
|
||||
var version int64 = -1
|
||||
@@ -1685,11 +1773,7 @@ func (t *Terminal) Loop() {
|
||||
if focusedIndex != currentIndex || version != t.version {
|
||||
version = t.version
|
||||
focusedIndex = currentIndex
|
||||
if t.isPreviewEnabled() {
|
||||
_, list := t.buildPlusList(t.preview.command, false)
|
||||
t.cancelPreview()
|
||||
t.previewBox.Set(reqPreviewEnqueue, list)
|
||||
}
|
||||
refreshPreview(t.preview.command)
|
||||
}
|
||||
case reqJump:
|
||||
if t.merger.Length() == 0 {
|
||||
@@ -1701,7 +1785,7 @@ func (t *Terminal) Loop() {
|
||||
case reqRefresh:
|
||||
t.suppress = false
|
||||
case reqReinit:
|
||||
t.tui.Resume(t.fullscreen)
|
||||
t.tui.Resume(t.fullscreen, true)
|
||||
t.redraw()
|
||||
case reqRedraw:
|
||||
t.redraw()
|
||||
@@ -1713,9 +1797,10 @@ func (t *Terminal) Loop() {
|
||||
return exitNoMatch
|
||||
})
|
||||
case reqPreviewDisplay:
|
||||
t.previewer.text = value.(string)
|
||||
result := value.(previewResult)
|
||||
t.previewer.text = result.content
|
||||
t.previewer.lines = strings.Count(t.previewer.text, "\n")
|
||||
t.previewer.offset = 0
|
||||
t.previewer.offset = util.Constrain(result.offset, 0, t.previewer.lines-1)
|
||||
t.printPreview()
|
||||
case reqPreviewRefresh:
|
||||
t.printPreview()
|
||||
@@ -1738,6 +1823,7 @@ func (t *Terminal) Loop() {
|
||||
for looping {
|
||||
var newCommand *string
|
||||
changed := false
|
||||
beof := false
|
||||
queryChanged := false
|
||||
|
||||
event := t.tui.GetChar()
|
||||
@@ -1754,6 +1840,14 @@ func (t *Terminal) Loop() {
|
||||
}
|
||||
}
|
||||
}
|
||||
togglePreview := func(enabled bool) {
|
||||
if t.previewer.enabled != enabled {
|
||||
t.previewer.enabled = enabled
|
||||
t.tui.Clear()
|
||||
t.resizeWindows()
|
||||
req(reqPrompt, reqList, reqInfo, reqHeader)
|
||||
}
|
||||
}
|
||||
toggle := func() bool {
|
||||
if t.cy < t.merger.Length() && t.toggleItem(t.merger.Get(t.cy).item) {
|
||||
req(reqInfo)
|
||||
@@ -1802,17 +1896,15 @@ func (t *Terminal) Loop() {
|
||||
return false
|
||||
case actTogglePreview:
|
||||
if t.hasPreviewer() {
|
||||
t.previewer.enabled = !t.previewer.enabled
|
||||
t.tui.Clear()
|
||||
t.resizeWindows()
|
||||
togglePreview(!t.previewer.enabled)
|
||||
if t.previewer.enabled {
|
||||
valid, list := t.buildPlusList(t.preview.command, false)
|
||||
if valid {
|
||||
t.cancelPreview()
|
||||
t.previewBox.Set(reqPreviewEnqueue, list)
|
||||
t.previewBox.Set(reqPreviewEnqueue,
|
||||
previewRequest{t.preview.command, list})
|
||||
}
|
||||
}
|
||||
req(reqPrompt, reqList, reqInfo, reqHeader)
|
||||
}
|
||||
case actTogglePreviewWrap:
|
||||
if t.hasPreviewWindow() {
|
||||
@@ -1846,6 +1938,11 @@ func (t *Terminal) Loop() {
|
||||
}
|
||||
case actPrintQuery:
|
||||
req(reqPrintQuery)
|
||||
case actPreview:
|
||||
togglePreview(true)
|
||||
refreshPreview(a.a)
|
||||
case actRefreshPreview:
|
||||
refreshPreview(t.preview.command)
|
||||
case actReplaceQuery:
|
||||
if t.cy >= 0 && t.cy < t.merger.Length() {
|
||||
t.input = t.merger.Get(t.cy).item.text.ToRunes()
|
||||
@@ -1881,6 +1978,7 @@ func (t *Terminal) Loop() {
|
||||
t.cx++
|
||||
}
|
||||
case actBackwardDeleteChar:
|
||||
beof = len(t.input) == 0
|
||||
if t.cx > 0 {
|
||||
t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
|
||||
t.cx--
|
||||
@@ -1973,16 +2071,19 @@ func (t *Terminal) Loop() {
|
||||
t.vset(0)
|
||||
req(reqList)
|
||||
case actUnixLineDiscard:
|
||||
beof = len(t.input) == 0
|
||||
if t.cx > 0 {
|
||||
t.yanked = copySlice(t.input[:t.cx])
|
||||
t.input = t.input[t.cx:]
|
||||
t.cx = 0
|
||||
}
|
||||
case actUnixWordRubout:
|
||||
beof = len(t.input) == 0
|
||||
if t.cx > 0 {
|
||||
t.rubout("\\s\\S")
|
||||
}
|
||||
case actBackwardKillWord:
|
||||
beof = len(t.input) == 0
|
||||
if t.cx > 0 {
|
||||
t.rubout(t.wordRubout)
|
||||
}
|
||||
@@ -2118,8 +2219,7 @@ func (t *Terminal) Loop() {
|
||||
valid = !slot || query
|
||||
}
|
||||
if valid {
|
||||
command := replacePlaceholder(a.a,
|
||||
t.ansi, t.delimiter, t.printsep, false, string(t.input), list)
|
||||
command := t.replacePlaceholder(a.a, false, string(t.input), list)
|
||||
newCommand = &command
|
||||
}
|
||||
}
|
||||
@@ -2145,6 +2245,11 @@ func (t *Terminal) Loop() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if onEOFs, prs := t.keymap[tui.BackwardEOF]; beof && prs {
|
||||
if !doActions(onEOFs, tui.BackwardEOF) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if mapkey == tui.Rune {
|
||||
if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() {
|
||||
|
||||
@@ -25,12 +25,12 @@ const (
|
||||
Reverse = Attr(1 << 6)
|
||||
)
|
||||
|
||||
func (r *FullscreenRenderer) Init() {}
|
||||
func (r *FullscreenRenderer) Pause(bool) {}
|
||||
func (r *FullscreenRenderer) Resume(bool) {}
|
||||
func (r *FullscreenRenderer) Clear() {}
|
||||
func (r *FullscreenRenderer) Refresh() {}
|
||||
func (r *FullscreenRenderer) Close() {}
|
||||
func (r *FullscreenRenderer) Init() {}
|
||||
func (r *FullscreenRenderer) Pause(bool) {}
|
||||
func (r *FullscreenRenderer) Resume(bool, bool) {}
|
||||
func (r *FullscreenRenderer) Clear() {}
|
||||
func (r *FullscreenRenderer) Refresh() {}
|
||||
func (r *FullscreenRenderer) Close() {}
|
||||
|
||||
func (r *FullscreenRenderer) DoesAutoWrap() bool { return false }
|
||||
func (r *FullscreenRenderer) GetChar() Event { return Event{} }
|
||||
|
||||
102
src/tui/light.go
102
src/tui/light.go
@@ -28,6 +28,7 @@ const (
|
||||
const consoleDevice string = "/dev/tty"
|
||||
|
||||
var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R")
|
||||
var offsetRegexpBegin *regexp.Regexp = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R")
|
||||
|
||||
func (r *LightRenderer) stderr(str string) {
|
||||
r.stderrInternal(str, true)
|
||||
@@ -333,6 +334,13 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
||||
if len(r.buffer) < 2 {
|
||||
return Event{ESC, 0, nil}
|
||||
}
|
||||
|
||||
loc := offsetRegexpBegin.FindIndex(r.buffer)
|
||||
if loc != nil && loc[0] == 0 {
|
||||
*sz = loc[1]
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
|
||||
*sz = 2
|
||||
if r.buffer[1] >= 1 && r.buffer[1] <= 'z'-'a'+1 {
|
||||
return Event{int(CtrlAltA + r.buffer[1] - 1), 0, nil}
|
||||
@@ -345,81 +353,81 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
||||
switch r.buffer[1] {
|
||||
case ESC:
|
||||
return Event{ESC, 0, nil}
|
||||
case 32:
|
||||
case ' ':
|
||||
return Event{AltSpace, 0, nil}
|
||||
case 47:
|
||||
case '/':
|
||||
return Event{AltSlash, 0, nil}
|
||||
case 98:
|
||||
case 'b':
|
||||
return Event{AltB, 0, nil}
|
||||
case 100:
|
||||
case 'd':
|
||||
return Event{AltD, 0, nil}
|
||||
case 102:
|
||||
case 'f':
|
||||
return Event{AltF, 0, nil}
|
||||
case 127:
|
||||
return Event{AltBS, 0, nil}
|
||||
case 91, 79:
|
||||
case '[', 'O':
|
||||
if len(r.buffer) < 3 {
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
*sz = 3
|
||||
switch r.buffer[2] {
|
||||
case 68:
|
||||
case 'D':
|
||||
if alt {
|
||||
return Event{AltLeft, 0, nil}
|
||||
}
|
||||
return Event{Left, 0, nil}
|
||||
case 67:
|
||||
case 'C':
|
||||
if alt {
|
||||
// Ugh..
|
||||
return Event{AltRight, 0, nil}
|
||||
}
|
||||
return Event{Right, 0, nil}
|
||||
case 66:
|
||||
case 'B':
|
||||
if alt {
|
||||
return Event{AltDown, 0, nil}
|
||||
}
|
||||
return Event{Down, 0, nil}
|
||||
case 65:
|
||||
case 'A':
|
||||
if alt {
|
||||
return Event{AltUp, 0, nil}
|
||||
}
|
||||
return Event{Up, 0, nil}
|
||||
case 90:
|
||||
case 'Z':
|
||||
return Event{BTab, 0, nil}
|
||||
case 72:
|
||||
case 'H':
|
||||
return Event{Home, 0, nil}
|
||||
case 70:
|
||||
case 'F':
|
||||
return Event{End, 0, nil}
|
||||
case 77:
|
||||
case 'M':
|
||||
return r.mouseSequence(sz)
|
||||
case 80:
|
||||
case 'P':
|
||||
return Event{F1, 0, nil}
|
||||
case 81:
|
||||
case 'Q':
|
||||
return Event{F2, 0, nil}
|
||||
case 82:
|
||||
case 'R':
|
||||
return Event{F3, 0, nil}
|
||||
case 83:
|
||||
case 'S':
|
||||
return Event{F4, 0, nil}
|
||||
case 49, 50, 51, 52, 53, 54:
|
||||
case '1', '2', '3', '4', '5', '6':
|
||||
if len(r.buffer) < 4 {
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
*sz = 4
|
||||
switch r.buffer[2] {
|
||||
case 50:
|
||||
if r.buffer[3] == 126 {
|
||||
case '2':
|
||||
if r.buffer[3] == '~' {
|
||||
return Event{Insert, 0, nil}
|
||||
}
|
||||
if len(r.buffer) > 4 && r.buffer[4] == 126 {
|
||||
if len(r.buffer) > 4 && r.buffer[4] == '~' {
|
||||
*sz = 5
|
||||
switch r.buffer[3] {
|
||||
case 48:
|
||||
case '0':
|
||||
return Event{F9, 0, nil}
|
||||
case 49:
|
||||
case '1':
|
||||
return Event{F10, 0, nil}
|
||||
case 51:
|
||||
case '3':
|
||||
return Event{F11, 0, nil}
|
||||
case 52:
|
||||
case '4':
|
||||
return Event{F12, 0, nil}
|
||||
}
|
||||
}
|
||||
@@ -431,37 +439,37 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
||||
return r.GetChar()
|
||||
}
|
||||
return Event{Invalid, 0, nil} // INS
|
||||
case 51:
|
||||
case '3':
|
||||
return Event{Del, 0, nil}
|
||||
case 52:
|
||||
case '4':
|
||||
return Event{End, 0, nil}
|
||||
case 53:
|
||||
case '5':
|
||||
return Event{PgUp, 0, nil}
|
||||
case 54:
|
||||
case '6':
|
||||
return Event{PgDn, 0, nil}
|
||||
case 49:
|
||||
case '1':
|
||||
switch r.buffer[3] {
|
||||
case 126:
|
||||
case '~':
|
||||
return Event{Home, 0, nil}
|
||||
case 49, 50, 51, 52, 53, 55, 56, 57:
|
||||
if len(r.buffer) == 5 && r.buffer[4] == 126 {
|
||||
case '1', '2', '3', '4', '5', '7', '8', '9':
|
||||
if len(r.buffer) == 5 && r.buffer[4] == '~' {
|
||||
*sz = 5
|
||||
switch r.buffer[3] {
|
||||
case 49:
|
||||
case '1':
|
||||
return Event{F1, 0, nil}
|
||||
case 50:
|
||||
case '2':
|
||||
return Event{F2, 0, nil}
|
||||
case 51:
|
||||
case '3':
|
||||
return Event{F3, 0, nil}
|
||||
case 52:
|
||||
case '4':
|
||||
return Event{F4, 0, nil}
|
||||
case 53:
|
||||
case '5':
|
||||
return Event{F5, 0, nil}
|
||||
case 55:
|
||||
case '7':
|
||||
return Event{F6, 0, nil}
|
||||
case 56:
|
||||
case '8':
|
||||
return Event{F7, 0, nil}
|
||||
case 57:
|
||||
case '9':
|
||||
return Event{F8, 0, nil}
|
||||
}
|
||||
}
|
||||
@@ -561,7 +569,7 @@ func (r *LightRenderer) Pause(clear bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LightRenderer) Resume(clear bool) {
|
||||
func (r *LightRenderer) Resume(clear bool, sigcont bool) {
|
||||
r.setupTerminal()
|
||||
if clear {
|
||||
if r.fullscreen {
|
||||
@@ -570,10 +578,10 @@ func (r *LightRenderer) Resume(clear bool) {
|
||||
r.rmcup()
|
||||
}
|
||||
r.flush()
|
||||
} else if !r.fullscreen && r.mouse {
|
||||
// NOTE: Resume(false) is only called on SIGCONT after SIGSTOP.
|
||||
// And It's highly likely that the offset we obtained at the beginning will
|
||||
// no longer be correct, so we simply disable mouse input.
|
||||
} else if sigcont && !r.fullscreen && r.mouse {
|
||||
// NOTE: SIGCONT (Coming back from CTRL-Z):
|
||||
// It's highly likely that the offset we obtained at the beginning is
|
||||
// no longer correct, so we simply disable mouse input.
|
||||
r.csi("?1000l")
|
||||
r.mouse = false
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ func (r *FullscreenRenderer) Pause(clear bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FullscreenRenderer) Resume(clear bool) {
|
||||
func (r *FullscreenRenderer) Resume(clear bool, sigcont bool) {
|
||||
if clear {
|
||||
r.initScreen()
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ const (
|
||||
F12
|
||||
|
||||
Change
|
||||
BackwardEOF
|
||||
|
||||
AltSpace
|
||||
AltSlash
|
||||
@@ -275,7 +276,7 @@ func MakeTransparentBorder() BorderStyle {
|
||||
type Renderer interface {
|
||||
Init()
|
||||
Pause(clear bool)
|
||||
Resume(clear bool)
|
||||
Resume(clear bool, sigcont bool)
|
||||
Clear()
|
||||
RefreshWindows(windows []Window)
|
||||
Refresh()
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# http://www.rubydoc.info/github/rest-client/rest-client/RestClient
|
||||
require 'rest_client'
|
||||
require 'json'
|
||||
|
||||
if ARGV.length < 3
|
||||
puts "usage: #$0 <token> <version> <files...>"
|
||||
puts "usage: #{$PROGRAM_NAME} <token> <version> <files...>"
|
||||
exit 1
|
||||
end
|
||||
|
||||
token, version, *files = ARGV
|
||||
base = "https://api.github.com/repos/junegunn/fzf-bin/releases"
|
||||
base = 'https://api.github.com/repos/junegunn/fzf-bin/releases'
|
||||
|
||||
# List releases
|
||||
rels = JSON.parse(RestClient.get(base, :authorization => "token #{token}"))
|
||||
rels = JSON.parse(RestClient.get(base, authorization: "token #{token}"))
|
||||
rel = rels.find { |r| r['tag_name'] == version }
|
||||
unless rel
|
||||
puts "#{version} not found"
|
||||
@@ -21,25 +22,26 @@ unless rel
|
||||
end
|
||||
|
||||
# List assets
|
||||
assets = Hash[rel['assets'].map { |a| a.values_at *%w[name id] }]
|
||||
assets = Hash[rel['assets'].map { |a| a.values_at('name', 'id') }]
|
||||
|
||||
files.select { |f| File.exists? f }.map do |file|
|
||||
files.select { |f| File.exist?(f) }.map do |file|
|
||||
Thread.new do
|
||||
name = File.basename file
|
||||
name = File.basename(file)
|
||||
|
||||
if asset_id = assets[name]
|
||||
if asset_id = assets[name] # rubocop:todo Lint/AssignmentInCondition
|
||||
puts "#{name} found. Deleting asset id #{asset_id}."
|
||||
RestClient.delete "#{base}/assets/#{asset_id}",
|
||||
:authorization => "token #{token}"
|
||||
RestClient.delete("#{base}/assets/#{asset_id}",
|
||||
authorization: "token #{token}")
|
||||
else
|
||||
puts "#{name} not found"
|
||||
end
|
||||
|
||||
puts "Uploading #{name}"
|
||||
RestClient.post(
|
||||
"#{base.sub 'api', 'uploads'}/#{rel['id']}/assets?name=#{name}",
|
||||
"#{base.sub('api', 'uploads')}/#{rel['id']}/assets?name=#{name}",
|
||||
File.read(file),
|
||||
:authorization => "token #{token}",
|
||||
:content_type => "application/octet-stream")
|
||||
authorization: "token #{token}",
|
||||
content_type: 'application/octet-stream'
|
||||
)
|
||||
end
|
||||
end.each(&:join)
|
||||
|
||||
1548
test/test_go.rb
1548
test/test_go.rb
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user