mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-14 14:23:47 -05:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2093667548 | ||
|
|
3c868d7961 | ||
|
|
707f4f5816 | ||
|
|
b3ab6311c5 | ||
|
|
d56f605b63 | ||
|
|
f8b713f425 | ||
|
|
5209e95bc7 | ||
|
|
ef67a45702 | ||
|
|
b88eb72ac2 | ||
|
|
32847f7254 | ||
|
|
71df93b534 | ||
|
|
bb028191f8 | ||
|
|
19af8fc7d8 | ||
|
|
a06671b47f | ||
|
|
5f385d88e0 | ||
|
|
9cb7a364a3 | ||
|
|
f68cbc577d |
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -4,3 +4,7 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|||||||
15
.github/workflows/codeql-analysis.yml
vendored
15
.github/workflows/codeql-analysis.yml
vendored
@@ -8,8 +8,15 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
|
permissions:
|
||||||
|
actions: read # for github/codeql-action/init to get workflow details
|
||||||
|
contents: read # for actions/checkout to fetch code
|
||||||
|
security-events: write # for github/codeql-action/autobuild to send a status report
|
||||||
name: Analyze
|
name: Analyze
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
@@ -20,18 +27,18 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@28eead240834b314f7def40f6fcba65d100d99b1 # v1
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@28eead240834b314f7def40f6fcba65d100d99b1 # v1
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@28eead240834b314f7def40f6fcba65d100d99b1 # v1
|
||||||
|
|||||||
14
.github/workflows/linux.yml
vendored
14
.github/workflows/linux.yml
vendored
@@ -8,24 +8,24 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
go: [1.14, 1.16]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab # v2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: 1.18
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@v1.62.0
|
uses: ruby/setup-ruby@bd94d6a504586da892a5753afdd1480096ed30df # v1.62.0
|
||||||
with:
|
with:
|
||||||
ruby-version: 3.0.0
|
ruby-version: 3.0.0
|
||||||
|
|
||||||
|
|||||||
14
.github/workflows/macos.yml
vendored
14
.github/workflows/macos.yml
vendored
@@ -8,24 +8,24 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
go: [1.14, 1.16]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab # v2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: 1.18
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@v1.62.0
|
uses: ruby/setup-ruby@bd94d6a504586da892a5753afdd1480096ed30df # v1.62.0
|
||||||
with:
|
with:
|
||||||
ruby-version: 3.0.0
|
ruby-version: 3.0.0
|
||||||
|
|
||||||
|
|||||||
1
.tool-versions
Normal file
1
.tool-versions
Normal file
@@ -0,0 +1 @@
|
|||||||
|
golang 1.18
|
||||||
35
ADVANCED.md
35
ADVANCED.md
@@ -17,6 +17,7 @@ Advanced fzf examples
|
|||||||
* [Using fzf as the secondary filter](#using-fzf-as-the-secondary-filter)
|
* [Using fzf as the secondary filter](#using-fzf-as-the-secondary-filter)
|
||||||
* [Using fzf as interative Ripgrep launcher](#using-fzf-as-interative-ripgrep-launcher)
|
* [Using fzf as interative Ripgrep launcher](#using-fzf-as-interative-ripgrep-launcher)
|
||||||
* [Switching to fzf-only search mode](#switching-to-fzf-only-search-mode)
|
* [Switching to fzf-only search mode](#switching-to-fzf-only-search-mode)
|
||||||
|
* [Switching between Ripgrep mode and fzf mode](#switching-between-ripgrep-mode-and-fzf-mode)
|
||||||
* [Log tailing](#log-tailing)
|
* [Log tailing](#log-tailing)
|
||||||
* [Key bindings for git objects](#key-bindings-for-git-objects)
|
* [Key bindings for git objects](#key-bindings-for-git-objects)
|
||||||
* [Files listed in `git status`](#files-listed-in-git-status)
|
* [Files listed in `git status`](#files-listed-in-git-status)
|
||||||
@@ -405,6 +406,40 @@ IFS=: read -ra selected < <(
|
|||||||
- We reverted `--color` option for customizing how the matching chunks are
|
- We reverted `--color` option for customizing how the matching chunks are
|
||||||
displayed in the second phase
|
displayed in the second phase
|
||||||
|
|
||||||
|
### Switching between Ripgrep mode and fzf mode
|
||||||
|
|
||||||
|
*(Requires fzf 0.30.0 or above)*
|
||||||
|
|
||||||
|
fzf 0.30.0 added `rebind` action so we can "rebind" the bindings that were
|
||||||
|
previously "unbound" via `unbind`.
|
||||||
|
|
||||||
|
This is an improved version of the previous example that allows us to switch
|
||||||
|
between Ripgrep launcher mode and fzf-only filtering mode via CTRL-R and
|
||||||
|
CTRL-F.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Switch between Ripgrep launcher mode (CTRL-R) and fzf filtering mode (CTRL-F)
|
||||||
|
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
|
||||||
|
INITIAL_QUERY="${*:-}"
|
||||||
|
IFS=: read -ra selected < <(
|
||||||
|
FZF_DEFAULT_COMMAND="$RG_PREFIX $(printf %q "$INITIAL_QUERY")" \
|
||||||
|
fzf --ansi \
|
||||||
|
--color "hl:-1:underline,hl+:-1:underline:reverse" \
|
||||||
|
--disabled --query "$INITIAL_QUERY" \
|
||||||
|
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
|
||||||
|
--bind "ctrl-f:unbind(change,ctrl-f)+change-prompt(2. fzf> )+enable-search+clear-query+rebind(ctrl-r)" \
|
||||||
|
--bind "ctrl-r:unbind(ctrl-r)+change-prompt(1. ripgrep> )+disable-search+reload($RG_PREFIX {q} || true)+rebind(change,ctrl-f)" \
|
||||||
|
--prompt '1. Ripgrep> ' \
|
||||||
|
--delimiter : \
|
||||||
|
--header '╱ CTRL-R (Ripgrep mode) ╱ CTRL-F (fzf mode) ╱' \
|
||||||
|
--preview 'bat --color=always {1} --highlight-line {2}' \
|
||||||
|
--preview-window 'up,60%,border-bottom,+{2}+3/3,~3'
|
||||||
|
)
|
||||||
|
[ -n "${selected[0]}" ] && vim "${selected[0]}" "+${selected[1]}"
|
||||||
|
```
|
||||||
|
|
||||||
Log tailing
|
Log tailing
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|||||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,6 +1,21 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.30.0
|
||||||
|
------
|
||||||
|
- Fixed cursor flickering over the screen by hiding it during rendering
|
||||||
|
- Added `--ellipsis` option. You can take advantage of it to make fzf
|
||||||
|
effectively search non-visible parts of the item.
|
||||||
|
```sh
|
||||||
|
# Search against hidden line numbers on the far right
|
||||||
|
nl /usr/share/dict/words |
|
||||||
|
awk '{printf "%s%1000s\n", $2, $1}' |
|
||||||
|
fzf --nth=-1 --no-hscroll --ellipsis='' |
|
||||||
|
awk '{print $2}'
|
||||||
|
```
|
||||||
|
- Added `rebind` action for restoring bindings after `unbind`
|
||||||
|
- Bug fixes and improvements
|
||||||
|
|
||||||
0.29.0
|
0.29.0
|
||||||
------
|
------
|
||||||
- Added `change-preview(...)` action to change the `--preview` command
|
- Added `change-preview(...)` action to change the `--preview` command
|
||||||
|
|||||||
@@ -592,6 +592,9 @@ If ripgrep doesn't find any matches, it will exit with a non-zero exit status,
|
|||||||
and fzf will warn you about it. To suppress the warning message, we added
|
and fzf will warn you about it. To suppress the warning message, we added
|
||||||
`|| true` to the command, so that it always exits with 0.
|
`|| true` to the command, so that it always exits with 0.
|
||||||
|
|
||||||
|
See ["Using fzf as interative Ripgrep launcher"](https://github.com/junegunn/fzf/blob/master/ADVANCED.md#using-fzf-as-interative-ripgrep-launcher)
|
||||||
|
for a fuller example with preview window options.
|
||||||
|
|
||||||
### Preview window
|
### Preview window
|
||||||
|
|
||||||
When the `--preview` option is set, fzf automatically starts an external process
|
When the `--preview` option is set, fzf automatically starts an external process
|
||||||
|
|||||||
7
install
7
install
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
version=0.29.0
|
version=0.30.0
|
||||||
auto_completion=
|
auto_completion=
|
||||||
key_bindings=
|
key_bindings=
|
||||||
update_config=2
|
update_config=2
|
||||||
@@ -280,11 +280,6 @@ EOF
|
|||||||
[ $? -eq 0 ] && echo "OK" || echo "Failed"
|
[ $? -eq 0 ] && echo "OK" || echo "Failed"
|
||||||
|
|
||||||
mkdir -p "${fish_dir}/functions"
|
mkdir -p "${fish_dir}/functions"
|
||||||
if [ -e "${fish_dir}/functions/fzf.fish" ]; then
|
|
||||||
echo -n "Remove unnecessary ${fish_dir}/functions/fzf.fish ... "
|
|
||||||
rm -f "${fish_dir}/functions/fzf.fish" && echo "OK" || echo "Failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fish_binding="${fish_dir}/functions/fzf_key_bindings.fish"
|
fish_binding="${fish_dir}/functions/fzf_key_bindings.fish"
|
||||||
if [ $key_bindings -ne 0 ]; then
|
if [ $key_bindings -ne 0 ]; then
|
||||||
echo -n "Symlink $fish_binding ... "
|
echo -n "Symlink $fish_binding ... "
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
$version="0.29.0"
|
$version="0.30.0"
|
||||||
|
|
||||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -5,7 +5,7 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/protector"
|
"github.com/junegunn/fzf/src/protector"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.29"
|
var version string = "0.30"
|
||||||
var revision string = "devel"
|
var revision string = "devel"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -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 "Dec 2021" "fzf 0.29.0" "fzf-tmux - open fzf in tmux split pane"
|
.TH fzf-tmux 1 "Apr 2022" "fzf 0.30.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 "Dec 2021" "fzf 0.29.0" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Apr 2022" "fzf 0.30.0" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@@ -302,6 +302,9 @@ lines that follow.
|
|||||||
.TP
|
.TP
|
||||||
.B "--header-first"
|
.B "--header-first"
|
||||||
Print header before the prompt line
|
Print header before the prompt line
|
||||||
|
.TP
|
||||||
|
.BI "--ellipsis=" "STR"
|
||||||
|
Ellipsis to show when line is truncated (default: '..')
|
||||||
.SS Display
|
.SS Display
|
||||||
.TP
|
.TP
|
||||||
.B "--ansi"
|
.B "--ansi"
|
||||||
@@ -864,6 +867,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBprint-query\fR (print query and exit)
|
\fBprint-query\fR (print query and exit)
|
||||||
\fBput\fR (put the character to the prompt)
|
\fBput\fR (put the character to the prompt)
|
||||||
\fBrefresh-preview\fR
|
\fBrefresh-preview\fR
|
||||||
|
\fBrebind(...)\fR (rebind bindings after \fBunbind\fR)
|
||||||
\fBreload(...)\fR (see below for the details)
|
\fBreload(...)\fR (see below for the details)
|
||||||
\fBreplace-query\fR (replace query string with the current selection)
|
\fBreplace-query\fR (replace query string with the current selection)
|
||||||
\fBselect\fR
|
\fBselect\fR
|
||||||
|
|||||||
@@ -65,8 +65,10 @@ fzf-file-widget() {
|
|||||||
zle reset-prompt
|
zle reset-prompt
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
zle -N fzf-file-widget
|
zle -N fzf-file-widget
|
||||||
bindkey '^T' fzf-file-widget
|
bindkey -M emacs '^T' fzf-file-widget
|
||||||
|
bindkey -M vicmd '^T' fzf-file-widget
|
||||||
|
bindkey -M viins '^T' fzf-file-widget
|
||||||
|
|
||||||
# ALT-C - cd into the selected directory
|
# ALT-C - cd into the selected directory
|
||||||
fzf-cd-widget() {
|
fzf-cd-widget() {
|
||||||
@@ -86,8 +88,10 @@ fzf-cd-widget() {
|
|||||||
zle reset-prompt
|
zle reset-prompt
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
zle -N fzf-cd-widget
|
zle -N fzf-cd-widget
|
||||||
bindkey '\ec' fzf-cd-widget
|
bindkey -M emacs '\ec' fzf-cd-widget
|
||||||
|
bindkey -M vicmd '\ec' fzf-cd-widget
|
||||||
|
bindkey -M viins '\ec' fzf-cd-widget
|
||||||
|
|
||||||
# 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() {
|
||||||
@@ -105,8 +109,10 @@ fzf-history-widget() {
|
|||||||
zle reset-prompt
|
zle reset-prompt
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
zle -N fzf-history-widget
|
zle -N fzf-history-widget
|
||||||
bindkey '^R' fzf-history-widget
|
bindkey -M emacs '^R' fzf-history-widget
|
||||||
|
bindkey -M vicmd '^R' fzf-history-widget
|
||||||
|
bindkey -M viins '^R' fzf-history-widget
|
||||||
|
|
||||||
} always {
|
} always {
|
||||||
eval $__fzf_key_bindings_options
|
eval $__fzf_key_bindings_options
|
||||||
|
|||||||
12
src/core.go
12
src/core.go
@@ -242,9 +242,11 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
for {
|
for {
|
||||||
delay := true
|
delay := true
|
||||||
ticks++
|
ticks++
|
||||||
input := func() []rune {
|
input := func(reloaded bool) []rune {
|
||||||
paused, input := terminal.Input()
|
paused, input := terminal.Input()
|
||||||
if !paused {
|
if reloaded && paused {
|
||||||
|
query = []rune{}
|
||||||
|
} else if !paused {
|
||||||
query = input
|
query = input
|
||||||
}
|
}
|
||||||
return query
|
return query
|
||||||
@@ -274,7 +276,8 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
opts.Sync = false
|
opts.Sync = false
|
||||||
terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false)
|
terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false)
|
||||||
}
|
}
|
||||||
matcher.Reset(snapshot, input(), false, !reading, sort, clearCache())
|
reset := clearCache()
|
||||||
|
matcher.Reset(snapshot, input(reset), false, !reading, sort, reset)
|
||||||
|
|
||||||
case EvtSearchNew:
|
case EvtSearchNew:
|
||||||
var command *string
|
var command *string
|
||||||
@@ -293,7 +296,8 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
snapshot, _ := chunkList.Snapshot()
|
snapshot, _ := chunkList.Snapshot()
|
||||||
matcher.Reset(snapshot, input(), true, !reading, sort, clearCache())
|
reset := clearCache()
|
||||||
|
matcher.Reset(snapshot, input(reset), true, !reading, sort, reset)
|
||||||
delay = false
|
delay = false
|
||||||
|
|
||||||
case EvtSearchProgress:
|
case EvtSearchProgress:
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ const usage = `usage: fzf [options]
|
|||||||
--header=STR String to print as header
|
--header=STR String to print as header
|
||||||
--header-lines=N The first N lines of the input are treated as header
|
--header-lines=N The first N lines of the input are treated as header
|
||||||
--header-first Print header before the prompt line
|
--header-first Print header before the prompt line
|
||||||
|
--ellipsis=STR Ellipsis to show when line is truncated (default: '..')
|
||||||
|
|
||||||
Display
|
Display
|
||||||
--ansi Enable processing of ANSI color codes
|
--ansi Enable processing of ANSI color codes
|
||||||
@@ -235,6 +236,7 @@ type Options struct {
|
|||||||
Header []string
|
Header []string
|
||||||
HeaderLines int
|
HeaderLines int
|
||||||
HeaderFirst bool
|
HeaderFirst bool
|
||||||
|
Ellipsis string
|
||||||
Margin [4]sizeSpec
|
Margin [4]sizeSpec
|
||||||
Padding [4]sizeSpec
|
Padding [4]sizeSpec
|
||||||
BorderShape tui.BorderShape
|
BorderShape tui.BorderShape
|
||||||
@@ -298,6 +300,7 @@ func defaultOptions() *Options {
|
|||||||
Header: make([]string, 0),
|
Header: make([]string, 0),
|
||||||
HeaderLines: 0,
|
HeaderLines: 0,
|
||||||
HeaderFirst: false,
|
HeaderFirst: false,
|
||||||
|
Ellipsis: "..",
|
||||||
Margin: defaultMargin(),
|
Margin: defaultMargin(),
|
||||||
Padding: defaultMargin(),
|
Padding: defaultMargin(),
|
||||||
Unicode: true,
|
Unicode: true,
|
||||||
@@ -795,7 +798,7 @@ func init() {
|
|||||||
// Backreferences are not supported.
|
// Backreferences are not supported.
|
||||||
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
||||||
executeRegexp = regexp.MustCompile(
|
executeRegexp = regexp.MustCompile(
|
||||||
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|unbind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|unbind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseKeymap(keymap map[tui.Event][]*action, str string) {
|
func parseKeymap(keymap map[tui.Event][]*action, str string) {
|
||||||
@@ -815,6 +818,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) {
|
|||||||
prefix = symbol + "preview"
|
prefix = symbol + "preview"
|
||||||
} else if strings.HasPrefix(src[1:], "unbind") {
|
} else if strings.HasPrefix(src[1:], "unbind") {
|
||||||
prefix = symbol + "unbind"
|
prefix = symbol + "unbind"
|
||||||
|
} else if strings.HasPrefix(src[1:], "rebind") {
|
||||||
|
prefix = symbol + "rebind"
|
||||||
} else if strings.HasPrefix(src[1:], "change-prompt") {
|
} else if strings.HasPrefix(src[1:], "change-prompt") {
|
||||||
prefix = symbol + "change-prompt"
|
prefix = symbol + "change-prompt"
|
||||||
} else if src[len(prefix)] == '-' {
|
} else if src[len(prefix)] == '-' {
|
||||||
@@ -1022,6 +1027,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) {
|
|||||||
offset = len("change-prompt")
|
offset = len("change-prompt")
|
||||||
case actUnbind:
|
case actUnbind:
|
||||||
offset = len("unbind")
|
offset = len("unbind")
|
||||||
|
case actRebind:
|
||||||
|
offset = len("rebind")
|
||||||
case actExecuteSilent:
|
case actExecuteSilent:
|
||||||
offset = len("execute-silent")
|
offset = len("execute-silent")
|
||||||
case actExecuteMulti:
|
case actExecuteMulti:
|
||||||
@@ -1042,8 +1049,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) {
|
|||||||
actionArg = spec[offset+1 : len(spec)-1]
|
actionArg = spec[offset+1 : len(spec)-1]
|
||||||
actions = append(actions, &action{t: t, a: actionArg})
|
actions = append(actions, &action{t: t, a: actionArg})
|
||||||
}
|
}
|
||||||
if t == actUnbind {
|
if t == actUnbind || t == actRebind {
|
||||||
parseKeyChords(actionArg, "unbind target required")
|
parseKeyChords(actionArg, spec[0:offset]+" target required")
|
||||||
} else if t == actChangePreviewWindow {
|
} else if t == actChangePreviewWindow {
|
||||||
opts := previewOpts{}
|
opts := previewOpts{}
|
||||||
for _, arg := range strings.Split(actionArg, "|") {
|
for _, arg := range strings.Split(actionArg, "|") {
|
||||||
@@ -1072,6 +1079,8 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actReload
|
return actReload
|
||||||
case "unbind":
|
case "unbind":
|
||||||
return actUnbind
|
return actUnbind
|
||||||
|
case "rebind":
|
||||||
|
return actRebind
|
||||||
case "preview":
|
case "preview":
|
||||||
return actPreview
|
return actPreview
|
||||||
case "change-preview-window":
|
case "change-preview-window":
|
||||||
@@ -1280,6 +1289,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
validateJumpLabels := false
|
validateJumpLabels := false
|
||||||
validatePointer := false
|
validatePointer := false
|
||||||
validateMarker := false
|
validateMarker := false
|
||||||
|
validateEllipsis := false
|
||||||
for i := 0; i < len(allArgs); i++ {
|
for i := 0; i < len(allArgs); i++ {
|
||||||
arg := allArgs[i]
|
arg := allArgs[i]
|
||||||
switch arg {
|
switch arg {
|
||||||
@@ -1465,6 +1475,9 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.HeaderFirst = true
|
opts.HeaderFirst = true
|
||||||
case "--no-header-first":
|
case "--no-header-first":
|
||||||
opts.HeaderFirst = false
|
opts.HeaderFirst = false
|
||||||
|
case "--ellipsis":
|
||||||
|
opts.Ellipsis = nextString(allArgs, &i, "ellipsis string required")
|
||||||
|
validateEllipsis = true
|
||||||
case "--preview":
|
case "--preview":
|
||||||
opts.Preview.command = nextString(allArgs, &i, "preview command required")
|
opts.Preview.command = nextString(allArgs, &i, "preview command required")
|
||||||
case "--no-preview":
|
case "--no-preview":
|
||||||
@@ -1562,6 +1575,9 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Header = strLines(value)
|
opts.Header = strLines(value)
|
||||||
} else if match, value := optString(arg, "--header-lines="); match {
|
} else if match, value := optString(arg, "--header-lines="); match {
|
||||||
opts.HeaderLines = atoi(value)
|
opts.HeaderLines = atoi(value)
|
||||||
|
} else if match, value := optString(arg, "--ellipsis="); match {
|
||||||
|
opts.Ellipsis = value
|
||||||
|
validateEllipsis = true
|
||||||
} else if match, value := optString(arg, "--preview="); match {
|
} else if match, value := optString(arg, "--preview="); match {
|
||||||
opts.Preview.command = value
|
opts.Preview.command = value
|
||||||
} else if match, value := optString(arg, "--preview-window="); match {
|
} else if match, value := optString(arg, "--preview-window="); match {
|
||||||
@@ -1624,6 +1640,14 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
errorExit(err.Error())
|
errorExit(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if validateEllipsis {
|
||||||
|
for _, r := range opts.Ellipsis {
|
||||||
|
if !unicode.IsGraphic(r) {
|
||||||
|
errorExit("invalid character in ellipsis")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSign(sign string, signOptName string) error {
|
func validateSign(sign string, signOptName string) error {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !openbsd
|
//go:build !openbsd
|
||||||
|
|
||||||
package protector
|
package protector
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build openbsd
|
//go:build openbsd
|
||||||
|
|
||||||
package protector
|
package protector
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !386,!amd64
|
//go:build !386 && !amd64
|
||||||
|
|
||||||
package fzf
|
package fzf
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// +build !tcell
|
|
||||||
|
|
||||||
package fzf
|
package fzf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build 386 amd64
|
//go:build 386 || amd64
|
||||||
|
|
||||||
package fzf
|
package fzf
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ var offsetComponentRegex *regexp.Regexp
|
|||||||
var offsetTrimCharsRegex *regexp.Regexp
|
var offsetTrimCharsRegex *regexp.Regexp
|
||||||
var activeTempFiles []string
|
var activeTempFiles []string
|
||||||
|
|
||||||
const ellipsis string = ".."
|
|
||||||
const clearCode string = "\x1b[2J"
|
const clearCode string = "\x1b[2J"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -137,6 +136,7 @@ type Terminal struct {
|
|||||||
delimiter Delimiter
|
delimiter Delimiter
|
||||||
expect map[tui.Event]string
|
expect map[tui.Event]string
|
||||||
keymap map[tui.Event][]*action
|
keymap map[tui.Event][]*action
|
||||||
|
keymapOrg map[tui.Event][]*action
|
||||||
pressed string
|
pressed string
|
||||||
printQuery bool
|
printQuery bool
|
||||||
history *History
|
history *History
|
||||||
@@ -145,6 +145,7 @@ type Terminal struct {
|
|||||||
headerLines int
|
headerLines int
|
||||||
header []string
|
header []string
|
||||||
header0 []string
|
header0 []string
|
||||||
|
ellipsis string
|
||||||
ansi bool
|
ansi bool
|
||||||
tabstop int
|
tabstop int
|
||||||
margin [4]sizeSpec
|
margin [4]sizeSpec
|
||||||
@@ -313,6 +314,7 @@ const (
|
|||||||
actSelect
|
actSelect
|
||||||
actDeselect
|
actDeselect
|
||||||
actUnbind
|
actUnbind
|
||||||
|
actRebind
|
||||||
)
|
)
|
||||||
|
|
||||||
type placeholderFlags struct {
|
type placeholderFlags struct {
|
||||||
@@ -501,6 +503,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
|
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
|
||||||
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
|
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
|
||||||
}
|
}
|
||||||
|
keymapCopy := make(map[tui.Event][]*action)
|
||||||
|
for key, action := range opts.Keymap {
|
||||||
|
keymapCopy[key] = action
|
||||||
|
}
|
||||||
t := Terminal{
|
t := Terminal{
|
||||||
initDelay: delay,
|
initDelay: delay,
|
||||||
infoStyle: opts.InfoStyle,
|
infoStyle: opts.InfoStyle,
|
||||||
@@ -526,6 +532,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
delimiter: opts.Delimiter,
|
delimiter: opts.Delimiter,
|
||||||
expect: opts.Expect,
|
expect: opts.Expect,
|
||||||
keymap: opts.Keymap,
|
keymap: opts.Keymap,
|
||||||
|
keymapOrg: keymapCopy,
|
||||||
pressed: "",
|
pressed: "",
|
||||||
printQuery: opts.PrintQuery,
|
printQuery: opts.PrintQuery,
|
||||||
history: opts.History,
|
history: opts.History,
|
||||||
@@ -541,6 +548,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
headerLines: opts.HeaderLines,
|
headerLines: opts.HeaderLines,
|
||||||
header: header,
|
header: header,
|
||||||
header0: header,
|
header0: header,
|
||||||
|
ellipsis: opts.Ellipsis,
|
||||||
ansi: opts.Ansi,
|
ansi: opts.Ansi,
|
||||||
tabstop: opts.Tabstop,
|
tabstop: opts.Tabstop,
|
||||||
reading: true,
|
reading: true,
|
||||||
@@ -672,6 +680,7 @@ func (t *Terminal) UpdateList(merger *Merger, reset bool) {
|
|||||||
t.merger = merger
|
t.merger = merger
|
||||||
if reset {
|
if reset {
|
||||||
t.selected = make(map[int32]selectedItem)
|
t.selected = make(map[int32]selectedItem)
|
||||||
|
t.version++
|
||||||
}
|
}
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
t.reqBox.Set(reqInfo, nil)
|
t.reqBox.Set(reqInfo, nil)
|
||||||
@@ -1261,47 +1270,54 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
|
|||||||
|
|
||||||
offsets := result.colorOffsets(charOffsets, t.theme, colBase, colMatch, current)
|
offsets := result.colorOffsets(charOffsets, t.theme, colBase, colMatch, current)
|
||||||
maxWidth := t.window.Width() - (t.pointerLen + t.markerLen + 1)
|
maxWidth := t.window.Width() - (t.pointerLen + t.markerLen + 1)
|
||||||
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
|
ellipsis, ellipsisWidth := util.Truncate(t.ellipsis, maxWidth/2)
|
||||||
|
maxe = util.Constrain(maxe+util.Min(maxWidth/2-ellipsisWidth, t.hscrollOff), 0, len(text))
|
||||||
displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
|
displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
|
||||||
if displayWidth > maxWidth {
|
if displayWidth > maxWidth {
|
||||||
transformOffsets := func(diff int32) {
|
transformOffsets := func(diff int32, rightTrim bool) {
|
||||||
for idx, offset := range offsets {
|
for idx, offset := range offsets {
|
||||||
b, e := offset.offset[0], offset.offset[1]
|
b, e := offset.offset[0], offset.offset[1]
|
||||||
b += 2 - diff
|
el := int32(len(ellipsis))
|
||||||
e += 2 - diff
|
b += el - diff
|
||||||
b = util.Max32(b, 2)
|
e += el - diff
|
||||||
|
b = util.Max32(b, el)
|
||||||
|
if rightTrim {
|
||||||
|
e = util.Min32(e, int32(maxWidth-ellipsisWidth))
|
||||||
|
}
|
||||||
offsets[idx].offset[0] = b
|
offsets[idx].offset[0] = b
|
||||||
offsets[idx].offset[1] = util.Max32(b, e)
|
offsets[idx].offset[1] = util.Max32(b, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.hscroll {
|
if t.hscroll {
|
||||||
if t.keepRight && pos == nil {
|
if t.keepRight && pos == nil {
|
||||||
trimmed, diff := t.trimLeft(text, maxWidth-2)
|
trimmed, diff := t.trimLeft(text, maxWidth-ellipsisWidth)
|
||||||
transformOffsets(diff)
|
transformOffsets(diff, false)
|
||||||
text = append([]rune(ellipsis), trimmed...)
|
text = append(ellipsis, trimmed...)
|
||||||
} else if !t.overflow(text[:maxe], maxWidth-2) {
|
} else if !t.overflow(text[:maxe], maxWidth-ellipsisWidth) {
|
||||||
// Stri..
|
// Stri..
|
||||||
text, _ = t.trimRight(text, maxWidth-2)
|
text, _ = t.trimRight(text, maxWidth-ellipsisWidth)
|
||||||
text = append(text, []rune(ellipsis)...)
|
text = append(text, ellipsis...)
|
||||||
} else {
|
} else {
|
||||||
// Stri..
|
// Stri..
|
||||||
if t.overflow(text[maxe:], 2) {
|
rightTrim := false
|
||||||
text = append(text[:maxe], []rune(ellipsis)...)
|
if t.overflow(text[maxe:], ellipsisWidth) {
|
||||||
|
text = append(text[:maxe], ellipsis...)
|
||||||
|
rightTrim = true
|
||||||
}
|
}
|
||||||
// ..ri..
|
// ..ri..
|
||||||
var diff int32
|
var diff int32
|
||||||
text, diff = t.trimLeft(text, maxWidth-2)
|
text, diff = t.trimLeft(text, maxWidth-ellipsisWidth)
|
||||||
|
|
||||||
// Transform offsets
|
// Transform offsets
|
||||||
transformOffsets(diff)
|
transformOffsets(diff, rightTrim)
|
||||||
text = append([]rune(ellipsis), text...)
|
text = append(ellipsis, text...)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
text, _ = t.trimRight(text, maxWidth-2)
|
text, _ = t.trimRight(text, maxWidth-ellipsisWidth)
|
||||||
text = append(text, []rune(ellipsis)...)
|
text = append(text, ellipsis...)
|
||||||
|
|
||||||
for idx, offset := range offsets {
|
for idx, offset := range offsets {
|
||||||
offsets[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-2))
|
offsets[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-len(ellipsis)))
|
||||||
offsets[idx].offset[1] = util.Min32(offset.offset[1], int32(maxWidth))
|
offsets[idx].offset[1] = util.Min32(offset.offset[1], int32(maxWidth))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2719,13 +2735,19 @@ func (t *Terminal) Loop() {
|
|||||||
command := t.replacePlaceholder(a.a, false, string(t.input), list)
|
command := t.replacePlaceholder(a.a, false, string(t.input), list)
|
||||||
newCommand = &command
|
newCommand = &command
|
||||||
t.reading = true
|
t.reading = true
|
||||||
t.version++
|
|
||||||
}
|
}
|
||||||
case actUnbind:
|
case actUnbind:
|
||||||
keys := parseKeyChords(a.a, "PANIC")
|
keys := parseKeyChords(a.a, "PANIC")
|
||||||
for key := range keys {
|
for key := range keys {
|
||||||
delete(t.keymap, key)
|
delete(t.keymap, key)
|
||||||
}
|
}
|
||||||
|
case actRebind:
|
||||||
|
keys := parseKeyChords(a.a, "PANIC")
|
||||||
|
for key := range keys {
|
||||||
|
if originalAction, found := t.keymapOrg[key]; found {
|
||||||
|
t.keymap[key] = originalAction
|
||||||
|
}
|
||||||
|
}
|
||||||
case actChangePreview:
|
case actChangePreview:
|
||||||
if t.previewOpts.command != a.a {
|
if t.previewOpts.command != a.a {
|
||||||
togglePreview(len(a.a) > 0)
|
togglePreview(len(a.a) > 0)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !windows
|
//go:build !windows
|
||||||
|
|
||||||
package fzf
|
package fzf
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package fzf
|
package fzf
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// +build !ncurses
|
//go:build !tcell && !windows
|
||||||
// +build !tcell
|
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const (
|
|||||||
defaultEscDelay = 100
|
defaultEscDelay = 100
|
||||||
escPollInterval = 5
|
escPollInterval = 5
|
||||||
offsetPollTries = 10
|
offsetPollTries = 10
|
||||||
maxInputBuffer = 10 * 1024
|
maxInputBuffer = 1024 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
const consoleDevice string = "/dev/tty"
|
const consoleDevice string = "/dev/tty"
|
||||||
@@ -60,7 +60,7 @@ func (r *LightRenderer) csi(code string) {
|
|||||||
|
|
||||||
func (r *LightRenderer) flush() {
|
func (r *LightRenderer) flush() {
|
||||||
if r.queued.Len() > 0 {
|
if r.queued.Len() > 0 {
|
||||||
fmt.Fprint(os.Stderr, r.queued.String())
|
fmt.Fprint(os.Stderr, "\x1b[?25l"+r.queued.String()+"\x1b[?25h")
|
||||||
r.queued.Reset()
|
r.queued.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !windows
|
//go:build !windows
|
||||||
|
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build windows
|
//go:build windows
|
||||||
|
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build tcell windows
|
//go:build tcell || windows
|
||||||
|
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build tcell windows
|
//go:build tcell || windows
|
||||||
|
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !windows
|
//go:build !windows
|
||||||
|
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,23 @@ func RunesWidth(runes []rune, prefixWidth int, tabstop int, limit int) (int, int
|
|||||||
return width, -1
|
return width, -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Truncate returns the truncated runes and its width
|
||||||
|
func Truncate(input string, limit int) ([]rune, int) {
|
||||||
|
runes := []rune{}
|
||||||
|
width := 0
|
||||||
|
gr := uniseg.NewGraphemes(input)
|
||||||
|
for gr.Next() {
|
||||||
|
rs := gr.Runes()
|
||||||
|
w := runewidth.StringWidth(string(rs))
|
||||||
|
if width+w > limit {
|
||||||
|
return runes, width
|
||||||
|
}
|
||||||
|
width += w
|
||||||
|
runes = append(runes, rs...)
|
||||||
|
}
|
||||||
|
return runes, width
|
||||||
|
}
|
||||||
|
|
||||||
// Max returns the largest integer
|
// Max returns the largest integer
|
||||||
func Max(first int, second int) int {
|
func Max(first int, second int) int {
|
||||||
if first >= second {
|
if first >= second {
|
||||||
|
|||||||
@@ -54,3 +54,13 @@ func TestRunesWidth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTruncate(t *testing.T) {
|
||||||
|
truncated, width := Truncate("가나다라마", 7)
|
||||||
|
if string(truncated) != "가나다" {
|
||||||
|
t.Errorf("Expected: 가나다, actual: %s", string(truncated))
|
||||||
|
}
|
||||||
|
if width != 6 {
|
||||||
|
t.Errorf("Expected: 6, actual: %d", width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !windows
|
//go:build !windows
|
||||||
|
|
||||||
package util
|
package util
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build windows
|
//go:build windows
|
||||||
|
|
||||||
package util
|
package util
|
||||||
|
|
||||||
|
|||||||
@@ -2043,8 +2043,8 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_equal(%w[1 2 3 4 5], top5[lines]) }
|
tmux.until { |lines| assert_equal(%w[1 2 3 4 5], top5[lines]) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_unbind
|
def test_unbind_rebind
|
||||||
tmux.send_keys "seq 100 | #{FZF} --bind 'c:clear-query,d:unbind(c,d)'", :Enter
|
tmux.send_keys "seq 100 | #{FZF} --bind 'c:clear-query,d:unbind(c,d),e:rebind(c,d)'", :Enter
|
||||||
tmux.until { |lines| assert_equal 100, lines.item_count }
|
tmux.until { |lines| assert_equal 100, lines.item_count }
|
||||||
tmux.send_keys 'ab'
|
tmux.send_keys 'ab'
|
||||||
tmux.until { |lines| assert_equal '> ab', lines[-1] }
|
tmux.until { |lines| assert_equal '> ab', lines[-1] }
|
||||||
@@ -2052,6 +2052,8 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_equal '>', lines[-1] }
|
tmux.until { |lines| assert_equal '>', lines[-1] }
|
||||||
tmux.send_keys 'dabcd'
|
tmux.send_keys 'dabcd'
|
||||||
tmux.until { |lines| assert_equal '> abcd', lines[-1] }
|
tmux.until { |lines| assert_equal '> abcd', lines[-1] }
|
||||||
|
tmux.send_keys 'ecabddc'
|
||||||
|
tmux.until { |lines| assert_equal '> abdc', lines[-1] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_item_index_reset_on_reload
|
def test_item_index_reset_on_reload
|
||||||
@@ -2077,6 +2079,15 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_includes lines[1], '4' }
|
tmux.until { |lines| assert_includes lines[1], '4' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_reload_and_change_preview_should_update_preview
|
||||||
|
tmux.send_keys "seq 3 | #{FZF} --bind 'ctrl-t:reload(echo 4)+change-preview(echo {})'", :Enter
|
||||||
|
tmux.until { |lines| assert_equal 3, lines.item_count }
|
||||||
|
tmux.until { |lines| refute_includes lines[1], '1' }
|
||||||
|
tmux.send_keys 'C-t'
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.item_count }
|
||||||
|
tmux.until { |lines| assert_includes lines[1], '4' }
|
||||||
|
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 }
|
||||||
@@ -2208,6 +2219,12 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys 'a'
|
tmux.send_keys 'a'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_ellipsis
|
||||||
|
tmux.send_keys 'seq 1000 | tr "\n" , | fzf --ellipsis=SNIPSNIP -e -q500', :Enter
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_match(/^> SNIPSNIP.*SNIPSNIP$/, lines[-3]) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module TestShell
|
module TestShell
|
||||||
|
|||||||
Reference in New Issue
Block a user