diff --git a/.bashrc.save b/.bashrc.save
deleted file mode 100644
index f2223dc..0000000
--- a/.bashrc.save
+++ /dev/null
@@ -1,123 +0,0 @@
-# ~/.bashrc: executed by bash(1) for non-login shells.
-# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
-# for examples
-
-# If not running interactively, don't do anything
-case $- in
- *i*) ;;
- *) return;;
-esac
-
-# don't put duplicate lines or lines starting with space in the history.
-# See bash(1) for more options
-HISTCONTROL=ignoreboth
-
-# append to the history file, don't overwrite it
-shopt -s histappend
-
-# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
-HISTSIZE=1000
-HISTFILESIZE=2000
-
-# check the window size after each command and, if necessary,
-# update the values of LINES and COLUMNS.
-shopt -s checkwinsize
-
-# If set, the pattern "**" used in a pathname expansion context will
-# match all files and zero or more directories and subdirectories.
-#shopt -s globstar
-
-# make less more friendly for non-text input files, see lesspipe(1)
-#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
-
-# set variable identifying the chroot you work in (used in the prompt below)
-if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
- debian_chroot=$(cat /etc/debian_chroot)
-fi
-
-# set a fancy prompt (non-color, unless we know we "want" color)
-case "$TERM" in
- xterm-color|*-256color) color_prompt=yes;;
-esac
-
-# uncomment for a colored prompt, if the terminal has the capability; turned
-# off by default to not distract the user: the focus in a terminal window
-# should be on the output of commands, not on the prompt
-#force_color_prompt=yes
-
-if [ -n "$force_color_prompt" ]; then
- if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
- # We have color support; assume it's compliant with Ecma-48
- # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
- # a case would tend to support setf rather than setaf.)
- color_prompt=yes
- else
- color_prompt=
- fi
-fi
-
-if [ "$color_prompt" = yes ]; then
- PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
-else
- PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
-fi
-unset color_prompt force_color_prompt
-
-# If this is an xterm set the title to user@host:dir
-case "$TERM" in
-xterm*|rxvt*)
- PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
- ;;
-*)
- ;;
-esac
-
-# enable color support of ls and also add handy aliases
-if [ -x /usr/bin/dircolors ]; then
- test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
- alias ls='ls --color=auto'
- #alias dir='dir --color=auto'
- #alias vdir='vdir --color=auto'
-
- #alias grep='grep --color=auto'
- #alias fgrep='fgrep --color=auto'
- #alias egrep='egrep --color=auto'fi
-
-# colored GCC warnings and errors
-#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
-
-# some more ls aliases
-alias ll='ls -l'
-alias la='ls -A'
-alias l='ls -CF'
-
-# Alias definitions.
-# You may want to put all your additions into a separate file like
-# ~/.bash_aliases, instead of adding them here directly.
-# See /usr/share/doc/bash-doc/examples in the bash-doc package.
-
-if [ -f ~/.bash_aliases ]; then
- . ~/.bash_aliases
-fi
-
-# enable programmable completion features (you don't need to enable
-# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
-# sources /etc/bash.bashrc).
-if ! shopt -oq posix; then
- if [ -f /usr/share/bash-completion/bash_completion ]; then
- . /usr/share/bash-completion/bash_completion
- elif [ -f /etc/bash_completion ]; then
- . /etc/bash_completion
- fi
-fi
-
-PATH=${PATH}:/opt/qt515/bin
-echo ""
-echo ""
-#screenfetch -p
-neofetch --color_blocks off
-echo ""
-zsh
-
-#[ -f ~/.fzf.bash ] && source ~/.fzf.bash
-
diff --git a/.fzf.zsh b/.fzf.zsh
deleted file mode 100644
index 0555318..0000000
--- a/.fzf.zsh
+++ /dev/null
@@ -1,13 +0,0 @@
-# Setup fzf
-# ---------
-if [[ ! "$PATH" == */home/q3aql/.fzf/bin* ]]; then
- export PATH="${PATH:+${PATH}:}/home/q3aql/.fzf/bin"
-fi
-
-# Auto-completion
-# ---------------
-[[ $- == *i* ]] && source "/home/q3aql/.fzf/shell/completion.zsh" 2> /dev/null
-
-# Key bindings
-# ------------
-source "/home/q3aql/.fzf/shell/key-bindings.zsh"
diff --git a/.fzf/.goreleaser.yml b/.fzf/.goreleaser.yml
deleted file mode 100644
index bab275d..0000000
--- a/.fzf/.goreleaser.yml
+++ /dev/null
@@ -1,119 +0,0 @@
----
-project_name: fzf
-
-before:
- hooks:
- - go mod download
-
-builds:
- - id: fzf-macos
- binary: fzf
- goos:
- - darwin
- goarch:
- - amd64
- ldflags:
- - "-s -w -X main.version={{ .Version }} -X main.revision={{ .ShortCommit }}"
- hooks:
- post: |
- sh -c '
- cat > /tmp/fzf-gon-amd64.hcl << EOF
- source = ["./dist/fzf-macos_darwin_amd64/fzf"]
- bundle_id = "kr.junegunn.fzf"
- apple_id {
- username = "junegunn.c@gmail.com"
- password = "@env:AC_PASSWORD"
- }
- sign {
- application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
- }
- zip {
- output_path = "./dist/fzf-{{ .Version }}-darwin_amd64.zip"
- }
- EOF
- gon /tmp/fzf-gon-amd64.hcl
- '
-
- - id: fzf-macos-arm
- binary: fzf
- goos:
- - darwin
- goarch:
- - arm64
- ldflags:
- - "-s -w -X main.version={{ .Version }} -X main.revision={{ .ShortCommit }}"
- hooks:
- post: |
- sh -c '
- cat > /tmp/fzf-gon-arm64.hcl << EOF
- source = ["./dist/fzf-macos-arm_darwin_arm64/fzf"]
- bundle_id = "kr.junegunn.fzf"
- apple_id {
- username = "junegunn.c@gmail.com"
- password = "@env:AC_PASSWORD"
- }
- sign {
- application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
- }
- zip {
- output_path = "./dist/fzf-{{ .Version }}-darwin_arm64.zip"
- }
- EOF
- gon /tmp/fzf-gon-arm64.hcl
- '
-
- - id: fzf
- goos:
- - linux
- - windows
- - freebsd
- - openbsd
- goarch:
- - amd64
- - arm
- - arm64
- goarm:
- - 5
- - 6
- - 7
- ldflags:
- - "-s -w -X main.version={{ .Version }} -X main.revision={{ .ShortCommit }}"
- ignore:
- - goos: freebsd
- goarch: arm
- - goos: openbsd
- goarch: arm
- - goos: freebsd
- goarch: arm64
- - goos: openbsd
- goarch: arm64
-
-archives:
- - name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
- builds:
- - fzf
- format: tar.gz
- format_overrides:
- - goos: windows
- format: zip
- files:
- - non-existent*
-
-release:
- github:
- owner: junegunn
- name: fzf
- prerelease: auto
- name_template: '{{ .Tag }}'
- extra_files:
- - glob: ./dist/fzf-*darwin*.zip
-
-snapshot:
- name_template: "{{ .Tag }}-devel"
-
-changelog:
- sort: asc
- filters:
- exclude:
- - README
- - test
diff --git a/.fzf/.rubocop.yml b/.fzf/.rubocop.yml
deleted file mode 100644
index c131deb..0000000
--- a/.fzf/.rubocop.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-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/StringConcatenation:
- Enabled: false
-Style/OptionalBooleanParameter:
- Enabled: false
-Style/WordArray:
- MinSize: 1
diff --git a/.fzf/ADVANCED.md b/.fzf/ADVANCED.md
deleted file mode 100644
index f00be0c..0000000
--- a/.fzf/ADVANCED.md
+++ /dev/null
@@ -1,565 +0,0 @@
-Advanced fzf examples
-======================
-
-*(Last update: 2021/05/22)*
-
-
-
-* [Introduction](#introduction)
-* [Screen Layout](#screen-layout)
- * [`--height`](#--height)
- * [`fzf-tmux`](#fzf-tmux)
- * [Popup window support](#popup-window-support)
-* [Dynamic reloading of the list](#dynamic-reloading-of-the-list)
- * [Updating the list of processes by pressing CTRL-R](#updating-the-list-of-processes-by-pressing-ctrl-r)
- * [Toggling between data sources](#toggling-between-data-sources)
-* [Ripgrep integration](#ripgrep-integration)
- * [Using fzf as the secondary filter](#using-fzf-as-the-secondary-filter)
- * [Using fzf as interative Ripgrep launcher](#using-fzf-as-interative-ripgrep-launcher)
- * [Switching to fzf-only search mode](#switching-to-fzf-only-search-mode)
-* [Log tailing](#log-tailing)
-* [Key bindings for git objects](#key-bindings-for-git-objects)
- * [Files listed in `git status`](#files-listed-in-git-status)
- * [Branches](#branches)
- * [Commit hashes](#commit-hashes)
-* [Color themes](#color-themes)
- * [Generating fzf color theme from Vim color schemes](#generating-fzf-color-theme-from-vim-color-schemes)
-
-
-
-Introduction
-------------
-
-fzf is an interactive [Unix filter][filter] program that is designed to be
-used with other Unix tools. It reads a list of items from the standard input,
-allows you to select a subset of the items, and prints the selected ones to
-the standard output. You can think of it as an interactive version of *grep*,
-and it's already useful even if you don't know any of its options.
-
-```sh
-# 1. ps: Feed the list of processes to fzf
-# 2. fzf: Interactively select a process using fuzzy matching algorithm
-# 3. awk: Take the PID from the selected line
-# 3. kill: Kill the process with the PID
-ps -ef | fzf | awk '{print $2}' | xargs kill -9
-```
-
-[filter]: https://en.wikipedia.org/wiki/Filter_(software)
-
-While the above example succinctly summarizes the fundamental concept of fzf,
-you can build much more sophisticated interactive workflows using fzf once you
-learn its wide variety of features.
-
-- To see the full list of options and features, see `man fzf`
-- To see the latest additions, see [CHANGELOG.md](CHANGELOG.md)
-
-This document will guide you through some examples that will familiarize you
-with the advanced features of fzf.
-
-Screen Layout
--------------
-
-### `--height`
-
-fzf by default opens in fullscreen mode, but it's not always desirable.
-Oftentimes, you want to see the current context of the terminal while using
-fzf. `--height` is an option for opening fzf below the cursor in
-non-fullscreen mode so you can still see the previous commands and their
-results above it.
-
-```sh
-fzf --height=40%
-```
-
-![image](https://user-images.githubusercontent.com/700826/113379893-c184c680-93b5-11eb-9676-c7c0a2f01748.png)
-
-You might also want to experiment with other layout options such as
-`--layout=reverse`, `--info=inline`, `--border`, `--margin`, etc.
-
-```sh
-fzf --height=40% --layout=reverse
-fzf --height=40% --layout=reverse --info=inline
-fzf --height=40% --layout=reverse --info=inline --border
-fzf --height=40% --layout=reverse --info=inline --border --margin=1
-fzf --height=40% --layout=reverse --info=inline --border --margin=1 --padding=1
-```
-
-![image](https://user-images.githubusercontent.com/700826/113379932-dfeac200-93b5-11eb-9e28-df1a2ee71f90.png)
-
-*(See `Layout` section of the man page to see the full list of options)*
-
-But you definitely don't want to repeat `--height=40% --layout=reverse
---info=inline --border --margin=1 --padding=1` every time you use fzf. You
-could write a wrapper script or shell alias, but there is an easier option.
-Define `$FZF_DEFAULT_OPTS` like so:
-
-```sh
-export FZF_DEFAULT_OPTS="--height=40% --layout=reverse --info=inline --border --margin=1 --padding=1"
-```
-
-### `fzf-tmux`
-
-Before fzf had `--height` option, we would open fzf in a tmux split pane not
-to take up the whole screen. This is done using `fzf-tmux` script.
-
-```sh
-# Open fzf on a tmux split pane below the current pane.
-# Takes the same set of options.
-fzf-tmux --layout=reverse
-```
-
-![image](https://user-images.githubusercontent.com/700826/113379973-f1cc6500-93b5-11eb-8860-c9bc4498aadf.png)
-
-The limitation of `fzf-tmux` is that it only works when you're on tmux unlike
-`--height` option. But the advantage of it is that it's more flexible.
-(See `man fzf-tmux` for available options.)
-
-```sh
-# On the right (50%)
-fzf-tmux -r
-
-# On the left (30%)
-fzf-tmux -l30%
-
-# Above the cursor
-fzf-tmux -u30%
-```
-
-![image](https://user-images.githubusercontent.com/700826/113379983-fa24a000-93b5-11eb-93eb-8a3d39b2f163.png)
-
-![image](https://user-images.githubusercontent.com/700826/113380001-0577cb80-93b6-11eb-95d0-2ba453866882.png)
-
-![image](https://user-images.githubusercontent.com/700826/113380040-1d4f4f80-93b6-11eb-9bef-737fb120aafe.png)
-
-#### Popup window support
-
-But here's the really cool part; tmux 3.2 added support for popup windows. So
-you can open fzf in a popup window, which is quite useful if you frequently
-use split panes.
-
-```sh
-# Open tmux in a tmux popup window (default size: 50% of the screen)
-fzf-tmux -p
-
-# 80% width, 60% height
-fzf-tmux -p 80%,60%
-```
-
-![image](https://user-images.githubusercontent.com/700826/113380106-4a9bfd80-93b6-11eb-8cee-aeb1c4ce1a1f.png)
-
-> You might also want to check out my tmux plugins which support this popup
-> window layout.
->
-> - https://github.com/junegunn/tmux-fzf-url
-> - https://github.com/junegunn/tmux-fzf-maccy
-
-Dynamic reloading of the list
------------------------------
-
-fzf can dynamically update the candidate list using an arbitrary program with
-`reload` bindings (The design document for `reload` can be found
-[here][reload]).
-
-[reload]: https://github.com/junegunn/fzf/issues/1750
-
-### Updating the list of processes by pressing CTRL-R
-
-This example shows how you can set up a binding for dynamically updating the
-list without restarting fzf.
-
-```sh
-(date; ps -ef) |
- fzf --bind='ctrl-r:reload(date; ps -ef)' \
- --header=$'Press CTRL-R to reload\n\n' --header-lines=2 \
- --preview='echo {}' --preview-window=down,3,wrap \
- --layout=reverse --height=80% | awk '{print $2}' | xargs kill -9
-```
-
-![image](https://user-images.githubusercontent.com/700826/113465047-200c7c00-946c-11eb-918c-268f37a900c8.png)
-
-- The initial command is `(date; ps -ef)`. It prints the current date and
- time, and the list of the processes.
-- With `--header` option, you can show any message as the fixed header.
-- To disallow selecting the first two lines (`date` and `ps` header), we use
- `--header-lines=2` option.
-- `--bind='ctrl-r:reload(date; ps -ef)'` binds CTRL-R to `reload` action that
- runs `date; ps -ef`, so we can update the list of the processes by pressing
- CTRL-R.
-- We use simple `echo {}` preview option, so we can see the entire line on the
- preview window below even if it's too long
-
-### Toggling between data sources
-
-You're not limited to just one reload binding. Set up multiple bindings so
-you can switch between data sources.
-
-```sh
-find * | fzf --prompt 'All> ' \
- --header 'CTRL-D: Directories / CTRL-F: Files' \
- --bind 'ctrl-d:change-prompt(Directories> )+reload(find * -type d)' \
- --bind 'ctrl-f:change-prompt(Files> )+reload(find * -type f)'
-```
-
-![image](https://user-images.githubusercontent.com/700826/113465073-4af6d000-946c-11eb-858f-2372c0955f67.png)
-
-![image](https://user-images.githubusercontent.com/700826/113465072-46321c00-946c-11eb-9b6f-cda3951df579.png)
-
-Ripgrep integration
--------------------
-
-### Using fzf as the secondary filter
-
-* Requires [bat][bat]
-* Requires [Ripgrep][rg]
-
-[bat]: https://github.com/sharkdp/bat
-[rg]: https://github.com/BurntSushi/ripgrep
-
-fzf is pretty fast for filtering a list that you will rarely have to think
-about its performance. But it is not the right tool for searching for text
-inside many large files, and in that case you should definitely use something
-like [Ripgrep][rg].
-
-In the next example, Ripgrep is the primary filter that searches for the given
-text in files, and fzf is used as the secondary fuzzy filter that adds
-interactivity to the workflow. And we use [bat][bat] to show the matching line in
-the preview window.
-
-This is a bash script and it will not run as expected on other non-compliant
-shells. To avoid the compatibility issue, let's save this snippet as a script
-file called `rfv`.
-
-```bash
-#!/usr/bin/env bash
-
-# 1. Search for text in files using Ripgrep
-# 2. Interactively narrow down the list using fzf
-# 3. Open the file in Vim
-IFS=: read -ra selected < <(
- rg --color=always --line-number --no-heading --smart-case "${*:-}" |
- fzf --ansi \
- --color "hl:-1:underline,hl+:-1:underline:reverse" \
- --delimiter : \
- --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]}"
-```
-
-And run it with an initial query string.
-
-```sh
-# Make the script executable
-chmod +x rfv
-
-# Run it with the initial query "algo"
-./rfv algo
-```
-
-> Ripgrep will perform the initial search and list all the lines that contain
-`algo`. Then we further narrow down the list on fzf.
-
-![image](https://user-images.githubusercontent.com/700826/113683873-a42a6200-96ff-11eb-9666-26ce4091b0e4.png)
-
-I know it's a lot to digest, let's try to break down the code.
-
-- Ripgrep prints the matching lines in the following format
- ```
- man/man1/fzf.1:54:.BI "--algo=" TYPE
- man/man1/fzf.1:55:Fuzzy matching algorithm (default: v2)
- man/man1/fzf.1:58:.BR v2 " Optimal scoring algorithm (quality)"
- src/pattern_test.go:7: "github.com/junegunn/fzf/src/algo"
- ```
- The first token delimited by `:` is the file path, and the second token is
- the line number of the matching line. They respectively correspond to `{1}`
- and `{2}` in the preview command.
- - `--preview 'bat --color=always {1} --highlight-line {2}'`
-- As we run `rg` with `--color=always` option, we should tell fzf to parse
- ANSI color codes in the input by setting `--ansi`.
-- We customize how fzf colors various text elements using `--color` option.
- `-1` tells fzf to keep the original color from the input. See `man fzf` for
- available color options.
-- The value of `--preview-window` option consists of 5 components delimited
- by `,`
- 1. `up` — Position of the preview window
- 1. `60%` — Size of the preview window
- 1. `border-bottom` — Preview window border only on the bottom side
- 1. `+{2}+3/3` — Scroll offset of the preview contents
- 1. `~3` — Fixed header
-- Let's break down the latter two. We want to display the bat output in the
- preview window with a certain scroll offset so that the matching line is
- positioned near the center of the preview window.
- - `+{2}` — The base offset is extracted from the second token
- - `+3` — We add 3 lines to the base offset to compensate for the header
- part of `bat` output
- - ```
- ───────┬──────────────────────────────────────────────────────────
- │ File: CHANGELOG.md
- ───────┼──────────────────────────────────────────────────────────
- 1 │ CHANGELOG
- 2 │ =========
- 3 │
- 4 │ 0.26.0
- 5 │ ------
- ```
- - `/3` adjusts the offset so that the matching line is shown at a third
- position in the window
- - `~3` makes the top three lines fixed header so that they are always
- visible regardless of the scroll offset
-- Once we selected a line, we open the file with `vim` (`vim
- "${selected[0]}"`) and move the cursor to the line (`+${selected[1]}`).
-
-### Using fzf as interative Ripgrep launcher
-
-We have learned that we can bind `reload` action to a key (e.g.
-`--bind=ctrl-r:execute(ps -ef)`). In the next example, we are going to **bind
-`reload` action to `change` event** so that whenever the user *changes* the
-query string on fzf, `reload` action is triggered.
-
-Here is a variation of the above `rfv` script. fzf will restart Ripgrep every
-time the user updates the query string on fzf. Searching and filtering is
-completely done by Ripgrep, and fzf merely provides the interactive interface.
-So we lose the "fuzziness", but the performance will be better on larger
-projects, and it will free up memory as you narrow down the results.
-
-```bash
-#!/usr/bin/env bash
-
-# 1. Search for text in files using Ripgrep
-# 2. Interactively restart Ripgrep with reload action
-# 3. Open the file in Vim
-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 \
- --disabled --query "$INITIAL_QUERY" \
- --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
- --delimiter : \
- --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]}"
-```
-
-![image](https://user-images.githubusercontent.com/700826/113684212-f9ff0a00-96ff-11eb-8737-7bb571d320cc.png)
-
-- Instead of starting fzf in `rg ... | fzf` form, we start fzf without an
- explicit input, but with a custom `FZF_DEFAULT_COMMAND` variable. This way
- fzf can kill the initial Ripgrep process it starts with the initial query.
- Otherwise, the initial Ripgrep process will keep consuming system resources
- even after `reload` is triggered.
-- Filtering is no longer a responsibility of fzf; hence `--disabled`
-- `{q}` in the reload command evaluates to the query string on fzf prompt.
-- `sleep 0.1` in the reload command is for "debouncing". This small delay will
- reduce the number of intermediate Ripgrep processes while we're typing in
- a query.
-
-### Switching to fzf-only search mode
-
-*(Requires fzf 0.27.1 or above)*
-
-In the previous example, we lost fuzzy matching capability as we completely
-delegated search functionality to Ripgrep. But we can dynamically switch to
-fzf-only search mode by *"unbinding"* `reload` action from `change` event.
-
-```sh
-#!/usr/bin/env bash
-
-# Two-phase filtering with Ripgrep and fzf
-#
-# 1. Search for text in files using Ripgrep
-# 2. Interactively restart Ripgrep with reload action
-# * Press alt-enter to switch to fzf-only filtering
-# 3. Open the file in Vim
-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 "alt-enter:unbind(change,alt-enter)+change-prompt(2. fzf> )+enable-search+clear-query" \
- --prompt '1. ripgrep> ' \
- --delimiter : \
- --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]}"
-```
-
-* Phase 1. Filtering with Ripgrep
-![image](https://user-images.githubusercontent.com/700826/119213880-735e8a80-bafd-11eb-8493-123e4be24fbc.png)
-* Phase 2. Filtering with fzf
-![image](https://user-images.githubusercontent.com/700826/119213887-7e191f80-bafd-11eb-98c9-71a1af9d7aab.png)
-
-- We added `--prompt` option to show that fzf is initially running in "Ripgrep
- launcher mode".
-- We added `alt-enter` binding that
- 1. unbinds `change` event, so Ripgrep is no longer restarted on key press
- 2. changes the prompt to `2. fzf>`
- 3. enables search functionality of fzf
- 4. clears the current query string that was used to start Ripgrep process
- 5. and unbinds `alt-enter` itself as this is a one-off event
-- We reverted `--color` option for customizing how the matching chunks are
- displayed in the second phase
-
-Log tailing
------------
-
-fzf can run long-running preview commands and render partial results before
-completion. And when you specify `follow` flag in `--preview-window` option,
-fzf will "`tail -f`" the result, automatically scrolling to the bottom.
-
-```bash
-# With "follow", preview window will automatically scroll to the bottom.
-# "\033[2J" is an ANSI escape sequence for clearing the screen.
-# When fzf reads this code it clears the previous preview contents.
-fzf --preview-window follow --preview 'for i in $(seq 100000); do
- echo "$i"
- sleep 0.01
- (( i % 300 == 0 )) && printf "\033[2J"
-done'
-```
-
-![image](https://user-images.githubusercontent.com/700826/113473303-dd669600-94a3-11eb-88a9-1f61b996bb0e.png)
-
-Admittedly, that was a silly example. Here's a practical one for browsing
-Kubernetes pods.
-
-```bash
-#!/usr/bin/env bash
-
-read -ra tokens < <(
- kubectl get pods --all-namespaces |
- fzf --info=inline --layout=reverse --header-lines=1 --border \
- --prompt "$(kubectl config current-context | sed 's/-context$//')> " \
- --header $'Press CTRL-O to open log in editor\n\n' \
- --bind ctrl-/:toggle-preview \
- --bind 'ctrl-o:execute:${EDITOR:-vim} <(kubectl logs --namespace {1} {2}) > /dev/tty' \
- --preview-window up,follow \
- --preview 'kubectl logs --follow --tail=100000 --namespace {1} {2}' "$@"
-)
-[ ${#tokens} -gt 1 ] &&
- kubectl exec -it --namespace "${tokens[0]}" "${tokens[1]}" -- bash
-```
-
-![image](https://user-images.githubusercontent.com/700826/113473547-1d7a4880-94a5-11eb-98ef-9aa6f0ed215a.png)
-
-- The preview window will *"log tail"* the pod
- - Holding on to a large amount of log will consume a lot of memory. So we
- limited the initial log amount with `--tail=100000`.
-- With `execute` binding, you can press CTRL-O to open the log in your editor
- without leaving fzf
-- Select a pod (with an enter key) to `kubectl exec` into it
-
-Key bindings for git objects
-----------------------------
-
-I have [blogged](https://junegunn.kr/2016/07/fzf-git) about my fzf+git key
-bindings a few years ago. I'm going to show them here again, because they are
-seriously useful.
-
-### Files listed in `git status`
-
-CTRL-GCTRL-F
-
-![image](https://user-images.githubusercontent.com/700826/113473779-a9d93b00-94a6-11eb-87b5-f62a8d0a0efc.png)
-
-### Branches
-
-CTRL-GCTRL-B
-
-![image](https://user-images.githubusercontent.com/700826/113473758-87dfb880-94a6-11eb-82f4-9218103f10bd.png)
-
-### Commit hashes
-
-CTRL-GCTRL-H
-
-![image](https://user-images.githubusercontent.com/700826/113473765-91692080-94a6-11eb-8d38-ed4d41f27ac1.png)
-
-
-The full source code can be found [here](https://gist.github.com/junegunn/8b572b8d4b5eddd8b85e5f4d40f17236).
-
-Color themes
-------------
-
-You can customize how fzf colors the text elements with `--color` option. Here
-are a few color themes. Note that you need a terminal emulator that can
-display 24-bit colors.
-
-```sh
-# junegunn/seoul256.vim (dark)
-export FZF_DEFAULT_OPTS='--color=bg+:#3F3F3F,bg:#4B4B4B,border:#6B6B6B,spinner:#98BC99,hl:#719872,fg:#D9D9D9,header:#719872,info:#BDBB72,pointer:#E12672,marker:#E17899,fg+:#D9D9D9,preview-bg:#3F3F3F,prompt:#98BEDE,hl+:#98BC99'
-```
-
-![seoul256](https://user-images.githubusercontent.com/700826/113475011-2c192d80-94ae-11eb-9d17-1e5867bae01f.png)
-
-```sh
-# junegunn/seoul256.vim (light)
-export FZF_DEFAULT_OPTS='--color=bg+:#D9D9D9,bg:#E1E1E1,border:#C8C8C8,spinner:#719899,hl:#719872,fg:#616161,header:#719872,info:#727100,pointer:#E12672,marker:#E17899,fg+:#616161,preview-bg:#D9D9D9,prompt:#0099BD,hl+:#719899'
-```
-
-![seoul256-light](https://user-images.githubusercontent.com/700826/113475022-389d8600-94ae-11eb-905f-0939dd535837.png)
-
-```sh
-# morhetz/gruvbox
-export FZF_DEFAULT_OPTS='--color=bg+:#3c3836,bg:#32302f,spinner:#fb4934,hl:#928374,fg:#ebdbb2,header:#928374,info:#8ec07c,pointer:#fb4934,marker:#fb4934,fg+:#ebdbb2,prompt:#fb4934,hl+:#fb4934'
-```
-
-![gruvbox](https://user-images.githubusercontent.com/700826/113475042-494dfc00-94ae-11eb-9322-cd03a027305a.png)
-
-```sh
-# arcticicestudio/nord-vim
-export FZF_DEFAULT_OPTS='--color=bg+:#3B4252,bg:#2E3440,spinner:#81A1C1,hl:#616E88,fg:#D8DEE9,header:#616E88,info:#81A1C1,pointer:#81A1C1,marker:#81A1C1,fg+:#D8DEE9,prompt:#81A1C1,hl+:#81A1C1'
-```
-
-![nord](https://user-images.githubusercontent.com/700826/113475063-67b3f780-94ae-11eb-9b24-5f0d22b63399.png)
-
-```sh
-# tomasr/molokai
-export FZF_DEFAULT_OPTS='--color=bg+:#293739,bg:#1B1D1E,border:#808080,spinner:#E6DB74,hl:#7E8E91,fg:#F8F8F2,header:#7E8E91,info:#A6E22E,pointer:#A6E22E,marker:#F92672,fg+:#F8F8F2,prompt:#F92672,hl+:#F92672'
-```
-
-![molokai](https://user-images.githubusercontent.com/700826/113475085-8619f300-94ae-11eb-85e4-2766fc3246bf.png)
-
-### Generating fzf color theme from Vim color schemes
-
-The Vim plugin of fzf can generate `--color` option from the current color
-scheme according to `g:fzf_colors` variable. You can find the detailed
-explanation [here](https://github.com/junegunn/fzf/blob/master/README-VIM.md#explanation-of-gfzf_colors).
-
-Here is an example. Add this to your Vim configuration file.
-
-```vim
-let g:fzf_colors =
-\ { 'fg': ['fg', 'Normal'],
- \ 'bg': ['bg', 'Normal'],
- \ 'preview-bg': ['bg', 'NormalFloat'],
- \ 'hl': ['fg', 'Comment'],
- \ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
- \ 'bg+': ['bg', 'CursorLine', 'CursorColumn'],
- \ 'hl+': ['fg', 'Statement'],
- \ 'info': ['fg', 'PreProc'],
- \ 'border': ['fg', 'Ignore'],
- \ 'prompt': ['fg', 'Conditional'],
- \ 'pointer': ['fg', 'Exception'],
- \ 'marker': ['fg', 'Keyword'],
- \ 'spinner': ['fg', 'Label'],
- \ 'header': ['fg', 'Comment'] }
-```
-
-Then you can see how the `--color` option is generated by printing the result
-of `fzf#wrap()`.
-
-```vim
-:echo fzf#wrap()
-```
-
-Use this command to append `export FZF_DEFAULT_OPTS="..."` line to the end of
-the current file.
-
-```vim
-:call append('$', printf('export FZF_DEFAULT_OPTS="%s"', matchstr(fzf#wrap().options, "--color[^']*")))
-```
diff --git a/.fzf/BUILD.md b/.fzf/BUILD.md
deleted file mode 100644
index 8c318f4..0000000
--- a/.fzf/BUILD.md
+++ /dev/null
@@ -1,49 +0,0 @@
-Building fzf
-============
-
-Build instructions
-------------------
-
-### Prerequisites
-
-- Go 1.13 or above
-
-### Using Makefile
-
-```sh
-# Build fzf binary for your platform in target
-make
-
-# Build fzf binary and copy it to bin directory
-make install
-
-# Build fzf binaries and archives for all platforms using goreleaser
-make build
-
-# Publish GitHub release
-make release
-```
-
-> :warning: Makefile uses git commands to determine the version and the
-> revision information for `fzf --version`. So if you're building fzf from an
-> environment where its git information is not available, you have to manually
-> set `$FZF_VERSION` and `$FZF_REVISION`.
->
-> e.g. `FZF_VERSION=0.24.0 FZF_REVISION=tarball make`
-
-Third-party libraries used
---------------------------
-
-- [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
- - Licensed under [MIT](http://mattn.mit-license.org)
-- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
- - Licensed under [MIT](http://mattn.mit-license.org)
-- [mattn/go-isatty](https://github.com/mattn/go-isatty)
- - Licensed under [MIT](http://mattn.mit-license.org)
-- [tcell](https://github.com/gdamore/tcell)
- - Licensed under [Apache License 2.0](https://github.com/gdamore/tcell/blob/master/LICENSE)
-
-License
--------
-
-[MIT](LICENSE)
diff --git a/.fzf/CHANGELOG.md b/.fzf/CHANGELOG.md
deleted file mode 100644
index 2b2d20c..0000000
--- a/.fzf/CHANGELOG.md
+++ /dev/null
@@ -1,1193 +0,0 @@
-CHANGELOG
-=========
-
-0.28.0
-------
-- Added `--header-first` option to print header before the prompt line
- ```sh
- fzf --header $'Welcome to fzf\n▔▔▔▔▔▔▔▔▔▔▔▔▔▔' --reverse --height 30% --border --header-first
- ```
-- Added `--scroll-off=LINES` option (similar to `scrolloff` option of Vim)
- - You can set it to a very large number so that the cursor stays in the
- middle of the screen while scrolling
- ```sh
- fzf --scroll-off=5
- fzf --scroll-off=999
- ```
-- Fixed bug where preview window is not updated on `reload` (#2644)
-- fzf on Windows will also use `$SHELL` to execute external programs
- - See #2638 and #2647
- - Thanks to @rashil2000, @vovcacik, and @janlazo
-
-0.27.3
-------
-- Preview window is `hidden` by default when there are `preview` bindings but
- `--preview` command is not given
-- Fixed bug where `{n}` is not properly reset on `reload`
-- Fixed bug where spinner is not displayed on `reload`
-- Enhancements in tcell renderer for Windows (#2616)
-- Vim plugin
- - `sinklist` is added as a synonym to `sink*` so that it's easier to add
- a function to a spec dictionary
- ```vim
- let spec = { 'source': 'ls', 'options': ['--multi', '--preview', 'cat {}'] }
- function spec.sinklist(matches)
- echom string(a:matches)
- endfunction
-
- call fzf#run(fzf#wrap(spec))
- ```
- - Vim 7 compatibility
-
-0.27.2
-------
-- 16 base ANSI colors can be specified by their names
- ```sh
- fzf --color fg:3,fg+:11
- fzf --color fg:yellow,fg+:bright-yellow
- ```
-- Fix bug where `--read0` not properly displaying long lines
-
-0.27.1
-------
-- Added `unbind` action. In the following Ripgrep launcher example, you can
- use `unbind(reload)` to switch to fzf-only filtering mode.
- - See https://github.com/junegunn/fzf/blob/master/ADVANCED.md#switching-to-fzf-only-search-mode
-- Vim plugin
- - Vim plugin will stop immediately even when the source command hasn't finished
- ```vim
- " fzf will read the stream file while allowing other processes to append to it
- call fzf#run({'source': 'cat /dev/null > /tmp/stream; tail -f /tmp/stream'})
- ```
- - It is now possible to open popup window relative to the current window
- ```vim
- let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': v:true, 'yoffset': 1.0 } }
- ```
-
-0.27.0
-------
-- More border options for `--preview-window`
- ```sh
- fzf --preview 'cat {}' --preview-window border-left
- fzf --preview 'cat {}' --preview-window border-left --border horizontal
- fzf --preview 'cat {}' --preview-window top:border-bottom
- fzf --preview 'cat {}' --preview-window top:border-horizontal
- ```
-- Automatically set `/dev/tty` as STDIN on execute action
- ```sh
- # Redirect /dev/tty to suppress "Vim: Warning: Input is not from a terminal"
- # ls | fzf --bind "enter:execute(vim {} < /dev/tty)"
-
- # "< /dev/tty" part is no longer needed
- ls | fzf --bind "enter:execute(vim {})"
- ```
-- Bug fixes and improvements
-- Signed and notarized macOS binaries
- (Huge thanks to [BACKERS.md](https://github.com/junegunn/junegunn/blob/main/BACKERS.md)!)
-
-0.26.0
-------
-- Added support for fixed header in preview window
- ```sh
- # Display top 3 lines as the fixed header
- fzf --preview 'bat --style=header,grid --color=always {}' --preview-window '~3'
- ```
-- More advanced preview offset expression to better support the fixed header
- ```sh
- # Preview with bat, matching line in the middle of the window below
- # the fixed header of the top 3 lines
- #
- # ~3 Top 3 lines as the fixed header
- # +{2} Base scroll offset extracted from the second field
- # +3 Extra offset to compensate for the 3-line header
- # /2 Put in the middle of the preview area
- #
- git grep --line-number '' |
- fzf --delimiter : \
- --preview 'bat --style=full --color=always --highlight-line {2} {1}' \
- --preview-window '~3:+{2}+3/2'
- ```
-- Added `select` and `deselect` action for unconditionally selecting or
- deselecting a single item in `--multi` mode. Complements `toggle` action.
-- Significant performance improvement in ANSI code processing
-- Bug fixes and improvements
-- Built with Go 1.16
-
-0.25.1
-------
-- Added `close` action
- - Close preview window if open, abort fzf otherwise
-- Bug fixes and improvements
-
-0.25.0
-------
-- Text attributes set in `--color` are not reset when fzf sees another
- `--color` option for the same element. This allows you to put custom text
- attributes in your `$FZF_DEFAULT_OPTS` and still have those attributes
- even when you override the colors.
-
- ```sh
- # Default colors and attributes
- fzf
-
- # Apply custom text attributes
- export FZF_DEFAULT_OPTS='--color fg+:italic,hl:-1:underline,hl+:-1:reverse:underline'
-
- fzf
-
- # Different colors but you still have the attributes
- fzf --color hl:176,hl+:177
-
- # Write "regular" if you want to clear the attributes
- fzf --color hl:176:regular,hl+:177:regular
- ```
-- Renamed `--phony` to `--disabled`
-- You can dynamically enable and disable the search functionality using the
- new `enable-search`, `disable-search`, and `toggle-search` actions
-- You can assign a different color to the query string for when search is disabled
- ```sh
- fzf --color query:#ffffff,disabled:#999999 --bind space:toggle-search
- ```
-- Added `last` action to move the cursor to the last match
- - The opposite action `top` is renamed to `first`, but `top` is still
- recognized as a synonym for backward compatibility
-- Added `preview-top` and `preview-bottom` actions
-- Extended support for alt key chords: alt with any case-sensitive single character
- ```sh
- fzf --bind alt-,:first,alt-.:last
- ```
-
-0.24.4
-------
-- Added `--preview-window` option `follow`
- ```sh
- # Preview window will automatically scroll to the bottom
- fzf --preview-window follow --preview 'for i in $(seq 100000); do
- echo "$i"
- sleep 0.01
- (( i % 300 == 0 )) && printf "\033[2J"
- done'
- ```
-- Added `change-prompt` action
- ```sh
- fzf --prompt 'foo> ' --bind $'a:change-prompt:\x1b[31mbar> '
- ```
-- Bug fixes and improvements
-
-0.24.3
-------
-- Added `--padding` option
- ```sh
- fzf --margin 5% --padding 5% --border --preview 'cat {}' \
- --color bg:#222222,preview-bg:#333333
- ```
-
-0.24.2
-------
-- Bug fixes and improvements
-
-0.24.1
-------
-- Fixed broken `--color=[bw|no]` option
-
-0.24.0
-------
-- Real-time rendering of preview window
- ```sh
- # fzf can render preview window before the command completes
- fzf --preview 'sleep 1; for i in $(seq 100); do echo $i; sleep 0.01; done'
-
- # Preview window can process ANSI escape sequence (CSI 2 J) for clearing the display
- fzf --preview 'for i in $(seq 100000); do
- (( i % 200 == 0 )) && printf "\033[2J"
- echo "$i"
- sleep 0.01
- done'
- ```
-- Updated `--color` option to support text styles
- - `regular` / `bold` / `dim` / `underline` / `italic` / `reverse` / `blink`
- ```sh
- # * Set -1 to keep the original color
- # * Multiple style attributes can be combined
- # * Italic style may not be supported by some terminals
- rg --line-number --no-heading --color=always "" |
- fzf --ansi --prompt "Rg: " \
- --color fg+:italic,hl:underline:-1,hl+:italic:underline:reverse:-1 \
- --color pointer:reverse,prompt:reverse,input:159 \
- --pointer ' '
- ```
-- More `--border` options
- - `vertical`, `top`, `bottom`, `left`, `right`
- - Updated Vim plugin to use these new `--border` options
- ```vim
- " Floating popup window in the center of the screen
- let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
-
- " Popup with 100% width
- let g:fzf_layout = { 'window': { 'width': 1.0, 'height': 0.5, 'border': 'horizontal' } }
-
- " Popup with 100% height
- let g:fzf_layout = { 'window': { 'width': 0.5, 'height': 1.0, 'border': 'vertical' } }
-
- " Similar to 'down' layout, but it uses a popup window and doesn't affect the window layout
- let g:fzf_layout = { 'window': { 'width': 1.0, 'height': 0.5, 'yoffset': 1.0, 'border': 'top' } }
-
- " Opens on the right;
- " 'highlight' option is still supported but it will only take the foreground color of the group
- let g:fzf_layout = { 'window': { 'width': 0.5, 'height': 1.0, 'xoffset': 1.0, 'border': 'left', 'highlight': 'Comment' } }
- ```
-- To indicate if `--multi` mode is enabled, fzf will print the number of
- selected items even when no item is selected
- ```sh
- seq 100 | fzf
- # 100/100
- seq 100 | fzf --multi
- # 100/100 (0)
- seq 100 | fzf --multi 5
- # 100/100 (0/5)
- ```
-- Since 0.24.0, release binaries will be uploaded to https://github.com/junegunn/fzf/releases
-
-0.23.1
-------
-- Added `--preview-window` options for disabling flags
- - `nocycle`
- - `nohidden`
- - `nowrap`
- - `default`
-- Built with Go 1.14.9 due to performance regression
- - https://github.com/golang/go/issues/40727
-
-0.23.0
-------
-- Support preview scroll offset relative to window height
- ```sh
- git grep --line-number '' |
- fzf --delimiter : \
- --preview 'bat --style=numbers --color=always --highlight-line {2} {1}' \
- --preview-window +{2}-/2
- ```
-- Added `--preview-window` option for sharp edges (`--preview-window sharp`)
-- Added `--preview-window` option for cyclic scrolling (`--preview-window cycle`)
-- Reduced vertical padding around the preview window when `--preview-window
- noborder` is used
-- Added actions for preview window
- - `preview-half-page-up`
- - `preview-half-page-down`
-- Vim
- - Popup width and height can be given in absolute integer values
- - Added `fzf#exec()` function for getting the path of fzf executable
- - It also downloads the latest binary if it's not available by running
- `./install --bin`
-- Built with Go 1.15.2
- - We no longer provide 32-bit binaries
-
-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
- - CTRL-R will remove duplicate commands
-- fzf-tmux
- - Supports tmux popup window (require tmux 3.2 or above)
- - ```sh
- # 50% width and height
- fzf-tmux -p
-
- # 80% width and height
- fzf-tmux -p 80%
-
- # 80% width and 40% height
- fzf-tmux -p 80%,40%
- fzf-tmux -w 80% -h 40%
-
- # Window position
- fzf-tmux -w 80% -h 40% -x 0 -y 0
- fzf-tmux -w 80% -h 40% -y 1000
-
- # Write ordinary fzf options after --
- fzf-tmux -p -- --reverse --info=inline --margin 2,4 --border
- ```
- - On macOS, you can build the latest tmux from the source with
- `brew install tmux --HEAD`
-- Bug fixes
- - Fixed Windows file traversal not to include directories
- - Fixed ANSI colors with `--keep-right`
- - Fixed _fzf_complete for zsh
-- Built with Go 1.14.1
-
-0.21.0
-------
-- `--height` option is now available on Windows as well (@kelleyma49)
-- Added `--pointer` and `--marker` options
-- Added `--keep-right` option that keeps the right end of the line visible
- when it's too long
-- Style changes
- - `--border` will now print border with rounded corners around the
- finder instead of printing horizontal lines above and below it.
- The previous style is available via `--border=horizontal`
- - Unicode spinner
-- More keys and actions for `--bind`
-- Added PowerShell script for downloading Windows binary
-- Vim plugin: Built-in floating windows support
- ```vim
- let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
- ```
-- bash: Various improvements in key bindings (CTRL-T, CTRL-R, ALT-C)
- - CTRL-R will start with the current command-line as the initial query
- - CTRL-R properly supports multi-line commands
-- Fuzzy completion API changed
- ```sh
- # Previous: fzf arguments given as a single string argument
- # - This style is still supported, but it's deprecated
- _fzf_complete "--multi --reverse --prompt=\"doge> \"" "$@" < <(
- echo foo
- )
-
- # New API: multiple fzf arguments before "--"
- # - Easier to write multiple options
- _fzf_complete --multi --reverse --prompt="doge> " -- "$@" < <(
- echo foo
- )
- ```
-- Bug fixes and improvements
-
-0.20.0
-------
-- Customizable preview window color (`preview-fg` and `preview-bg` for `--color`)
- ```sh
- fzf --preview 'cat {}' \
- --color 'fg:#bbccdd,fg+:#ddeeff,bg:#334455,preview-bg:#223344,border:#778899' \
- --border --height 20 --layout reverse --info inline
- ```
-- Removed the immediate flicking of the screen on `reload` action.
- ```sh
- : | fzf --bind 'change:reload:seq {q}' --phony
- ```
-- Added `clear-query` and `clear-selection` actions for `--bind`
-- It is now possible to split a composite bind action over multiple `--bind`
- expressions by prefixing the later ones with `+`.
- ```sh
- fzf --bind 'ctrl-a:up+up'
-
- # Can be now written as
- fzf --bind 'ctrl-a:up' --bind 'ctrl-a:+up'
-
- # This is useful when you need to write special execute/reload form (i.e. `execute:...`)
- # to avoid parse errors and add more actions to the same key
- fzf --multi --bind 'ctrl-l:select-all+execute:less {+f}' --bind 'ctrl-l:+deselect-all'
- ```
-- Fixed parse error of `--bind` expression where concatenated execute/reload
- action contains `+` character.
- ```sh
- fzf --multi --bind 'ctrl-l:select-all+execute(less {+f})+deselect-all'
- ```
-- Fixed bugs of reload action
- - Not triggered when there's no match even when the command doesn't have
- any placeholder expressions
- - Screen not properly cleared when `--header-lines` not filled on reload
-
-0.19.0
-------
-
-- Added `--phony` option which completely disables search functionality.
- Useful when you want to use fzf only as a selector interface. See below.
-- Added "reload" action for dynamically updating the input list without
- restarting fzf. See https://github.com/junegunn/fzf/issues/1750 to learn
- more about it.
- ```sh
- # Using fzf as the selector interface for ripgrep
- RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
- INITIAL_QUERY="foo"
- FZF_DEFAULT_COMMAND="$RG_PREFIX '$INITIAL_QUERY' || true" \
- fzf --bind "change:reload:$RG_PREFIX {q} || true" \
- --ansi --phony --query "$INITIAL_QUERY"
- ```
-- `--multi` now takes an optional integer argument which indicates the maximum
- number of items that can be selected
- ```sh
- seq 100 | fzf --multi 3 --reverse --height 50%
- ```
-- If a placeholder expression for `--preview` and `execute` action (and the
- new `reload` action) contains `f` flag, it is replaced to the
- path of a temporary file that holds the evaluated list. This is useful
- when you multi-select a large number of items and the length of the
- evaluated string may exceed [`ARG_MAX`][argmax].
- ```sh
- # Press CTRL-A to select 100K items and see the sum of all the numbers
- seq 100000 | fzf --multi --bind ctrl-a:select-all \
- --preview "awk '{sum+=\$1} END {print sum}' {+f}"
- ```
-- `deselect-all` no longer deselects unmatched items. It is now consistent
- with `select-all` and `toggle-all` in that it only affects matched items.
-- Due to the limitation of bash, fuzzy completion is enabled by default for
- a fixed set of commands. A helper function for easily setting up fuzzy
- completion for any command is now provided.
- ```sh
- # usage: _fzf_setup_completion path|dir COMMANDS...
- _fzf_setup_completion path git kubectl
- ```
-- Info line style can be changed by `--info=STYLE`
- - `--info=default`
- - `--info=inline` (same as old `--inline-info`)
- - `--info=hidden`
-- Preview window border can be disabled by adding `noborder` to
- `--preview-window`.
-- When you transform the input with `--with-nth`, the trailing white spaces
- are removed.
-- `ctrl-\`, `ctrl-]`, `ctrl-^`, and `ctrl-/` can now be used with `--bind`
-- See https://github.com/junegunn/fzf/milestone/15?closed=1 for more details
-
-[argmax]: https://unix.stackexchange.com/questions/120642/what-defines-the-maximum-size-for-a-command-single-argument
-
-0.18.0
-------
-
-- Added placeholder expression for zero-based item index: `{n}` and `{+n}`
- - `fzf --preview 'echo {n}: {}'`
-- Added color option for the gutter: `--color gutter:-1`
-- Added `--no-unicode` option for drawing borders in non-Unicode, ASCII
- characters
-- `FZF_PREVIEW_LINES` and `FZF_PREVIEW_COLUMNS` are exported to preview process
- - fzf still overrides `LINES` and `COLUMNS` as before, but they may be
- reset by the default shell.
-- Bug fixes and improvements
- - See https://github.com/junegunn/fzf/milestone/14?closed=1
-- Built with Go 1.12.1
-
-0.17.5
-------
-
-- Bug fixes and improvements
- - See https://github.com/junegunn/fzf/milestone/13?closed=1
-- Search query longer than the screen width is allowed (up to 300 chars)
-- Built with Go 1.11.1
-
-0.17.4
-------
-
-- Added `--layout` option with a new layout called `reverse-list`.
- - `--layout=reverse` is a synonym for `--reverse`
- - `--layout=default` is a synonym for `--no-reverse`
-- Preview window will be updated even when there is no match for the query
- if any of the placeholder expressions (e.g. `{q}`, `{+}`) evaluates to
- a non-empty string.
-- More keys for binding: `shift-{up,down}`, `alt-{up,down,left,right}`
-- fzf can now start even when `/dev/tty` is not available by making an
- educated guess.
-- Updated the default command for Windows.
-- Fixes and improvements on bash/zsh completion
-- install and uninstall scripts now supports generating files under
- `XDG_CONFIG_HOME` on `--xdg` flag.
-
-See https://github.com/junegunn/fzf/milestone/12?closed=1 for the full list of
-changes.
-
-0.17.3
-------
-- `$LINES` and `$COLUMNS` are exported to preview command so that the command
- knows the exact size of the preview window.
-- Better error messages when the default command or `$FZF_DEFAULT_COMMAND`
- fails.
-- Reverted #1061 to avoid having duplicate entries in the list when find
- command detected a file system loop (#1120). The default command now
- requires that find supports `-fstype` option.
-- fzf now distinguishes mouse left click and right click (#1130)
- - Right click is now bound to `toggle` action by default
- - `--bind` understands `left-click` and `right-click`
-- Added `replace-query` action (#1137)
- - Replaces query string with the current selection
-- Added `accept-non-empty` action (#1162)
- - Same as accept, except that it prevents fzf from exiting without any
- selection
-
-0.17.1
-------
-
-- Fixed custom background color of preview window (#1046)
-- Fixed background color issues of Windows binary
-- Fixed Windows binary to execute command using cmd.exe with no parsing and
- escaping (#1072)
-- Added support for `window` layout on Vim 8 using Vim 8 terminal (#1055)
-
-0.17.0-2
---------
-
-A maintenance release for auxiliary scripts. fzf binaries are not updated.
-
-- Experimental support for the builtin terminal of Vim 8
- - fzf can now run inside GVim
-- Updated Vim plugin to better handle `&shell` issue on fish
-- Fixed a bug of fzf-tmux where invalid output is generated
-- Fixed fzf-tmux to work even when `tput` does not work
-
-0.17.0
-------
-- Performance optimization
-- One can match literal spaces in extended-search mode with a space prepended
- by a backslash.
-- `--expect` is now additive and can be specified multiple times.
-
-0.16.11
--------
-- Performance optimization
-- Fixed missing preview update
-
-0.16.10
--------
-- Fixed invalid handling of ANSI colors in preview window
-- Further improved `--ansi` performance
-
-0.16.9
-------
-- Memory and performance optimization
- - Around 20% performance improvement for general use cases
- - Up to 5x faster processing of `--ansi`
- - Up to 50% reduction of memory usage
-- Bug fixes and usability improvements
- - Fixed handling of bracketed paste mode
- - [ERROR] on info line when the default command failed
- - More efficient rendering of preview window
- - `--no-clear` updated for repetitive relaunching scenarios
-
-0.16.8
-------
-- New `change` event and `top` action for `--bind`
- - `fzf --bind change:top`
- - Move cursor to the top result whenever the query string is changed
- - `fzf --bind 'ctrl-w:unix-word-rubout+top,ctrl-u:unix-line-discard+top'`
- - `top` combined with `unix-word-rubout` and `unix-line-discard`
-- Fixed inconsistent tiebreak scores when `--nth` is used
-- Proper display of tab characters in `--prompt`
-- Fixed not to `--cycle` on page-up/page-down to prevent overshoot
-- Git revision in `--version` output
-- Basic support for Cygwin environment
-- Many fixes in Vim plugin on Windows/Cygwin (thanks to @janlazo)
-
-0.16.7
-------
-- Added support for `ctrl-alt-[a-z]` key chords
-- CTRL-Z (SIGSTOP) now works with fzf
-- fzf will export `$FZF_PREVIEW_WINDOW` so that the scripts can use it
-- Bug fixes and improvements in Vim plugin and shell extensions
-
-0.16.6
-------
-- Minor bug fixes and improvements
-- Added `--no-clear` option for scripting purposes
-
-0.16.5
-------
-- Minor bug fixes
-- Added `toggle-preview-wrap` action
-- Built with Go 1.8
-
-0.16.4
-------
-- Added `--border` option to draw border above and below the finder
-- Bug fixes and improvements
-
-0.16.3
-------
-- Fixed a bug where fzf incorrectly display the lines when straddling tab
- characters are trimmed
-- Placeholder expression used in `--preview` and `execute` action can
- optionally take `+` flag to be used with multiple selections
- - e.g. `git log --oneline | fzf --multi --preview 'git show {+1}'`
-- Added `execute-silent` action for executing a command silently without
- switching to the alternate screen. This is useful when the process is
- short-lived and you're not interested in its output.
- - e.g. `fzf --bind 'ctrl-y:execute!(echo -n {} | pbcopy)'`
-- `ctrl-space` is allowed in `--bind`
-
-0.16.2
-------
-- Dropped ncurses dependency
-- Binaries for freebsd, openbsd, arm5, arm6, arm7, and arm8
-- Official 24-bit color support
-- Added support for composite actions in `--bind`. Multiple actions can be
- chained using `+` separator.
- - e.g. `fzf --bind 'ctrl-y:execute(echo -n {} | pbcopy)+abort'`
-- `--preview-window` with size 0 is allowed. This is used to make fzf execute
- preview command in the background without displaying the result.
-- Minor bug fixes and improvements
-
-0.16.1
-------
-- Fixed `--height` option to properly fill the window with the background
- color
-- Added `half-page-up` and `half-page-down` actions
-- Added `-L` flag to the default find command
-
-0.16.0
-------
-- *Added `--height HEIGHT[%]` option*
- - fzf can now display finder without occupying the full screen
-- Preview window will truncate long lines by default. Line wrap can be enabled
- by `:wrap` flag in `--preview-window`.
-- Latin script letters will be normalized before matching so that it's easier
- to match against accented letters. e.g. `sodanco` can match `Só Danço Samba`.
- - Normalization can be disabled via `--literal`
-- Added `--filepath-word` to make word-wise movements/actions (`alt-b`,
- `alt-f`, `alt-bs`, `alt-d`) respect path separators
-
-0.15.9
-------
-- Fixed rendering glitches introduced in 0.15.8
-- The default escape delay is reduced to 50ms and is configurable via
- `$ESCDELAY`
-- Scroll indicator at the top-right corner of the preview window is always
- displayed when there's overflow
-- Can now be built with ncurses 6 or tcell to support extra features
- - *ncurses 6*
- - Supports more than 256 color pairs
- - Supports italics
- - *tcell*
- - 24-bit color support
- - See https://github.com/junegunn/fzf/blob/master/BUILD.md
-
-0.15.8
-------
-- Updated ANSI processor to handle more VT-100 escape sequences
-- Added `--no-bold` (and `--bold`) option
-- Improved escape sequence processing for WSL
-- Added support for `alt-[0-9]`, `f11`, and `f12` for `--bind` and `--expect`
-
-0.15.7
-------
-- Fixed panic when color is disabled and header lines contain ANSI colors
-
-0.15.6
-------
-- Windows binaries! (@kelleyma49)
-- Fixed the bug where header lines are cleared when preview window is toggled
-- Fixed not to display ^N and ^O on screen
-- Fixed cursor keys (or any key sequence that starts with ESC) on WSL by
- making fzf wait for additional keystrokes after ESC for up to 100ms
-
-0.15.5
-------
-- Setting foreground color will no longer set background color to black
- - e.g. `fzf --color fg:153`
-- `--tiebreak=end` will consider relative position instead of absolute distance
-- Updated `fzf#wrap` function to respect `g:fzf_colors`
-
-0.15.4
-------
-- Added support for range expression in preview and execute action
- - e.g. `ls -l | fzf --preview="echo user={3} when={-4..-2}; cat {-1}" --header-lines=1`
- - `{q}` will be replaced to the single-quoted string of the current query
-- Fixed to properly handle unicode whitespace characters
-- Display scroll indicator in preview window
-- Inverse search term will use exact matcher by default
- - This is a breaking change, but I believe it makes much more sense. It is
- almost impossible to predict which entries will be filtered out due to
- a fuzzy inverse term. You can still perform inverse-fuzzy-match by
- prepending `!'` to the term.
-
-0.15.3
-------
-- Added support for more ANSI attributes: dim, underline, blink, and reverse
-- Fixed race condition in `toggle-preview`
-
-0.15.2
-------
-- Preview window is now scrollable
- - With mouse scroll or with bindable actions
- - `preview-up`
- - `preview-down`
- - `preview-page-up`
- - `preview-page-down`
-- Updated ANSI processor to support high intensity colors and ignore
- some VT100-related escape sequences
-
-0.15.1
-------
-- Fixed panic when the pattern occurs after 2^15-th column
-- Fixed rendering delay when displaying extremely long lines
-
-0.15.0
-------
-- Improved fuzzy search algorithm
- - Added `--algo=[v1|v2]` option so one can still choose the old algorithm
- which values the search performance over the quality of the result
-- Advanced scoring criteria
-- `--read0` to read input delimited by ASCII NUL character
-- `--print0` to print output delimited by ASCII NUL character
-
-0.13.5
-------
-- Memory and performance optimization
- - Up to 2x performance with half the amount of memory
-
-0.13.4
-------
-- Performance optimization
- - Memory footprint for ascii string is reduced by 60%
- - 15 to 20% improvement of query performance
- - Up to 45% better performance of `--nth` with non-regex delimiters
-- Fixed invalid handling of `hidden` property of `--preview-window`
-
-0.13.3
-------
-- Fixed duplicate rendering of the last line in preview window
-
-0.13.2
-------
-- Fixed race condition where preview window is not properly cleared
-
-0.13.1
-------
-- Fixed UI issue with large `--preview` output with many ANSI codes
-
-0.13.0
-------
-- Added preview feature
- - `--preview CMD`
- - `--preview-window POS[:SIZE][:hidden]`
-- `{}` in execute action is now replaced to the single-quoted (instead of
- double-quoted) string of the current line
-- Fixed to ignore control characters for bracketed paste mode
-
-0.12.2
-------
-
-- 256-color capability detection does not require `256` in `$TERM`
-- Added `print-query` action
-- More named keys for binding; F1 ~ F10,
- ALT-/, ALT-space, and ALT-enter
-- Added `jump` and `jump-accept` actions that implement [EasyMotion][em]-like
- movement
- ![][jump]
-
-[em]: https://github.com/easymotion/vim-easymotion
-[jump]: https://cloud.githubusercontent.com/assets/700826/15367574/b3999dc4-1d64-11e6-85da-28ceeb1a9bc2.png
-
-0.12.1
-------
-
-- Ranking algorithm introduced in 0.12.0 is now universally applied
-- Fixed invalid cache reference in exact mode
-- Fixes and improvements in Vim plugin and shell extensions
-
-0.12.0
-------
-
-- Enhanced ranking algorithm
-- Minor bug fixes
-
-0.11.4
-------
-
-- Added `--hscroll-off=COL` option (default: 10) (#513)
-- Some fixes in Vim plugin and shell extensions
-
-0.11.3
-------
-
-- Graceful exit on SIGTERM (#482)
-- `$SHELL` instead of `sh` for `execute` action and `$FZF_DEFAULT_COMMAND` (#481)
-- Changes in fuzzy completion API
- - [`_fzf_compgen_{path,dir}`](https://github.com/junegunn/fzf/commit/9617647)
- - [`_fzf_complete_COMMAND_post`](https://github.com/junegunn/fzf/commit/8206746)
- for post-processing
-
-0.11.2
-------
-
-- `--tiebreak` now accepts comma-separated list of sort criteria
- - Each criterion should appear only once in the list
- - `index` is only allowed at the end of the list
- - `index` is implicitly appended to the list when not specified
- - Default is `length` (or equivalently `length,index`)
-- `begin` criterion will ignore leading whitespaces when calculating the index
-- Added `toggle-in` and `toggle-out` actions
- - Switch direction depending on `--reverse`-ness
- - `export FZF_DEFAULT_OPTS="--bind tab:toggle-out,shift-tab:toggle-in"`
-- Reduced the initial delay when `--tac` is not given
- - fzf defers the initial rendering of the screen up to 100ms if the input
- stream is ongoing to prevent unnecessary redraw during the initial
- phase. However, 100ms delay is quite noticeable and might give the
- impression that fzf is not snappy enough. This commit reduces the
- maximum delay down to 20ms when `--tac` is not specified, in which case
- the input list quickly fills the entire screen.
-
-0.11.1
-------
-
-- Added `--tabstop=SPACES` option
-
-0.11.0
-------
-
-- Added OR operator for extended-search mode
-- Added `--execute-multi` action
-- Fixed incorrect cursor position when unicode wide characters are used in
- `--prompt`
-- Fixes and improvements in shell extensions
-
-0.10.9
-------
-
-- Extended-search mode is now enabled by default
- - `--extended-exact` is deprecated and instead we have `--exact` for
- orthogonally controlling "exactness" of search
-- Fixed not to display non-printable characters
-- Added `double-click` for `--bind` option
-- More robust handling of SIGWINCH
-
-0.10.8
-------
-
-- Fixed panic when trying to set colors after colors are disabled (#370)
-
-0.10.7
-------
-
-- Fixed unserialized interrupt handling during execute action which often
- caused invalid memory access and crash
-- Changed `--tiebreak=length` (default) to use trimmed length when `--nth` is
- used
-
-0.10.6
-------
-
-- Replaced `--header-file` with `--header` option
-- `--header` and `--header-lines` can be used together
-- Changed exit status
- - 0: Okay
- - 1: No match
- - 2: Error
- - 130: Interrupted
-- 64-bit linux binary is statically-linked with ncurses to avoid
- compatibility issues.
-
-0.10.5
-------
-
-- `'`-prefix to unquote the term in `--extended-exact` mode
-- Backward scan when `--tiebreak=end` is set
-
-0.10.4
-------
-
-- Fixed to remove ANSI code from output when `--with-nth` is set
-
-0.10.3
-------
-
-- Fixed slow performance of `--with-nth` when used with `--delimiter`
- - Regular expression engine of Golang as of now is very slow, so the fixed
- version will treat the given delimiter pattern as a plain string instead
- of a regular expression unless it contains special characters and is
- a valid regular expression.
- - Simpler regular expression for delimiter for better performance
-
-0.10.2
-------
-
-### Fixes and improvements
-
-- Improvement in perceived response time of queries
- - Eager, efficient rune array conversion
-- Graceful exit when failed to initialize ncurses (invalid $TERM)
-- Improved ranking algorithm when `--nth` option is set
-- Changed the default command not to fail when there are files whose names
- start with dash
-
-0.10.1
-------
-
-### New features
-
-- Added `--margin` option
-- Added options for sticky header
- - `--header-file`
- - `--header-lines`
-- Added `cancel` action which clears the input or closes the finder when the
- input is already empty
- - e.g. `export FZF_DEFAULT_OPTS="--bind esc:cancel"`
-- Added `delete-char/eof` action to differentiate `CTRL-D` and `DEL`
-
-### Minor improvements/fixes
-
-- Fixed to allow binding colon and comma keys
-- Fixed ANSI processor to handle color regions spanning multiple lines
-
-0.10.0
-------
-
-### New features
-
-- More actions for `--bind`
- - `select-all`
- - `deselect-all`
- - `toggle-all`
- - `ignore`
-- `execute(...)` action for running arbitrary command without leaving fzf
- - `fzf --bind "ctrl-m:execute(less {})"`
- - `fzf --bind "ctrl-t:execute(tmux new-window -d 'vim {}')"`
- - If the command contains parentheses, use any of the follows alternative
- notations to avoid parse errors
- - `execute[...]`
- - `execute~...~`
- - `execute!...!`
- - `execute@...@`
- - `execute#...#`
- - `execute$...$`
- - `execute%...%`
- - `execute^...^`
- - `execute&...&`
- - `execute*...*`
- - `execute;...;`
- - `execute/.../`
- - `execute|...|`
- - `execute:...`
- - This is the special form that frees you from parse errors as it
- does not expect the closing character
- - The catch is that it should be the last one in the
- comma-separated list
-- Added support for optional search history
- - `--history HISTORY_FILE`
- - When used, `CTRL-N` and `CTRL-P` are automatically remapped to
- `next-history` and `previous-history`
- - `--history-size MAX_ENTRIES` (default: 1000)
-- Cyclic scrolling can be enabled with `--cycle`
-- Fixed the bug where the spinner was not spinning on idle input stream
- - e.g. `sleep 100 | fzf`
-
-### Minor improvements/fixes
-
-- Added synonyms for key names that can be specified for `--bind`,
- `--toggle-sort`, and `--expect`
-- Fixed the color of multi-select marker on the current line
-- Fixed to allow `^pattern$` in extended-search mode
-
-
-0.9.13
-------
-
-### New features
-
-- Color customization with the extended `--color` option
-
-### Bug fixes
-
-- Fixed premature termination of Reader in the presence of a long line which
- is longer than 64KB
-
-0.9.12
-------
-
-### New features
-
-- Added `--bind` option for custom key bindings
-
-### Bug fixes
-
-- Fixed to update "inline-info" immediately after terminal resize
-- Fixed ANSI code offset calculation
-
-0.9.11
-------
-
-### New features
-
-- Added `--inline-info` option for saving screen estate (#202)
- - Useful inside Neovim
- - e.g. `let $FZF_DEFAULT_OPTS = $FZF_DEFAULT_OPTS.' --inline-info'`
-
-### Bug fixes
-
-- Invalid mutation of input on case conversion (#209)
-- Smart-case for each term in extended-search mode (#208)
-- Fixed double-click result when scroll offset is positive
-
-0.9.10
-------
-
-### Improvements
-
-- Performance optimization
-- Less aggressive memoization to limit memory usage
-
-### New features
-
-- Added color scheme for light background: `--color=light`
-
-0.9.9
------
-
-### New features
-
-- Added `--tiebreak` option (#191)
-- Added `--no-hscroll` option (#193)
-- Visual indication of `--toggle-sort` (#194)
-
-0.9.8
------
-
-### Bug fixes
-
-- Fixed Unicode case handling (#186)
-- Fixed to terminate on RuneError (#185)
-
-0.9.7
------
-
-### New features
-
-- Added `--toggle-sort` option (#173)
- - `--toggle-sort=ctrl-r` is applied to `CTRL-R` shell extension
-
-### Bug fixes
-
-- Fixed to print empty line if `--expect` is set and fzf is completed by
- `--select-1` or `--exit-0` (#172)
-- Fixed to allow comma character as an argument to `--expect` option
-
-0.9.6
------
-
-### New features
-
-#### Added `--expect` option (#163)
-
-If you provide a comma-separated list of keys with `--expect` option, fzf will
-allow you to select the match and complete the finder when any of the keys is
-pressed. Additionally, fzf will print the name of the key pressed as the first
-line of the output so that your script can decide what to do next based on the
-information.
-
-```sh
-fzf --expect=ctrl-v,ctrl-t,alt-s,f1,f2,~,@
-```
-
-The updated vim plugin uses this option to implement
-[ctrlp](https://github.com/kien/ctrlp.vim)-compatible key bindings.
-
-### Bug fixes
-
-- Fixed to ignore ANSI escape code `\e[K` (#162)
-
-0.9.5
------
-
-### New features
-
-#### Added `--ansi` option (#150)
-
-If you give `--ansi` option to fzf, fzf will interpret ANSI color codes from
-the input, display the item with the ANSI colors (true colors are not
-supported), and strips the codes from the output. This option is off by
-default as it entails some overhead.
-
-### Improvements
-
-#### Reduced initial memory footprint (#151)
-
-By removing unnecessary copy of pointers, fzf will use significantly smaller
-amount of memory when it's started. The difference is hugely noticeable when
-the input is extremely large. (e.g. `locate / | fzf`)
-
-### Bug fixes
-
-- Fixed panic on `--no-sort --filter ''` (#149)
-
-0.9.4
------
-
-### New features
-
-#### Added `--tac` option to reverse the order of the input.
-
-One might argue that this option is unnecessary since we can already put `tac`
-or `tail -r` in the command pipeline to achieve the same result. However, the
-advantage of `--tac` is that it does not block until the input is complete.
-
-### *Backward incompatible changes*
-
-#### Changed behavior on `--no-sort`
-
-`--no-sort` option will no longer reverse the display order within finder. You
-may want to use the new `--tac` option with `--no-sort`.
-
-```
-history | fzf +s --tac
-```
-
-### Improvements
-
-#### `--filter` will not block when sort is disabled
-
-When fzf works in filtering mode (`--filter`) and sort is disabled
-(`--no-sort`), there's no need to block until input is complete. The new
-version of fzf will print the matches on-the-fly when the following condition
-is met:
-
- --filter TERM --no-sort [--no-tac --no-sync]
-
-or simply:
-
- -f TERM +s
-
-This change removes unnecessary delay in the use cases like the following:
-
- fzf -f xxx +s | head -5
-
-However, in this case, fzf processes the lines sequentially, so it cannot
-utilize multiple cores, and fzf will run slightly slower than the previous
-mode of execution where filtering is done in parallel after the entire input
-is loaded. If the user is concerned about this performance problem, one can
-add `--sync` option to re-enable buffering.
-
-0.9.3
------
-
-### New features
-- Added `--sync` option for multi-staged filtering
-
-### Improvements
-- `--select-1` and `--exit-0` will start finder immediately when the condition
- cannot be met
diff --git a/.fzf/Dockerfile b/.fzf/Dockerfile
deleted file mode 100644
index 45fa5ee..0000000
--- a/.fzf/Dockerfile
+++ /dev/null
@@ -1,11 +0,0 @@
-FROM archlinux/base:latest
-RUN pacman -Sy && pacman --noconfirm -S awk git tmux zsh fish ruby procps go make gcc
-RUN gem install --no-document -v 5.14.2 minitest
-RUN echo '. /usr/share/bash-completion/completions/git' >> ~/.bashrc
-RUN echo '. ~/.bashrc' >> ~/.bash_profile
-
-# Do not set default PS1
-RUN rm -f /etc/bash.bashrc
-COPY . /fzf
-RUN cd /fzf && make install && ./install --all
-CMD tmux new 'set -o pipefail; ruby /fzf/test/test_go.rb | tee out && touch ok' && cat out && [ -e ok ]
diff --git a/.fzf/LICENSE b/.fzf/LICENSE
deleted file mode 100644
index 50aa5d9..0000000
--- a/.fzf/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2013-2021 Junegunn Choi
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-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.
diff --git a/.fzf/Makefile b/.fzf/Makefile
deleted file mode 100644
index 91c5711..0000000
--- a/.fzf/Makefile
+++ /dev/null
@@ -1,166 +0,0 @@
-SHELL := bash
-GO ?= go
-GOOS ?= $(word 1, $(subst /, " ", $(word 4, $(shell go version))))
-
-MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
-ROOT_DIR := $(shell dirname $(MAKEFILE))
-SOURCES := $(wildcard *.go src/*.go src/*/*.go) $(MAKEFILE)
-
-ifdef FZF_VERSION
-VERSION := $(FZF_VERSION)
-else
-VERSION := $(shell git describe --abbrev=0 2> /dev/null)
-endif
-ifeq ($(VERSION),)
-$(error Not on git repository; cannot determine $$FZF_VERSION)
-endif
-VERSION_TRIM := $(shell sed "s/-.*//" <<< $(VERSION))
-VERSION_REGEX := $(subst .,\.,$(VERSION_TRIM))
-
-ifdef FZF_REVISION
-REVISION := $(FZF_REVISION)
-else
-REVISION := $(shell git log -n 1 --pretty=format:%h -- $(SOURCES) 2> /dev/null)
-endif
-ifeq ($(REVISION),)
-$(error Not on git repository; cannot determine $$FZF_REVISION)
-endif
-BUILD_FLAGS := -a -ldflags "-s -w -X main.version=$(VERSION) -X main.revision=$(REVISION)" -tags "$(TAGS)"
-
-BINARY32 := fzf-$(GOOS)_386
-BINARY64 := fzf-$(GOOS)_amd64
-BINARYARM5 := fzf-$(GOOS)_arm5
-BINARYARM6 := fzf-$(GOOS)_arm6
-BINARYARM7 := fzf-$(GOOS)_arm7
-BINARYARM8 := fzf-$(GOOS)_arm8
-BINARYPPC64LE := fzf-$(GOOS)_ppc64le
-BINARYRISCV64 := fzf-$(GOOS)_riscv64
-
-# https://en.wikipedia.org/wiki/Uname
-UNAME_M := $(shell uname -m)
-ifeq ($(UNAME_M),x86_64)
- BINARY := $(BINARY64)
-else ifeq ($(UNAME_M),amd64)
- BINARY := $(BINARY64)
-else ifeq ($(UNAME_M),i686)
- BINARY := $(BINARY32)
-else ifeq ($(UNAME_M),i386)
- BINARY := $(BINARY32)
-else ifeq ($(UNAME_M),armv5l)
- BINARY := $(BINARYARM5)
-else ifeq ($(UNAME_M),armv6l)
- BINARY := $(BINARYARM6)
-else ifeq ($(UNAME_M),armv7l)
- BINARY := $(BINARYARM7)
-else ifeq ($(UNAME_M),armv8l)
- BINARY := $(BINARYARM8)
-else ifeq ($(UNAME_M),arm64)
- BINARY := $(BINARYARM8)
-else ifeq ($(UNAME_M),aarch64)
- BINARY := $(BINARYARM8)
-else ifeq ($(UNAME_M),ppc64le)
- BINARY := $(BINARYPPC64LE)
-else ifeq ($(UNAME_M),riscv64)
- BINARY := $(BINARYRISCV64)
-else
-$(error Build on $(UNAME_M) is not supported, yet.)
-endif
-
-all: target/$(BINARY)
-
-test: $(SOURCES)
- [ -z "$$(gofmt -s -d src)" ] || (gofmt -s -d src; exit 1)
- SHELL=/bin/sh GOOS= $(GO) test -v -tags "$(TAGS)" \
- github.com/junegunn/fzf/src \
- github.com/junegunn/fzf/src/algo \
- github.com/junegunn/fzf/src/tui \
- github.com/junegunn/fzf/src/util
-
-bench:
- cd src && SHELL=/bin/sh GOOS= $(GO) test -v -tags "$(TAGS)" -run=Bench -bench=. -benchmem
-
-install: bin/fzf
-
-build:
- goreleaser --rm-dist --snapshot
-
-release:
-ifndef GITHUB_TOKEN
- $(error GITHUB_TOKEN is not defined)
-endif
-
- # Check if we are on master branch
-ifneq ($(shell git symbolic-ref --short HEAD),master)
- $(error Not on master branch)
-endif
-
- # Check if version numbers are properly updated
- grep -q ^$(VERSION_REGEX)$$ CHANGELOG.md
- grep -qF '"fzf $(VERSION_TRIM)"' man/man1/fzf.1
- grep -qF '"fzf $(VERSION_TRIM)"' man/man1/fzf-tmux.1
- grep -qF $(VERSION) install
- grep -qF $(VERSION) install.ps1
-
- # Make release note out of CHANGELOG.md
- mkdir -p tmp
- sed -n '/^$(VERSION_REGEX)$$/,/^[0-9]/p' CHANGELOG.md | tail -r | \
- sed '1,/^ *$$/d' | tail -r | sed 1,2d | tee tmp/release-note
-
- # Push to temp branch first so that install scripts always works on master branch
- git checkout -B temp master
- git push origin temp --follow-tags --force
-
- # Make a GitHub release
- goreleaser --rm-dist --release-notes tmp/release-note
-
- # Push to master
- git checkout master
- git push origin master
-
- # Delete temp branch
- git push origin --delete temp
-
-clean:
- $(RM) -r dist target
-
-target/$(BINARY32): $(SOURCES)
- GOARCH=386 $(GO) build $(BUILD_FLAGS) -o $@
-
-target/$(BINARY64): $(SOURCES)
- GOARCH=amd64 $(GO) build $(BUILD_FLAGS) -o $@
-
-# https://github.com/golang/go/wiki/GoArm
-target/$(BINARYARM5): $(SOURCES)
- GOARCH=arm GOARM=5 $(GO) build $(BUILD_FLAGS) -o $@
-
-target/$(BINARYARM6): $(SOURCES)
- GOARCH=arm GOARM=6 $(GO) build $(BUILD_FLAGS) -o $@
-
-target/$(BINARYARM7): $(SOURCES)
- GOARCH=arm GOARM=7 $(GO) build $(BUILD_FLAGS) -o $@
-
-target/$(BINARYARM8): $(SOURCES)
- GOARCH=arm64 $(GO) build $(BUILD_FLAGS) -o $@
-
-target/$(BINARYPPC64LE): $(SOURCES)
- GOARCH=ppc64le $(GO) build $(BUILD_FLAGS) -o $@
-
-target/$(BINARYRISCV64): $(SOURCES)
- GOARCH=riscv64 $(GO) build $(BUILD_FLAGS) -o $@
-
-bin/fzf: target/$(BINARY) | bin
- cp -f target/$(BINARY) bin/fzf
-
-docker:
- docker build -t fzf-arch .
- docker run -it fzf-arch tmux
-
-docker-test:
- docker build -t fzf-arch .
- docker run -it fzf-arch
-
-update:
- $(GO) get -u
- $(GO) mod tidy
-
-.PHONY: all build release test bench install clean docker docker-test update
diff --git a/.fzf/README-VIM.md b/.fzf/README-VIM.md
deleted file mode 100644
index 425bf67..0000000
--- a/.fzf/README-VIM.md
+++ /dev/null
@@ -1,486 +0,0 @@
-FZF Vim integration
-===================
-
-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.
-
-```vim
-" If installed using Homebrew
-set rtp+=/usr/local/opt/fzf
-
-" If installed using git
-set rtp+=~/.fzf
-```
-
-If you use [vim-plug](https://github.com/junegunn/vim-plug), the same can be
-written as:
-
-```vim
-" 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:
-
-```vim
-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:
-
-```vim
-Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
-```
-
-Summary
--------
-
-The Vim plugin of fzf provides two core functions, and `:FZF` command which is
-the basic file selector command built on top of them.
-
-1. **`fzf#run([spec dict])`**
- - Starts fzf inside Vim with the given spec
- - `:call fzf#run({'source': 'ls'})`
-2. **`fzf#wrap([spec dict]) -> (dict)`**
- - Takes a spec for `fzf#run` and returns an extended version of it with
- additional options for addressing global preferences (`g:fzf_xxx`)
- - `:echo fzf#wrap({'source': 'ls'})`
- - We usually *wrap* a spec with `fzf#wrap` before passing it to `fzf#run`
- - `:call fzf#run(fzf#wrap({'source': 'ls'}))`
-3. **`:FZF [fzf_options string] [path string]`**
- - 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](https://github.com/junegunn/fzf.vim) 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.
-
-`:FZF[!]`
----------
-
-```vim
-" Look for files under current directory
-:FZF
-
-" Look for files under your home directory
-:FZF ~
-
-" With fzf command-line options
-:FZF --reverse --info=inline /tmp
-
-" Bang version starts fzf in fullscreen mode
-:FZF!
-```
-
-Similarly to [ctrlp.vim](https://github.com/kien/ctrlp.vim), use enter key,
-`CTRL-T`, `CTRL-X` or `CTRL-V` to open selected files in the current window,
-in new tabs, in horizontal splits, or in vertical splits respectively.
-
-Note that the environment variables `FZF_DEFAULT_COMMAND` and
-`FZF_DEFAULT_OPTS` also apply here.
-
-### Configuration
-
-- `g:fzf_action`
- - Customizable extra key bindings for opening selected files in different ways
-- `g:fzf_layout`
- - Determines the size and position of fzf window
-- `g:fzf_colors`
- - Customizes fzf colors to match the current color scheme
-- `g:fzf_history_dir`
- - Enables history feature
-
-#### Examples
-
-```vim
-" This is the default extra key bindings
-let g:fzf_action = {
- \ 'ctrl-t': 'tab split',
- \ 'ctrl-x': 'split',
- \ 'ctrl-v': 'vsplit' }
-
-" An action can be a reference to a function that processes selected lines
-function! s:build_quickfix_list(lines)
- call setqflist(map(copy(a:lines), '{ "filename": v:val }'))
- copen
- cc
-endfunction
-
-let g:fzf_action = {
- \ 'ctrl-q': function('s:build_quickfix_list'),
- \ 'ctrl-t': 'tab split',
- \ 'ctrl-x': 'split',
- \ 'ctrl-v': 'vsplit' }
-
-" Default fzf layout
-" - Popup window (center of the screen)
-let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
-
-" - Popup window (center of the current window)
-let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': v:true } }
-
-" - Popup window (anchored to the bottom of the current window)
-let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': v:true, 'yoffset': 1.0 } }
-
-" - down / up / left / right
-let g:fzf_layout = { 'down': '40%' }
-
-" - Window using a Vim command
-let g:fzf_layout = { 'window': 'enew' }
-let g:fzf_layout = { 'window': '-tabnew' }
-let g:fzf_layout = { 'window': '10new' }
-
-" Customize fzf colors to match your color scheme
-" - fzf#wrap translates this to a set of `--color` options
-let g:fzf_colors =
-\ { 'fg': ['fg', 'Normal'],
- \ 'bg': ['bg', 'Normal'],
- \ 'hl': ['fg', 'Comment'],
- \ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
- \ 'bg+': ['bg', 'CursorLine', 'CursorColumn'],
- \ 'hl+': ['fg', 'Statement'],
- \ 'info': ['fg', 'PreProc'],
- \ 'border': ['fg', 'Ignore'],
- \ 'prompt': ['fg', 'Conditional'],
- \ 'pointer': ['fg', 'Exception'],
- \ 'marker': ['fg', 'Keyword'],
- \ 'spinner': ['fg', 'Label'],
- \ 'header': ['fg', 'Comment'] }
-
-" Enable per-command history
-" - History files will be stored in the specified directory
-" - When set, CTRL-N and CTRL-P will be bound to 'next-history' and
-" 'previous-history' instead of 'down' and 'up'.
-let g:fzf_history_dir = '~/.local/share/fzf-history'
-```
-
-##### Explanation of `g:fzf_colors`
-
-`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) |
- | `preview-fg` / `preview-bg` | Preview window text and background |
- | `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 |
- | `query` | Query string |
- | `disabled` | Query string when search is disabled |
- | `prompt` | Prompt before query (`> `) |
- | `pointer` | Pointer to the current line (`>`) |
-
-- `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:
-
-```vim
- '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:
-
-```vim
-:echo fzf#wrap()
-```
-
-`fzf#run`
----------
-
-`fzf#run()` function is the core of Vim integration. It takes a single
-dictionary argument, *a spec*, and starts fzf process accordingly. At the very
-least, specify `sink` option to tell what it should do with the selected
-entry.
-
-```vim
-call fzf#run({'sink': 'e'})
-```
-
-We haven't specified the `source`, so this is equivalent to starting fzf on
-command line without standard input pipe; fzf will use find command (or
-`$FZF_DEFAULT_COMMAND` if defined) to list the files under the current
-directory. When you select one, it will open it with the sink, `:e` command.
-If you want to open it in a new tab, you can pass `:tabedit` command instead
-as the sink.
-
-```vim
-call fzf#run({'sink': 'tabedit'})
-```
-
-Instead of using the default find command, you can use any shell command as
-the source. The following example will list the files managed by git. It's
-equivalent to running `git ls-files | fzf` on shell.
-
-```vim
-call fzf#run({'source': 'git ls-files', 'sink': 'e'})
-```
-
-fzf options can be specified as `options` entry in spec dictionary.
-
-```vim
-call fzf#run({'sink': 'tabedit', 'options': '--multi --reverse'})
-```
-
-You can also pass a layout option if you don't want fzf window to take up the
-entire screen.
-
-```vim
-" up / down / left / right / window are allowed
-call fzf#run({'source': 'git ls-files', 'sink': 'e', 'left': '40%'})
-call fzf#run({'source': 'git ls-files', 'sink': 'e', 'window': '30vnew'})
-```
-
-`source` doesn't have to be an external shell command, you can pass a Vim
-array as the source. In the next example, we pass the names of color
-schemes as the source to implement a color scheme selector.
-
-```vim
-call fzf#run({'source': map(split(globpath(&rtp, 'colors/*.vim')),
- \ 'fnamemodify(v:val, ":t:r")'),
- \ 'sink': 'colo', 'left': '25%'})
-```
-
-The following table summarizes the available options.
-
-| Option name | Type | Description |
-| -------------------------- | ------------- | ---------------------------------------------------------------- |
-| `source` | string | External command to generate input to fzf (e.g. `find .`) |
-| `source` | list | Vim list as input to fzf |
-| `sink` | string | Vim command to handle the selected item (e.g. `e`, `tabe`) |
-| `sink` | funcref | Reference to function to process each selected item |
-| `sinklist` (or `sink*`) | funcref | Similar to `sink`, but takes the list of output lines at once |
-| `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}`) |
-
-`options` entry can be either a string or a list. For simple cases, string
-should suffice, but prefer to use list type to avoid escaping issues.
-
-```vim
-call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'})
-call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']})
-```
-
-When `window` entry is a dictionary, fzf will start in a popup window. The
-following options are allowed:
-
-- Required:
- - `width` [float range [0 ~ 1]] or [integer range [8 ~ ]]
- - `height` [float range [0 ~ 1]] or [integer range [4 ~ ]]
-- Optional:
- - `yoffset` [float default 0.5 range [0 ~ 1]]
- - `xoffset` [float default 0.5 range [0 ~ 1]]
- - `relative` [boolean default v:false]
- - `border` [string default `rounded`]: Border style
- - `rounded` / `sharp` / `horizontal` / `vertical` / `top` / `bottom` / `left` / `right` / `no[ne]`
-
-`fzf#wrap`
-----------
-
-We have seen that several aspects of `:FZF` command can be configured with
-a set of global option variables; different ways to open files
-(`g:fzf_action`), window position and size (`g:fzf_layout`), color palette
-(`g:fzf_colors`), etc.
-
-So how can we make our custom `fzf#run` calls also respect those variables?
-Simply by *"wrapping"* the spec dictionary with `fzf#wrap` before passing it
-to `fzf#run`.
-
-- **`fzf#wrap([name string], [spec dict], [fullscreen bool]) -> (dict)`**
- - All arguments are optional. Usually we only need to pass a spec dictionary.
- - `name` is for managing history files. It is ignored if
- `g:fzf_history_dir` is not defined.
- - `fullscreen` can be either `0` or `1` (default: 0).
-
-`fzf#wrap` takes a spec and returns an extended version of it (also
-a dictionary) with additional options for addressing global preferences. You
-can examine the return value of it like so:
-
-```vim
-echo fzf#wrap({'source': 'ls'})
-```
-
-After we *"wrap"* our spec, we pass it to `fzf#run`.
-
-```vim
-call fzf#run(fzf#wrap({'source': 'ls'}))
-```
-
-Now it supports `CTRL-T`, `CTRL-V`, and `CTRL-X` key bindings (configurable
-via `g:fzf_action`) and it opens fzf window according to `g:fzf_layout`
-setting.
-
-To make it easier to use, let's define `LS` command.
-
-```vim
-command! LS call fzf#run(fzf#wrap({'source': 'ls'}))
-```
-
-Type `:LS` and see how it works.
-
-We would like to make `:LS!` (bang version) open fzf in fullscreen, just like
-`:FZF!`. Add `-bang` to command definition, and use `` value to set
-the last `fullscreen` argument of `fzf#wrap` (see `:help `).
-
-```vim
-" On :LS!, evaluates to '!', and '!0' becomes 1
-command! -bang LS call fzf#run(fzf#wrap({'source': 'ls'}, 0))
-```
-
-Our `:LS` command will be much more useful if we can pass a directory argument
-to it, so that something like `:LS /tmp` is possible.
-
-```vim
-command! -bang -complete=dir -nargs=? LS
- \ call fzf#run(fzf#wrap({'source': 'ls', 'dir': }, 0))
-```
-
-Lastly, if you have enabled `g:fzf_history_dir`, you might want to assign
-a unique name to our command and pass it as the first argument to `fzf#wrap`.
-
-```vim
-" The query history for this command will be stored as 'ls' inside g:fzf_history_dir.
-" The name is ignored if g:fzf_history_dir is not defined.
-command! -bang -complete=dir -nargs=? LS
- \ call fzf#run(fzf#wrap('ls', {'source': 'ls', 'dir': }, 0))
-```
-
-### Global options supported by `fzf#wrap`
-
-- `g:fzf_layout`
-- `g:fzf_action`
- - **Works only when no custom `sink` (or `sinklist`) is provided**
- - Having custom sink usually means that each entry is not an ordinary
- file path (e.g. name of color scheme), so we can't blindly apply the
- same strategy (i.e. `tabedit some-color-scheme` doesn't make sense)
-- `g:fzf_colors`
-- `g:fzf_history_dir`
-
-Tips
-----
-
-### fzf inside terminal buffer
-
-On the latest versions of Vim and Neovim, fzf will start in a terminal buffer.
-If you find the default ANSI colors to be different, consider configuring the
-colors using `g:terminal_ansi_colors` in regular Vim or `g:terminal_color_x`
-in Neovim.
-
-```vim
-" Terminal colors for seoul256 color scheme
-if has('nvim')
- let g:terminal_color_0 = '#4e4e4e'
- let g:terminal_color_1 = '#d68787'
- let g:terminal_color_2 = '#5f865f'
- let g:terminal_color_3 = '#d8af5f'
- let g:terminal_color_4 = '#85add4'
- let g:terminal_color_5 = '#d7afaf'
- let g:terminal_color_6 = '#87afaf'
- let g:terminal_color_7 = '#d0d0d0'
- let g:terminal_color_8 = '#626262'
- let g:terminal_color_9 = '#d75f87'
- let g:terminal_color_10 = '#87af87'
- let g:terminal_color_11 = '#ffd787'
- let g:terminal_color_12 = '#add4fb'
- let g:terminal_color_13 = '#ffafaf'
- let g:terminal_color_14 = '#87d7d7'
- let g:terminal_color_15 = '#e4e4e4'
-else
- let g:terminal_ansi_colors = [
- \ '#4e4e4e', '#d68787', '#5f865f', '#d8af5f',
- \ '#85add4', '#d7afaf', '#87afaf', '#d0d0d0',
- \ '#626262', '#d75f87', '#87af87', '#ffd787',
- \ '#add4fb', '#ffafaf', '#87d7d7', '#e4e4e4'
- \ ]
-endif
-```
-
-### Starting fzf in a popup window
-
-```vim
-" Required:
-" - width [float range [0 ~ 1]] or [integer range [8 ~ ]]
-" - height [float range [0 ~ 1]] or [integer range [4 ~ ]]
-"
-" Optional:
-" - xoffset [float default 0.5 range [0 ~ 1]]
-" - yoffset [float default 0.5 range [0 ~ 1]]
-" - relative [boolean default v:false]
-" - border [string default 'rounded']: Border style
-" - '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.
-
-```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 open fzf on the bottom on the screen (e.g. `{'down':
-'40%'}`), you might want to temporarily disable the statusline for a cleaner
-look.
-
-```vim
-let g:fzf_layout = { 'down': '30%' }
-autocmd! FileType fzf
-autocmd FileType fzf set laststatus=0 noshowmode noruler
- \| autocmd BufLeave set laststatus=2 showmode ruler
-```
-
-[License](LICENSE)
-------------------
-
-The MIT License (MIT)
-
-Copyright (c) 2013-2021 Junegunn Choi
diff --git a/.fzf/README.md b/.fzf/README.md
deleted file mode 100644
index aaa7454..0000000
--- a/.fzf/README.md
+++ /dev/null
@@ -1,712 +0,0 @@
- [![github-actions](https://github.com/junegunn/fzf/workflows/Test%20fzf%20on%20Linux/badge.svg)](https://github.com/junegunn/fzf/actions)
-===
-
-fzf is a general-purpose command-line fuzzy finder.
-
-
-
-It's an interactive Unix filter for command-line that can be used with any
-list; files, command history, processes, hostnames, bookmarks, git commits,
-etc.
-
-Pros
-----
-
-- Portable, no dependencies
-- Blazingly fast
-- The most comprehensive feature set
-- Flexible layout
-- Batteries included
- - Vim/Neovim plugin, key bindings, and fuzzy auto-completion
-
-Table of Contents
------------------
-
-
-
-* [Installation](#installation)
- * [Using Homebrew](#using-homebrew)
- * [Using git](#using-git)
- * [Using Linux package managers](#using-linux-package-managers)
- * [Windows](#windows)
- * [As Vim plugin](#as-vim-plugin)
-* [Upgrading fzf](#upgrading-fzf)
-* [Building fzf](#building-fzf)
-* [Usage](#usage)
- * [Using the finder](#using-the-finder)
- * [Layout](#layout)
- * [Search syntax](#search-syntax)
- * [Environment variables](#environment-variables)
- * [Options](#options)
- * [Demo](#demo)
-* [Examples](#examples)
-* [`fzf-tmux` script](#fzf-tmux-script)
-* [Key bindings for command-line](#key-bindings-for-command-line)
-* [Fuzzy completion for bash and zsh](#fuzzy-completion-for-bash-and-zsh)
- * [Files and directories](#files-and-directories)
- * [Process IDs](#process-ids)
- * [Host names](#host-names)
- * [Environment variables / Aliases](#environment-variables--aliases)
- * [Settings](#settings)
- * [Supported commands](#supported-commands)
- * [Custom fuzzy completion](#custom-fuzzy-completion)
-* [Vim plugin](#vim-plugin)
-* [Advanced topics](#advanced-topics)
- * [Performance](#performance)
- * [Executing external programs](#executing-external-programs)
- * [Reloading the candidate list](#reloading-the-candidate-list)
- * [1. Update the list of processes by pressing CTRL-R](#1-update-the-list-of-processes-by-pressing-ctrl-r)
- * [2. Switch between sources by pressing CTRL-D or CTRL-F](#2-switch-between-sources-by-pressing-ctrl-d-or-ctrl-f)
- * [3. Interactive ripgrep integration](#3-interactive-ripgrep-integration)
- * [Preview window](#preview-window)
-* [Tips](#tips)
- * [Respecting `.gitignore`](#respecting-gitignore)
- * [Fish shell](#fish-shell)
-* [Related projects](#related-projects)
-* [License](#license)
-
-
-
-Installation
-------------
-
-fzf project consists of the following components:
-
-- `fzf` executable
-- `fzf-tmux` script for launching fzf in a tmux pane
-- Shell extensions
- - Key bindings (`CTRL-T`, `CTRL-R`, and `ALT-C`) (bash, zsh, fish)
- - Fuzzy auto-completion (bash, zsh)
-- Vim/Neovim plugin
-
-You can [download fzf executable][bin] alone if you don't need the extra
-stuff.
-
-[bin]: https://github.com/junegunn/fzf/releases
-
-### Using Homebrew
-
-You can use [Homebrew](http://brew.sh/) (on macOS or Linux)
-to install fzf.
-
-```sh
-brew install fzf
-
-# To install useful key bindings and fuzzy completion:
-$(brew --prefix)/opt/fzf/install
-```
-
-fzf is also available [via MacPorts][portfile]: `sudo port install fzf`
-
-[portfile]: https://github.com/macports/macports-ports/blob/master/sysutils/fzf/Portfile
-
-### Using git
-
-Alternatively, you can "git clone" this repository to any directory and run
-[install](https://github.com/junegunn/fzf/blob/master/install) script.
-
-```sh
-git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
-~/.fzf/install
-```
-
-### Using Linux package managers
-
-| 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` |
-| pkgin | NetBSD | `pkgin install fzf` |
-| pkg_add | OpenBSD | `pkg_add fzf` |
-| XBPS | Void Linux | `sudo xbps-install -S fzf` |
-| Zypper | openSUSE | `sudo zypper install fzf` |
-
-> :warning: **Key bindings (CTRL-T / CTRL-R / ALT-C) and fuzzy auto-completion
-> may not be enabled by default.**
->
-> Refer to the package documentation for more information. (e.g. `apt-cache show fzf`)
-
-[![Packaging status](https://repology.org/badge/vertical-allrepos/fzf.svg)](https://repology.org/project/fzf/versions)
-
-### Windows
-
-Pre-built binaries for Windows can be downloaded [here][bin]. fzf is also
-available via [Chocolatey][choco] and [Scoop][scoop]:
-
-| Package manager | Command |
-| --- | --- |
-| Chocolatey | `choco install fzf` |
-| Scoop | `scoop install fzf` |
-
-[choco]: https://chocolatey.org/packages/fzf
-[scoop]: https://github.com/ScoopInstaller/Main/blob/master/bucket/fzf.json
-
-Known issues and limitations on Windows can be found on [the wiki
-page][windows-wiki].
-
-[windows-wiki]: https://github.com/junegunn/fzf/wiki/Windows
-
-### As Vim plugin
-
-If you use
-[vim-plug](https://github.com/junegunn/vim-plug), add this line to your Vim
-configuration file:
-
-```vim
-Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
-```
-
-`fzf#install()` makes sure that you have the latest binary, but it's optional,
-so you can omit it if you use a plugin manager that doesn't support hooks.
-
-For more installation options, see [README-VIM.md](README-VIM.md).
-
-Upgrading fzf
--------------
-
-fzf is being actively developed, and you might want to upgrade it once in a
-while. Please follow the instruction below depending on the installation
-method used.
-
-- git: `cd ~/.fzf && git pull && ./install`
-- brew: `brew update; brew upgrade fzf`
-- macports: `sudo port upgrade fzf`
-- chocolatey: `choco upgrade fzf`
-- vim-plug: `:PlugUpdate fzf`
-
-Building fzf
-------------
-
-See [BUILD.md](BUILD.md).
-
-Usage
------
-
-fzf will launch interactive finder, read the list from STDIN, and write the
-selected item to STDOUT.
-
-```sh
-find * -type f | fzf > selected
-```
-
-Without STDIN pipe, fzf will use find command to fetch the list of
-files excluding hidden ones. (You can override the default command with
-`FZF_DEFAULT_COMMAND`)
-
-```sh
-vim $(fzf)
-```
-
-#### Using the finder
-
-- `CTRL-K` / `CTRL-J` (or `CTRL-P` / `CTRL-N`) to move cursor up and down
-- `Enter` key to select the item, `CTRL-C` / `CTRL-G` / `ESC` to exit
-- On multi-select mode (`-m`), `TAB` and `Shift-TAB` to mark multiple items
-- Emacs style key bindings
-- Mouse: scroll, click, double-click; shift-click and shift-scroll on
- multi-select mode
-
-#### Layout
-
-fzf by default starts in fullscreen mode, but you can make it start below the
-cursor with `--height` option.
-
-```sh
-vim $(fzf --height 40%)
-```
-
-Also, check out `--reverse` and `--layout` options if you prefer
-"top-down" layout instead of the default "bottom-up" layout.
-
-```sh
-vim $(fzf --height 40% --reverse)
-```
-
-You can add these options to `$FZF_DEFAULT_OPTS` so that they're applied by
-default. For example,
-
-```sh
-export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'
-```
-
-#### Search syntax
-
-Unless otherwise specified, fzf starts in "extended-search mode" where you can
-type in multiple search terms delimited by spaces. e.g. `^music .mp3$ sbtrkt
-!fire`
-
-| Token | Match type | Description |
-| --------- | -------------------------- | ------------------------------------ |
-| `sbtrkt` | fuzzy-match | Items that match `sbtrkt` |
-| `'wild` | exact-match (quoted) | Items that include `wild` |
-| `^music` | prefix-exact-match | Items that start with `music` |
-| `.mp3$` | suffix-exact-match | Items that end with `.mp3` |
-| `!fire` | inverse-exact-match | Items that do not include `fire` |
-| `!^music` | inverse-prefix-exact-match | Items that do not start with `music` |
-| `!.mp3$` | inverse-suffix-exact-match | Items that do not end with `.mp3` |
-
-If you don't prefer fuzzy matching and do not wish to "quote" every word,
-start fzf with `-e` or `--exact` option. Note that when `--exact` is set,
-`'`-prefix "unquotes" the term.
-
-A single bar character term acts as an OR operator. For example, the following
-query matches entries that start with `core` and end with either `go`, `rb`,
-or `py`.
-
-```
-^core go$ | rb$ | py$
-```
-
-#### Environment variables
-
-- `FZF_DEFAULT_COMMAND`
- - Default command to use when input is tty
- - e.g. `export FZF_DEFAULT_COMMAND='fd --type f'`
- - > :warning: This variable is not used by shell extensions due to the
- > slight difference in requirements.
- >
- > (e.g. `CTRL-T` runs `$FZF_CTRL_T_COMMAND` instead, `vim **` runs
- > `_fzf_compgen_path()`, and `cd **` runs `_fzf_compgen_dir()`)
- >
- > The available options are described later in this document.
-- `FZF_DEFAULT_OPTS`
- - Default options
- - e.g. `export FZF_DEFAULT_OPTS="--layout=reverse --inline-info"`
-
-#### Options
-
-See the man page (`man fzf`) for the full list of options.
-
-#### Demo
-If you learn by watching videos, check out this screencast by [@samoshkin](https://github.com/samoshkin) to explore `fzf` features.
-
-
-
-
-
-Examples
---------
-
-* [Wiki page of examples](https://github.com/junegunn/fzf/wiki/examples)
- * *Disclaimer: The examples on this page are maintained by the community
- and are not thoroughly tested*
-* [Advanced fzf examples](https://github.com/junegunn/fzf/blob/master/ADVANCED.md)
-
-`fzf-tmux` script
------------------
-
-[fzf-tmux](bin/fzf-tmux) is a bash script that opens fzf in a tmux pane.
-
-```sh
-# usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
-
-# See available options
-fzf-tmux --help
-
-# select git branches in horizontal split below (15 lines)
-git branch | fzf-tmux -d 15
-
-# select multiple words in vertical split on the left (20% of screen width)
-cat /usr/share/dict/words | fzf-tmux -l 20% --multi --reverse
-```
-
-It will still work even when you're not on tmux, silently ignoring `-[pudlr]`
-options, so you can invariably use `fzf-tmux` in your scripts.
-
-Alternatively, you can use `--height HEIGHT[%]` option not to start fzf in
-fullscreen mode.
-
-```sh
-fzf --height 40%
-```
-
-Key bindings for command-line
------------------------------
-
-The install script will setup the following key bindings for bash, zsh, and
-fish.
-
-- `CTRL-T` - Paste the selected files and directories onto the command-line
- - Set `FZF_CTRL_T_COMMAND` to override the default command
- - Set `FZF_CTRL_T_OPTS` to pass additional options
-- `CTRL-R` - Paste the selected command from history onto the command-line
- - If you want to see the commands in chronological order, press `CTRL-R`
- again which toggles sorting by relevance
- - Set `FZF_CTRL_R_OPTS` to pass additional options
-- `ALT-C` - cd into the selected directory
- - Set `FZF_ALT_C_COMMAND` to override the default command
- - Set `FZF_ALT_C_OPTS` to pass additional options
-
-If you're on a tmux session, you can start fzf in a tmux split-pane or in
-a tmux popup window by setting `FZF_TMUX_OPTS` (e.g. `-d 40%`).
-See `fzf-tmux --help` for available options.
-
-More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Configuring-shell-key-bindings).
-
-Fuzzy completion for bash and zsh
----------------------------------
-
-#### Files and directories
-
-Fuzzy completion for files and directories can be triggered if the word before
-the cursor ends with the trigger sequence, which is by default `**`.
-
-- `COMMAND [DIRECTORY/][FUZZY_PATTERN]**`
-
-```sh
-# Files under the current directory
-# - You can select multiple items with TAB key
-vim **
-
-# Files under parent directory
-vim ../**
-
-# Files under parent directory that match `fzf`
-vim ../fzf**
-
-# Files under your home directory
-vim ~/**
-
-
-# Directories under current directory (single-selection)
-cd **
-
-# Directories under ~/github that match `fzf`
-cd ~/github/fzf**
-```
-
-#### Process IDs
-
-Fuzzy completion for PIDs is provided for kill command. In this case,
-there is no trigger sequence; just press the tab key after the kill command.
-
-```sh
-# Can select multiple processes with or keys
-kill -9
-```
-
-#### Host names
-
-For ssh and telnet commands, fuzzy completion for hostnames is provided. The
-names are extracted from /etc/hosts and ~/.ssh/config.
-
-```sh
-ssh **
-telnet **
-```
-
-#### Environment variables / Aliases
-
-```sh
-unset **
-export **
-unalias **
-```
-
-#### Settings
-
-```sh
-# Use ~~ as the trigger sequence instead of the default **
-export FZF_COMPLETION_TRIGGER='~~'
-
-# Options to fzf command
-export FZF_COMPLETION_OPTS='--border --info=inline'
-
-# Use fd (https://github.com/sharkdp/fd) instead of the default find
-# command for listing path candidates.
-# - The first argument to the function ($1) is the base path to start traversal
-# - See the source code (completion.{bash,zsh}) for the details.
-_fzf_compgen_path() {
- fd --hidden --follow --exclude ".git" . "$1"
-}
-
-# Use fd to generate the list for directory completion
-_fzf_compgen_dir() {
- fd --type d --hidden --follow --exclude ".git" . "$1"
-}
-
-# (EXPERIMENTAL) Advanced customization of fzf options via _fzf_comprun function
-# - The first argument to the function is the name of the command.
-# - You should make sure to pass the rest of the arguments to fzf.
-_fzf_comprun() {
- local command=$1
- shift
-
- case "$command" in
- cd) fzf "$@" --preview 'tree -C {} | head -200' ;;
- export|unset) fzf "$@" --preview "eval 'echo \$'{}" ;;
- ssh) fzf "$@" --preview 'dig {}' ;;
- *) fzf "$@" ;;
- esac
-}
-```
-
-#### Supported commands
-
-On bash, fuzzy completion is enabled only for a predefined set of commands
-(`complete | grep _fzf` to see the list). But you can enable it for other
-commands as well by using `_fzf_setup_completion` helper function.
-
-```sh
-# usage: _fzf_setup_completion path|dir|var|alias|host COMMANDS...
-_fzf_setup_completion path ag git kubectl
-_fzf_setup_completion dir tree
-```
-
-#### Custom fuzzy completion
-
-_**(Custom completion API is experimental and subject to change)**_
-
-For a command named _"COMMAND"_, define `_fzf_complete_COMMAND` function using
-`_fzf_complete` helper.
-
-```sh
-# Custom fuzzy completion for "doge" command
-# e.g. doge **
-_fzf_complete_doge() {
- _fzf_complete --multi --reverse --prompt="doge> " -- "$@" < <(
- echo very
- echo wow
- echo such
- echo doge
- )
-}
-```
-
-- The arguments before `--` are the options to fzf.
-- After `--`, simply pass the original completion arguments unchanged (`"$@"`).
-- Then, write a set of commands that generates the completion candidates and
- feed its output to the function using process substitution (`< <(...)`).
-
-zsh will automatically pick up the function using the naming convention but in
-bash you have to manually associate the function with the command using the
-`complete` command.
-
-```sh
-[ -n "$BASH" ] && complete -F _fzf_complete_doge -o default -o bashdefault doge
-```
-
-If you need to post-process the output from fzf, define
-`_fzf_complete_COMMAND_post` as follows.
-
-```sh
-_fzf_complete_foo() {
- _fzf_complete --multi --reverse --header-lines=3 -- "$@" < <(
- ls -al
- )
-}
-
-_fzf_complete_foo_post() {
- awk '{print $NF}'
-}
-
-[ -n "$BASH" ] && complete -F _fzf_complete_foo -o default -o bashdefault foo
-```
-
-Vim plugin
-----------
-
-See [README-VIM.md](README-VIM.md).
-
-Advanced topics
----------------
-
-### Performance
-
-fzf is fast and is [getting even faster][perf]. Performance should not be
-a problem in most use cases. However, you might want to be aware of the
-options that affect performance.
-
-- `--ansi` tells fzf to extract and parse ANSI color codes in the input, and it
- makes the initial scanning slower. So it's not recommended that you add it
- to your `$FZF_DEFAULT_OPTS`.
-- `--nth` makes fzf slower because it has to tokenize each line.
-- `--with-nth` makes fzf slower as fzf has to tokenize and reassemble each
- line.
-- If you absolutely need better performance, you can consider using
- `--algo=v1` (the default being `v2`) to make fzf use a faster greedy
- algorithm. However, this algorithm is not guaranteed to find the optimal
- ordering of the matches and is not recommended.
-
-[perf]: https://junegunn.kr/images/fzf-0.17.0.png
-
-### Executing external programs
-
-You can set up key bindings for starting external processes without leaving
-fzf (`execute`, `execute-silent`).
-
-```bash
-# Press F1 to open the file with less without leaving fzf
-# Press CTRL-Y to copy the line to clipboard and aborts fzf (requires pbcopy)
-fzf --bind 'f1:execute(less -f {}),ctrl-y:execute-silent(echo {} | pbcopy)+abort'
-```
-
-See *KEY BINDINGS* section of the man page for details.
-
-### Reloading the candidate list
-
-By binding `reload` action to a key or an event, you can make fzf dynamically
-reload the candidate list. See https://github.com/junegunn/fzf/issues/1750 for
-more details.
-
-#### 1. Update the list of processes by pressing CTRL-R
-
-```sh
-FZF_DEFAULT_COMMAND='ps -ef' \
- fzf --bind 'ctrl-r:reload($FZF_DEFAULT_COMMAND)' \
- --header 'Press CTRL-R to reload' --header-lines=1 \
- --height=50% --layout=reverse
-```
-
-#### 2. Switch between sources by pressing CTRL-D or CTRL-F
-
-```sh
-FZF_DEFAULT_COMMAND='find . -type f' \
- fzf --bind 'ctrl-d:reload(find . -type d),ctrl-f:reload($FZF_DEFAULT_COMMAND)' \
- --height=50% --layout=reverse
-```
-
-#### 3. Interactive ripgrep integration
-
-The following example uses fzf as the selector interface for ripgrep. We bound
-`reload` action to `change` event, so every time you type on fzf, the ripgrep
-process will restart with the updated query string denoted by the placeholder
-expression `{q}`. Also, note that we used `--disabled` option so that fzf
-doesn't perform any secondary filtering.
-
-```sh
-INITIAL_QUERY=""
-RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
-FZF_DEFAULT_COMMAND="$RG_PREFIX '$INITIAL_QUERY'" \
- fzf --bind "change:reload:$RG_PREFIX {q} || true" \
- --ansi --disabled --query "$INITIAL_QUERY" \
- --height=50% --layout=reverse
-```
-
-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
-`|| true` to the command, so that it always exits with 0.
-
-### Preview window
-
-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 with the single-quoted string of the focused line
-fzf --preview 'cat {}'
-```
-
-Preview window supports ANSI colors, so you can use any program that
-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 --line-range :500 {}'
-```
-
-You can customize the size, position, and border of the preview window using
-`--preview-window` option, and the foreground and background color of it with
-`--color` option. For example,
-
-```bash
-fzf --height 40% --layout reverse --info inline --border \
- --preview 'file {}' --preview-window up,1,border-horizontal \
- --color 'fg:#bbccdd,fg+:#ddeeff,bg:#334455,preview-bg:#223344,border:#778899'
-```
-
-See the man page (`man fzf`) for the full list of options.
-
-For more advanced examples, see [Key bindings for git with fzf][fzf-git]
-([code](https://gist.github.com/junegunn/8b572b8d4b5eddd8b85e5f4d40f17236)).
-
-[fzf-git]: https://junegunn.kr/2016/07/fzf-git/
-
-----
-
-Since fzf is a general-purpose text filter rather than a file finder, **it is
-not a good idea to add `--preview` option to your `$FZF_DEFAULT_OPTS`**.
-
-```sh
-# *********************
-# ** DO NOT DO THIS! **
-# *********************
-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
-seq 100 | fzf
-history | fzf
-```
-
-Tips
-----
-
-#### Respecting `.gitignore`
-
-You can use [fd](https://github.com/sharkdp/fd),
-[ripgrep](https://github.com/BurntSushi/ripgrep), or [the silver
-searcher](https://github.com/ggreer/the_silver_searcher) instead of the
-default find command to traverse the file system while respecting
-`.gitignore`.
-
-```sh
-# Feed the output of fd into fzf
-fd --type f | fzf
-
-# Setting fd as the default source for fzf
-export FZF_DEFAULT_COMMAND='fd --type f'
-
-# Now fzf (w/o pipe) will use fd instead of find
-fzf
-
-# To apply the command to CTRL-T as well
-export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
-```
-
-If you want the command to follow symbolic links and don't want it to exclude
-hidden files, use the following command:
-
-```sh
-export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'
-```
-
-#### Fish shell
-
-`CTRL-T` key binding of fish, unlike those of bash and zsh, will use the last
-token on the command-line as the root directory for the recursive search. For
-instance, hitting `CTRL-T` at the end of the following command-line
-
-```sh
-ls /var/
-```
-
-will list all files and directories under `/var/`.
-
-When using a custom `FZF_CTRL_T_COMMAND`, use the unexpanded `$dir` variable to
-make use of this feature. `$dir` defaults to `.` when the last token is not a
-valid directory. Example:
-
-```sh
-set -g FZF_CTRL_T_COMMAND "command find -L \$dir -type f 2> /dev/null | sed '1d; s#^\./##'"
-```
-
-Related projects
-----------------
-
-https://github.com/junegunn/fzf/wiki/Related-projects
-
-[License](LICENSE)
-------------------
-
-The MIT License (MIT)
-
-Copyright (c) 2013-2021 Junegunn Choi
diff --git a/.fzf/bin/fzf-tmux b/.fzf/bin/fzf-tmux
deleted file mode 100755
index 6a18cf8..0000000
--- a/.fzf/bin/fzf-tmux
+++ /dev/null
@@ -1,233 +0,0 @@
-#!/usr/bin/env bash
-# fzf-tmux: starts fzf in a tmux pane
-# usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
-
-fail() {
- >&2 echo "$1"
- exit 2
-}
-
-fzf="$(command -v fzf 2> /dev/null)" || fzf="$(dirname "$0")/fzf"
-[[ -x "$fzf" ]] || fail 'fzf executable not found'
-
-tmux_args=()
-args=()
-opt=""
-skip=""
-swap=""
-close=""
-term=""
-[[ -n "$LINES" ]] && lines=$LINES || lines=$(tput lines) || lines=$(tmux display-message -p "#{pane_height}")
-[[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}")
-
-help() {
- >&2 echo 'usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
-
- LAYOUT OPTIONS:
- (default layout: -d 50%)
-
- Popup window (requires tmux 3.2 or above):
- -p [WIDTH[%][,HEIGHT[%]]] (default: 50%)
- -w WIDTH[%]
- -h HEIGHT[%]
- -x COL
- -y ROW
-
- Split pane:
- -u [HEIGHT[%]] Split above (up)
- -d [HEIGHT[%]] Split below (down)
- -l [WIDTH[%]] Split left
- -r [WIDTH[%]] Split right
-'
- exit
-}
-
-while [[ $# -gt 0 ]]; do
- arg="$1"
- shift
- [[ -z "$skip" ]] && case "$arg" in
- -)
- term=1
- ;;
- --help)
- help
- ;;
- --version)
- echo "fzf-tmux (with fzf $("$fzf" --version))"
- exit
- ;;
- -p*|-w*|-h*|-x*|-y*|-d*|-u*|-r*|-l*)
- if [[ "$arg" =~ ^-[pwhxy] ]]; then
- [[ "$opt" =~ "-K -E" ]] || opt="-K -E"
- elif [[ "$arg" =~ ^.[lr] ]]; then
- opt="-h"
- if [[ "$arg" =~ ^.l ]]; then
- opt="$opt -d"
- swap="; swap-pane -D ; select-pane -L"
- close="; tmux swap-pane -D"
- fi
- else
- opt=""
- if [[ "$arg" =~ ^.u ]]; then
- opt="$opt -d"
- swap="; swap-pane -D ; select-pane -U"
- close="; tmux swap-pane -D"
- fi
- fi
- if [[ ${#arg} -gt 2 ]]; then
- size="${arg:2}"
- else
- if [[ "$1" =~ ^[0-9%,]+$ ]] || [[ "$1" =~ ^[A-Z]$ ]]; then
- size="$1"
- shift
- else
- continue
- fi
- fi
-
- if [[ "$arg" =~ ^-p ]]; then
- if [[ -n "$size" ]]; then
- w=${size%%,*}
- h=${size##*,}
- opt="$opt -w$w -h$h"
- fi
- elif [[ "$arg" =~ ^-[whxy] ]]; then
- opt="$opt ${arg:0:2}$size"
- elif [[ "$size" =~ %$ ]]; then
- size=${size:0:((${#size}-1))}
- if [[ -n "$swap" ]]; then
- opt="$opt -p $(( 100 - size ))"
- else
- opt="$opt -p $size"
- fi
- else
- if [[ -n "$swap" ]]; then
- if [[ "$arg" =~ ^.l ]]; then
- max=$columns
- else
- max=$lines
- fi
- size=$(( max - size ))
- [[ $size -lt 0 ]] && size=0
- opt="$opt -l $size"
- else
- opt="$opt -l $size"
- fi
- fi
- ;;
- --)
- # "--" can be used to separate fzf-tmux options from fzf options to
- # avoid conflicts
- skip=1
- tmux_args=("${args[@]}")
- args=()
- continue
- ;;
- *)
- args+=("$arg")
- ;;
- esac
- [[ -n "$skip" ]] && args+=("$arg")
-done
-
-if [[ -z "$TMUX" ]]; then
- "$fzf" "${args[@]}"
- exit $?
-fi
-
-# --height option is not allowed. CTRL-Z is also disabled.
-args=("${args[@]}" "--no-height" "--bind=ctrl-z:ignore")
-
-# 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
-fi
-
-set -e
-
-# Clean up named pipes on exit
-id=$RANDOM
-argsf="${TMPDIR:-/tmp}/fzf-args-$id"
-fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id"
-fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id"
-fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id"
-tmux_win_opts=( $(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/') )
-cleanup() {
- \rm -f $argsf $fifo1 $fifo2 $fifo3
-
- # Restore tmux window options
- if [[ "${#tmux_win_opts[@]}" -gt 0 ]]; then
- eval "tmux ${tmux_win_opts[*]}"
- fi
-
- # 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 \; \
- kill-window -t $tmp_window \; \
- resize-pane -Z
- fi
-
- if [[ $# -gt 0 ]]; then
- trap - EXIT
- exit 130
- fi
-}
-trap 'cleanup 1' SIGUSR1
-trap 'cleanup' EXIT
-
-envs="export TERM=$TERM "
-[[ "$opt" =~ "-K -E" ]] && FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS"
-[[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
-[[ -n "$FZF_DEFAULT_COMMAND" ]] && envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
-echo "$envs;" > "$argsf"
-
-# Build arguments to fzf
-opts=$(printf "%q " "${args[@]}")
-
-pppid=$$
-echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" >> $argsf
-close="; trap - EXIT SIGINT SIGTERM $close"
-
-export TMUX=$(cut -d , -f 1,2 <<< "$TMUX")
-mkfifo -m o+w $fifo2
-if [[ "$opt" =~ "-K -E" ]]; then
- cat $fifo2 &
- if [[ -n "$term" ]] || [[ -t 0 ]]; then
- cat <<< "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
- else
- mkfifo $fifo1
- cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; out=\$? $close; exit \$out" >> $argsf
- cat <&0 > $fifo1 &
- fi
-
- # tmux dropped the support for `-K`, `-R` to popup command
- # TODO: We can remove this once tmux 3.2 is released
- if [[ ! "$(tmux popup --help 2>&1)" =~ '-R shell-command' ]]; then
- opt="${opt/-K/}"
- else
- opt="${opt} -R"
- fi
-
- tmux popup -d "$PWD" "${tmux_args[@]}" $opt "bash $argsf" > /dev/null 2>&1
- exit $?
-fi
-
-mkfifo -m o+w $fifo3
-if [[ -n "$term" ]] || [[ -t 0 ]]; then
- cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf
-else
- mkfifo $fifo1
- cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; echo \$? > $fifo3 $close" >> $argsf
- cat <&0 > $fifo1 &
-fi
-tmux set-window-option synchronize-panes off \;\
- set-window-option remain-on-exit off \;\
- split-window -c "$PWD" $opt "${tmux_args[@]}" "bash -c 'exec -a fzf bash $argsf'" $swap \
- > /dev/null 2>&1 || { "$fzf" "${args[@]}"; exit $?; }
-cat $fifo2
-exit "$(cat $fifo3)"
diff --git a/.fzf/doc/fzf.txt b/.fzf/doc/fzf.txt
deleted file mode 100644
index 9485723..0000000
--- a/.fzf/doc/fzf.txt
+++ /dev/null
@@ -1,512 +0,0 @@
-fzf.txt fzf Last change: May 19 2021
-FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
-==============================================================================
-
- FZF Vim integration |fzf-vim-integration|
- Installation |fzf-installation|
- Summary |fzf-summary|
- :FZF[!] |:FZF|
- Configuration |fzf-configuration|
- Examples |fzf-examples|
- Explanation of g:fzf_colors |fzf-explanation-of-gfzfcolors|
- fzf#run |fzf#run|
- fzf#wrap |fzf#wrap|
- Global options supported by fzf#wrap |fzf-global-options-supported-by-fzf#wrap|
- Tips |fzf-tips|
- fzf inside terminal buffer |fzf-inside-terminal-buffer|
- Starting fzf in a popup window |fzf-starting-fzf-in-a-popup-window|
- Hide statusline |fzf-hide-statusline|
- License |fzf-license|
-
-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*
-==============================================================================
-
-The Vim plugin of fzf provides two core functions, and `:FZF` command which is
-the basic file selector command built on top of them.
-
- 1. `fzf#run([spec dict])`
- - Starts fzf inside Vim with the given spec
- - `:call fzf#run({'source': 'ls'})`
- 2. `fzf#wrap([spec dict]) -> (dict)`
- - Takes a spec for `fzf#run` and returns an extended version of it with
- additional options for addressing global preferences (`g:fzf_xxx`)
- - `:echo fzf#wrap({'source': 'ls'})`
- - We usually wrap a spec with `fzf#wrap` before passing it to `fzf#run`
- - `:call fzf#run(fzf#wrap({'source': 'ls'}))`
- 3. `:FZF [fzf_options string] [path string]`
- - 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}{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.
-
- {2} https://github.com/junegunn/fzf.vim
-
-
-:FZF[!]
-==============================================================================
-
- *:FZF*
->
- " Look for files under current directory
- :FZF
-
- " Look for files under your home directory
- :FZF ~
-
- " With fzf command-line options
- :FZF --reverse --info=inline /tmp
-
- " Bang version starts fzf in fullscreen mode
- :FZF!
-<
-Similarly to {ctrlp.vim}{3}, use enter key, CTRL-T, CTRL-X or CTRL-V to open
-selected files in the current window, in new tabs, in horizontal splits, or in
-vertical splits respectively.
-
-Note that the environment variables `FZF_DEFAULT_COMMAND` and
-`FZF_DEFAULT_OPTS` also apply here.
-
- {3} https://github.com/kien/ctrlp.vim
-
-
-< Configuration >_____________________________________________________________~
- *fzf-configuration*
-
- *g:fzf_action* *g:fzf_layout* *g:fzf_colors* *g:fzf_history_dir*
-
- - `g:fzf_action`
- - Customizable extra key bindings for opening selected files in different
- ways
- - `g:fzf_layout`
- - Determines the size and position of fzf window
- - `g:fzf_colors`
- - Customizes fzf colors to match the current color scheme
- - `g:fzf_history_dir`
- - Enables history feature
-
-
-Examples~
- *fzf-examples*
->
- " This is the default extra key bindings
- let g:fzf_action = {
- \ 'ctrl-t': 'tab split',
- \ 'ctrl-x': 'split',
- \ 'ctrl-v': 'vsplit' }
-
- " An action can be a reference to a function that processes selected lines
- function! s:build_quickfix_list(lines)
- call setqflist(map(copy(a:lines), '{ "filename": v:val }'))
- copen
- cc
- endfunction
-
- let g:fzf_action = {
- \ 'ctrl-q': function('s:build_quickfix_list'),
- \ 'ctrl-t': 'tab split',
- \ 'ctrl-x': 'split',
- \ 'ctrl-v': 'vsplit' }
-
- " Default fzf layout
- " - Popup window (center of the screen)
- let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
-
- " - Popup window (center of the current window)
- let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': v:true } }
-
- " - Popup window (anchored to the bottom of the current window)
- let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': v:true, 'yoffset': 1.0 } }
-
- " - down / up / left / right
- let g:fzf_layout = { 'down': '40%' }
-
- " - Window using a Vim command
- let g:fzf_layout = { 'window': 'enew' }
- let g:fzf_layout = { 'window': '-tabnew' }
- let g:fzf_layout = { 'window': '10new' }
-
- " Customize fzf colors to match your color scheme
- " - fzf#wrap translates this to a set of `--color` options
- let g:fzf_colors =
- \ { 'fg': ['fg', 'Normal'],
- \ 'bg': ['bg', 'Normal'],
- \ 'hl': ['fg', 'Comment'],
- \ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
- \ 'bg+': ['bg', 'CursorLine', 'CursorColumn'],
- \ 'hl+': ['fg', 'Statement'],
- \ 'info': ['fg', 'PreProc'],
- \ 'border': ['fg', 'Ignore'],
- \ 'prompt': ['fg', 'Conditional'],
- \ 'pointer': ['fg', 'Exception'],
- \ 'marker': ['fg', 'Keyword'],
- \ 'spinner': ['fg', 'Label'],
- \ 'header': ['fg', 'Comment'] }
-
- " Enable per-command history
- " - History files will be stored in the specified directory
- " - When set, CTRL-N and CTRL-P will be bound to 'next-history' and
- " 'previous-history' instead of 'down' and 'up'.
- 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)
- `preview-fg` / `preview-bg` | Preview window text and background
- `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
- `query` | Query string
- `disabled` | Query string when search is disabled
- `prompt` | Prompt before query ( `> ` )
- `pointer` | Pointer to the current line ( `>` )
- ----------------------------+------------------------------------------------------
- - `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
-==============================================================================
-
- *fzf#run*
-
-`fzf#run()` function is the core of Vim integration. It takes a single
-dictionary argument, a spec, and starts fzf process accordingly. At the very
-least, specify `sink` option to tell what it should do with the selected
-entry.
->
- call fzf#run({'sink': 'e'})
-<
-We haven't specified the `source`, so this is equivalent to starting fzf on
-command line without standard input pipe; fzf will use find command (or
-`$FZF_DEFAULT_COMMAND` if defined) to list the files under the current
-directory. When you select one, it will open it with the sink, `:e` command.
-If you want to open it in a new tab, you can pass `:tabedit` command instead
-as the sink.
->
- call fzf#run({'sink': 'tabedit'})
-<
-Instead of using the default find command, you can use any shell command as
-the source. The following example will list the files managed by git. It's
-equivalent to running `git ls-files | fzf` on shell.
->
- call fzf#run({'source': 'git ls-files', 'sink': 'e'})
-<
-fzf options can be specified as `options` entry in spec dictionary.
->
- call fzf#run({'sink': 'tabedit', 'options': '--multi --reverse'})
-<
-You can also pass a layout option if you don't want fzf window to take up the
-entire screen.
->
- " up / down / left / right / window are allowed
- call fzf#run({'source': 'git ls-files', 'sink': 'e', 'left': '40%'})
- call fzf#run({'source': 'git ls-files', 'sink': 'e', 'window': '30vnew'})
-<
-`source` doesn't have to be an external shell command, you can pass a Vim
-array as the source. In the next example, we pass the names of color schemes
-as the source to implement a color scheme selector.
->
- call fzf#run({'source': map(split(globpath(&rtp, 'colors/*.vim')),
- \ 'fnamemodify(v:val, ":t:r")'),
- \ 'sink': 'colo', 'left': '25%'})
-<
-The following table summarizes the available options.
-
- ---------------------------+---------------+----------------------------------------------------------------------
- Option name | Type | Description ~
- ---------------------------+---------------+----------------------------------------------------------------------
- `source` | string | External command to generate input to fzf (e.g. `find .` )
- `source` | list | Vim list as input to fzf
- `sink` | string | Vim command to handle the selected item (e.g. `e` , `tabe` )
- `sink` | funcref | Reference to function to process each selected item
- `sinklist` (or `sink*` ) | funcref | Similar to `sink` , but takes the list of output lines at once
- `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}` )
- ---------------------------+---------------+----------------------------------------------------------------------
-
-`options` entry can be either a string or a list. For simple cases, string
-should suffice, but prefer to use list type to avoid escaping issues.
->
- call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'})
- call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']})
-<
-When `window` entry is a dictionary, fzf will start in a popup window. The
-following options are allowed:
-
- - Required:
- - `width` [float range [0 ~ 1]] or [integer range [8 ~ ]]
- - `height` [float range [0 ~ 1]] or [integer range [4 ~ ]]
- - Optional:
- - `yoffset` [float default 0.5 range [0 ~ 1]]
- - `xoffset` [float default 0.5 range [0 ~ 1]]
- - `relative` [boolean default v:false]
- - `border` [string default `rounded`]: Border style
- - `rounded` / `sharp` / `horizontal` / `vertical` / `top` / `bottom` / `left` / `right` / `no[ne]`
-
-
-FZF#WRAP
-==============================================================================
-
- *fzf#wrap*
-
-We have seen that several aspects of `:FZF` command can be configured with a
-set of global option variables; different ways to open files (`g:fzf_action`),
-window position and size (`g:fzf_layout`), color palette (`g:fzf_colors`),
-etc.
-
-So how can we make our custom `fzf#run` calls also respect those variables?
-Simply by "wrapping" the spec dictionary with `fzf#wrap` before passing it to
-`fzf#run`.
-
- - `fzf#wrap([name string], [spec dict], [fullscreen bool]) -> (dict)`
- - All arguments are optional. Usually we only need to pass a spec
- dictionary.
- - `name` is for managing history files. It is ignored if `g:fzf_history_dir`
- is not defined.
- - `fullscreen` can be either `0` or `1` (default: 0).
-
-`fzf#wrap` takes a spec and returns an extended version of it (also a
-dictionary) with additional options for addressing global preferences. You can
-examine the return value of it like so:
->
- echo fzf#wrap({'source': 'ls'})
-<
-After we "wrap" our spec, we pass it to `fzf#run`.
->
- call fzf#run(fzf#wrap({'source': 'ls'}))
-<
-Now it supports CTRL-T, CTRL-V, and CTRL-X key bindings (configurable via
-`g:fzf_action`) and it opens fzf window according to `g:fzf_layout` setting.
-
-To make it easier to use, let's define `LS` command.
->
- command! LS call fzf#run(fzf#wrap({'source': 'ls'}))
-<
-Type `:LS` and see how it works.
-
-We would like to make `:LS!` (bang version) open fzf in fullscreen, just like
-`:FZF!`. Add `-bang` to command definition, and use value to set the
-last `fullscreen` argument of `fzf#wrap` (see :help ).
->
- " On :LS!, evaluates to '!', and '!0' becomes 1
- command! -bang LS call fzf#run(fzf#wrap({'source': 'ls'}, 0))
-<
-Our `:LS` command will be much more useful if we can pass a directory argument
-to it, so that something like `:LS /tmp` is possible.
->
- command! -bang -complete=dir -nargs=? LS
- \ call fzf#run(fzf#wrap({'source': 'ls', 'dir': }, 0))
-<
-Lastly, if you have enabled `g:fzf_history_dir`, you might want to assign a
-unique name to our command and pass it as the first argument to `fzf#wrap`.
->
- " The query history for this command will be stored as 'ls' inside g:fzf_history_dir.
- " The name is ignored if g:fzf_history_dir is not defined.
- command! -bang -complete=dir -nargs=? LS
- \ call fzf#run(fzf#wrap('ls', {'source': 'ls', 'dir': }, 0))
-<
-
-< Global options supported by fzf#wrap >______________________________________~
- *fzf-global-options-supported-by-fzf#wrap*
-
- - `g:fzf_layout`
- - `g:fzf_action`
- - Works only when no custom `sink` (or `sink*`) is provided
- - Having custom sink usually means that each entry is not an ordinary
- file path (e.g. name of color scheme), so we can't blindly apply the
- same strategy (i.e. `tabedit some-color-scheme` doesn't make sense)
- - `g:fzf_colors`
- - `g:fzf_history_dir`
-
-
-TIPS *fzf-tips*
-==============================================================================
-
-
-< fzf inside terminal buffer >________________________________________________~
- *fzf-inside-terminal-buffer*
-
-The latest versions of Vim and Neovim include builtin terminal emulator
-(`:terminal`) and fzf will start in a terminal buffer in the following cases:
-
- - On Neovim
- - On GVim
- - On Terminal Vim with a non-default layout
- - `call fzf#run({'left': '30%'})` or `let g:fzf_layout = {'left': '30%'}`
-
-On the latest versions of Vim and Neovim, fzf will start in a terminal buffer.
-If you find the default ANSI colors to be different, consider configuring the
-colors using `g:terminal_ansi_colors` in regular Vim or `g:terminal_color_x`
-in Neovim.
-
- *g:terminal_color_15* *g:terminal_color_14* *g:terminal_color_13*
-*g:terminal_color_12* *g:terminal_color_11* *g:terminal_color_10* *g:terminal_color_9*
- *g:terminal_color_8* *g:terminal_color_7* *g:terminal_color_6* *g:terminal_color_5*
- *g:terminal_color_4* *g:terminal_color_3* *g:terminal_color_2* *g:terminal_color_1*
- *g:terminal_color_0*
->
- " Terminal colors for seoul256 color scheme
- if has('nvim')
- let g:terminal_color_0 = '#4e4e4e'
- let g:terminal_color_1 = '#d68787'
- let g:terminal_color_2 = '#5f865f'
- let g:terminal_color_3 = '#d8af5f'
- let g:terminal_color_4 = '#85add4'
- let g:terminal_color_5 = '#d7afaf'
- let g:terminal_color_6 = '#87afaf'
- let g:terminal_color_7 = '#d0d0d0'
- let g:terminal_color_8 = '#626262'
- let g:terminal_color_9 = '#d75f87'
- let g:terminal_color_10 = '#87af87'
- let g:terminal_color_11 = '#ffd787'
- let g:terminal_color_12 = '#add4fb'
- let g:terminal_color_13 = '#ffafaf'
- let g:terminal_color_14 = '#87d7d7'
- let g:terminal_color_15 = '#e4e4e4'
- else
- let g:terminal_ansi_colors = [
- \ '#4e4e4e', '#d68787', '#5f865f', '#d8af5f',
- \ '#85add4', '#d7afaf', '#87afaf', '#d0d0d0',
- \ '#626262', '#d75f87', '#87af87', '#ffd787',
- \ '#add4fb', '#ffafaf', '#87d7d7', '#e4e4e4'
- \ ]
- endif
-<
-
-< Starting fzf in a popup window >____________________________________________~
- *fzf-starting-fzf-in-a-popup-window*
->
- " Required:
- " - width [float range [0 ~ 1]] or [integer range [8 ~ ]]
- " - height [float range [0 ~ 1]] or [integer range [4 ~ ]]
- "
- " Optional:
- " - xoffset [float default 0.5 range [0 ~ 1]]
- " - yoffset [float default 0.5 range [0 ~ 1]]
- " - relative [boolean default v:false]
- " - border [string default 'rounded']: Border style
- " - '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*
-
-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 open fzf on the bottom on the screen (e.g. `{'down':
-'40%'}`), you might want to temporarily disable the statusline for a cleaner
-look.
->
- let g:fzf_layout = { 'down': '30%' }
- autocmd! FileType fzf
- autocmd FileType fzf set laststatus=0 noshowmode noruler
- \| autocmd BufLeave set laststatus=2 showmode ruler
-<
-
-LICENSE *fzf-license*
-==============================================================================
-
-The MIT License (MIT)
-
-Copyright (c) 2013-2021 Junegunn Choi
-
-==============================================================================
-vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap:
diff --git a/.fzf/go.mod b/.fzf/go.mod
deleted file mode 100644
index d7c3b3c..0000000
--- a/.fzf/go.mod
+++ /dev/null
@@ -1,17 +0,0 @@
-module github.com/junegunn/fzf
-
-require (
- github.com/gdamore/tcell v1.4.0
- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
- github.com/mattn/go-isatty v0.0.14
- github.com/mattn/go-runewidth v0.0.13
- github.com/mattn/go-shellwords v1.0.12
- github.com/rivo/uniseg v0.2.0
- github.com/saracen/walker v0.1.2
- golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
- golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
- golang.org/x/term v0.0.0-20210317153231-de623e64d2a6
- golang.org/x/text v0.3.6 // indirect
-)
-
-go 1.13
diff --git a/.fzf/go.sum b/.fzf/go.sum
deleted file mode 100644
index f1bb671..0000000
--- a/.fzf/go.sum
+++ /dev/null
@@ -1,31 +0,0 @@
-github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
-github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
-github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=
-github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
-github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
-github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
-github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
-github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/saracen/walker v0.1.2 h1:/o1TxP82n8thLvmL4GpJXduYaRmJ7qXp8u9dSlV0zmo=
-github.com/saracen/walker v0.1.2/go.mod h1:0oKYMsKVhSJ+ful4p/XbjvXbMgLEkLITZaxozsl4CGE=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 h1:EC6+IGYTjPpRfv9a2b/6Puw0W+hLtAhkV1tPsXhutqs=
-golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/.fzf/install b/.fzf/install
deleted file mode 100755
index f604dcf..0000000
--- a/.fzf/install
+++ /dev/null
@@ -1,382 +0,0 @@
-#!/usr/bin/env bash
-
-set -u
-
-version=0.28.0
-auto_completion=
-key_bindings=
-update_config=2
-shells="bash zsh fish"
-prefix='~/.fzf'
-prefix_expand=~/.fzf
-fish_dir=${XDG_CONFIG_HOME:-$HOME/.config}/fish
-
-help() {
- cat << EOF
-usage: $0 [OPTIONS]
-
- --help Show this message
- --bin Download fzf binary only; Do not generate ~/.fzf.{bash,zsh}
- --all Download fzf binary and update configuration files
- to enable key bindings and fuzzy completion
- --xdg Generate files under \$XDG_CONFIG_HOME/fzf
- --[no-]key-bindings Enable/disable key bindings (CTRL-T, CTRL-R, ALT-C)
- --[no-]completion Enable/disable fuzzy completion (bash & zsh)
- --[no-]update-rc Whether or not to update shell configuration files
-
- --no-bash Do not set up bash configuration
- --no-zsh Do not set up zsh configuration
- --no-fish Do not set up fish configuration
-EOF
-}
-
-for opt in "$@"; do
- case $opt in
- --help)
- help
- exit 0
- ;;
- --all)
- auto_completion=1
- key_bindings=1
- update_config=1
- ;;
- --xdg)
- prefix='"${XDG_CONFIG_HOME:-$HOME/.config}"/fzf/fzf'
- prefix_expand=${XDG_CONFIG_HOME:-$HOME/.config}/fzf/fzf
- mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/fzf"
- ;;
- --key-bindings) key_bindings=1 ;;
- --no-key-bindings) key_bindings=0 ;;
- --completion) auto_completion=1 ;;
- --no-completion) auto_completion=0 ;;
- --update-rc) update_config=1 ;;
- --no-update-rc) update_config=0 ;;
- --bin) ;;
- --no-bash) shells=${shells/bash/} ;;
- --no-zsh) shells=${shells/zsh/} ;;
- --no-fish) shells=${shells/fish/} ;;
- *)
- echo "unknown option: $opt"
- help
- exit 1
- ;;
- esac
-done
-
-cd "$(dirname "${BASH_SOURCE[0]}")"
-fzf_base=$(pwd)
-fzf_base_esc=$(printf %q "$fzf_base")
-
-ask() {
- while true; do
- read -p "$1 ([y]/n) " -r
- REPLY=${REPLY:-"y"}
- if [[ $REPLY =~ ^[Yy]$ ]]; then
- return 1
- elif [[ $REPLY =~ ^[Nn]$ ]]; then
- return 0
- fi
- done
-}
-
-check_binary() {
- echo -n " - Checking fzf executable ... "
- local output
- output=$("$fzf_base"/bin/fzf --version 2>&1)
- if [ $? -ne 0 ]; then
- echo "Error: $output"
- binary_error="Invalid binary"
- else
- output=${output/ */}
- if [ "$version" != "$output" ]; then
- echo "$output != $version"
- binary_error="Invalid version"
- else
- echo "$output"
- binary_error=""
- return 0
- fi
- fi
- rm -f "$fzf_base"/bin/fzf
- return 1
-}
-
-link_fzf_in_path() {
- if which_fzf="$(command -v fzf)"; then
- echo " - Found in \$PATH"
- echo " - Creating symlink: bin/fzf -> $which_fzf"
- (cd "$fzf_base"/bin && rm -f fzf && ln -sf "$which_fzf" fzf)
- check_binary && return
- fi
- return 1
-}
-
-try_curl() {
- command -v curl > /dev/null &&
- if [[ $1 =~ tar.gz$ ]]; then
- curl -fL $1 | tar -xzf -
- else
- local temp=${TMPDIR:-/tmp}/fzf.zip
- curl -fLo "$temp" $1 && unzip -o "$temp" && rm -f "$temp"
- fi
-}
-
-try_wget() {
- command -v wget > /dev/null &&
- if [[ $1 =~ tar.gz$ ]]; then
- wget -O - $1 | tar -xzf -
- else
- local temp=${TMPDIR:-/tmp}/fzf.zip
- wget -O "$temp" $1 && unzip -o "$temp" && rm -f "$temp"
- fi
-}
-
-download() {
- echo "Downloading bin/fzf ..."
- if [ -x "$fzf_base"/bin/fzf ]; then
- echo " - Already exists"
- check_binary && return
- fi
- link_fzf_in_path && return
- mkdir -p "$fzf_base"/bin && cd "$fzf_base"/bin
- if [ $? -ne 0 ]; then
- binary_error="Failed to create bin directory"
- return
- fi
-
- local url
- url=https://github.com/junegunn/fzf/releases/download/$version/${1}
- set -o pipefail
- if ! (try_curl $url || try_wget $url); then
- set +o pipefail
- binary_error="Failed to download with curl and wget"
- return
- fi
- set +o pipefail
-
- if [ ! -f fzf ]; then
- binary_error="Failed to download ${1}"
- return
- fi
-
- chmod +x fzf && check_binary
-}
-
-# Try to download binary executable
-archi=$(uname -sm)
-binary_available=1
-binary_error=""
-case "$archi" in
- Darwin\ arm64) download fzf-$version-darwin_arm64.zip ;;
- Darwin\ x86_64) download fzf-$version-darwin_amd64.zip ;;
- Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;;
- Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;;
- Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;;
- Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;;
- Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;;
- Linux\ *64) download fzf-$version-linux_amd64.tar.gz ;;
- FreeBSD\ *64) download fzf-$version-freebsd_amd64.tar.gz ;;
- OpenBSD\ *64) download fzf-$version-openbsd_amd64.tar.gz ;;
- CYGWIN*\ *64) download fzf-$version-windows_amd64.zip ;;
- MINGW*\ *64) download fzf-$version-windows_amd64.zip ;;
- MSYS*\ *64) download fzf-$version-windows_amd64.zip ;;
- Windows*\ *64) download fzf-$version-windows_amd64.zip ;;
- *) binary_available=0 binary_error=1 ;;
-esac
-
-cd "$fzf_base"
-if [ -n "$binary_error" ]; then
- if [ $binary_available -eq 0 ]; then
- echo "No prebuilt binary for $archi ..."
- else
- echo " - $binary_error !!!"
- fi
- if command -v go > /dev/null; then
- echo -n "Building binary (go get -u github.com/junegunn/fzf) ... "
- if [ -z "${GOPATH-}" ]; then
- export GOPATH="${TMPDIR:-/tmp}/fzf-gopath"
- mkdir -p "$GOPATH"
- fi
- if go get -ldflags "-s -w -X main.version=$version -X main.revision=go-get" github.com/junegunn/fzf; then
- echo "OK"
- cp "$GOPATH/bin/fzf" "$fzf_base/bin/"
- else
- echo "Failed to build binary. Installation failed."
- exit 1
- fi
- else
- echo "go executable not found. Installation failed."
- exit 1
- fi
-fi
-
-[[ "$*" =~ "--bin" ]] && exit 0
-
-for s in $shells; do
- if ! command -v "$s" > /dev/null; then
- shells=${shells/$s/}
- fi
-done
-
-if [[ ${#shells} -lt 3 ]]; then
- echo "No shell configuration to be updated."
- exit 0
-fi
-
-# Auto-completion
-if [ -z "$auto_completion" ]; then
- ask "Do you want to enable fuzzy auto-completion?"
- auto_completion=$?
-fi
-
-# Key-bindings
-if [ -z "$key_bindings" ]; then
- ask "Do you want to enable key bindings?"
- key_bindings=$?
-fi
-
-echo
-for shell in $shells; do
- [[ "$shell" = fish ]] && continue
- src=${prefix_expand}.${shell}
- echo -n "Generate $src ... "
-
- fzf_completion="[[ \$- == *i* ]] && source \"$fzf_base/shell/completion.${shell}\" 2> /dev/null"
- if [ $auto_completion -eq 0 ]; then
- fzf_completion="# $fzf_completion"
- fi
-
- fzf_key_bindings="source \"$fzf_base/shell/key-bindings.${shell}\""
- if [ $key_bindings -eq 0 ]; then
- fzf_key_bindings="# $fzf_key_bindings"
- fi
-
- cat > "$src" << EOF
-# Setup fzf
-# ---------
-if [[ ! "\$PATH" == *$fzf_base_esc/bin* ]]; then
- export PATH="\${PATH:+\${PATH}:}$fzf_base/bin"
-fi
-
-# Auto-completion
-# ---------------
-$fzf_completion
-
-# Key bindings
-# ------------
-$fzf_key_bindings
-EOF
- echo "OK"
-done
-
-# fish
-if [[ "$shells" =~ fish ]]; then
- echo -n "Update fish_user_paths ... "
- fish << EOF
- echo \$fish_user_paths | \grep "$fzf_base"/bin > /dev/null
- or set --universal fish_user_paths \$fish_user_paths "$fzf_base"/bin
-EOF
- [ $? -eq 0 ] && echo "OK" || echo "Failed"
-
- 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"
- if [ $key_bindings -ne 0 ]; then
- echo -n "Symlink $fish_binding ... "
- ln -sf "$fzf_base/shell/key-bindings.fish" \
- "$fish_binding" && echo "OK" || echo "Failed"
- else
- echo -n "Removing $fish_binding ... "
- rm -f "$fish_binding"
- echo "OK"
- fi
-fi
-
-append_line() {
- set -e
-
- local update line file pat lno
- update="$1"
- line="$2"
- file="$3"
- pat="${4:-}"
- lno=""
-
- echo "Update $file:"
- echo " - $line"
- if [ -f "$file" ]; then
- if [ $# -lt 4 ]; then
- lno=$(\grep -nF "$line" "$file" | sed 's/:.*//' | tr '\n' ' ')
- else
- lno=$(\grep -nF "$pat" "$file" | sed 's/:.*//' | tr '\n' ' ')
- fi
- fi
- if [ -n "$lno" ]; then
- echo " - Already exists: line #$lno"
- else
- if [ $update -eq 1 ]; then
- [ -f "$file" ] && echo >> "$file"
- echo "$line" >> "$file"
- echo " + Added"
- else
- echo " ~ Skipped"
- fi
- fi
- echo
- set +e
-}
-
-create_file() {
- local file="$1"
- shift
- echo "Create $file:"
- for line in "$@"; do
- echo " $line"
- echo "$line" >> "$file"
- done
- echo
-}
-
-if [ $update_config -eq 2 ]; then
- echo
- ask "Do you want to update your shell configuration files?"
- update_config=$?
-fi
-echo
-for shell in $shells; do
- [[ "$shell" = fish ]] && continue
- [ $shell = zsh ] && dest=${ZDOTDIR:-~}/.zshrc || dest=~/.bashrc
- append_line $update_config "[ -f ${prefix}.${shell} ] && source ${prefix}.${shell}" "$dest" "${prefix}.${shell}"
-done
-
-if [ $key_bindings -eq 1 ] && [[ "$shells" =~ fish ]]; then
- bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
- if [ ! -e "$bind_file" ]; then
- create_file "$bind_file" \
- 'function fish_user_key_bindings' \
- ' fzf_key_bindings' \
- 'end'
- else
- append_line $update_config "fzf_key_bindings" "$bind_file"
- fi
-fi
-
-if [ $update_config -eq 1 ]; then
- echo 'Finished. Restart your shell or reload config file.'
- 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
- echo 'Use uninstall script to remove fzf.'
- echo
-fi
-echo 'For more information, see: https://github.com/junegunn/fzf'
diff --git a/.fzf/install.ps1 b/.fzf/install.ps1
deleted file mode 100644
index 77af997..0000000
--- a/.fzf/install.ps1
+++ /dev/null
@@ -1,65 +0,0 @@
-$version="0.28.0"
-
-$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
-
-function check_binary () {
- Write-Host " - Checking fzf executable ... " -NoNewline
- $output=cmd /c $fzf_base\bin\fzf.exe --version 2>&1
- if (-not $?) {
- Write-Host "Error: $output"
- $binary_error="Invalid binary"
- } else {
- $output=(-Split $output)[0]
- if ($version -ne $output) {
- Write-Host "$output != $version"
- $binary_error="Invalid version"
- } else {
- Write-Host "$output"
- $binary_error=""
- return 1
- }
- }
- Remove-Item "$fzf_base\bin\fzf.exe"
- return 0
-}
-
-function download {
- param($file)
- Write-Host "Downloading bin/fzf ..."
- if (Test-Path "$fzf_base\bin\fzf.exe") {
- Write-Host " - Already exists"
- if (check_binary) {
- return
- }
- }
- if (-not (Test-Path "$fzf_base\bin")) {
- md "$fzf_base\bin"
- }
- if (-not $?) {
- $binary_error="Failed to create bin directory"
- return
- }
- cd "$fzf_base\bin"
- $url="https://github.com/junegunn/fzf/releases/download/$version/$file"
- $temp=$env:TMP + "\fzf.zip"
- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
- if ($PSVersionTable.PSVersion.Major -ge 3) {
- Invoke-WebRequest -Uri $url -OutFile $temp
- } else {
- (New-Object Net.WebClient).DownloadFile($url, $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$temp"))
- }
- if ($?) {
- (Microsoft.PowerShell.Archive\Expand-Archive -Path $temp -DestinationPath .); (Remove-Item $temp)
- } else {
- $binary_error="Failed to download with powershell"
- }
- if (-not (Test-Path fzf.exe)) {
- $binary_error="Failed to download $file"
- return
- }
- echo y | icacls $fzf_base\bin\fzf.exe /grant Administrator:F ; check_binary >$null
-}
-
-download "fzf-$version-windows_amd64.zip"
-
-Write-Host 'For more information, see: https://github.com/junegunn/fzf'
diff --git a/.fzf/main.go b/.fzf/main.go
deleted file mode 100644
index 83a9d58..0000000
--- a/.fzf/main.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package main
-
-import (
- fzf "github.com/junegunn/fzf/src"
- "github.com/junegunn/fzf/src/protector"
-)
-
-var version string = "0.28"
-var revision string = "devel"
-
-func main() {
- protector.Protect()
- fzf.Run(fzf.ParseOptions(), version, revision)
-}
diff --git a/.fzf/man/man1/fzf-tmux.1 b/.fzf/man/man1/fzf-tmux.1
deleted file mode 100644
index 7f1b36d..0000000
--- a/.fzf/man/man1/fzf-tmux.1
+++ /dev/null
@@ -1,68 +0,0 @@
-.ig
-The MIT License (MIT)
-
-Copyright (c) 2013-2021 Junegunn Choi
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-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 "Nov 2021" "fzf 0.28.0" "fzf-tmux - open fzf in tmux split pane"
-
-.SH NAME
-fzf-tmux - open fzf in tmux split pane
-
-.SH SYNOPSIS
-.B fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
-
-.SH DESCRIPTION
-fzf-tmux is a wrapper script for fzf that opens fzf in a tmux split pane or in
-a tmux popup window. It is designed to work just like fzf except that it does
-not take up the whole screen. You can safely use fzf-tmux instead of fzf in
-your scripts as the extra options will be silently ignored if you're not on
-tmux.
-
-.SH LAYOUT OPTIONS
-
-(default layout: \fB-d 50%\fR)
-
-.SS Popup window
-(requires tmux 3.2 or above)
-.TP
-.B "-p [WIDTH[%][,HEIGHT[%]]]"
-.TP
-.B "-w WIDTH[%]"
-.TP
-.B "-h WIDTH[%]"
-.TP
-.B "-x COL"
-.TP
-.B "-y ROW"
-
-.SS Split pane
-.TP
-.B "-u [height[%]]"
-Split above (up)
-.TP
-.B "-d [height[%]]"
-Split below (down)
-.TP
-.B "-l [width[%]]"
-Split left
-.TP
-.B "-r [width[%]]"
-Split right
diff --git a/.fzf/man/man1/fzf.1 b/.fzf/man/man1/fzf.1
deleted file mode 100644
index 5f5eb91..0000000
--- a/.fzf/man/man1/fzf.1
+++ /dev/null
@@ -1,1001 +0,0 @@
-.ig
-The MIT License (MIT)
-
-Copyright (c) 2013-2021 Junegunn Choi
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-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 "Nov 2021" "fzf 0.28.0" "fzf - a command-line fuzzy finder"
-
-.SH NAME
-fzf - a command-line fuzzy finder
-
-.SH SYNOPSIS
-fzf [options]
-
-.SH DESCRIPTION
-fzf is a general-purpose command-line fuzzy finder.
-
-.SH OPTIONS
-.SS Search mode
-.TP
-.B "-x, --extended"
-Extended-search mode. Since 0.10.9, this is enabled by default. You can disable
-it with \fB+x\fR or \fB--no-extended\fR.
-.TP
-.B "-e, --exact"
-Enable exact-match
-.TP
-.B "-i"
-Case-insensitive match (default: smart-case match)
-.TP
-.B "+i"
-Case-sensitive match
-.TP
-.B "--literal"
-Do not normalize latin script letters for matching.
-.TP
-.BI "--algo=" TYPE
-Fuzzy matching algorithm (default: v2)
-
-.br
-.BR v2 " Optimal scoring algorithm (quality)"
-.br
-.BR v1 " Faster but not guaranteed to find the optimal result (performance)"
-.br
-
-.TP
-.BI "-n, --nth=" "N[,..]"
-Comma-separated list of field index expressions for limiting search scope.
-See \fBFIELD INDEX EXPRESSION\fR for the details.
-.TP
-.BI "--with-nth=" "N[,..]"
-Transform the presentation of each line using field index expressions
-.TP
-.BI "-d, --delimiter=" "STR"
-Field delimiter regex for \fB--nth\fR and \fB--with-nth\fR (default: AWK-style)
-.TP
-.BI "--disabled"
-Do not perform search. With this option, fzf becomes a simple selector
-interface rather than a "fuzzy finder". You can later enable the search using
-\fBenable-search\fR or \fBtoggle-search\fR action.
-.SS Search result
-.TP
-.B "+s, --no-sort"
-Do not sort the result
-.TP
-.B "--tac"
-Reverse the order of the input
-
-.RS
-e.g.
- \fBhistory | fzf --tac --no-sort\fR
-.RE
-.TP
-.BI "--tiebreak=" "CRI[,..]"
-Comma-separated list of sort criteria to apply when the scores are tied.
-.br
-
-.br
-.BR length " Prefers line with shorter length"
-.br
-.BR begin " Prefers line with matched substring closer to the beginning"
-.br
-.BR end " Prefers line with matched substring closer to the end"
-.br
-.BR index " Prefers line that appeared earlier in the input stream"
-.br
-
-.br
-- Each criterion should appear only once in the list
-.br
-- \fBindex\fR is only allowed at the end of the list
-.br
-- \fBindex\fR is implicitly appended to the list when not specified
-.br
-- Default is \fBlength\fR (or equivalently \fBlength\fR,index)
-.br
-- If \fBend\fR is found in the list, fzf will scan each line backwards
-.SS Interface
-.TP
-.B "-m, --multi"
-Enable multi-select with tab/shift-tab. It optionally takes an integer argument
-which denotes the maximum number of items that can be selected.
-.TP
-.B "+m, --no-multi"
-Disable multi-select
-.TP
-.B "--no-mouse"
-Disable mouse
-.TP
-.BI "--bind=" "KEYBINDS"
-Comma-separated list of custom key bindings. See \fBKEY/EVENT BINDINGS\fR for
-the details.
-.TP
-.B "--cycle"
-Enable cyclic scroll
-.TP
-.B "--keep-right"
-Keep the right end of the line visible when it's too long. Effective only when
-the query string is empty.
-.TP
-.BI "--scroll-off=" "LINES"
-Number of screen lines to keep above or below when scrolling to the top or to
-the bottom (default: 0).
-.TP
-.B "--no-hscroll"
-Disable horizontal scroll
-.TP
-.BI "--hscroll-off=" "COLS"
-Number of screen columns to keep to the right of the highlighted substring
-(default: 10). Setting it to a large value will cause the text to be positioned
-on the center of the screen.
-.TP
-.B "--filepath-word"
-Make word-wise movements and actions respect path separators. The following
-actions are affected:
-
-\fBbackward-kill-word\fR
-.br
-\fBbackward-word\fR
-.br
-\fBforward-word\fR
-.br
-\fBkill-word\fR
-.TP
-.BI "--jump-labels=" "CHARS"
-Label characters for \fBjump\fR and \fBjump-accept\fR
-.SS Layout
-.TP
-.BI "--height=" "HEIGHT[%]"
-Display fzf window below the cursor with the given height instead of using
-the full screen.
-.TP
-.BI "--min-height=" "HEIGHT"
-Minimum height when \fB--height\fR is given in percent (default: 10).
-Ignored when \fB--height\fR is not specified.
-.TP
-.BI "--layout=" "LAYOUT"
-Choose the layout (default: default)
-
-.br
-.BR default " Display from the bottom of the screen"
-.br
-.BR reverse " Display from the top of the screen"
-.br
-.BR reverse-list " Display from the top of the screen, prompt at the bottom"
-.br
-
-.TP
-.B "--reverse"
-A synonym for \fB--layout=reverse\fB
-
-.TP
-.BI "--border" [=STYLE]
-Draw border around the finder
-
-.br
-.BR rounded " Border with rounded corners (default)"
-.br
-.BR sharp " Border with sharp corners"
-.br
-.BR horizontal " Horizontal lines above and below the finder"
-.br
-.BR vertical " Vertical lines on each side of the finder"
-.br
-.BR top
-.br
-.BR bottom
-.br
-.BR left
-.br
-.BR right
-.br
-.BR none
-.br
-
-.TP
-.B "--no-unicode"
-Use ASCII characters instead of Unicode box drawing characters to draw border
-
-.TP
-.BI "--margin=" MARGIN
-Comma-separated expression for margins around the finder.
-.br
-
-.br
-.RS
-.BR TRBL " Same margin for top, right, bottom, and left"
-.br
-.BR TB,RL " Vertical, horizontal margin"
-.br
-.BR T,RL,B " Top, horizontal, bottom margin"
-.br
-.BR T,R,B,L " Top, right, bottom, left margin"
-.br
-
-.br
-Each part can be given in absolute number or in percentage relative to the
-terminal size with \fB%\fR suffix.
-.br
-
-.br
-e.g.
- \fBfzf --margin 10%
- fzf --margin 1,5%\fR
-.RE
-.TP
-.BI "--padding=" PADDING
-Comma-separated expression for padding inside the border. Padding is
-distinguishable from margin only when \fB--border\fR option is used.
-.br
-
-.br
-e.g.
- \fBfzf --margin 5% --padding 5% --border --preview 'cat {}' \\
- --color bg:#222222,preview-bg:#333333\fR
-
-.br
-.RS
-.BR TRBL " Same padding for top, right, bottom, and left"
-.br
-.BR TB,RL " Vertical, horizontal padding"
-.br
-.BR T,RL,B " Top, horizontal, bottom padding"
-.br
-.BR T,R,B,L " Top, right, bottom, left padding"
-.br
-.RE
-
-.TP
-.BI "--info=" "STYLE"
-Determines the display style of finder info.
-
-.br
-.BR default " Display on the next line to the prompt"
-.br
-.BR inline " Display on the same line"
-.br
-.BR hidden " Do not display finder info"
-.br
-
-.TP
-.B "--no-info"
-A synonym for \fB--info=hidden\fB
-
-.TP
-.BI "--prompt=" "STR"
-Input prompt (default: '> ')
-.TP
-.BI "--pointer=" "STR"
-Pointer to the current line (default: '>')
-.TP
-.BI "--marker=" "STR"
-Multi-select marker (default: '>')
-.TP
-.BI "--header=" "STR"
-The given string will be printed as the sticky header. The lines are displayed
-in the given order from top to bottom regardless of \fB--layout\fR option, and
-are not affected by \fB--with-nth\fR. ANSI color codes are processed even when
-\fB--ansi\fR is not set.
-.TP
-.BI "--header-lines=" "N"
-The first N lines of the input are treated as the sticky header. When
-\fB--with-nth\fR is set, the lines are transformed just like the other
-lines that follow.
-.TP
-.B "--header-first"
-Print header before the prompt line
-.SS Display
-.TP
-.B "--ansi"
-Enable processing of ANSI color codes
-.TP
-.BI "--tabstop=" SPACES
-Number of spaces for a tab character (default: 8)
-.TP
-.BI "--color=" "[BASE_SCHEME][,COLOR_NAME[:ANSI_COLOR][:ANSI_ATTRIBUTES]]..."
-Color configuration. The name of the base color scheme is followed by custom
-color mappings.
-
-.RS
-.B BASE SCHEME:
- (default: dark on 256-color terminal, otherwise 16)
-
- \fBdark \fRColor scheme for dark 256-color terminal
- \fBlight \fRColor scheme for light 256-color terminal
- \fB16 \fRColor scheme for 16-color terminal
- \fBbw \fRNo colors (equivalent to \fB--no-color\fR)
-
-.B COLOR NAMES:
- \fBfg \fRText
- \fBbg \fRBackground
- \fBpreview-fg \fRPreview window text
- \fBpreview-bg \fRPreview window background
- \fBhl \fRHighlighted substrings
- \fBfg+ \fRText (current line)
- \fBbg+ \fRBackground (current line)
- \fBgutter \fRGutter on the left (defaults to \fBbg+\fR)
- \fBhl+ \fRHighlighted substrings (current line)
- \fBquery \fRQuery string
- \fBdisabled \fRQuery string when search is disabled
- \fBinfo \fRInfo line (match counters)
- \fBborder \fRBorder around the window (\fB--border\fR and \fB--preview\fR)
- \fBprompt \fRPrompt
- \fBpointer \fRPointer to the current line
- \fBmarker \fRMulti-select marker
- \fBspinner \fRStreaming input indicator
- \fBheader \fRHeader
-
-.B ANSI COLORS:
- \fB-1 \fRDefault terminal foreground/background color
- \fB \fR(or the original color of the text)
- \fB0 ~ 15 \fR16 base colors
- \fBblack\fR
- \fBred\fR
- \fBgreen\fR
- \fByellow\fR
- \fBblue\fR
- \fBmagenta\fR
- \fBcyan\fR
- \fBwhite\fR
- \fBbright-black\fR (gray | grey)
- \fBbright-red\fR
- \fBbright-green\fR
- \fBbright-yellow\fR
- \fBbright-blue\fR
- \fBbright-magenta\fR
- \fBbright-cyan\fR
- \fBbright-white\fR
- \fB16 ~ 255 \fRANSI 256 colors
- \fB#rrggbb \fR24-bit colors
-
-.B ANSI ATTRIBUTES: (Only applies to foreground colors)
- \fBregular \fRClears previously set attributes; should precede the other ones
- \fBbold\fR
- \fBunderline\fR
- \fBreverse\fR
- \fBdim\fR
- \fBitalic\fR
-
-.B EXAMPLES:
-
- \fB# Seoul256 theme with 8-bit colors
- # (https://github.com/junegunn/seoul256.vim)
- fzf --color='bg:237,bg+:236,info:143,border:240,spinner:108' \\
- --color='hl:65,fg:252,header:65,fg+:252' \\
- --color='pointer:161,marker:168,prompt:110,hl+:108'
-
- # Seoul256 theme with 24-bit colors
- fzf --color='bg:#4B4B4B,bg+:#3F3F3F,info:#BDBB72,border:#6B6B6B,spinner:#98BC99' \\
- --color='hl:#719872,fg:#D9D9D9,header:#719872,fg+:#D9D9D9' \\
- --color='pointer:#E12672,marker:#E17899,prompt:#98BEDE,hl+:#98BC99'\fR
-.RE
-.TP
-.B "--no-bold"
-Do not use bold text
-.TP
-.B "--black"
-Use black background
-.SS History
-.TP
-.BI "--history=" "HISTORY_FILE"
-Load search history from the specified file and update the file on completion.
-When enabled, \fBCTRL-N\fR and \fBCTRL-P\fR are automatically remapped to
-\fBnext-history\fR and \fBprevious-history\fR.
-.TP
-.BI "--history-size=" "N"
-Maximum number of entries in the history file (default: 1000). The file is
-automatically truncated when the number of the lines exceeds the value.
-.SS Preview
-.TP
-.BI "--preview=" "COMMAND"
-Execute the given command for the current line and display the result on the
-preview window. \fB{}\fR in the command is the placeholder that is replaced to
-the single-quoted string of the current line. To transform the replacement
-string, specify field index expressions between the braces (See \fBFIELD INDEX
-EXPRESSION\fR for the details).
-
-.RS
-e.g.
- \fBfzf --preview='head -$LINES {}'
- ls -l | fzf --preview="echo user={3} when={-4..-2}; cat {-1}" --header-lines=1\fR
-
-fzf exports \fB$FZF_PREVIEW_LINES\fR and \fB$FZF_PREVIEW_COLUMNS\fR so that
-they represent the exact size of the preview window. (It also overrides
-\fB$LINES\fR and \fB$COLUMNS\fR with the same values but they can be reset
-by the default shell, so prefer to refer to the ones with \fBFZF_PREVIEW_\fR
-prefix.)
-
-A placeholder expression starting with \fB+\fR flag will be replaced to the
-space-separated list of the selected lines (or the current line if no selection
-was made) individually quoted.
-
-e.g.
- \fBfzf --multi --preview='head -10 {+}'
- git log --oneline | fzf --multi --preview 'git show {+1}'\fR
-
-When using a field index expression, leading and trailing whitespace is stripped
-from the replacement string. To preserve the whitespace, use the \fBs\fR flag.
-
-Also, \fB{q}\fR is replaced to the current query string, and \fB{n}\fR is
-replaced to zero-based ordinal index of the line. Use \fB{+n}\fR if you want
-all index numbers when multiple lines are selected.
-
-A placeholder expression with \fBf\fR flag is replaced to the path of
-a temporary file that holds the evaluated list. This is useful when you
-multi-select a large number of items and the length of the evaluated string may
-exceed \fBARG_MAX\fR.
-
-e.g.
- \fB# Press CTRL-A to select 100K items and see the sum of all the numbers.
- # This won't work properly without 'f' flag due to ARG_MAX limit.
- seq 100000 | fzf --multi --bind ctrl-a:select-all \\
- --preview "awk '{sum+=\$1} END {print sum}' {+f}"\fR
-
-Note that you can escape a placeholder pattern by prepending a backslash.
-
-Preview window will be updated even when there is no match for the current
-query if any of the placeholder expressions evaluates to a non-empty string.
-
-Since 0.24.0, fzf can render partial preview content before the preview command
-completes. ANSI escape sequence for clearing the display (\fBCSI 2 J\fR) is
-supported, so you can use it to implement preview window that is constantly
-updating.
-
-e.g.
- \fBfzf --preview 'for i in $(seq 100000); do
- (( i % 200 == 0 )) && printf "\\033[2J"
- echo "$i"
- sleep 0.01
- done'\fR
-.RE
-.TP
-.BI "--preview-window=" "[POSITION][,SIZE[%]][,border-BORDER_OPT][,[no]wrap][,[no]follow][,[no]cycle][,[no]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default]"
-
-.RS
-.B POSITION: (default: right)
- \fBup
- \fBdown
- \fBleft
- \fBright
-
-\fRDetermines the layout of the preview window.
-
-* If the argument contains \fB:hidden\fR, the preview window will be hidden by
-default until \fBtoggle-preview\fR action is triggered.
-
-* If size is given as 0, preview window will not be visible, but fzf will still
-execute the command in the background.
-
-* Long lines are truncated by default. Line wrap can be enabled with
-\fB:wrap\fR flag.
-
-* Preview window will automatically scroll to the bottom when \fB:follow\fR
-flag is set, similarly to how \fBtail -f\fR works.
-
-.RS
-e.g.
- \fBfzf --preview-window follow --preview 'for i in $(seq 100000); do
- echo "$i"
- sleep 0.01
- (( i % 300 == 0 )) && printf "\\033[2J"
- done'\fR
-.RE
-
-* Cyclic scrolling is enabled with \fB:cycle\fR flag.
-
-* To change the style of the border of the preview window, specify one of
-the options for \fB--border\fR with \fBborder-\fR prefix.
-e.g. \fBborder-rounded\fR (border with rounded edges, default),
-\fBborder-sharp\fR (border with sharp edges), \fBborder-left\fR,
-\fBborder-none\fR, etc.
-
-* \fB[:+SCROLL[OFFSETS][/DENOM]]\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 \fBOFFSETS\fR part is for adjusting the base offset. It should be given as a series of signed integers (\fB-INTEGER\fR or \fB+INTEGER\fR).
-
- - The final \fB/DENOM\fR part is for specifying a fraction of the preview window height.
-
-* \fB~HEADER_LINES\fR keeps the top N lines as the fixed header so that they
-are always visible.
-
-* \fBdefault\fR resets all options previously set to the default.
-
-.RS
-e.g.
- \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 (-5)
- git grep --line-number '' |
- fzf --delimiter : --preview 'nl {1}' --preview-window '+{2}-5'
-
- # Preview with bat, matching line in the middle of the window below
- # the fixed header of the top 3 lines
- #
- # ~3 Top 3 lines as the fixed header
- # +{2} Base scroll offset extracted from the second field
- # +3 Extra offset to compensate for the 3-line header
- # /2 Put in the middle of the preview area
- #
- git grep --line-number '' |
- fzf --delimiter : \\
- --preview 'bat --style=full --color=always --highlight-line {2} {1}' \\
- --preview-window '~3,+{2}+3/2'
-
- # Display top 3 lines as the fixed header
- fzf --preview 'bat --style=full --color=always {}' --preview-window '~3'\fR
-.RE
-
-.SS Scripting
-.TP
-.BI "-q, --query=" "STR"
-Start the finder with the given query
-.TP
-.B "-1, --select-1"
-If there is only one match for the initial query (\fB--query\fR), do not start
-interactive finder and automatically select the only match
-.TP
-.B "-0, --exit-0"
-If there is no match for the initial query (\fB--query\fR), do not start
-interactive finder and exit immediately
-.TP
-.BI "-f, --filter=" "STR"
-Filter mode. Do not start interactive finder. When used with \fB--no-sort\fR,
-fzf becomes a fuzzy-version of grep.
-.TP
-.B "--print-query"
-Print query as the first line
-.TP
-.BI "--expect=" "KEY[,..]"
-Comma-separated list of keys that can be used to complete fzf in addition to
-the default enter key. When this option is set, fzf will print the name of the
-key pressed as the first line of its output (or as the second line if
-\fB--print-query\fR is also used). The line will be empty if fzf is completed
-with the default enter key. If \fB--expect\fR option is specified multiple
-times, fzf will expect the union of the keys. \fB--no-expect\fR will clear the
-list.
-
-.RS
-e.g.
- \fBfzf --expect=ctrl-v,ctrl-t,alt-s --expect=f1,f2,~,@\fR
-.RE
-.TP
-.B "--read0"
-Read input delimited by ASCII NUL characters instead of newline characters
-.TP
-.B "--print0"
-Print output delimited by ASCII NUL characters instead of newline characters
-.TP
-.B "--no-clear"
-Do not clear finder interface on exit. If fzf was started in full screen mode,
-it will not switch back to the original screen, so you'll have to manually run
-\fBtput rmcup\fR to return. This option can be used to avoid flickering of the
-screen when your application needs to start fzf multiple times in order.
-.TP
-.B "--sync"
-Synchronous search for multi-staged filtering. If specified, fzf will launch
-ncurses finder only after the input stream is complete.
-
-.RS
-e.g. \fBfzf --multi | fzf --sync\fR
-.RE
-.TP
-.B "--version"
-Display version information and exit
-
-.TP
-Note that most options have the opposite versions with \fB--no-\fR prefix.
-
-.SH ENVIRONMENT VARIABLES
-.TP
-.B FZF_DEFAULT_COMMAND
-Default command to use when input is tty. On *nix systems, fzf runs the command
-with \fB$SHELL -c\fR if \fBSHELL\fR is set, otherwise with \fBsh -c\fR, so in
-this case make sure that the command is POSIX-compliant.
-.TP
-.B FZF_DEFAULT_OPTS
-Default options. e.g. \fBexport FZF_DEFAULT_OPTS="--extended --cycle"\fR
-
-.SH EXIT STATUS
-.BR 0 " Normal exit"
-.br
-.BR 1 " No match"
-.br
-.BR 2 " Error"
-.br
-.BR 130 " Interrupted with \fBCTRL-C\fR or \fBESC\fR"
-
-.SH FIELD INDEX EXPRESSION
-
-A field index expression can be a non-zero integer or a range expression
-([BEGIN]..[END]). \fB--nth\fR and \fB--with-nth\fR take a comma-separated list
-of field index expressions.
-
-.SS Examples
-.BR 1 " The 1st field"
-.br
-.BR 2 " The 2nd field"
-.br
-.BR -1 " The last field"
-.br
-.BR -2 " The 2nd to last field"
-.br
-.BR 3..5 " From the 3rd field to the 5th field"
-.br
-.BR 2.. " From the 2nd field to the last field"
-.br
-.BR ..-3 " From the 1st field to the 3rd to the last field"
-.br
-.BR .. " All the fields"
-.br
-
-.SH EXTENDED SEARCH MODE
-
-Unless specified otherwise, fzf will start in "extended-search mode". In this
-mode, you can specify multiple patterns delimited by spaces, such as: \fB'wild
-^music .mp3$ sbtrkt !rmx\fR
-
-You can prepend a backslash to a space (\fB\\ \fR) to match a literal space
-character.
-
-.SS Exact-match (quoted)
-A term that is prefixed by a single-quote character (\fB'\fR) is interpreted as
-an "exact-match" (or "non-fuzzy") term. fzf will search for the exact
-occurrences of the string.
-
-.SS Anchored-match
-A term can be prefixed by \fB^\fR, or suffixed by \fB$\fR to become an
-anchored-match term. Then fzf will search for the lines that start with or end
-with the given string. An anchored-match term is also an exact-match term.
-
-.SS Negation
-If a term is prefixed by \fB!\fR, fzf will exclude the lines that satisfy the
-term from the result. In this case, fzf performs exact match by default.
-
-.SS Exact-match by default
-If you don't prefer fuzzy matching and do not wish to "quote" (prefixing with
-\fB'\fR) every word, start fzf with \fB-e\fR or \fB--exact\fR option. Note that
-when \fB--exact\fR is set, \fB'\fR-prefix "unquotes" the term.
-
-.SS OR operator
-A single bar character term acts as an OR operator. For example, the following
-query matches entries that start with \fBcore\fR and end with either \fBgo\fR,
-\fBrb\fR, or \fBpy\fR.
-
-e.g. \fB^core go$ | rb$ | py$\fR
-
-.SH KEY/EVENT BINDINGS
-\fB--bind\fR option allows you to bind \fBa key\fR or \fBan event\fR to one or
-more \fBactions\fR. You can use it to customize key bindings or implement
-dynamic behaviors.
-
-\fB--bind\fR takes a comma-separated list of binding expressions. Each binding
-expression is \fBKEY:ACTION\fR or \fBEVENT:ACTION\fR.
-
-e.g.
- \fBfzf --bind=ctrl-j:accept,ctrl-k:kill-line\fR
-
-.SS AVAILABLE KEYS: (SYNONYMS)
-\fIctrl-[a-z]\fR
-.br
-\fIctrl-space\fR
-.br
-\fIctrl-\\\fR
-.br
-\fIctrl-]\fR
-.br
-\fIctrl-^\fR (\fIctrl-6\fR)
-.br
-\fIctrl-/\fR (\fIctrl-_\fR)
-.br
-\fIctrl-alt-[a-z]\fR
-.br
-\fIalt-[*]\fR (Any case-sensitive single character is allowed)
-.br
-\fIf[1-12]\fR
-.br
-\fIenter\fR (\fIreturn\fR \fIctrl-m\fR)
-.br
-\fIspace\fR
-.br
-\fIbspace\fR (\fIbs\fR)
-.br
-\fIalt-up\fR
-.br
-\fIalt-down\fR
-.br
-\fIalt-left\fR
-.br
-\fIalt-right\fR
-.br
-\fIalt-enter\fR
-.br
-\fIalt-space\fR
-.br
-\fIalt-bspace\fR (\fIalt-bs\fR)
-.br
-\fItab\fR
-.br
-\fIbtab\fR (\fIshift-tab\fR)
-.br
-\fIesc\fR
-.br
-\fIdel\fR
-.br
-\fIup\fR
-.br
-\fIdown\fR
-.br
-\fIleft\fR
-.br
-\fIright\fR
-.br
-\fIhome\fR
-.br
-\fIend\fR
-.br
-\fIinsert\fR
-.br
-\fIpgup\fR (\fIpage-up\fR)
-.br
-\fIpgdn\fR (\fIpage-down\fR)
-.br
-\fIshift-up\fR
-.br
-\fIshift-down\fR
-.br
-\fIshift-left\fR
-.br
-\fIshift-right\fR
-.br
-\fIalt-shift-up\fR
-.br
-\fIalt-shift-down\fR
-.br
-\fIalt-shift-left\fR
-.br
-\fIalt-shift-right\fR
-.br
-\fIleft-click\fR
-.br
-\fIright-click\fR
-.br
-\fIdouble-click\fR
-.br
-or any single character
-
-.SS AVAILABLE EVENTS:
-\fIchange\fR
-.RS
-Triggered whenever the query string is changed
-
-e.g.
- \fB# Move cursor to the first entry whenever the query is changed
- fzf --bind change:first\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.
-
- \fBACTION: DEFAULT BINDINGS (NOTES):
- \fBabort\fR \fIctrl-c ctrl-g ctrl-q esc\fR
- \fBaccept\fR \fIenter double-click\fR
- \fBaccept-non-empty\fR (same as \fBaccept\fR except that it prevents fzf from exiting without selection)
- \fBbackward-char\fR \fIctrl-b left\fR
- \fBbackward-delete-char\fR \fIctrl-h bspace\fR
- \fBbackward-delete-char/eof\fR (same as \fBbackward-delete-char\fR except aborts fzf if query is empty)
- \fBbackward-kill-word\fR \fIalt-bs\fR
- \fBbackward-word\fR \fIalt-b shift-left\fR
- \fBbeginning-of-line\fR \fIctrl-a home\fR
- \fBcancel\fR (clear query string if not empty, abort fzf otherwise)
- \fBchange-prompt(...)\fR (change prompt to the given string)
- \fBclear-screen\fR \fIctrl-l\fR
- \fBclear-selection\fR (clear multi-selection)
- \fBclose\fR (close preview window if open, abort fzf otherwise)
- \fBclear-query\fR (clear query string)
- \fBdelete-char\fR \fIdel\fR
- \fBdelete-char/eof\fR \fIctrl-d\fR (same as \fBdelete-char\fR except aborts fzf if query is empty)
- \fBdeselect\fR
- \fBdeselect-all\fR (deselect all matches)
- \fBdisable-search\fR (disable search functionality)
- \fBdown\fR \fIctrl-j ctrl-n down\fR
- \fBenable-search\fR (enable search functionality)
- \fBend-of-line\fR \fIctrl-e end\fR
- \fBexecute(...)\fR (see below for the details)
- \fBexecute-silent(...)\fR (see below for the details)
- \fBfirst\fR (move to the first match)
- \fBforward-char\fR \fIctrl-f right\fR
- \fBforward-word\fR \fIalt-f shift-right\fR
- \fBignore\fR
- \fBjump\fR (EasyMotion-like 2-keystroke movement)
- \fBjump-accept\fR (jump and accept)
- \fBkill-line\fR
- \fBkill-word\fR \fIalt-d\fR
- \fBlast\fR (move to the last match)
- \fBnext-history\fR (\fIctrl-n\fR on \fB--history\fR)
- \fBpage-down\fR \fIpgdn\fR
- \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
- \fBpreview-half-page-down\fR
- \fBpreview-half-page-up\fR
- \fBpreview-bottom\fR
- \fBpreview-top\fR
- \fBprevious-history\fR (\fIctrl-p\fR on \fB--history\fR)
- \fBprint-query\fR (print query and exit)
- \fBput\fR (put the character to the prompt)
- \fBrefresh-preview\fR
- \fBreload(...)\fR (see below for the details)
- \fBreplace-query\fR (replace query string with the current selection)
- \fBselect\fR
- \fBselect-all\fR (select all matches)
- \fBtoggle\fR (\fIright-click\fR)
- \fBtoggle-all\fR (toggle all matches)
- \fBtoggle+down\fR \fIctrl-i (tab)\fR
- \fBtoggle-in\fR (\fB--layout=reverse*\fR ? \fBtoggle+up\fR : \fBtoggle+down\fR)
- \fBtoggle-out\fR (\fB--layout=reverse*\fR ? \fBtoggle+down\fR : \fBtoggle+up\fR)
- \fBtoggle-preview\fR
- \fBtoggle-preview-wrap\fR
- \fBtoggle-search\fR (toggle search functionality)
- \fBtoggle-sort\fR
- \fBtoggle+up\fR \fIbtab (shift-tab)\fR
- \fBunbind(...)\fR (unbind bindings)
- \fBunix-line-discard\fR \fIctrl-u\fR
- \fBunix-word-rubout\fR \fIctrl-w\fR
- \fBup\fR \fIctrl-k ctrl-p up\fR
- \fByank\fR \fIctrl-y\fR
-
-.SS ACTION COMPOSITION
-
-Multiple actions can be chained using \fB+\fR separator.
-
-e.g.
- \fBfzf --multi --bind 'ctrl-a:select-all+accept'\fR
- \fBfzf --multi --bind 'ctrl-a:select-all' --bind 'ctrl-a:+accept'\fR
-
-.SS ACTION ARGUMENT
-
-An action denoted with \fB(...)\fR suffix takes an argument.
-
-e.g.
- \fBfzf --bind 'ctrl-a:change-prompt(NewPrompt> )'\fR
- \fBfzf --bind 'ctrl-v:preview(cat {})' --preview-window hidden\fR
-
-If the argument contains parentheses, fzf may fail to parse the expression. In
-that case, you can use any of the following alternative notations to avoid
-parse errors.
-
- \fBaction-name[...]\fR
- \fBaction-name~...~\fR
- \fBaction-name!...!\fR
- \fBaction-name@...@\fR
- \fBaction-name#...#\fR
- \fBaction-name$...$\fR
- \fBaction-name%...%\fR
- \fBaction-name^...^\fR
- \fBaction-name&...&\fR
- \fBaction-name*...*\fR
- \fBaction-name;...;\fR
- \fBaction-name/.../\fR
- \fBaction-name|...|\fR
- \fBaction-name:...\fR
-.RS
-The last one is the special form that frees you from parse errors as it does
-not expect the closing character. The catch is that it should be the last one
-in the comma-separated list of key-action pairs.
-.RE
-
-.SS COMMAND EXECUTION
-
-With \fBexecute(...)\fR action, you can execute arbitrary commands without
-leaving fzf. For example, you can turn fzf into a simple file browser by
-binding \fBenter\fR key to \fBless\fR command like follows.
-
- \fBfzf --bind "enter:execute(less {})"\fR
-
-You can use the same placeholder expressions as in \fB--preview\fR.
-
-fzf switches to the alternate screen when executing a command. However, if the
-command is expected to complete quickly, and you are not interested in its
-output, you might want to use \fBexecute-silent\fR instead, which silently
-executes the command without the switching. Note that fzf will not be
-responsive until the command is complete. For asynchronous execution, start
-your command as a background process (i.e. appending \fB&\fR).
-
-On *nix systems, fzf runs the command with \fB$SHELL -c\fR if \fBSHELL\fR is
-set, otherwise with \fBsh -c\fR, so in this case make sure that the command is
-POSIX-compliant.
-
-.SS RELOAD INPUT
-
-\fBreload(...)\fR action is used to dynamically update the input list
-without restarting fzf. It takes the same command template with placeholder
-expressions as \fBexecute(...)\fR.
-
-See \fIhttps://github.com/junegunn/fzf/issues/1750\fR for more info.
-
-e.g.
- \fB# Update the list of processes by pressing CTRL-R
- ps -ef | fzf --bind 'ctrl-r:reload(ps -ef)' --header 'Press CTRL-R to reload' \\
- --header-lines=1 --layout=reverse
-
- # Integration with ripgrep
- RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
- INITIAL_QUERY="foobar"
- FZF_DEFAULT_COMMAND="$RG_PREFIX '$INITIAL_QUERY'" \\
- fzf --bind "change:reload:$RG_PREFIX {q} || true" \\
- --ansi --disabled --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)
-
-.SH SEE ALSO
-.B Project homepage:
-.RS
-.I https://github.com/junegunn/fzf
-.RE
-.br
-
-.br
-.B Extra Vim plugin:
-.RS
-.I https://github.com/junegunn/fzf.vim
-.RE
-
-.SH LICENSE
-MIT
diff --git a/.fzf/plugin/fzf.vim b/.fzf/plugin/fzf.vim
deleted file mode 100644
index b17361d..0000000
--- a/.fzf/plugin/fzf.vim
+++ /dev/null
@@ -1,1048 +0,0 @@
-" Copyright (c) 2017 Junegunn Choi
-"
-" MIT License
-"
-" Permission is hereby granted, free of charge, to any person obtaining
-" a copy of this software and associated documentation files (the
-" "Software"), to deal in the Software without restriction, including
-" without limitation the rights to use, copy, modify, merge, publish,
-" distribute, sublicense, and/or sell copies of the Software, and to
-" permit persons to whom the Software is furnished to do so, subject to
-" the following conditions:
-"
-" The above copyright notice and this permission notice shall be
-" included in all copies or substantial portions of the Software.
-"
-" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.
-
-if exists('g:loaded_fzf')
- finish
-endif
-let g:loaded_fzf = 1
-
-let s:is_win = has('win32') || has('win64')
-if s:is_win && &shellslash
- set noshellslash
- let s:base_dir = expand(':h:h')
- set shellslash
-else
- let s:base_dir = expand(':h:h')
-endif
-if s:is_win
- let s:term_marker = '&::FZF'
-
- function! s:fzf_call(fn, ...)
- let shellslash = &shellslash
- try
- set noshellslash
- return call(a:fn, a:000)
- finally
- let &shellslash = shellslash
- endtry
- endfunction
-
- " Use utf-8 for fzf.vim commands
- " Return array of shell commands for cmd.exe
- function! s:enc_to_cp(str)
- if !has('iconv')
- return a:str
- endif
- if !exists('s:codepage')
- let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)
- endif
- return iconv(a:str, &encoding, 'cp'.s:codepage)
- endfunction
- function! s:wrap_cmds(cmds)
- return map([
- \ '@echo off',
- \ 'setlocal enabledelayedexpansion']
- \ + (has('gui_running') ? ['set TERM= > nul'] : [])
- \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])
- \ + ['endlocal'],
- \ 'enc_to_cp(v:val."\r")')
- endfunction
-else
- let s:term_marker = ";#FZF"
-
- function! s:fzf_call(fn, ...)
- return call(a:fn, a:000)
- endfunction
-
- function! s:wrap_cmds(cmds)
- return a:cmds
- endfunction
-
- function! s:enc_to_cp(str)
- return a:str
- endfunction
-endif
-
-function! s:shellesc_cmd(arg)
- let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g')
- let escaped = substitute(escaped, '%', '%%', 'g')
- let escaped = substitute(escaped, '"', '\\^&', 'g')
- let escaped = substitute(escaped, '\(\\\+\)\(\\^\)', '\1\1\2', 'g')
- return '^"'.substitute(escaped, '\(\\\+\)$', '\1\1', '').'^"'
-endfunction
-
-function! fzf#shellescape(arg, ...)
- let shell = get(a:000, 0, s:is_win ? 'cmd.exe' : 'sh')
- if shell =~# 'cmd.exe$'
- return s:shellesc_cmd(a:arg)
- endif
- return s:fzf_call('shellescape', a:arg)
-endfunction
-
-function! s:fzf_getcwd()
- return s:fzf_call('getcwd')
-endfunction
-
-function! s:fzf_fnamemodify(fname, mods)
- return s:fzf_call('fnamemodify', a:fname, a:mods)
-endfunction
-
-function! s:fzf_expand(fmt)
- return s:fzf_call('expand', a:fmt, 1)
-endfunction
-
-function! s:fzf_tempname()
- return s:fzf_call('tempname')
-endfunction
-
-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'
-
-let s:cpo_save = &cpo
-set cpo&vim
-
-function! s:popup_support()
- return has('nvim') ? has('nvim-0.4') : has('popupwin') && has('patch-8.2.191')
-endfunction
-
-function! s:default_layout()
- return s:popup_support()
- \ ? { 'window' : { 'width': 0.9, 'height': 0.6 } }
- \ : { 'down': '~40%' }
-endfunction
-
-function! fzf#install()
- if s:is_win && !has('win32unix')
- let script = s:base_dir.'/install.ps1'
- if !filereadable(script)
- throw script.' not found'
- endif
- let script = 'powershell -ExecutionPolicy Bypass -file ' . script
- else
- let script = s:base_dir.'/install'
- if !executable(script)
- throw script.' not found'
- endif
- let script .= ' --bin'
- endif
-
- call s:warn('Running fzf installer ...')
- call system(script)
- if v:shell_error
- throw 'Failed to download fzf: '.script
- endif
-endfunction
-
-let s:versions = {}
-function s:get_version(bin)
- if has_key(s:versions, a:bin)
- return s:versions[a:bin]
- end
- let command = a:bin . ' --version --no-height'
- let output = systemlist(command)
- if v:shell_error || empty(output)
- return ''
- endif
- let ver = matchstr(output[-1], '[0-9.]\+')
- let s:versions[a:bin] = ver
- return ver
-endfunction
-
-function! s:compare_versions(a, b)
- let a = split(a:a, '\.')
- let b = split(a:b, '\.')
- for idx in range(0, max([len(a), len(b)]) - 1)
- let v1 = str2nr(get(a, idx, 0))
- let v2 = str2nr(get(b, idx, 0))
- if v1 < v2 | return -1
- elseif v1 > v2 | return 1
- endif
- endfor
- return 0
-endfunction
-
-function! s:compare_binary_versions(a, b)
- return s:compare_versions(s:get_version(a:a), s:get_version(a:b))
-endfunction
-
-let s:checked = {}
-function! fzf#exec(...)
- if !exists('s:exec')
- let binaries = []
- if executable('fzf')
- call add(binaries, 'fzf')
- endif
- if executable(s:fzf_go)
- call add(binaries, s:fzf_go)
- endif
-
- if empty(binaries)
- if input('fzf executable not found. Download binary? (y/n) ') =~? '^y'
- redraw
- call fzf#install()
- return fzf#exec()
- else
- redraw
- throw 'fzf executable not found'
- endif
- elseif len(binaries) > 1
- call sort(binaries, 's:compare_binary_versions')
- endif
-
- let s:exec = binaries[-1]
- endif
-
- if a:0 && !has_key(s:checked, a:1)
- let fzf_version = s:get_version(s:exec)
- if empty(fzf_version)
- let message = printf('Failed to run "%s --version"', s:exec)
- unlet s:exec
- throw message
- end
-
- if s:compare_versions(fzf_version, a:1) >= 0
- let s:checked[a:1] = 1
- return s:exec
- elseif a:0 < 2 && input(printf('You need fzf %s or above. Found: %s. Download binary? (y/n) ', a:1, fzf_version)) =~? '^y'
- let s:versions = {}
- unlet s:exec
- redraw
- call fzf#install()
- return fzf#exec(a:1, 1)
- else
- throw printf('You need to upgrade fzf (required: %s or above)', a:1)
- endif
- endif
-
- return s:exec
-endfunction
-
-function! s:tmux_enabled()
- if has('gui_running') || !exists('$TMUX')
- return 0
- endif
-
- if exists('s:tmux')
- return s:tmux
- endif
-
- let s:tmux = 0
- if !executable(s:fzf_tmux)
- if executable('fzf-tmux')
- let s:fzf_tmux = 'fzf-tmux'
- else
- return 0
- endif
- endif
-
- let output = system('tmux -V')
- let s:tmux = !v:shell_error && output >= 'tmux 1.7'
- return s:tmux
-endfunction
-
-function! s:escape(path)
- let path = fnameescape(a:path)
- return s:is_win ? escape(path, '$') : path
-endfunction
-
-function! s:error(msg)
- echohl ErrorMsg
- echom a:msg
- echohl None
-endfunction
-
-function! s:warn(msg)
- echohl WarningMsg
- echom a:msg
- echohl None
-endfunction
-
-function! s:has_any(dict, keys)
- for key in a:keys
- if has_key(a:dict, key)
- return 1
- endif
- endfor
- return 0
-endfunction
-
-function! s:open(cmd, target)
- if stridx('edit', a:cmd) == 0 && s:fzf_fnamemodify(a:target, ':p') ==# s:fzf_expand('%:p')
- return
- endif
- execute a:cmd s:escape(a:target)
-endfunction
-
-function! s:common_sink(action, lines) abort
- if len(a:lines) < 2
- return
- endif
- let key = remove(a:lines, 0)
- let Cmd = get(a:action, key, 'e')
- if type(Cmd) == type(function('call'))
- return Cmd(a:lines)
- endif
- if len(a:lines) > 1
- augroup fzf_swap
- autocmd SwapExists * let v:swapchoice='o'
- \| call s:warn('fzf: E325: swap file exists: '.s:fzf_expand(''))
- augroup END
- endif
- try
- let empty = empty(s:fzf_expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified
- " 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 sep = s:is_win ? '\' : '/'
- let item = join([cwd, item], cwd[len(cwd)-1] == sep ? '' : sep)
- endif
- if empty
- execute 'e' s:escape(item)
- let empty = 0
- else
- call s:open(Cmd, item)
- endif
- if !has('patch-8.0.0177') && !has('nvim-0.2') && exists('#BufEnter')
- \ && isdirectory(item)
- doautocmd BufEnter
- endif
- endfor
- catch /^Vim:Interrupt$/
- finally
- silent! autocmd! fzf_swap
- endtry
-endfunction
-
-function! s:get_color(attr, ...)
- let gui = !s:is_win && !has('win32unix') && has('termguicolors') && &termguicolors
- let fam = gui ? 'gui' : 'cterm'
- let pat = gui ? '^#[a-f0-9]\+' : '^[0-9]\+$'
- for group in a:000
- let code = synIDattr(synIDtrans(hlID(group)), a:attr, fam)
- if code =~? pat
- return code
- endif
- endfor
- return ''
-endfunction
-
-function! s:defaults()
- let rules = copy(get(g:, 'fzf_colors', {}))
- let colors = join(map(items(filter(map(rules, 'call("s:get_color", v:val)'), '!empty(v:val)')), 'join(v:val, ":")'), ',')
- return empty(colors) ? '' : fzf#shellescape('--color='.colors)
-endfunction
-
-function! s:validate_layout(layout)
- for key in keys(a:layout)
- if index(s:layout_keys, key) < 0
- throw printf('Invalid entry in g:fzf_layout: %s (allowed: %s)%s',
- \ key, join(s:layout_keys, ', '), key == 'options' ? '. Use $FZF_DEFAULT_OPTS.' : '')
- endif
- endfor
- return a:layout
-endfunction
-
-function! s:evaluate_opts(options)
- return type(a:options) == type([]) ?
- \ join(map(copy(a:options), 'fzf#shellescape(v:val)')) : a:options
-endfunction
-
-" [name string,] [opts dict,] [fullscreen boolean]
-function! fzf#wrap(...)
- let args = ['', {}, 0]
- let expects = map(copy(args), 'type(v:val)')
- let tidx = 0
- for arg in copy(a:000)
- 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
- let args[tidx] = arg
- let tidx += 1
- unlet arg
- endfor
- let [name, opts, bang] = args
-
- if len(name)
- let opts.name = name
- end
-
- " Layout: g:fzf_layout (and deprecated g:fzf_height)
- if bang
- for key in s:layout_keys
- if has_key(opts, key)
- call remove(opts, key)
- endif
- endfor
- elseif !s:has_any(opts, s:layout_keys)
- if !exists('g:fzf_layout') && exists('g:fzf_height')
- let opts.down = g:fzf_height
- else
- let opts = extend(opts, s:validate_layout(get(g:, 'fzf_layout', s:default_layout())))
- endif
- endif
-
- " Colors: g:fzf_colors
- let opts.options = s:defaults() .' '. s:evaluate_opts(get(opts, 'options', ''))
-
- " History: g:fzf_history_dir
- if len(name) && len(get(g:, 'fzf_history_dir', ''))
- let dir = s:fzf_expand(g:fzf_history_dir)
- if !isdirectory(dir)
- call mkdir(dir, 'p')
- endif
- let history = fzf#shellescape(dir.'/'.name)
- let opts.options = join(['--history', history, opts.options])
- endif
-
- " Action: g:fzf_action
- if !s:has_any(opts, ['sink', 'sinklist', 'sink*'])
- let opts._action = get(g:, 'fzf_action', s:default_action)
- let opts.options .= ' --expect='.join(keys(opts._action), ',')
- function! opts.sinklist(lines) abort
- return s:common_sink(self._action, a:lines)
- endfunction
- let opts['sink*'] = opts.sinklist " For backward compatibility
- endif
-
- return opts
-endfunction
-
-function! s:use_sh()
- let [shell, shellslash, shellcmdflag, shellxquote] = [&shell, &shellslash, &shellcmdflag, &shellxquote]
- if s:is_win
- set shell=cmd.exe
- set noshellslash
- let &shellcmdflag = has('nvim') ? '/s /c' : '/c'
- let &shellxquote = has('nvim') ? '"' : '('
- else
- set shell=sh
- endif
- return [shell, shellslash, shellcmdflag, shellxquote]
-endfunction
-
-function! fzf#run(...) abort
-try
- let [shell, shellslash, shellcmdflag, shellxquote] = s:use_sh()
-
- let dict = exists('a:1') ? copy(a:1) : {}
- let temps = { 'result': s:fzf_tempname() }
- let optstr = s:evaluate_opts(get(dict, 'options', ''))
- try
- let fzf_exec = fzf#shellescape(fzf#exec())
- catch
- throw v:exception
- endtry
-
- if !s:present(dict, 'dir')
- let dict.dir = s:fzf_getcwd()
- endif
- if has('win32unix') && s:present(dict, 'dir')
- let dict.dir = fnamemodify(dict.dir, ':p')
- endif
-
- if has_key(dict, 'source')
- let source = remove(dict, 'source')
- let type = type(source)
- if type == 1
- let source_command = source
- elseif type == 3
- let temps.input = s:fzf_tempname()
- call writefile(source, temps.input)
- let source_command = (s:is_win ? 'type ' : 'cat ').fzf#shellescape(temps.input)
- else
- throw 'Invalid source type'
- endif
- else
- let source_command = ''
- endif
-
- 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')
- let has_vim8_term = has('terminal') && has('patch-8.0.995')
- 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 || s:present(dict, 'down', 'up', 'left', 'right', 'window'))
- 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
- endif
- if use_term
- let optstr .= ' --no-height'
- elseif use_height
- let height = s:calc_size(&lines, dict.down, dict)
- let optstr .= ' --height='.height
- endif
- let optstr .= s:border_opt(get(dict, 'window', 0))
- let prev_default_command = $FZF_DEFAULT_COMMAND
- if len(source_command)
- let $FZF_DEFAULT_COMMAND = source_command
- endif
- let command = (use_tmux ? s:fzf_tmux(dict) : fzf_exec).' '.optstr.' > '.temps.result
-
- if use_term
- return s:execute_term(dict, command, temps)
- endif
-
- let lines = use_tmux ? s:execute_tmux(dict, command, temps)
- \ : s:execute(dict, command, use_height, temps)
- call s:callback(dict, lines)
- return lines
-finally
- if len(source_command)
- if len(prev_default_command)
- let $FZF_DEFAULT_COMMAND = prev_default_command
- else
- let $FZF_DEFAULT_COMMAND = ''
- silent! execute 'unlet $FZF_DEFAULT_COMMAND'
- endif
- endif
- let [&shell, &shellslash, &shellcmdflag, &shellxquote] = [shell, shellslash, shellcmdflag, shellxquote]
-endtry
-endfunction
-
-function! s:present(dict, ...)
- for key in a:000
- if !empty(get(a:dict, key, ''))
- return 1
- endif
- endfor
- return 0
-endfunction
-
-function! s:fzf_tmux(dict)
- 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
- endfor
- endif
- return printf('LINES=%d COLUMNS=%d %s %s - --',
- \ &lines, &columns, fzf#shellescape(s:fzf_tmux), size)
-endfunction
-
-function! s:splittable(dict)
- return s:present(a:dict, 'up', 'down') && &lines > 15 ||
- \ s:present(a:dict, 'left', 'right') && &columns > 40
-endfunction
-
-function! s:pushd(dict)
- if s:present(a:dict, 'dir')
- let cwd = s:fzf_getcwd()
- let w:fzf_pushd = {
- \ 'command': haslocaldir() ? 'lcd' : (exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd'),
- \ 'origin': cwd,
- \ 'bufname': bufname('')
- \ }
- execute 'lcd' s:escape(a:dict.dir)
- let cwd = s:fzf_getcwd()
- let w:fzf_pushd.dir = cwd
- let a:dict.pushd = w:fzf_pushd
- return cwd
- endif
- return ''
-endfunction
-
-augroup fzf_popd
- autocmd!
- autocmd WinEnter * call s:dopopd()
-augroup END
-
-function! s:dopopd()
- if !exists('w:fzf_pushd')
- return
- endif
-
- " FIXME: We temporarily change the working directory to 'dir' entry
- " of options dictionary (set to the current working directory if not given)
- " before running fzf.
- "
- " e.g. call fzf#run({'dir': '/tmp', 'source': 'ls', 'sink': 'e'})
- "
- " After processing the sink function, we have to restore the current working
- " directory. But doing so may not be desirable if the function changed the
- " working directory on purpose.
- "
- " So how can we tell if we should do it or not? A simple heuristic we use
- " here is that we change directory only if the current working directory
- " matches 'dir' entry. However, it is possible that the sink function did
- " change the directory to 'dir'. In that case, the user will have an
- " unexpected result.
- 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
-endfunction
-
-function! s:xterm_launcher()
- let fmt = 'xterm -T "[fzf]" -bg "%s" -fg "%s" -geometry %dx%d+%d+%d -e bash -ic %%s'
- if has('gui_macvim')
- let fmt .= '&& osascript -e "tell application \"MacVim\" to activate"'
- endif
- return printf(fmt,
- \ escape(synIDattr(hlID("Normal"), "bg"), '#'), escape(synIDattr(hlID("Normal"), "fg"), '#'),
- \ &columns, &lines/2, getwinposx(), getwinposy())
-endfunction
-unlet! s:launcher
-if s:is_win || has('win32unix')
- let s:launcher = '%s'
-else
- let s:launcher = function('s:xterm_launcher')
-endif
-
-function! s:exit_handler(code, command, ...)
- if a:code == 130
- return 0
- elseif has('nvim') && a:code == 129
- " When deleting the terminal buffer while fzf is still running,
- " Nvim sends SIGHUP.
- return 0
- elseif a:code > 1
- call s:error('Error running ' . a:command)
- if !empty(a:000)
- sleep
- endif
- return 0
- endif
- return 1
-endfunction
-
-function! s:execute(dict, command, use_height, temps) abort
- call s:pushd(a:dict)
- if has('unix') && !a:use_height
- silent! !clear 2> /dev/null
- endif
- let escaped = (a:use_height || s:is_win) ? a:command : escape(substitute(a:command, '\n', '\\n', 'g'), '%#!')
- if has('gui_running')
- let Launcher = get(a:dict, 'launcher', get(g:, 'Fzf_launcher', get(g:, 'fzf_launcher', s:launcher)))
- let fmt = type(Launcher) == 2 ? call(Launcher, []) : Launcher
- if has('unix')
- let escaped = "'".substitute(escaped, "'", "'\"'\"'", 'g')."'"
- endif
- let command = printf(fmt, escaped)
- else
- let command = escaped
- endif
- if s:is_win
- let batchfile = s:fzf_tempname().'.bat'
- call writefile(s:wrap_cmds(command), batchfile)
- let command = batchfile
- let a:temps.batchfile = batchfile
- if has('nvim')
- let fzf = {}
- let fzf.dict = a:dict
- let fzf.temps = a:temps
- function! fzf.on_exit(job_id, exit_status, event) dict
- call s:pushd(self.dict)
- let lines = s:collect(self.temps)
- call s:callback(self.dict, lines)
- endfunction
- let cmd = 'start /wait cmd /c '.command
- call jobstart(cmd, fzf)
- return []
- endif
- elseif has('win32unix') && $TERM !=# 'cygwin'
- let shellscript = s:fzf_tempname()
- call writefile([command], shellscript)
- let command = 'cmd.exe /C '.fzf#shellescape('set "TERM=" & start /WAIT sh -c '.shellscript)
- let a:temps.shellscript = shellscript
- endif
- if a:use_height
- call system(printf('tput cup %d > /dev/tty; tput cnorm > /dev/tty; %s < /dev/tty 2> /dev/tty', &lines, command))
- else
- execute 'silent !'.command
- endif
- let exit_status = v:shell_error
- redraw!
- let lines = s:collect(a:temps)
- return s:exit_handler(exit_status, command) ? lines : []
-endfunction
-
-function! s:execute_tmux(dict, command, temps) abort
- let command = a:command
- let cwd = s:pushd(a:dict)
- if len(cwd)
- " -c '#{pane_current_path}' is only available on tmux 1.9 or above
- let command = join(['cd', fzf#shellescape(cwd), '&&', command])
- endif
-
- call system(command)
- let exit_status = v:shell_error
- redraw!
- let lines = s:collect(a:temps)
- return s:exit_handler(exit_status, command) ? lines : []
-endfunction
-
-function! s:calc_size(max, val, dict)
- let val = substitute(a:val, '^\~', '', '')
- if val =~ '%$'
- let size = a:max * str2nr(val[:-2]) / 100
- else
- let size = min([a:max, str2nr(val)])
- endif
-
- let srcsz = -1
- if type(get(a:dict, 'source', 0)) == type([])
- let srcsz = len(a:dict.source)
- 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')
- let margin += len(split(opts, "\n"))
- endif
- return srcsz >= 0 ? min([srcsz + margin, size]) : size
-endfunction
-
-function! s:getpos()
- return {'tab': tabpagenr(), 'win': winnr(), 'winid': win_getid(), 'cnt': winnr('$'), 'tcnt': tabpagenr('$')}
-endfunction
-
-function! s:border_opt(window)
- if type(a:window) != type({})
- return ''
- endif
-
- " Border style
- let style = tolower(get(a:window, 'border', 'rounded'))
- if !has_key(a:window, 'border') && !get(a:window, 'rounded', 1)
- let style = 'sharp'
- endif
- if style == 'none' || style == 'no'
- return ''
- endif
-
- " For --border styles, we need fzf 0.24.0 or above
- call fzf#exec('0.24.0')
- let opt = ' --border=' . style
- if has_key(a:window, 'highlight')
- let color = s:get_color('fg', a:window.highlight)
- if len(color)
- let opt .= ' --color=border:' . color
- endif
- endif
- return opt
-endfunction
-
-function! s:split(dict)
- let directions = {
- \ 'up': ['topleft', 'resize', &lines],
- \ 'down': ['botright', 'resize', &lines],
- \ '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 !s:popup_support()
- 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
- elseif !s:splittable(a:dict)
- execute (tabpagenr()-1).'tabnew'
- else
- for [dir, triple] in items(directions)
- let val = get(a:dict, dir, '')
- if !empty(val)
- let [cmd, resz, max] = triple
- if (dir == 'up' || dir == 'down') && val[0] == '~'
- let sz = s:calc_size(max, val, a:dict)
- else
- let sz = s:calc_size(max, val, {})
- endif
- execute cmd sz.'new'
- execute resz sz
- return [ppos, {}, is_popup]
- endif
- endfor
- endif
- return [ppos, is_popup ? {} : { '&l:wfw': &l:wfw, '&l:wfh': &l:wfh }, is_popup]
- finally
- if !is_popup
- setlocal winfixwidth winfixheight
- endif
- endtry
-endfunction
-
-nnoremap (fzf-insert) i
-nnoremap (fzf-normal)
-if exists(':tnoremap')
- tnoremap (fzf-insert) i
- tnoremap (fzf-normal)
-endif
-
-function! s:execute_term(dict, command, temps) abort
- let winrest = winrestcmd()
- let pbuf = bufnr('')
- 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,
- \ 'winopts': winopts, 'winrest': winrest, 'lines': &lines,
- \ 'columns': &columns, 'command': a:command }
- function! fzf.switch_back(inplace)
- if a:inplace && bufnr('') == self.buf
- if bufexists(self.pbuf)
- execute 'keepalt keepjumps b' self.pbuf
- endif
- " No other listed buffer
- if bufnr('') == self.buf
- enew
- endif
- endif
- endfunction
- function! fzf.on_exit(id, code, ...)
- if s:getpos() == self.ppos " {'window': 'enew'}
- for [opt, val] in items(self.winopts)
- execute 'let' opt '=' val
- endfor
- call self.switch_back(1)
- else
- if bufnr('') == self.buf
- " We use close instead of bd! since Vim does not close the split when
- " there's no other listed buffer (nvim +'set nobuflisted')
- close
- endif
- silent! execute 'tabnext' self.ppos.tab
- silent! execute self.ppos.win.'wincmd w'
- endif
-
- if bufexists(self.buf)
- execute 'bd!' self.buf
- endif
-
- if &lines == self.lines && &columns == self.columns && s:getpos() == self.ppos
- execute self.winrest
- endif
-
- let lines = s:collect(self.temps)
- if !s:exit_handler(a:code, self.command, 1)
- return
- endif
-
- call s:pushd(self.dict)
- call s:callback(self.dict, lines)
- call self.switch_back(s:getpos() == self.ppos)
-
- if &buftype == 'terminal'
- call feedkeys(&filetype == 'fzf' ? "\(fzf-insert)" : "\(fzf-normal)")
- endif
- endfunction
-
- try
- call s:pushd(a:dict)
- if s:is_win
- let fzf.temps.batchfile = s:fzf_tempname().'.bat'
- call writefile(s:wrap_cmds(a:command), fzf.temps.batchfile)
- let command = fzf.temps.batchfile
- else
- let command = a:command
- endif
- let command .= s:term_marker
- if has('nvim')
- call termopen(command, fzf)
- else
- let term_opts = {'exit_cb': function(fzf.on_exit)}
- if v:version >= 802
- let term_opts.term_kill = 'term'
- endif
- if is_popup
- let term_opts.hidden = 1
- else
- let term_opts.curwin = 1
- endif
- let fzf.buf = term_start([&shell, &shellcmdflag, command], term_opts)
- if is_popup && exists('#TerminalWinOpen')
- doautocmd TerminalWinOpen
- endif
- if !has('patch-8.0.1261') && !s:is_win
- call term_wait(fzf.buf, 20)
- endif
- endif
- tnoremap
- if exists('&termwinkey') && (empty(&termwinkey) || &termwinkey =~? '')
- tnoremap .
- endif
- finally
- call s:dopopd()
- endtry
- setlocal nospell bufhidden=wipe nobuflisted nonumber
- setf fzf
- startinsert
- return []
-endfunction
-
-function! s:collect(temps) abort
- try
- return filereadable(a:temps.result) ? readfile(a:temps.result) : []
- finally
- for tf in values(a:temps)
- silent! call delete(tf)
- endfor
- endtry
-endfunction
-
-function! s:callback(dict, lines) abort
- let popd = has_key(a:dict, 'pushd')
- if popd
- let w:fzf_pushd = a:dict.pushd
- endif
-
- try
- if has_key(a:dict, 'sink')
- for line in a:lines
- if type(a:dict.sink) == 2
- call a:dict.sink(line)
- else
- execute a:dict.sink s:escape(line)
- endif
- endfor
- endif
- if has_key(a:dict, 'sink*')
- call a:dict['sink*'](a:lines)
- elseif has_key(a:dict, 'sinklist')
- call a:dict['sinklist'](a:lines)
- endif
- catch
- if stridx(v:exception, ':E325:') < 0
- echoerr v:exception
- endif
- endtry
-
- " We may have opened a new window or tab
- if popd
- let w:fzf_pushd = a:dict.pushd
- call s:dopopd()
- endif
-endfunction
-
-if has('nvim')
- function s:create_popup(hl, opts) abort
- let buf = nvim_create_buf(v:false, v:true)
- let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts)
- let win = nvim_open_win(buf, v:true, opts)
- call setwinvar(win, '&winhighlight', 'NormalFloat:'..a:hl)
- call setwinvar(win, '&colorcolumn', '')
- return buf
- endfunction
-else
- function! s:create_popup(hl, opts) abort
- let s:popup_create = {buf -> popup_create(buf, #{
- \ line: a:opts.row,
- \ col: a:opts.col,
- \ minwidth: a:opts.width,
- \ maxwidth: a:opts.width,
- \ minheight: a:opts.height,
- \ maxheight: a:opts.height,
- \ zindex: 1000,
- \ })}
- autocmd TerminalOpen * ++once call s:popup_create(str2nr(expand('')))
- endfunction
-endif
-
-function! s:popup(opts) abort
- let xoffset = get(a:opts, 'xoffset', 0.5)
- let yoffset = get(a:opts, 'yoffset', 0.5)
- let relative = get(a:opts, 'relative', 0)
-
- " Use current window size for positioning relatively positioned popups
- let columns = relative ? winwidth(0) : &columns
- let lines = relative ? winheight(0) : (&lines - has('nvim'))
-
- " Size and position
- let width = min([max([8, a:opts.width > 1 ? a:opts.width : float2nr(columns * a:opts.width)]), columns])
- let height = min([max([4, a:opts.height > 1 ? a:opts.height : float2nr(lines * a:opts.height)]), lines])
- let row = float2nr(yoffset * (lines - height)) + (relative ? win_screenpos(0)[0] - 1 : 0)
- let col = float2nr(xoffset * (columns - width)) + (relative ? win_screenpos(0)[1] - 1 : 0)
-
- " Managing the differences
- let row = min([max([0, row]), &lines - has('nvim') - height])
- let col = min([max([0, col]), &columns - width])
- let row += !has('nvim')
- let col += !has('nvim')
-
- call s:create_popup('Normal', {
- \ 'row': row, 'col': col, 'width': width, 'height': height
- \ })
-endfunction
-
-let s:default_action = {
- \ 'ctrl-t': 'tab split',
- \ 'ctrl-x': 'split',
- \ 'ctrl-v': 'vsplit' }
-
-function! s:shortpath()
- let short = fnamemodify(getcwd(), ':~:.')
- if !has('win32unix')
- let short = pathshorten(short)
- endif
- let slash = (s:is_win && !&shellslash) ? '\' : '/'
- return empty(short) ? '~'.slash : short . (short =~ escape(slash, '\').'$' ? '' : slash)
-endfunction
-
-function! s:cmd(bang, ...) abort
- let args = copy(a:000)
- let opts = { 'options': ['--multi'] }
- if len(args) && isdirectory(expand(args[-1]))
- let opts.dir = substitute(substitute(remove(args, -1), '\\\(["'']\)', '\1', 'g'), '[/\\]*$', '/', '')
- if s:is_win && !&shellslash
- let opts.dir = substitute(opts.dir, '/', '\\', 'g')
- endif
- let prompt = opts.dir
- else
- let prompt = s:shortpath()
- endif
- let prompt = strwidth(prompt) < &columns - 20 ? prompt : '> '
- call extend(opts.options, ['--prompt', prompt])
- call extend(opts.options, args)
- call fzf#run(fzf#wrap('FZF', opts, a:bang))
-endfunction
-
-command! -nargs=* -complete=dir -bang FZF call s:cmd(0, )
-
-let &cpo = s:cpo_save
-unlet s:cpo_save
diff --git a/.fzf/shell/completion.bash b/.fzf/shell/completion.bash
deleted file mode 100644
index 21aa450..0000000
--- a/.fzf/shell/completion.bash
+++ /dev/null
@@ -1,381 +0,0 @@
-# ____ ____
-# / __/___ / __/
-# / /_/_ / / /_
-# / __/ / /_/ __/
-# /_/ /___/_/ completion.bash
-#
-# - $FZF_TMUX (default: 0)
-# - $FZF_TMUX_OPTS (default: empty)
-# - $FZF_COMPLETION_TRIGGER (default: '**')
-# - $FZF_COMPLETION_OPTS (default: empty)
-
-if [[ $- =~ i ]]; then
-
-# To use custom commands instead of find, override _fzf_compgen_{path,dir}
-if ! declare -f _fzf_compgen_path > /dev/null; then
- _fzf_compgen_path() {
- echo "$1"
- command find -L "$1" \
- -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
- -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
- }
-fi
-
-if ! declare -f _fzf_compgen_dir > /dev/null; then
- _fzf_compgen_dir() {
- command find -L "$1" \
- -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
- -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
- }
-fi
-
-###########################################################
-
-# To redraw line after fzf closes (printf '\e[5n')
-bind '"\e[0n": redraw-current-line' 2> /dev/null
-
-__fzf_comprun() {
- if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
- _fzf_comprun "$@"
- elif [[ -n "$TMUX_PANE" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "$FZF_TMUX_OPTS" ]]; }; then
- shift
- fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@"
- else
- shift
- fzf "$@"
- fi
-}
-
-__fzf_orig_completion() {
- local l comp f cmd
- while read -r l; do
- if [[ "$l" =~ ^(.*\ -F)\ *([^ ]*).*\ ([^ ]*)$ ]]; then
- comp="${BASH_REMATCH[1]}"
- f="${BASH_REMATCH[2]}"
- cmd="${BASH_REMATCH[3]}"
- [[ "$f" = _fzf_* ]] && continue
- printf -v "_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" "%s" "${comp} %s ${cmd} #${f}"
- if [[ "$l" = *" -o nospace "* ]] && [[ ! "$__fzf_nospace_commands" = *" $cmd "* ]]; then
- __fzf_nospace_commands="$__fzf_nospace_commands $cmd "
- fi
- fi
- done
-}
-
-_fzf_opts_completion() {
- local cur prev opts
- COMPREPLY=()
- cur="${COMP_WORDS[COMP_CWORD]}"
- prev="${COMP_WORDS[COMP_CWORD-1]}"
- opts="
- -x --extended
- -e --exact
- --algo
- -i +i
- -n --nth
- --with-nth
- -d --delimiter
- +s --no-sort
- --tac
- --tiebreak
- -m --multi
- --no-mouse
- --bind
- --cycle
- --no-hscroll
- --jump-labels
- --height
- --literal
- --reverse
- --margin
- --inline-info
- --prompt
- --pointer
- --marker
- --header
- --header-lines
- --ansi
- --tabstop
- --color
- --no-bold
- --history
- --history-size
- --preview
- --preview-window
- -q --query
- -1 --select-1
- -0 --exit-0
- -f --filter
- --print-query
- --expect
- --sync"
-
- case "${prev}" in
- --tiebreak)
- COMPREPLY=( $(compgen -W "length begin end index" -- "$cur") )
- return 0
- ;;
- --color)
- COMPREPLY=( $(compgen -W "dark light 16 bw" -- "$cur") )
- return 0
- ;;
- --history)
- COMPREPLY=()
- return 0
- ;;
- esac
-
- if [[ "$cur" =~ ^-|\+ ]]; then
- COMPREPLY=( $(compgen -W "${opts}" -- "$cur") )
- return 0
- fi
-
- return 0
-}
-
-_fzf_handle_dynamic_completion() {
- local cmd orig_var orig ret orig_cmd orig_complete
- cmd="$1"
- shift
- orig_cmd="$1"
- orig_var="_fzf_orig_completion_$cmd"
- orig="${!orig_var##*#}"
- if [[ -n "$orig" ]] && type "$orig" > /dev/null 2>&1; then
- $orig "$@"
- elif [[ -n "$_fzf_completion_loader" ]]; then
- orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
- _completion_loader "$@"
- ret=$?
- # _completion_loader may not have updated completion for the command
- if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
- __fzf_orig_completion < <(complete -p "$orig_cmd" 2> /dev/null)
- if [[ "$__fzf_nospace_commands" = *" $orig_cmd "* ]]; then
- eval "${orig_complete/ -F / -o nospace -F }"
- else
- eval "$orig_complete"
- fi
- fi
- return $ret
- fi
-}
-
-__fzf_generic_path_completion() {
- local cur base dir leftover matches trigger cmd
- cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
- COMPREPLY=()
- trigger=${FZF_COMPLETION_TRIGGER-'**'}
- cur="${COMP_WORDS[COMP_CWORD]}"
- if [[ "$cur" == *"$trigger" ]]; then
- base=${cur:0:${#cur}-${#trigger}}
- eval "base=$base"
-
- [[ $base = *"/"* ]] && dir="$base"
- while true; do
- if [[ -z "$dir" ]] || [[ -d "$dir" ]]; then
- leftover=${base/#"$dir"}
- leftover=${leftover/#\/}
- [[ -z "$dir" ]] && dir='.'
- [[ "$dir" != "/" ]] && dir="${dir/%\//}"
- matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do
- printf "%q$3 " "$item"
- done)
- matches=${matches% }
- [[ -z "$3" ]] && [[ "$__fzf_nospace_commands" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
- if [[ -n "$matches" ]]; then
- COMPREPLY=( "$matches" )
- else
- COMPREPLY=( "$cur" )
- fi
- printf '\e[5n'
- return 0
- fi
- dir=$(dirname "$dir")
- [[ "$dir" =~ /$ ]] || dir="$dir"/
- done
- else
- shift
- shift
- shift
- _fzf_handle_dynamic_completion "$cmd" "$@"
- fi
-}
-
-_fzf_complete() {
- # Split arguments around --
- local args rest str_arg i sep
- args=("$@")
- sep=
- for i in "${!args[@]}"; do
- if [[ "${args[$i]}" = -- ]]; then
- sep=$i
- break
- fi
- done
- if [[ -n "$sep" ]]; then
- str_arg=
- rest=("${args[@]:$((sep + 1)):${#args[@]}}")
- args=("${args[@]:0:$sep}")
- else
- str_arg=$1
- args=()
- shift
- rest=("$@")
- fi
-
- local cur selected trigger cmd post
- post="$(caller 0 | awk '{print $2}')_post"
- type -t "$post" > /dev/null 2>&1 || post=cat
-
- cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
- trigger=${FZF_COMPLETION_TRIGGER-'**'}
- cur="${COMP_WORDS[COMP_CWORD]}"
- if [[ "$cur" == *"$trigger" ]]; then
- cur=${cur:0:${#cur}-${#trigger}}
-
- selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
- selected=${selected% } # Strip trailing space not to repeat "-o nospace"
- if [[ -n "$selected" ]]; then
- COMPREPLY=("$selected")
- else
- COMPREPLY=("$cur")
- fi
- printf '\e[5n'
- return 0
- else
- _fzf_handle_dynamic_completion "$cmd" "${rest[@]}"
- fi
-}
-
-_fzf_path_completion() {
- __fzf_generic_path_completion _fzf_compgen_path "-m" "" "$@"
-}
-
-# Deprecated. No file only completion.
-_fzf_file_completion() {
- _fzf_path_completion "$@"
-}
-
-_fzf_dir_completion() {
- __fzf_generic_path_completion _fzf_compgen_dir "" "/" "$@"
-}
-
-_fzf_complete_kill() {
- 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() {
- _fzf_complete +m -- "$@" < <(
- command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
- <(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
- <(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
- awk '{if (length($2) > 0) {print $2}}' | sort -u
- )
-}
-
-_fzf_var_completion() {
- _fzf_complete -m -- "$@" < <(
- declare -xp | sed 's/=.*//' | sed 's/.* //'
- )
-}
-
-_fzf_alias_completion() {
- _fzf_complete -m -- "$@" < <(
- alias | sed 's/=.*//' | sed 's/.* //'
- )
-}
-
-# fzf options
-complete -o default -F _fzf_opts_completion fzf
-
-d_cmds="${FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}"
-a_cmds="
- awk cat diff diff3
- emacs emacsclient ex file ftp g++ gcc gvim head hg java
- javac ld less more mvim nvim patch perl python ruby
- sed sftp sort source tail tee uniq vi view vim wc xdg-open
- basename bunzip2 bzip2 chmod chown curl cp dirname du
- find git grep gunzip gzip hg jar
- ln ls mv open rm rsync scp
- svn tar unzip zip"
-
-# Preserve existing completion
-__fzf_orig_completion < <(complete -p $d_cmds $a_cmds 2> /dev/null)
-
-if type _completion_loader > /dev/null 2>&1; then
- _fzf_completion_loader=1
-fi
-
-__fzf_defc() {
- local cmd func opts orig_var orig def
- cmd="$1"
- func="$2"
- opts="$3"
- orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
- orig="${!orig_var}"
- if [[ -n "$orig" ]]; then
- printf -v def "$orig" "$func"
- eval "$def"
- else
- complete -F "$func" $opts "$cmd"
- fi
-}
-
-# Anything
-for cmd in $a_cmds; do
- __fzf_defc "$cmd" _fzf_path_completion "-o default -o bashdefault"
-done
-
-# Directory
-for cmd in $d_cmds; do
- __fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o dirnames"
-done
-
-# Kill completion (supports empty completion trigger)
-complete -F _fzf_complete_kill -o default -o bashdefault kill
-
-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|proc COMMANDS..."
- return 1
- fi
- shift
- __fzf_orig_completion < <(complete -p "$@" 2> /dev/null)
- for cmd in "$@"; do
- case "$kind" in
- dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
- var) __fzf_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
- alias) __fzf_defc "$cmd" "$fn" "-a" ;;
- *) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
- esac
- done
-}
-
-# Environment variables / Aliases / Hosts
-_fzf_setup_completion 'var' export unset
-_fzf_setup_completion 'alias' unalias
-_fzf_setup_completion 'host' ssh telnet
-
-fi
diff --git a/.fzf/shell/completion.zsh b/.fzf/shell/completion.zsh
deleted file mode 100644
index f12afca..0000000
--- a/.fzf/shell/completion.zsh
+++ /dev/null
@@ -1,329 +0,0 @@
-# ____ ____
-# / __/___ / __/
-# / /_/_ / / /_
-# / __/ / /_/ __/
-# /_/ /___/_/ completion.zsh
-#
-# - $FZF_TMUX (default: 0)
-# - $FZF_TMUX_OPTS (default: '-d 40%')
-# - $FZF_COMPLETION_TRIGGER (default: '**')
-# - $FZF_COMPLETION_OPTS (default: empty)
-
-# Both branches of the following `if` do the same thing -- define
-# __fzf_completion_options such that `eval $__fzf_completion_options` sets
-# all options to the same values they currently have. We'll do just that at
-# the bottom of the file after changing options to what we prefer.
-#
-# IMPORTANT: Until we get to the `emulate` line, all words that *can* be quoted
-# *must* be quoted in order to prevent alias expansion. In addition, code must
-# be written in a way works with any set of zsh options. This is very tricky, so
-# careful when you change it.
-#
-# Start by loading the builtin zsh/parameter module. It provides `options`
-# associative array that stores current shell options.
-if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
- # This is the fast branch and it gets taken on virtually all Zsh installations.
- #
- # ${(kv)options[@]} expands to array of keys (option names) and values ("on"
- # or "off"). The subsequent expansion# with (j: :) flag joins all elements
- # together separated by spaces. __fzf_completion_options ends up with a value
- # like this: "options=(shwordsplit off aliases on ...)".
- __fzf_completion_options="options=(${(j: :)${(kv)options[@]}})"
-else
- # This branch is much slower because it forks to get the names of all
- # zsh options. It's possible to eliminate this fork but it's not worth the
- # trouble because this branch gets taken only on very ancient or broken
- # zsh installations.
- () {
- # That `()` above defines an anonymous function. This is essentially a scope
- # for local parameters. We use it to avoid polluting global scope.
- 'local' '__fzf_opt'
- __fzf_completion_options="setopt"
- # `set -o` prints one line for every zsh option. Each line contains option
- # name, some spaces, and then either "on" or "off". We just want option names.
- # Expansion with (@f) flag splits a string into lines. The outer expansion
- # removes spaces and everything that follow them on every line. __fzf_opt
- # ends up iterating over option names: shwordsplit, aliases, etc.
- for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
- if [[ -o "$__fzf_opt" ]]; then
- # Option $__fzf_opt is currently on, so remember to set it back on.
- __fzf_completion_options+=" -o $__fzf_opt"
- else
- # Option $__fzf_opt is currently off, so remember to set it back off.
- __fzf_completion_options+=" +o $__fzf_opt"
- fi
- done
- # The value of __fzf_completion_options here looks like this:
- # "setopt +o shwordsplit -o aliases ..."
- }
-fi
-
-# Enable the default zsh options (those marked with in `man zshoptions`)
-# but without `aliases`. Aliases in functions are expanded when functions are
-# defined, so if we disable aliases here, we'll be sure to have no pesky
-# aliases in any of our functions. This way we won't need prefix every
-# command with `command` or to quote every word to defend against global
-# aliases. Note that `aliases` is not the only option that's important to
-# control. There are several others that could wreck havoc if they are set
-# to values we don't expect. With the following `emulate` command we
-# sidestep this issue entirely.
-'emulate' 'zsh' '-o' 'no_aliases'
-
-# This brace is the start of try-always block. The `always` part is like
-# `finally` in lesser languages. We use it to *always* restore user options.
-{
-
-# Bail out if not interactive shell.
-[[ -o interactive ]] || return 0
-
-# To use custom commands instead of find, override _fzf_compgen_{path,dir}
-if ! declare -f _fzf_compgen_path > /dev/null; then
- _fzf_compgen_path() {
- echo "$1"
- command find -L "$1" \
- -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
- -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
- }
-fi
-
-if ! declare -f _fzf_compgen_dir > /dev/null; then
- _fzf_compgen_dir() {
- command find -L "$1" \
- -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
- -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
- }
-fi
-
-###########################################################
-
-__fzf_comprun() {
- if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
- _fzf_comprun "$@"
- elif [ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; }; then
- shift
- if [ -n "$FZF_TMUX_OPTS" ]; then
- fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@"
- else
- fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@"
- fi
- else
- shift
- fzf "$@"
- fi
-}
-
-# Extract the name of the command. e.g. foo=1 bar baz**
-__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
- fi
- done
- echo "${tokens[1]}"
-}
-
-__fzf_generic_path_completion() {
- local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
- base=$1
- lbuf=$2
- cmd=$(__fzf_extract_command "$lbuf")
- compgen=$3
- fzf_opts=$4
- suffix=$5
- tail=$6
-
- setopt localoptions nonomatch
- eval "base=$base"
- [[ $base = *"/"* ]] && dir="$base"
- while [ 1 ]; do
- if [[ -z "$dir" || -d ${dir} ]]; then
- leftover=${base/#"$dir"}
- leftover=${leftover/#\/}
- [ -z "$dir" ] && dir='.'
- [ "$dir" != "/" ] && dir="${dir/%\//}"
- matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
- echo -n "${(q)item}$suffix "
- done)
- matches=${matches% }
- if [ -n "$matches" ]; then
- LBUFFER="$lbuf$matches$tail"
- fi
- zle reset-prompt
- break
- fi
- dir=$(dirname "$dir")
- dir=${dir%/}/
- done
-}
-
-_fzf_path_completion() {
- __fzf_generic_path_completion "$1" "$2" _fzf_compgen_path \
- "-m" "" " "
-}
-
-_fzf_dir_completion() {
- __fzf_generic_path_completion "$1" "$2" _fzf_compgen_dir \
- "" "/" ""
-}
-
-_fzf_feed_fifo() (
- command rm -f "$1"
- mkfifo "$1"
- cat <&0 > "$1" &
-)
-
-_fzf_complete() {
- setopt localoptions ksh_arrays
- # Split arguments around --
- local args rest str_arg i sep
- args=("$@")
- sep=
- for i in {0..${#args[@]}}; do
- if [[ "${args[$i]}" = -- ]]; then
- sep=$i
- break
- fi
- done
- if [[ -n "$sep" ]]; then
- str_arg=
- rest=("${args[@]:$((sep + 1)):${#args[@]}}")
- args=("${args[@]:0:$sep}")
- else
- str_arg=$1
- args=()
- shift
- rest=("$@")
- fi
-
- local fifo lbuf cmd matches post
- fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
- lbuf=${rest[0]}
- cmd=$(__fzf_extract_command "$lbuf")
- post="${funcstack[1]}_post"
- type $post > /dev/null 2>&1 || post=cat
-
- _fzf_feed_fifo "$fifo"
- matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
- if [ -n "$matches" ]; then
- LBUFFER="$lbuf$matches"
- fi
- command rm -f "$fifo"
-}
-
-_fzf_complete_telnet() {
- _fzf_complete +m -- "$@" < <(
- command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
- awk '{if (length($2) > 0) {print $2}}' | sort -u
- )
-}
-
-_fzf_complete_ssh() {
- _fzf_complete +m -- "$@" < <(
- setopt localoptions nonomatch
- command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
- <(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
- <(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
- awk '{if (length($2) > 0) {print $2}}' | sort -u
- )
-}
-
-_fzf_complete_export() {
- _fzf_complete -m -- "$@" < <(
- declare -xp | sed 's/=.*//' | sed 's/.* //'
- )
-}
-
-_fzf_complete_unset() {
- _fzf_complete -m -- "$@" < <(
- declare -xp | sed 's/=.*//' | sed 's/.* //'
- )
-}
-
-_fzf_complete_unalias() {
- _fzf_complete +m -- "$@" < <(
- alias | sed 's/=.*//'
- )
-}
-
-_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
-
- # http://zsh.sourceforge.net/FAQ/zshfaq03.html
- # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
- tokens=(${(z)LBUFFER})
- if [ ${#tokens} -lt 1 ]; then
- zle ${fzf_default_completion:-expand-or-complete}
- return
- fi
-
- cmd=$(__fzf_extract_command "$LBUFFER")
-
- # Explicitly allow for empty trigger.
- trigger=${FZF_COMPLETION_TRIGGER-'**'}
- [ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
-
- # When the trigger starts with ';', it becomes a separate token
- if [[ ${LBUFFER} = *"${tokens[-2]}${tokens[-1]}" ]]; then
- tokens[-2]="${tokens[-2]}${tokens[-1]}"
- 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
- tail=$trigger
- tokens+=$trigger
- lbuf="$lbuf$trigger"
- fi
-
- # Trigger sequence given
- 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}}
- [ -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}
- zle reset-prompt
- elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
- _fzf_dir_completion "$prefix" "$lbuf"
- else
- _fzf_path_completion "$prefix" "$lbuf"
- fi
- # Fall back to default completion
- else
- zle ${fzf_default_completion:-expand-or-complete}
- fi
-}
-
-[ -z "$fzf_default_completion" ] && {
- binding=$(bindkey '^I')
- [[ $binding =~ 'undefined-key' ]] || fzf_default_completion=$binding[(s: :w)2]
- unset binding
-}
-
-zle -N fzf-completion
-bindkey '^I' fzf-completion
-
-} always {
- # Restore the original options.
- eval $__fzf_completion_options
- 'unset' '__fzf_completion_options'
-}
diff --git a/.fzf/shell/key-bindings.bash b/.fzf/shell/key-bindings.bash
deleted file mode 100644
index 3ea8e76..0000000
--- a/.fzf/shell/key-bindings.bash
+++ /dev/null
@@ -1,96 +0,0 @@
-# ____ ____
-# / __/___ / __/
-# / /_/_ / / /_
-# / __/ / /_/ __/
-# /_/ /___/_/ key-bindings.bash
-#
-# - $FZF_TMUX_OPTS
-# - $FZF_CTRL_T_COMMAND
-# - $FZF_CTRL_T_OPTS
-# - $FZF_CTRL_R_OPTS
-# - $FZF_ALT_C_COMMAND
-# - $FZF_ALT_C_OPTS
-
-# Key bindings
-# ------------
-__fzf_select__() {
- local cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
- -o -type f -print \
- -o -type d -print \
- -o -type l -print 2> /dev/null | cut -b3-"}"
- eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS" $(__fzfcmd) -m "$@" | while read -r item; do
- printf '%q ' "$item"
- done
- echo
-}
-
-if [[ $- =~ i ]]; then
-
-__fzfcmd() {
- [[ -n "$TMUX_PANE" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "$FZF_TMUX_OPTS" ]]; } &&
- echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
-}
-
-fzf-file-widget() {
- local selected="$(__fzf_select__)"
- READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
- READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
-}
-
-__fzf_cd__() {
- local cmd dir
- cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
- -o -type d -print 2> /dev/null | cut -b3-"}"
- dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) +m) && printf 'cd %q' "$dir"
-}
-
-__fzf_history__() {
- local output
- output=$(
- builtin fc -lnr -2147483648 |
- last_hist=$(HISTTIMEFORMAT='' builtin history 1) perl -n -l0 -e 'BEGIN { getc; $/ = "\n\t"; $HISTCMD = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCMD - $. . "\t$_" if !$seen{$_}++' |
- FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort,ctrl-z:ignore $FZF_CTRL_R_OPTS +m --read0" $(__fzfcmd) --query "$READLINE_LINE"
- ) || return
- READLINE_LINE=${output#*$'\t'}
- if [[ -z "$READLINE_POINT" ]]; then
- echo "$READLINE_LINE"
- else
- READLINE_POINT=0x7fffffff
- fi
-}
-
-# Required to refresh the prompt after fzf
-bind -m emacs-standard '"\er": redraw-current-line'
-
-bind -m vi-command '"\C-z": emacs-editing-mode'
-bind -m vi-insert '"\C-z": emacs-editing-mode'
-bind -m emacs-standard '"\C-z": vi-editing-mode'
-
-if (( BASH_VERSINFO[0] < 4 )); then
- # CTRL-T - Paste the selected file path into the command line
- bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
- bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
- bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
-
- # CTRL-R - Paste the selected command from history into the command line
- bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u"$(__fzf_history__)"\e\C-e\er"'
- bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
- bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
-else
- # CTRL-T - Paste the selected file path into the command line
- bind -m emacs-standard -x '"\C-t": fzf-file-widget'
- bind -m vi-command -x '"\C-t": fzf-file-widget'
- bind -m vi-insert -x '"\C-t": fzf-file-widget'
-
- # CTRL-R - Paste the selected command from history into the command line
- bind -m emacs-standard -x '"\C-r": __fzf_history__'
- bind -m vi-command -x '"\C-r": __fzf_history__'
- bind -m vi-insert -x '"\C-r": __fzf_history__'
-fi
-
-# ALT-C - cd into the selected directory
-bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
-bind -m vi-command '"\ec": "\C-z\ec\C-z"'
-bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
-
-fi
diff --git a/.fzf/shell/key-bindings.fish b/.fzf/shell/key-bindings.fish
deleted file mode 100644
index 6f73903..0000000
--- a/.fzf/shell/key-bindings.fish
+++ /dev/null
@@ -1,172 +0,0 @@
-# ____ ____
-# / __/___ / __/
-# / /_/_ / / /_
-# / __/ / /_/ __/
-# /_/ /___/_/ key-bindings.fish
-#
-# - $FZF_TMUX_OPTS
-# - $FZF_CTRL_T_COMMAND
-# - $FZF_CTRL_T_OPTS
-# - $FZF_CTRL_R_OPTS
-# - $FZF_ALT_C_COMMAND
-# - $FZF_ALT_C_OPTS
-
-# Key bindings
-# ------------
-function fzf_key_bindings
-
- # Store current token in $dir as root for the 'find' command
- function fzf-file-widget -d "List files and folders"
- set -l commandline (__fzf_parse_commandline)
- set -l dir $commandline[1]
- set -l fzf_query $commandline[2]
- set -l prefix $commandline[3]
-
- # "-path \$dir'*/\\.*'" matches hidden files/folders inside $dir but not
- # $dir itself, even if hidden.
- test -n "$FZF_CTRL_T_COMMAND"; or set -l FZF_CTRL_T_COMMAND "
- command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
- -o -type f -print \
- -o -type d -print \
- -o -type l -print 2> /dev/null | sed 's@^\./@@'"
-
- test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
- begin
- set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
- eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)' -m --query "'$fzf_query'"' | while read -l r; set result $result $r; end
- end
- if [ -z "$result" ]
- commandline -f repaint
- return
- else
- # Remove last token from commandline.
- commandline -t ""
- end
- for i in $result
- commandline -it -- $prefix
- commandline -it -- (string escape $i)
- commandline -it -- ' '
- end
- commandline -f repaint
- end
-
- function fzf-history-widget -d "Show command history"
- test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
- begin
- set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT $FZF_DEFAULT_OPTS --tiebreak=index --bind=ctrl-r:toggle-sort,ctrl-z:ignore $FZF_CTRL_R_OPTS +m"
-
- set -l FISH_MAJOR (echo $version | cut -f1 -d.)
- set -l FISH_MINOR (echo $version | cut -f2 -d.)
-
- # history's -z flag is needed for multi-line support.
- # history's -z flag was added in fish 2.4.0, so don't use it for versions
- # before 2.4.0.
- if [ "$FISH_MAJOR" -gt 2 -o \( "$FISH_MAJOR" -eq 2 -a "$FISH_MINOR" -ge 4 \) ];
- history -z | eval (__fzfcmd) --read0 --print0 -q '(commandline)' | read -lz result
- and commandline -- $result
- else
- history | eval (__fzfcmd) -q '(commandline)' | read -l result
- and commandline -- $result
- end
- end
- commandline -f repaint
- end
-
- function fzf-cd-widget -d "Change directory"
- set -l commandline (__fzf_parse_commandline)
- set -l dir $commandline[1]
- set -l fzf_query $commandline[2]
- set -l prefix $commandline[3]
-
- test -n "$FZF_ALT_C_COMMAND"; or set -l FZF_ALT_C_COMMAND "
- command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
- -o -type d -print 2> /dev/null | sed 's@^\./@@'"
- test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
- begin
- set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS"
- eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)' +m --query "'$fzf_query'"' | read -l result
-
- if [ -n "$result" ]
- cd $result
-
- # Remove last token from commandline.
- commandline -t ""
- commandline -it -- $prefix
- end
- end
-
- commandline -f repaint
- end
-
- function __fzfcmd
- test -n "$FZF_TMUX"; or set FZF_TMUX 0
- test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
- if [ -n "$FZF_TMUX_OPTS" ]
- echo "fzf-tmux $FZF_TMUX_OPTS -- "
- else if [ $FZF_TMUX -eq 1 ]
- echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
- else
- echo "fzf"
- end
- end
-
- bind \ct fzf-file-widget
- bind \cr fzf-history-widget
- bind \ec fzf-cd-widget
-
- if bind -M insert > /dev/null 2>&1
- bind -M insert \ct fzf-file-widget
- bind -M insert \cr fzf-history-widget
- bind -M insert \ec fzf-cd-widget
- end
-
- function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
- set -l commandline (commandline -t)
-
- # strip -option= from token if present
- set -l prefix (string match -r -- '^-[^\s=]+=' $commandline)
- set commandline (string replace -- "$prefix" '' $commandline)
-
- # eval is used to do shell expansion on paths
- eval set commandline $commandline
-
- if [ -z $commandline ]
- # Default to current directory with no --query
- set dir '.'
- set fzf_query ''
- else
- set dir (__fzf_get_dir $commandline)
-
- if [ "$dir" = "." -a (string sub -l 1 -- $commandline) != '.' ]
- # if $dir is "." but commandline is not a relative path, this means no file path found
- set fzf_query $commandline
- else
- # Also remove trailing slash after dir, to "split" input properly
- set fzf_query (string replace -r "^$dir/?" -- '' "$commandline")
- end
- end
-
- echo $dir
- echo $fzf_query
- echo $prefix
- end
-
- function __fzf_get_dir -d 'Find the longest existing filepath from input string'
- set dir $argv
-
- # Strip all trailing slashes. Ignore if $dir is root dir (/)
- if [ (string length -- $dir) -gt 1 ]
- set dir (string replace -r '/*$' -- '' $dir)
- end
-
- # Iteratively check if dir exists and strip tail end of path
- while [ ! -d "$dir" ]
- # If path is absolute, this can keep going until ends up at /
- # If path is relative, this can keep going until entire input is consumed, dirname returns "."
- set dir (dirname -- "$dir")
- end
-
- echo $dir
- end
-
-end
diff --git a/.fzf/shell/key-bindings.zsh b/.fzf/shell/key-bindings.zsh
deleted file mode 100644
index 8efa6af..0000000
--- a/.fzf/shell/key-bindings.zsh
+++ /dev/null
@@ -1,114 +0,0 @@
-# ____ ____
-# / __/___ / __/
-# / /_/_ / / /_
-# / __/ / /_/ __/
-# /_/ /___/_/ key-bindings.zsh
-#
-# - $FZF_TMUX_OPTS
-# - $FZF_CTRL_T_COMMAND
-# - $FZF_CTRL_T_OPTS
-# - $FZF_CTRL_R_OPTS
-# - $FZF_ALT_C_COMMAND
-# - $FZF_ALT_C_OPTS
-
-# Key bindings
-# ------------
-
-# The code at the top and the bottom of this file is the same as in completion.zsh.
-# Refer to that file for explanation.
-if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
- __fzf_key_bindings_options="options=(${(j: :)${(kv)options[@]}})"
-else
- () {
- __fzf_key_bindings_options="setopt"
- 'local' '__fzf_opt'
- for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
- if [[ -o "$__fzf_opt" ]]; then
- __fzf_key_bindings_options+=" -o $__fzf_opt"
- else
- __fzf_key_bindings_options+=" +o $__fzf_opt"
- fi
- done
- }
-fi
-
-'emulate' 'zsh' '-o' 'no_aliases'
-
-{
-
-[[ -o interactive ]] || return 0
-
-# CTRL-T - Paste the selected file path(s) into the command line
-__fsel() {
- local cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
- -o -type f -print \
- -o -type d -print \
- -o -type l -print 2> /dev/null | cut -b3-"}"
- setopt localoptions pipefail no_aliases 2> /dev/null
- local item
- eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS" $(__fzfcmd) -m "$@" | while read item; do
- echo -n "${(q)item} "
- done
- local ret=$?
- echo
- return $ret
-}
-
-__fzfcmd() {
- [ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; } &&
- echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
-}
-
-fzf-file-widget() {
- LBUFFER="${LBUFFER}$(__fsel)"
- local ret=$?
- zle reset-prompt
- return $ret
-}
-zle -N fzf-file-widget
-bindkey '^T' fzf-file-widget
-
-# ALT-C - cd into the selected directory
-fzf-cd-widget() {
- local cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
- -o -type d -print 2> /dev/null | cut -b3-"}"
- setopt localoptions pipefail no_aliases 2> /dev/null
- local dir="$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) +m)"
- if [[ -z "$dir" ]]; then
- zle redisplay
- return 0
- fi
- zle push-line # Clear buffer. Auto-restored on next prompt.
- BUFFER="cd ${(q)dir}"
- zle accept-line
- local ret=$?
- unset dir # ensure this doesn't end up appearing in prompt expansion
- zle reset-prompt
- return $ret
-}
-zle -N fzf-cd-widget
-bindkey '\ec' fzf-cd-widget
-
-# CTRL-R - Paste the selected command from history into the command line
-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*[0-9]+\**\s+(.*)/, $1)}++' |
- FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort,ctrl-z:ignore $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
- local ret=$?
- if [ -n "$selected" ]; then
- num=$selected[1]
- if [ -n "$num" ]; then
- zle vi-fetch-history -n $num
- fi
- fi
- zle reset-prompt
- return $ret
-}
-zle -N fzf-history-widget
-bindkey '^R' fzf-history-widget
-
-} always {
- eval $__fzf_key_bindings_options
- 'unset' '__fzf_key_bindings_options'
-}
diff --git a/.fzf/src/LICENSE b/.fzf/src/LICENSE
deleted file mode 100644
index 50aa5d9..0000000
--- a/.fzf/src/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2013-2021 Junegunn Choi
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-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.
diff --git a/.fzf/src/algo/algo.go b/.fzf/src/algo/algo.go
deleted file mode 100644
index 40fb2af..0000000
--- a/.fzf/src/algo/algo.go
+++ /dev/null
@@ -1,884 +0,0 @@
-package algo
-
-/*
-
-Algorithm
----------
-
-FuzzyMatchV1 finds the first "fuzzy" occurrence of the pattern within the given
-text in O(n) time where n is the length of the text. Once the position of the
-last character is located, it traverses backwards to see if there's a shorter
-substring that matches the pattern.
-
- a_____b___abc__ To find "abc"
- *-----*-----*> 1. Forward scan
- <*** 2. Backward scan
-
-The algorithm is simple and fast, but as it only sees the first occurrence,
-it is not guaranteed to find the occurrence with the highest score.
-
- a_____b__c__abc
- *-----*--* ***
-
-FuzzyMatchV2 implements a modified version of Smith-Waterman algorithm to find
-the optimal solution (highest score) according to the scoring criteria. Unlike
-the original algorithm, omission or mismatch of a character in the pattern is
-not allowed.
-
-Performance
------------
-
-The new V2 algorithm is slower than V1 as it examines all occurrences of the
-pattern instead of stopping immediately after finding the first one. The time
-complexity of the algorithm is O(nm) if a match is found and O(n) otherwise
-where n is the length of the item and m is the length of the pattern. Thus, the
-performance overhead may not be noticeable for a query with high selectivity.
-However, if the performance is more important than the quality of the result,
-you can still choose v1 algorithm with --algo=v1.
-
-Scoring criteria
-----------------
-
-- We prefer matches at special positions, such as the start of a word, or
- uppercase character in camelCase words.
-
-- That is, we prefer an occurrence of the pattern with more characters
- matching at special positions, even if the total match length is longer.
- e.g. "fuzzyfinder" vs. "fuzzy-finder" on "ff"
- ````````````
-- Also, if the first character in the pattern appears at one of the special
- positions, the bonus point for the position is multiplied by a constant
- as it is extremely likely that the first character in the typed pattern
- has more significance than the rest.
- e.g. "fo-bar" vs. "foob-r" on "br"
- ``````
-- But since fzf is still a fuzzy finder, not an acronym finder, we should also
- consider the total length of the matched substring. This is why we have the
- gap penalty. The gap penalty increases as the length of the gap (distance
- between the matching characters) increases, so the effect of the bonus is
- eventually cancelled at some point.
- e.g. "fuzzyfinder" vs. "fuzzy-blurry-finder" on "ff"
- ```````````
-- Consequently, it is crucial to find the right balance between the bonus
- and the gap penalty. The parameters were chosen that the bonus is cancelled
- when the gap size increases beyond 8 characters.
-
-- The bonus mechanism can have the undesirable side effect where consecutive
- matches are ranked lower than the ones with gaps.
- e.g. "foobar" vs. "foo-bar" on "foob"
- ```````
-- To correct this anomaly, we also give extra bonus point to each character
- in a consecutive matching chunk.
- e.g. "foobar" vs. "foo-bar" on "foob"
- ``````
-- The amount of consecutive bonus is primarily determined by the bonus of the
- first character in the chunk.
- e.g. "foobar" vs. "out-of-bound" on "oob"
- ````````````
-*/
-
-import (
- "bytes"
- "fmt"
- "strings"
- "unicode"
- "unicode/utf8"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-var DEBUG bool
-
-func indexAt(index int, max int, forward bool) int {
- if forward {
- return index
- }
- return max - index - 1
-}
-
-// Result contains the results of running a match function.
-type Result struct {
- // TODO int32 should suffice
- Start int
- End int
- Score int
-}
-
-const (
- scoreMatch = 16
- scoreGapStart = -3
- scoreGapExtension = -1
-
- // We prefer matches at the beginning of a word, but the bonus should not be
- // too great to prevent the longer acronym matches from always winning over
- // shorter fuzzy matches. The bonus point here was specifically chosen that
- // the bonus is cancelled when the gap between the acronyms grows over
- // 8 characters, which is approximately the average length of the words found
- // in web2 dictionary and my file system.
- bonusBoundary = scoreMatch / 2
-
- // Although bonus point for non-word characters is non-contextual, we need it
- // for computing bonus points for consecutive chunks starting with a non-word
- // character.
- bonusNonWord = scoreMatch / 2
-
- // Edge-triggered bonus for matches in camelCase words.
- // Compared to word-boundary case, they don't accompany single-character gaps
- // (e.g. FooBar vs. foo-bar), so we deduct bonus point accordingly.
- bonusCamel123 = bonusBoundary + scoreGapExtension
-
- // Minimum bonus point given to characters in consecutive chunks.
- // Note that bonus points for consecutive matches shouldn't have needed if we
- // used fixed match score as in the original algorithm.
- bonusConsecutive = -(scoreGapStart + scoreGapExtension)
-
- // The first character in the typed pattern usually has more significance
- // than the rest so it's important that it appears at special positions where
- // bonus points are given, e.g. "to-go" vs. "ongoing" on "og" or on "ogo".
- // The amount of the extra bonus should be limited so that the gap penalty is
- // still respected.
- bonusFirstCharMultiplier = 2
-)
-
-type charClass int
-
-const (
- charNonWord charClass = iota
- charLower
- charUpper
- charLetter
- charNumber
-)
-
-func posArray(withPos bool, len int) *[]int {
- if withPos {
- pos := make([]int, 0, len)
- return &pos
- }
- return nil
-}
-
-func alloc16(offset int, slab *util.Slab, size int) (int, []int16) {
- if slab != nil && cap(slab.I16) > offset+size {
- slice := slab.I16[offset : offset+size]
- return offset + size, slice
- }
- return offset, make([]int16, size)
-}
-
-func alloc32(offset int, slab *util.Slab, size int) (int, []int32) {
- if slab != nil && cap(slab.I32) > offset+size {
- slice := slab.I32[offset : offset+size]
- return offset + size, slice
- }
- return offset, make([]int32, size)
-}
-
-func charClassOfAscii(char rune) charClass {
- if char >= 'a' && char <= 'z' {
- return charLower
- } else if char >= 'A' && char <= 'Z' {
- return charUpper
- } else if char >= '0' && char <= '9' {
- return charNumber
- }
- return charNonWord
-}
-
-func charClassOfNonAscii(char rune) charClass {
- if unicode.IsLower(char) {
- return charLower
- } else if unicode.IsUpper(char) {
- return charUpper
- } else if unicode.IsNumber(char) {
- return charNumber
- } else if unicode.IsLetter(char) {
- return charLetter
- }
- return charNonWord
-}
-
-func charClassOf(char rune) charClass {
- if char <= unicode.MaxASCII {
- return charClassOfAscii(char)
- }
- return charClassOfNonAscii(char)
-}
-
-func bonusFor(prevClass charClass, class charClass) int16 {
- if prevClass == charNonWord && class != charNonWord {
- // Word boundary
- return bonusBoundary
- } else if prevClass == charLower && class == charUpper ||
- prevClass != charNumber && class == charNumber {
- // camelCase letter123
- return bonusCamel123
- } else if class == charNonWord {
- return bonusNonWord
- }
- return 0
-}
-
-func bonusAt(input *util.Chars, idx int) int16 {
- if idx == 0 {
- return bonusBoundary
- }
- return bonusFor(charClassOf(input.Get(idx-1)), charClassOf(input.Get(idx)))
-}
-
-func normalizeRune(r rune) rune {
- if r < 0x00C0 || r > 0x2184 {
- return r
- }
-
- n := normalized[r]
- if n > 0 {
- return n
- }
- return r
-}
-
-// Algo functions make two assumptions
-// 1. "pattern" is given in lowercase if "caseSensitive" is false
-// 2. "pattern" is already normalized if "normalize" is true
-type Algo func(caseSensitive bool, normalize bool, forward bool, input *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int)
-
-func trySkip(input *util.Chars, caseSensitive bool, b byte, from int) int {
- byteArray := input.Bytes()[from:]
- idx := bytes.IndexByte(byteArray, b)
- if idx == 0 {
- // Can't skip any further
- return from
- }
- // We may need to search for the uppercase letter again. We don't have to
- // consider normalization as we can be sure that this is an ASCII string.
- if !caseSensitive && b >= 'a' && b <= 'z' {
- if idx > 0 {
- byteArray = byteArray[:idx]
- }
- uidx := bytes.IndexByte(byteArray, b-32)
- if uidx >= 0 {
- idx = uidx
- }
- }
- if idx < 0 {
- return -1
- }
- return from + idx
-}
-
-func isAscii(runes []rune) bool {
- for _, r := range runes {
- if r >= utf8.RuneSelf {
- return false
- }
- }
- return true
-}
-
-func asciiFuzzyIndex(input *util.Chars, pattern []rune, caseSensitive bool) int {
- // Can't determine
- if !input.IsBytes() {
- return 0
- }
-
- // Not possible
- if !isAscii(pattern) {
- return -1
- }
-
- firstIdx, idx := 0, 0
- for pidx := 0; pidx < len(pattern); pidx++ {
- idx = trySkip(input, caseSensitive, byte(pattern[pidx]), idx)
- if idx < 0 {
- return -1
- }
- if pidx == 0 && idx > 0 {
- // Step back to find the right bonus point
- firstIdx = idx - 1
- }
- idx++
- }
- return firstIdx
-}
-
-func debugV2(T []rune, pattern []rune, F []int32, lastIdx int, H []int16, C []int16) {
- width := lastIdx - int(F[0]) + 1
-
- for i, f := range F {
- I := i * width
- if i == 0 {
- fmt.Print(" ")
- for j := int(f); j <= lastIdx; j++ {
- fmt.Printf(" " + string(T[j]) + " ")
- }
- fmt.Println()
- }
- fmt.Print(string(pattern[i]) + " ")
- for idx := int(F[0]); idx < int(f); idx++ {
- fmt.Print(" 0 ")
- }
- for idx := int(f); idx <= lastIdx; idx++ {
- fmt.Printf("%2d ", H[i*width+idx-int(F[0])])
- }
- fmt.Println()
-
- fmt.Print(" ")
- for idx, p := range C[I : I+width] {
- if idx+int(F[0]) < int(F[i]) {
- p = 0
- }
- if p > 0 {
- fmt.Printf("%2d ", p)
- } else {
- fmt.Print(" ")
- }
- }
- fmt.Println()
- }
-}
-
-func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
- // Assume that pattern is given in lowercase if case-insensitive.
- // First check if there's a match and calculate bonus for each position.
- // If the input string is too long, consider finding the matching chars in
- // this phase as well (non-optimal alignment).
- M := len(pattern)
- if M == 0 {
- return Result{0, 0, 0}, posArray(withPos, M)
- }
- N := input.Length()
-
- // Since O(nm) algorithm can be prohibitively expensive for large input,
- // we fall back to the greedy algorithm.
- if slab != nil && N*M > cap(slab.I16) {
- return FuzzyMatchV1(caseSensitive, normalize, forward, input, pattern, withPos, slab)
- }
-
- // Phase 1. Optimized search for ASCII string
- idx := asciiFuzzyIndex(input, pattern, caseSensitive)
- if idx < 0 {
- return Result{-1, -1, 0}, nil
- }
-
- // Reuse pre-allocated integer slice to avoid unnecessary sweeping of garbages
- offset16 := 0
- offset32 := 0
- offset16, H0 := alloc16(offset16, slab, N)
- offset16, C0 := alloc16(offset16, slab, N)
- // Bonus point for each position
- offset16, B := alloc16(offset16, slab, N)
- // The first occurrence of each character in the pattern
- offset32, F := alloc32(offset32, slab, M)
- // Rune array
- _, T := alloc32(offset32, slab, N)
- input.CopyRunes(T)
-
- // Phase 2. Calculate bonus for each point
- maxScore, maxScorePos := int16(0), 0
- pidx, lastIdx := 0, 0
- pchar0, pchar, prevH0, prevClass, inGap := pattern[0], pattern[0], int16(0), charNonWord, false
- Tsub := T[idx:]
- H0sub, C0sub, Bsub := H0[idx:][:len(Tsub)], C0[idx:][:len(Tsub)], B[idx:][:len(Tsub)]
- for off, char := range Tsub {
- var class charClass
- if char <= unicode.MaxASCII {
- class = charClassOfAscii(char)
- if !caseSensitive && class == charUpper {
- char += 32
- }
- } else {
- class = charClassOfNonAscii(char)
- if !caseSensitive && class == charUpper {
- char = unicode.To(unicode.LowerCase, char)
- }
- if normalize {
- char = normalizeRune(char)
- }
- }
-
- Tsub[off] = char
- bonus := bonusFor(prevClass, class)
- Bsub[off] = bonus
- prevClass = class
-
- if char == pchar {
- if pidx < M {
- F[pidx] = int32(idx + off)
- pidx++
- pchar = pattern[util.Min(pidx, M-1)]
- }
- lastIdx = idx + off
- }
-
- if char == pchar0 {
- score := scoreMatch + bonus*bonusFirstCharMultiplier
- H0sub[off] = score
- C0sub[off] = 1
- if M == 1 && (forward && score > maxScore || !forward && score >= maxScore) {
- maxScore, maxScorePos = score, idx+off
- if forward && bonus == bonusBoundary {
- break
- }
- }
- inGap = false
- } else {
- if inGap {
- H0sub[off] = util.Max16(prevH0+scoreGapExtension, 0)
- } else {
- H0sub[off] = util.Max16(prevH0+scoreGapStart, 0)
- }
- C0sub[off] = 0
- inGap = true
- }
- prevH0 = H0sub[off]
- }
- if pidx != M {
- return Result{-1, -1, 0}, nil
- }
- if M == 1 {
- result := Result{maxScorePos, maxScorePos + 1, int(maxScore)}
- if !withPos {
- return result, nil
- }
- pos := []int{maxScorePos}
- return result, &pos
- }
-
- // Phase 3. Fill in score matrix (H)
- // Unlike the original algorithm, we do not allow omission.
- f0 := int(F[0])
- width := lastIdx - f0 + 1
- offset16, H := alloc16(offset16, slab, width*M)
- copy(H, H0[f0:lastIdx+1])
-
- // Possible length of consecutive chunk at each position.
- _, C := alloc16(offset16, slab, width*M)
- copy(C, C0[f0:lastIdx+1])
-
- Fsub := F[1:]
- Psub := pattern[1:][:len(Fsub)]
- for off, f := range Fsub {
- f := int(f)
- pchar := Psub[off]
- pidx := off + 1
- row := pidx * width
- inGap := false
- Tsub := T[f : lastIdx+1]
- Bsub := B[f:][:len(Tsub)]
- Csub := C[row+f-f0:][:len(Tsub)]
- Cdiag := C[row+f-f0-1-width:][:len(Tsub)]
- Hsub := H[row+f-f0:][:len(Tsub)]
- Hdiag := H[row+f-f0-1-width:][:len(Tsub)]
- Hleft := H[row+f-f0-1:][:len(Tsub)]
- Hleft[0] = 0
- for off, char := range Tsub {
- col := off + f
- var s1, s2, consecutive int16
-
- if inGap {
- s2 = Hleft[off] + scoreGapExtension
- } else {
- s2 = Hleft[off] + scoreGapStart
- }
-
- if pchar == char {
- s1 = Hdiag[off] + scoreMatch
- b := Bsub[off]
- consecutive = Cdiag[off] + 1
- // Break consecutive chunk
- if b == bonusBoundary {
- consecutive = 1
- } else if consecutive > 1 {
- b = util.Max16(b, util.Max16(bonusConsecutive, B[col-int(consecutive)+1]))
- }
- if s1+b < s2 {
- s1 += Bsub[off]
- consecutive = 0
- } else {
- s1 += b
- }
- }
- Csub[off] = consecutive
-
- inGap = s1 < s2
- score := util.Max16(util.Max16(s1, s2), 0)
- if pidx == M-1 && (forward && score > maxScore || !forward && score >= maxScore) {
- maxScore, maxScorePos = score, col
- }
- Hsub[off] = score
- }
- }
-
- if DEBUG {
- debugV2(T, pattern, F, lastIdx, H, C)
- }
-
- // Phase 4. (Optional) Backtrace to find character positions
- pos := posArray(withPos, M)
- j := f0
- if withPos {
- i := M - 1
- j = maxScorePos
- preferMatch := true
- for {
- I := i * width
- j0 := j - f0
- s := H[I+j0]
-
- var s1, s2 int16
- if i > 0 && j >= int(F[i]) {
- s1 = H[I-width+j0-1]
- }
- if j > int(F[i]) {
- s2 = H[I+j0-1]
- }
-
- if s > s1 && (s > s2 || s == s2 && preferMatch) {
- *pos = append(*pos, j)
- if i == 0 {
- break
- }
- i--
- }
- preferMatch = C[I+j0] > 1 || I+width+j0+1 < len(C) && C[I+width+j0+1] > 0
- j--
- }
- }
- // Start offset we return here is only relevant when begin tiebreak is used.
- // However finding the accurate offset requires backtracking, and we don't
- // want to pay extra cost for the option that has lost its importance.
- return Result{j, maxScorePos + 1, int(maxScore)}, pos
-}
-
-// Implement the same sorting criteria as V2
-func calculateScore(caseSensitive bool, normalize bool, text *util.Chars, pattern []rune, sidx int, eidx int, withPos bool) (int, *[]int) {
- pidx, score, inGap, consecutive, firstBonus := 0, 0, false, 0, int16(0)
- pos := posArray(withPos, len(pattern))
- prevClass := charNonWord
- if sidx > 0 {
- prevClass = charClassOf(text.Get(sidx - 1))
- }
- for idx := sidx; idx < eidx; idx++ {
- char := text.Get(idx)
- class := charClassOf(char)
- if !caseSensitive {
- if char >= 'A' && char <= 'Z' {
- char += 32
- } else if char > unicode.MaxASCII {
- char = unicode.To(unicode.LowerCase, char)
- }
- }
- // pattern is already normalized
- if normalize {
- char = normalizeRune(char)
- }
- if char == pattern[pidx] {
- if withPos {
- *pos = append(*pos, idx)
- }
- score += scoreMatch
- bonus := bonusFor(prevClass, class)
- if consecutive == 0 {
- firstBonus = bonus
- } else {
- // Break consecutive chunk
- if bonus == bonusBoundary {
- firstBonus = bonus
- }
- bonus = util.Max16(util.Max16(bonus, firstBonus), bonusConsecutive)
- }
- if pidx == 0 {
- score += int(bonus * bonusFirstCharMultiplier)
- } else {
- score += int(bonus)
- }
- inGap = false
- consecutive++
- pidx++
- } else {
- if inGap {
- score += scoreGapExtension
- } else {
- score += scoreGapStart
- }
- inGap = true
- consecutive = 0
- firstBonus = 0
- }
- prevClass = class
- }
- return score, pos
-}
-
-// FuzzyMatchV1 performs fuzzy-match
-func FuzzyMatchV1(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
- if len(pattern) == 0 {
- return Result{0, 0, 0}, nil
- }
- if asciiFuzzyIndex(text, pattern, caseSensitive) < 0 {
- return Result{-1, -1, 0}, nil
- }
-
- pidx := 0
- sidx := -1
- eidx := -1
-
- lenRunes := text.Length()
- lenPattern := len(pattern)
-
- for index := 0; index < lenRunes; index++ {
- char := text.Get(indexAt(index, lenRunes, forward))
- // This is considerably faster than blindly applying strings.ToLower to the
- // whole string
- if !caseSensitive {
- // Partially inlining `unicode.ToLower`. Ugly, but makes a noticeable
- // difference in CPU cost. (Measured on Go 1.4.1. Also note that the Go
- // compiler as of now does not inline non-leaf functions.)
- if char >= 'A' && char <= 'Z' {
- char += 32
- } else if char > unicode.MaxASCII {
- char = unicode.To(unicode.LowerCase, char)
- }
- }
- if normalize {
- char = normalizeRune(char)
- }
- pchar := pattern[indexAt(pidx, lenPattern, forward)]
- if char == pchar {
- if sidx < 0 {
- sidx = index
- }
- if pidx++; pidx == lenPattern {
- eidx = index + 1
- break
- }
- }
- }
-
- if sidx >= 0 && eidx >= 0 {
- pidx--
- for index := eidx - 1; index >= sidx; index-- {
- tidx := indexAt(index, lenRunes, forward)
- char := text.Get(tidx)
- if !caseSensitive {
- if char >= 'A' && char <= 'Z' {
- char += 32
- } else if char > unicode.MaxASCII {
- char = unicode.To(unicode.LowerCase, char)
- }
- }
-
- pidx_ := indexAt(pidx, lenPattern, forward)
- pchar := pattern[pidx_]
- if char == pchar {
- if pidx--; pidx < 0 {
- sidx = index
- break
- }
- }
- }
-
- if !forward {
- sidx, eidx = lenRunes-eidx, lenRunes-sidx
- }
-
- score, pos := calculateScore(caseSensitive, normalize, text, pattern, sidx, eidx, withPos)
- return Result{sidx, eidx, score}, pos
- }
- return Result{-1, -1, 0}, nil
-}
-
-// ExactMatchNaive is a basic string searching algorithm that handles case
-// sensitivity. Although naive, it still performs better than the combination
-// of strings.ToLower + strings.Index for typical fzf use cases where input
-// strings and patterns are not very long.
-//
-// Since 0.15.0, this function searches for the match with the highest
-// bonus point, instead of stopping immediately after finding the first match.
-// The solution is much cheaper since there is only one possible alignment of
-// the pattern.
-func ExactMatchNaive(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
- if len(pattern) == 0 {
- return Result{0, 0, 0}, nil
- }
-
- lenRunes := text.Length()
- lenPattern := len(pattern)
-
- if lenRunes < lenPattern {
- return Result{-1, -1, 0}, nil
- }
-
- if asciiFuzzyIndex(text, pattern, caseSensitive) < 0 {
- return Result{-1, -1, 0}, nil
- }
-
- // For simplicity, only look at the bonus at the first character position
- pidx := 0
- bestPos, bonus, bestBonus := -1, int16(0), int16(-1)
- for index := 0; index < lenRunes; index++ {
- index_ := indexAt(index, lenRunes, forward)
- char := text.Get(index_)
- if !caseSensitive {
- if char >= 'A' && char <= 'Z' {
- char += 32
- } else if char > unicode.MaxASCII {
- char = unicode.To(unicode.LowerCase, char)
- }
- }
- if normalize {
- char = normalizeRune(char)
- }
- pidx_ := indexAt(pidx, lenPattern, forward)
- pchar := pattern[pidx_]
- if pchar == char {
- if pidx_ == 0 {
- bonus = bonusAt(text, index_)
- }
- pidx++
- if pidx == lenPattern {
- if bonus > bestBonus {
- bestPos, bestBonus = index, bonus
- }
- if bonus == bonusBoundary {
- break
- }
- index -= pidx - 1
- pidx, bonus = 0, 0
- }
- } else {
- index -= pidx
- pidx, bonus = 0, 0
- }
- }
- if bestPos >= 0 {
- var sidx, eidx int
- if forward {
- sidx = bestPos - lenPattern + 1
- eidx = bestPos + 1
- } else {
- sidx = lenRunes - (bestPos + 1)
- eidx = lenRunes - (bestPos - lenPattern + 1)
- }
- score, _ := calculateScore(caseSensitive, normalize, text, pattern, sidx, eidx, false)
- return Result{sidx, eidx, score}, nil
- }
- return Result{-1, -1, 0}, nil
-}
-
-// PrefixMatch performs prefix-match
-func PrefixMatch(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
- if len(pattern) == 0 {
- return Result{0, 0, 0}, nil
- }
-
- trimmedLen := 0
- if !unicode.IsSpace(pattern[0]) {
- trimmedLen = text.LeadingWhitespaces()
- }
-
- if text.Length()-trimmedLen < len(pattern) {
- return Result{-1, -1, 0}, nil
- }
-
- for index, r := range pattern {
- char := text.Get(trimmedLen + index)
- if !caseSensitive {
- char = unicode.ToLower(char)
- }
- if normalize {
- char = normalizeRune(char)
- }
- if char != r {
- return Result{-1, -1, 0}, nil
- }
- }
- lenPattern := len(pattern)
- score, _ := calculateScore(caseSensitive, normalize, text, pattern, trimmedLen, trimmedLen+lenPattern, false)
- return Result{trimmedLen, trimmedLen + lenPattern, score}, nil
-}
-
-// SuffixMatch performs suffix-match
-func SuffixMatch(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
- lenRunes := text.Length()
- trimmedLen := lenRunes
- if len(pattern) == 0 || !unicode.IsSpace(pattern[len(pattern)-1]) {
- trimmedLen -= text.TrailingWhitespaces()
- }
- if len(pattern) == 0 {
- return Result{trimmedLen, trimmedLen, 0}, nil
- }
- diff := trimmedLen - len(pattern)
- if diff < 0 {
- return Result{-1, -1, 0}, nil
- }
-
- for index, r := range pattern {
- char := text.Get(index + diff)
- if !caseSensitive {
- char = unicode.ToLower(char)
- }
- if normalize {
- char = normalizeRune(char)
- }
- if char != r {
- return Result{-1, -1, 0}, nil
- }
- }
- lenPattern := len(pattern)
- sidx := trimmedLen - lenPattern
- eidx := trimmedLen
- score, _ := calculateScore(caseSensitive, normalize, text, pattern, sidx, eidx, false)
- return Result{sidx, eidx, score}, nil
-}
-
-// EqualMatch performs equal-match
-func EqualMatch(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
- lenPattern := len(pattern)
- if lenPattern == 0 {
- return Result{-1, -1, 0}, nil
- }
-
- // Strip leading whitespaces
- trimmedLen := 0
- if !unicode.IsSpace(pattern[0]) {
- trimmedLen = text.LeadingWhitespaces()
- }
-
- // Strip trailing whitespaces
- trimmedEndLen := 0
- if !unicode.IsSpace(pattern[lenPattern-1]) {
- trimmedEndLen = text.TrailingWhitespaces()
- }
-
- if text.Length()-trimmedLen-trimmedEndLen != lenPattern {
- return Result{-1, -1, 0}, nil
- }
- match := true
- if normalize {
- runes := text.ToRunes()
- for idx, pchar := range pattern {
- char := runes[trimmedLen+idx]
- if !caseSensitive {
- char = unicode.To(unicode.LowerCase, char)
- }
- if normalizeRune(pchar) != normalizeRune(char) {
- match = false
- break
- }
- }
- } else {
- runes := text.ToRunes()
- runesStr := string(runes[trimmedLen : len(runes)-trimmedEndLen])
- if !caseSensitive {
- runesStr = strings.ToLower(runesStr)
- }
- match = runesStr == string(pattern)
- }
- if match {
- return Result{trimmedLen, trimmedLen + lenPattern, (scoreMatch+bonusBoundary)*lenPattern +
- (bonusFirstCharMultiplier-1)*bonusBoundary}, nil
- }
- return Result{-1, -1, 0}, nil
-}
diff --git a/.fzf/src/algo/algo_test.go b/.fzf/src/algo/algo_test.go
deleted file mode 100644
index 218ca1f..0000000
--- a/.fzf/src/algo/algo_test.go
+++ /dev/null
@@ -1,197 +0,0 @@
-package algo
-
-import (
- "math"
- "sort"
- "strings"
- "testing"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-func assertMatch(t *testing.T, fun Algo, caseSensitive, forward bool, input, pattern string, sidx int, eidx int, score int) {
- assertMatch2(t, fun, caseSensitive, false, forward, input, pattern, sidx, eidx, score)
-}
-
-func assertMatch2(t *testing.T, fun Algo, caseSensitive, normalize, forward bool, input, pattern string, sidx int, eidx int, score int) {
- if !caseSensitive {
- pattern = strings.ToLower(pattern)
- }
- chars := util.ToChars([]byte(input))
- res, pos := fun(caseSensitive, normalize, forward, &chars, []rune(pattern), true, nil)
- var start, end int
- if pos == nil || len(*pos) == 0 {
- start = res.Start
- end = res.End
- } else {
- sort.Ints(*pos)
- start = (*pos)[0]
- end = (*pos)[len(*pos)-1] + 1
- }
- if start != sidx {
- t.Errorf("Invalid start index: %d (expected: %d, %s / %s)", start, sidx, input, pattern)
- }
- if end != eidx {
- t.Errorf("Invalid end index: %d (expected: %d, %s / %s)", end, eidx, input, pattern)
- }
- if res.Score != score {
- t.Errorf("Invalid score: %d (expected: %d, %s / %s)", res.Score, score, input, pattern)
- }
-}
-
-func TestFuzzyMatch(t *testing.T) {
- for _, fn := range []Algo{FuzzyMatchV1, FuzzyMatchV2} {
- for _, forward := range []bool{true, false} {
- assertMatch(t, fn, false, forward, "fooBarbaz1", "oBZ", 2, 9,
- scoreMatch*3+bonusCamel123+scoreGapStart+scoreGapExtension*3)
- assertMatch(t, fn, false, forward, "foo bar baz", "fbb", 0, 9,
- scoreMatch*3+bonusBoundary*bonusFirstCharMultiplier+
- bonusBoundary*2+2*scoreGapStart+4*scoreGapExtension)
- assertMatch(t, fn, false, forward, "/AutomatorDocument.icns", "rdoc", 9, 13,
- scoreMatch*4+bonusCamel123+bonusConsecutive*2)
- assertMatch(t, fn, false, forward, "/man1/zshcompctl.1", "zshc", 6, 10,
- scoreMatch*4+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary*3)
- assertMatch(t, fn, false, forward, "/.oh-my-zsh/cache", "zshc", 8, 13,
- scoreMatch*4+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary*3+scoreGapStart)
- assertMatch(t, fn, false, forward, "ab0123 456", "12356", 3, 10,
- scoreMatch*5+bonusConsecutive*3+scoreGapStart+scoreGapExtension)
- assertMatch(t, fn, false, forward, "abc123 456", "12356", 3, 10,
- scoreMatch*5+bonusCamel123*bonusFirstCharMultiplier+bonusCamel123*2+bonusConsecutive+scoreGapStart+scoreGapExtension)
- assertMatch(t, fn, false, forward, "foo/bar/baz", "fbb", 0, 9,
- scoreMatch*3+bonusBoundary*bonusFirstCharMultiplier+
- bonusBoundary*2+2*scoreGapStart+4*scoreGapExtension)
- assertMatch(t, fn, false, forward, "fooBarBaz", "fbb", 0, 7,
- scoreMatch*3+bonusBoundary*bonusFirstCharMultiplier+
- bonusCamel123*2+2*scoreGapStart+2*scoreGapExtension)
- assertMatch(t, fn, false, forward, "foo barbaz", "fbb", 0, 8,
- scoreMatch*3+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary+
- scoreGapStart*2+scoreGapExtension*3)
- assertMatch(t, fn, false, forward, "fooBar Baz", "foob", 0, 4,
- scoreMatch*4+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary*3)
- assertMatch(t, fn, false, forward, "xFoo-Bar Baz", "foo-b", 1, 6,
- scoreMatch*5+bonusCamel123*bonusFirstCharMultiplier+bonusCamel123*2+
- bonusNonWord+bonusBoundary)
-
- assertMatch(t, fn, true, forward, "fooBarbaz", "oBz", 2, 9,
- scoreMatch*3+bonusCamel123+scoreGapStart+scoreGapExtension*3)
- assertMatch(t, fn, true, forward, "Foo/Bar/Baz", "FBB", 0, 9,
- scoreMatch*3+bonusBoundary*(bonusFirstCharMultiplier+2)+
- scoreGapStart*2+scoreGapExtension*4)
- assertMatch(t, fn, true, forward, "FooBarBaz", "FBB", 0, 7,
- scoreMatch*3+bonusBoundary*bonusFirstCharMultiplier+bonusCamel123*2+
- scoreGapStart*2+scoreGapExtension*2)
- assertMatch(t, fn, true, forward, "FooBar Baz", "FooB", 0, 4,
- scoreMatch*4+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary*2+
- util.Max(bonusCamel123, bonusBoundary))
-
- // Consecutive bonus updated
- assertMatch(t, fn, true, forward, "foo-bar", "o-ba", 2, 6,
- scoreMatch*4+bonusBoundary*3)
-
- // Non-match
- assertMatch(t, fn, true, forward, "fooBarbaz", "oBZ", -1, -1, 0)
- assertMatch(t, fn, true, forward, "Foo Bar Baz", "fbb", -1, -1, 0)
- assertMatch(t, fn, true, forward, "fooBarbaz", "fooBarbazz", -1, -1, 0)
- }
- }
-}
-
-func TestFuzzyMatchBackward(t *testing.T) {
- assertMatch(t, FuzzyMatchV1, false, true, "foobar fb", "fb", 0, 4,
- scoreMatch*2+bonusBoundary*bonusFirstCharMultiplier+
- scoreGapStart+scoreGapExtension)
- assertMatch(t, FuzzyMatchV1, false, false, "foobar fb", "fb", 7, 9,
- scoreMatch*2+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary)
-}
-
-func TestExactMatchNaive(t *testing.T) {
- for _, dir := range []bool{true, false} {
- assertMatch(t, ExactMatchNaive, true, dir, "fooBarbaz", "oBA", -1, -1, 0)
- assertMatch(t, ExactMatchNaive, true, dir, "fooBarbaz", "fooBarbazz", -1, -1, 0)
-
- assertMatch(t, ExactMatchNaive, false, dir, "fooBarbaz", "oBA", 2, 5,
- scoreMatch*3+bonusCamel123+bonusConsecutive)
- assertMatch(t, ExactMatchNaive, false, dir, "/AutomatorDocument.icns", "rdoc", 9, 13,
- scoreMatch*4+bonusCamel123+bonusConsecutive*2)
- assertMatch(t, ExactMatchNaive, false, dir, "/man1/zshcompctl.1", "zshc", 6, 10,
- scoreMatch*4+bonusBoundary*(bonusFirstCharMultiplier+3))
- assertMatch(t, ExactMatchNaive, false, dir, "/.oh-my-zsh/cache", "zsh/c", 8, 13,
- scoreMatch*5+bonusBoundary*(bonusFirstCharMultiplier+4))
- }
-}
-
-func TestExactMatchNaiveBackward(t *testing.T) {
- assertMatch(t, ExactMatchNaive, false, true, "foobar foob", "oo", 1, 3,
- scoreMatch*2+bonusConsecutive)
- assertMatch(t, ExactMatchNaive, false, false, "foobar foob", "oo", 8, 10,
- scoreMatch*2+bonusConsecutive)
-}
-
-func TestPrefixMatch(t *testing.T) {
- score := (scoreMatch+bonusBoundary)*3 + bonusBoundary*(bonusFirstCharMultiplier-1)
-
- for _, dir := range []bool{true, false} {
- assertMatch(t, PrefixMatch, true, dir, "fooBarbaz", "Foo", -1, -1, 0)
- assertMatch(t, PrefixMatch, false, dir, "fooBarBaz", "baz", -1, -1, 0)
- assertMatch(t, PrefixMatch, false, dir, "fooBarbaz", "Foo", 0, 3, score)
- assertMatch(t, PrefixMatch, false, dir, "foOBarBaZ", "foo", 0, 3, score)
- assertMatch(t, PrefixMatch, false, dir, "f-oBarbaz", "f-o", 0, 3, score)
-
- assertMatch(t, PrefixMatch, false, dir, " fooBar", "foo", 1, 4, score)
- assertMatch(t, PrefixMatch, false, dir, " fooBar", " fo", 0, 3, score)
- assertMatch(t, PrefixMatch, false, dir, " fo", "foo", -1, -1, 0)
- }
-}
-
-func TestSuffixMatch(t *testing.T) {
- for _, dir := range []bool{true, false} {
- assertMatch(t, SuffixMatch, true, dir, "fooBarbaz", "Baz", -1, -1, 0)
- assertMatch(t, SuffixMatch, false, dir, "fooBarbaz", "Foo", -1, -1, 0)
-
- assertMatch(t, SuffixMatch, false, dir, "fooBarbaz", "baz", 6, 9,
- scoreMatch*3+bonusConsecutive*2)
- assertMatch(t, SuffixMatch, false, dir, "fooBarBaZ", "baz", 6, 9,
- (scoreMatch+bonusCamel123)*3+bonusCamel123*(bonusFirstCharMultiplier-1))
-
- // Strip trailing white space from the string
- assertMatch(t, SuffixMatch, false, dir, "fooBarbaz ", "baz", 6, 9,
- scoreMatch*3+bonusConsecutive*2)
- // Only when the pattern doesn't end with a space
- assertMatch(t, SuffixMatch, false, dir, "fooBarbaz ", "baz ", 6, 10,
- scoreMatch*4+bonusConsecutive*2+bonusNonWord)
- }
-}
-
-func TestEmptyPattern(t *testing.T) {
- for _, dir := range []bool{true, false} {
- assertMatch(t, FuzzyMatchV1, true, dir, "foobar", "", 0, 0, 0)
- assertMatch(t, FuzzyMatchV2, true, dir, "foobar", "", 0, 0, 0)
- assertMatch(t, ExactMatchNaive, true, dir, "foobar", "", 0, 0, 0)
- assertMatch(t, PrefixMatch, true, dir, "foobar", "", 0, 0, 0)
- assertMatch(t, SuffixMatch, true, dir, "foobar", "", 6, 6, 0)
- }
-}
-
-func TestNormalize(t *testing.T) {
- caseSensitive := false
- normalize := true
- forward := true
- test := func(input, pattern string, sidx, eidx, score int, funs ...Algo) {
- for _, fun := range funs {
- assertMatch2(t, fun, caseSensitive, normalize, forward,
- input, pattern, sidx, eidx, score)
- }
- }
- test("Só Danço Samba", "So", 0, 2, 56, FuzzyMatchV1, FuzzyMatchV2, PrefixMatch, ExactMatchNaive)
- test("Só Danço Samba", "sodc", 0, 7, 89, FuzzyMatchV1, FuzzyMatchV2)
- test("Danço", "danco", 0, 5, 128, FuzzyMatchV1, FuzzyMatchV2, PrefixMatch, SuffixMatch, ExactMatchNaive, EqualMatch)
-}
-
-func TestLongString(t *testing.T) {
- bytes := make([]byte, math.MaxUint16*2)
- for i := range bytes {
- bytes[i] = 'x'
- }
- bytes[math.MaxUint16] = 'z'
- assertMatch(t, FuzzyMatchV2, true, true, string(bytes), "zx", math.MaxUint16, math.MaxUint16+2, scoreMatch*2+bonusConsecutive)
-}
diff --git a/.fzf/src/algo/normalize.go b/.fzf/src/algo/normalize.go
deleted file mode 100644
index 9324790..0000000
--- a/.fzf/src/algo/normalize.go
+++ /dev/null
@@ -1,492 +0,0 @@
-// Normalization of latin script letters
-// Reference: http://www.unicode.org/Public/UCD/latest/ucd/Index.txt
-
-package algo
-
-var normalized map[rune]rune = map[rune]rune{
- 0x00E1: 'a', // WITH ACUTE, LATIN SMALL LETTER
- 0x0103: 'a', // WITH BREVE, LATIN SMALL LETTER
- 0x01CE: 'a', // WITH CARON, LATIN SMALL LETTER
- 0x00E2: 'a', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x00E4: 'a', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x0227: 'a', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1EA1: 'a', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0201: 'a', // WITH DOUBLE GRAVE, LATIN SMALL LETTER
- 0x00E0: 'a', // WITH GRAVE, LATIN SMALL LETTER
- 0x1EA3: 'a', // WITH HOOK ABOVE, LATIN SMALL LETTER
- 0x0203: 'a', // WITH INVERTED BREVE, LATIN SMALL LETTER
- 0x0101: 'a', // WITH MACRON, LATIN SMALL LETTER
- 0x0105: 'a', // WITH OGONEK, LATIN SMALL LETTER
- 0x1E9A: 'a', // WITH RIGHT HALF RING, LATIN SMALL LETTER
- 0x00E5: 'a', // WITH RING ABOVE, LATIN SMALL LETTER
- 0x1E01: 'a', // WITH RING BELOW, LATIN SMALL LETTER
- 0x00E3: 'a', // WITH TILDE, LATIN SMALL LETTER
- 0x0363: 'a', // , COMBINING LATIN SMALL LETTER
- 0x0250: 'a', // , LATIN SMALL LETTER TURNED
- 0x1E03: 'b', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E05: 'b', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0253: 'b', // WITH HOOK, LATIN SMALL LETTER
- 0x1E07: 'b', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x0180: 'b', // WITH STROKE, LATIN SMALL LETTER
- 0x0183: 'b', // WITH TOPBAR, LATIN SMALL LETTER
- 0x0107: 'c', // WITH ACUTE, LATIN SMALL LETTER
- 0x010D: 'c', // WITH CARON, LATIN SMALL LETTER
- 0x00E7: 'c', // WITH CEDILLA, LATIN SMALL LETTER
- 0x0109: 'c', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x0255: 'c', // WITH CURL, LATIN SMALL LETTER
- 0x010B: 'c', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x0188: 'c', // WITH HOOK, LATIN SMALL LETTER
- 0x023C: 'c', // WITH STROKE, LATIN SMALL LETTER
- 0x0368: 'c', // , COMBINING LATIN SMALL LETTER
- 0x0297: 'c', // , LATIN LETTER STRETCHED
- 0x2184: 'c', // , LATIN SMALL LETTER REVERSED
- 0x010F: 'd', // WITH CARON, LATIN SMALL LETTER
- 0x1E11: 'd', // WITH CEDILLA, LATIN SMALL LETTER
- 0x1E13: 'd', // WITH CIRCUMFLEX BELOW, LATIN SMALL LETTER
- 0x0221: 'd', // WITH CURL, LATIN SMALL LETTER
- 0x1E0B: 'd', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E0D: 'd', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0257: 'd', // WITH HOOK, LATIN SMALL LETTER
- 0x1E0F: 'd', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x0111: 'd', // WITH STROKE, LATIN SMALL LETTER
- 0x0256: 'd', // WITH TAIL, LATIN SMALL LETTER
- 0x018C: 'd', // WITH TOPBAR, LATIN SMALL LETTER
- 0x0369: 'd', // , COMBINING LATIN SMALL LETTER
- 0x00E9: 'e', // WITH ACUTE, LATIN SMALL LETTER
- 0x0115: 'e', // WITH BREVE, LATIN SMALL LETTER
- 0x011B: 'e', // WITH CARON, LATIN SMALL LETTER
- 0x0229: 'e', // WITH CEDILLA, LATIN SMALL LETTER
- 0x1E19: 'e', // WITH CIRCUMFLEX BELOW, LATIN SMALL LETTER
- 0x00EA: 'e', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x00EB: 'e', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x0117: 'e', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1EB9: 'e', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0205: 'e', // WITH DOUBLE GRAVE, LATIN SMALL LETTER
- 0x00E8: 'e', // WITH GRAVE, LATIN SMALL LETTER
- 0x1EBB: 'e', // WITH HOOK ABOVE, LATIN SMALL LETTER
- 0x025D: 'e', // WITH HOOK, LATIN SMALL LETTER REVERSED OPEN
- 0x0207: 'e', // WITH INVERTED BREVE, LATIN SMALL LETTER
- 0x0113: 'e', // WITH MACRON, LATIN SMALL LETTER
- 0x0119: 'e', // WITH OGONEK, LATIN SMALL LETTER
- 0x0247: 'e', // WITH STROKE, LATIN SMALL LETTER
- 0x1E1B: 'e', // WITH TILDE BELOW, LATIN SMALL LETTER
- 0x1EBD: 'e', // WITH TILDE, LATIN SMALL LETTER
- 0x0364: 'e', // , COMBINING LATIN SMALL LETTER
- 0x029A: 'e', // , LATIN SMALL LETTER CLOSED OPEN
- 0x025E: 'e', // , LATIN SMALL LETTER CLOSED REVERSED OPEN
- 0x025B: 'e', // , LATIN SMALL LETTER OPEN
- 0x0258: 'e', // , LATIN SMALL LETTER REVERSED
- 0x025C: 'e', // , LATIN SMALL LETTER REVERSED OPEN
- 0x01DD: 'e', // , LATIN SMALL LETTER TURNED
- 0x1D08: 'e', // , LATIN SMALL LETTER TURNED OPEN
- 0x1E1F: 'f', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x0192: 'f', // WITH HOOK, LATIN SMALL LETTER
- 0x01F5: 'g', // WITH ACUTE, LATIN SMALL LETTER
- 0x011F: 'g', // WITH BREVE, LATIN SMALL LETTER
- 0x01E7: 'g', // WITH CARON, LATIN SMALL LETTER
- 0x0123: 'g', // WITH CEDILLA, LATIN SMALL LETTER
- 0x011D: 'g', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x0121: 'g', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x0260: 'g', // WITH HOOK, LATIN SMALL LETTER
- 0x1E21: 'g', // WITH MACRON, LATIN SMALL LETTER
- 0x01E5: 'g', // WITH STROKE, LATIN SMALL LETTER
- 0x0261: 'g', // , LATIN SMALL LETTER SCRIPT
- 0x1E2B: 'h', // WITH BREVE BELOW, LATIN SMALL LETTER
- 0x021F: 'h', // WITH CARON, LATIN SMALL LETTER
- 0x1E29: 'h', // WITH CEDILLA, LATIN SMALL LETTER
- 0x0125: 'h', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x1E27: 'h', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x1E23: 'h', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E25: 'h', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x02AE: 'h', // WITH FISHHOOK, LATIN SMALL LETTER TURNED
- 0x0266: 'h', // WITH HOOK, LATIN SMALL LETTER
- 0x1E96: 'h', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x0127: 'h', // WITH STROKE, LATIN SMALL LETTER
- 0x036A: 'h', // , COMBINING LATIN SMALL LETTER
- 0x0265: 'h', // , LATIN SMALL LETTER TURNED
- 0x2095: 'h', // , LATIN SUBSCRIPT SMALL LETTER
- 0x00ED: 'i', // WITH ACUTE, LATIN SMALL LETTER
- 0x012D: 'i', // WITH BREVE, LATIN SMALL LETTER
- 0x01D0: 'i', // WITH CARON, LATIN SMALL LETTER
- 0x00EE: 'i', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x00EF: 'i', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x1ECB: 'i', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0209: 'i', // WITH DOUBLE GRAVE, LATIN SMALL LETTER
- 0x00EC: 'i', // WITH GRAVE, LATIN SMALL LETTER
- 0x1EC9: 'i', // WITH HOOK ABOVE, LATIN SMALL LETTER
- 0x020B: 'i', // WITH INVERTED BREVE, LATIN SMALL LETTER
- 0x012B: 'i', // WITH MACRON, LATIN SMALL LETTER
- 0x012F: 'i', // WITH OGONEK, LATIN SMALL LETTER
- 0x0268: 'i', // WITH STROKE, LATIN SMALL LETTER
- 0x1E2D: 'i', // WITH TILDE BELOW, LATIN SMALL LETTER
- 0x0129: 'i', // WITH TILDE, LATIN SMALL LETTER
- 0x0365: 'i', // , COMBINING LATIN SMALL LETTER
- 0x0131: 'i', // , LATIN SMALL LETTER DOTLESS
- 0x1D09: 'i', // , LATIN SMALL LETTER TURNED
- 0x1D62: 'i', // , LATIN SUBSCRIPT SMALL LETTER
- 0x2071: 'i', // , SUPERSCRIPT LATIN SMALL LETTER
- 0x01F0: 'j', // WITH CARON, LATIN SMALL LETTER
- 0x0135: 'j', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x029D: 'j', // WITH CROSSED-TAIL, LATIN SMALL LETTER
- 0x0249: 'j', // WITH STROKE, LATIN SMALL LETTER
- 0x025F: 'j', // WITH STROKE, LATIN SMALL LETTER DOTLESS
- 0x0237: 'j', // , LATIN SMALL LETTER DOTLESS
- 0x1E31: 'k', // WITH ACUTE, LATIN SMALL LETTER
- 0x01E9: 'k', // WITH CARON, LATIN SMALL LETTER
- 0x0137: 'k', // WITH CEDILLA, LATIN SMALL LETTER
- 0x1E33: 'k', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0199: 'k', // WITH HOOK, LATIN SMALL LETTER
- 0x1E35: 'k', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x029E: 'k', // , LATIN SMALL LETTER TURNED
- 0x2096: 'k', // , LATIN SUBSCRIPT SMALL LETTER
- 0x013A: 'l', // WITH ACUTE, LATIN SMALL LETTER
- 0x019A: 'l', // WITH BAR, LATIN SMALL LETTER
- 0x026C: 'l', // WITH BELT, LATIN SMALL LETTER
- 0x013E: 'l', // WITH CARON, LATIN SMALL LETTER
- 0x013C: 'l', // WITH CEDILLA, LATIN SMALL LETTER
- 0x1E3D: 'l', // WITH CIRCUMFLEX BELOW, LATIN SMALL LETTER
- 0x0234: 'l', // WITH CURL, LATIN SMALL LETTER
- 0x1E37: 'l', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x1E3B: 'l', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x0140: 'l', // WITH MIDDLE DOT, LATIN SMALL LETTER
- 0x026B: 'l', // WITH MIDDLE TILDE, LATIN SMALL LETTER
- 0x026D: 'l', // WITH RETROFLEX HOOK, LATIN SMALL LETTER
- 0x0142: 'l', // WITH STROKE, LATIN SMALL LETTER
- 0x2097: 'l', // , LATIN SUBSCRIPT SMALL LETTER
- 0x1E3F: 'm', // WITH ACUTE, LATIN SMALL LETTER
- 0x1E41: 'm', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E43: 'm', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0271: 'm', // WITH HOOK, LATIN SMALL LETTER
- 0x0270: 'm', // WITH LONG LEG, LATIN SMALL LETTER TURNED
- 0x036B: 'm', // , COMBINING LATIN SMALL LETTER
- 0x1D1F: 'm', // , LATIN SMALL LETTER SIDEWAYS TURNED
- 0x026F: 'm', // , LATIN SMALL LETTER TURNED
- 0x2098: 'm', // , LATIN SUBSCRIPT SMALL LETTER
- 0x0144: 'n', // WITH ACUTE, LATIN SMALL LETTER
- 0x0148: 'n', // WITH CARON, LATIN SMALL LETTER
- 0x0146: 'n', // WITH CEDILLA, LATIN SMALL LETTER
- 0x1E4B: 'n', // WITH CIRCUMFLEX BELOW, LATIN SMALL LETTER
- 0x0235: 'n', // WITH CURL, LATIN SMALL LETTER
- 0x1E45: 'n', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E47: 'n', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x01F9: 'n', // WITH GRAVE, LATIN SMALL LETTER
- 0x0272: 'n', // WITH LEFT HOOK, LATIN SMALL LETTER
- 0x1E49: 'n', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x019E: 'n', // WITH LONG RIGHT LEG, LATIN SMALL LETTER
- 0x0273: 'n', // WITH RETROFLEX HOOK, LATIN SMALL LETTER
- 0x00F1: 'n', // WITH TILDE, LATIN SMALL LETTER
- 0x2099: 'n', // , LATIN SUBSCRIPT SMALL LETTER
- 0x00F3: 'o', // WITH ACUTE, LATIN SMALL LETTER
- 0x014F: 'o', // WITH BREVE, LATIN SMALL LETTER
- 0x01D2: 'o', // WITH CARON, LATIN SMALL LETTER
- 0x00F4: 'o', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x00F6: 'o', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x022F: 'o', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1ECD: 'o', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0151: 'o', // WITH DOUBLE ACUTE, LATIN SMALL LETTER
- 0x020D: 'o', // WITH DOUBLE GRAVE, LATIN SMALL LETTER
- 0x00F2: 'o', // WITH GRAVE, LATIN SMALL LETTER
- 0x1ECF: 'o', // WITH HOOK ABOVE, LATIN SMALL LETTER
- 0x01A1: 'o', // WITH HORN, LATIN SMALL LETTER
- 0x020F: 'o', // WITH INVERTED BREVE, LATIN SMALL LETTER
- 0x014D: 'o', // WITH MACRON, LATIN SMALL LETTER
- 0x01EB: 'o', // WITH OGONEK, LATIN SMALL LETTER
- 0x00F8: 'o', // WITH STROKE, LATIN SMALL LETTER
- 0x1D13: 'o', // WITH STROKE, LATIN SMALL LETTER SIDEWAYS
- 0x00F5: 'o', // WITH TILDE, LATIN SMALL LETTER
- 0x0366: 'o', // , COMBINING LATIN SMALL LETTER
- 0x0275: 'o', // , LATIN SMALL LETTER BARRED
- 0x1D17: 'o', // , LATIN SMALL LETTER BOTTOM HALF
- 0x0254: 'o', // , LATIN SMALL LETTER OPEN
- 0x1D11: 'o', // , LATIN SMALL LETTER SIDEWAYS
- 0x1D12: 'o', // , LATIN SMALL LETTER SIDEWAYS OPEN
- 0x1D16: 'o', // , LATIN SMALL LETTER TOP HALF
- 0x1E55: 'p', // WITH ACUTE, LATIN SMALL LETTER
- 0x1E57: 'p', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x01A5: 'p', // WITH HOOK, LATIN SMALL LETTER
- 0x209A: 'p', // , LATIN SUBSCRIPT SMALL LETTER
- 0x024B: 'q', // WITH HOOK TAIL, LATIN SMALL LETTER
- 0x02A0: 'q', // WITH HOOK, LATIN SMALL LETTER
- 0x0155: 'r', // WITH ACUTE, LATIN SMALL LETTER
- 0x0159: 'r', // WITH CARON, LATIN SMALL LETTER
- 0x0157: 'r', // WITH CEDILLA, LATIN SMALL LETTER
- 0x1E59: 'r', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E5B: 'r', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0211: 'r', // WITH DOUBLE GRAVE, LATIN SMALL LETTER
- 0x027E: 'r', // WITH FISHHOOK, LATIN SMALL LETTER
- 0x027F: 'r', // WITH FISHHOOK, LATIN SMALL LETTER REVERSED
- 0x027B: 'r', // WITH HOOK, LATIN SMALL LETTER TURNED
- 0x0213: 'r', // WITH INVERTED BREVE, LATIN SMALL LETTER
- 0x1E5F: 'r', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x027C: 'r', // WITH LONG LEG, LATIN SMALL LETTER
- 0x027A: 'r', // WITH LONG LEG, LATIN SMALL LETTER TURNED
- 0x024D: 'r', // WITH STROKE, LATIN SMALL LETTER
- 0x027D: 'r', // WITH TAIL, LATIN SMALL LETTER
- 0x036C: 'r', // , COMBINING LATIN SMALL LETTER
- 0x0279: 'r', // , LATIN SMALL LETTER TURNED
- 0x1D63: 'r', // , LATIN SUBSCRIPT SMALL LETTER
- 0x015B: 's', // WITH ACUTE, LATIN SMALL LETTER
- 0x0161: 's', // WITH CARON, LATIN SMALL LETTER
- 0x015F: 's', // WITH CEDILLA, LATIN SMALL LETTER
- 0x015D: 's', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x0219: 's', // WITH COMMA BELOW, LATIN SMALL LETTER
- 0x1E61: 's', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E9B: 's', // WITH DOT ABOVE, LATIN SMALL LETTER LONG
- 0x1E63: 's', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0282: 's', // WITH HOOK, LATIN SMALL LETTER
- 0x023F: 's', // WITH SWASH TAIL, LATIN SMALL LETTER
- 0x017F: 's', // , LATIN SMALL LETTER LONG
- 0x00DF: 's', // , LATIN SMALL LETTER SHARP
- 0x209B: 's', // , LATIN SUBSCRIPT SMALL LETTER
- 0x0165: 't', // WITH CARON, LATIN SMALL LETTER
- 0x0163: 't', // WITH CEDILLA, LATIN SMALL LETTER
- 0x1E71: 't', // WITH CIRCUMFLEX BELOW, LATIN SMALL LETTER
- 0x021B: 't', // WITH COMMA BELOW, LATIN SMALL LETTER
- 0x0236: 't', // WITH CURL, LATIN SMALL LETTER
- 0x1E97: 't', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x1E6B: 't', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E6D: 't', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x01AD: 't', // WITH HOOK, LATIN SMALL LETTER
- 0x1E6F: 't', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x01AB: 't', // WITH PALATAL HOOK, LATIN SMALL LETTER
- 0x0288: 't', // WITH RETROFLEX HOOK, LATIN SMALL LETTER
- 0x0167: 't', // WITH STROKE, LATIN SMALL LETTER
- 0x036D: 't', // , COMBINING LATIN SMALL LETTER
- 0x0287: 't', // , LATIN SMALL LETTER TURNED
- 0x209C: 't', // , LATIN SUBSCRIPT SMALL LETTER
- 0x0289: 'u', // BAR, LATIN SMALL LETTER
- 0x00FA: 'u', // WITH ACUTE, LATIN SMALL LETTER
- 0x016D: 'u', // WITH BREVE, LATIN SMALL LETTER
- 0x01D4: 'u', // WITH CARON, LATIN SMALL LETTER
- 0x1E77: 'u', // WITH CIRCUMFLEX BELOW, LATIN SMALL LETTER
- 0x00FB: 'u', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x1E73: 'u', // WITH DIAERESIS BELOW, LATIN SMALL LETTER
- 0x00FC: 'u', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x1EE5: 'u', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0171: 'u', // WITH DOUBLE ACUTE, LATIN SMALL LETTER
- 0x0215: 'u', // WITH DOUBLE GRAVE, LATIN SMALL LETTER
- 0x00F9: 'u', // WITH GRAVE, LATIN SMALL LETTER
- 0x1EE7: 'u', // WITH HOOK ABOVE, LATIN SMALL LETTER
- 0x01B0: 'u', // WITH HORN, LATIN SMALL LETTER
- 0x0217: 'u', // WITH INVERTED BREVE, LATIN SMALL LETTER
- 0x016B: 'u', // WITH MACRON, LATIN SMALL LETTER
- 0x0173: 'u', // WITH OGONEK, LATIN SMALL LETTER
- 0x016F: 'u', // WITH RING ABOVE, LATIN SMALL LETTER
- 0x1E75: 'u', // WITH TILDE BELOW, LATIN SMALL LETTER
- 0x0169: 'u', // WITH TILDE, LATIN SMALL LETTER
- 0x0367: 'u', // , COMBINING LATIN SMALL LETTER
- 0x1D1D: 'u', // , LATIN SMALL LETTER SIDEWAYS
- 0x1D1E: 'u', // , LATIN SMALL LETTER SIDEWAYS DIAERESIZED
- 0x1D64: 'u', // , LATIN SUBSCRIPT SMALL LETTER
- 0x1E7F: 'v', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x028B: 'v', // WITH HOOK, LATIN SMALL LETTER
- 0x1E7D: 'v', // WITH TILDE, LATIN SMALL LETTER
- 0x036E: 'v', // , COMBINING LATIN SMALL LETTER
- 0x028C: 'v', // , LATIN SMALL LETTER TURNED
- 0x1D65: 'v', // , LATIN SUBSCRIPT SMALL LETTER
- 0x1E83: 'w', // WITH ACUTE, LATIN SMALL LETTER
- 0x0175: 'w', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x1E85: 'w', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x1E87: 'w', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E89: 'w', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x1E81: 'w', // WITH GRAVE, LATIN SMALL LETTER
- 0x1E98: 'w', // WITH RING ABOVE, LATIN SMALL LETTER
- 0x028D: 'w', // , LATIN SMALL LETTER TURNED
- 0x1E8D: 'x', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x1E8B: 'x', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x036F: 'x', // , COMBINING LATIN SMALL LETTER
- 0x00FD: 'y', // WITH ACUTE, LATIN SMALL LETTER
- 0x0177: 'y', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x00FF: 'y', // WITH DIAERESIS, LATIN SMALL LETTER
- 0x1E8F: 'y', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1EF5: 'y', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x1EF3: 'y', // WITH GRAVE, LATIN SMALL LETTER
- 0x1EF7: 'y', // WITH HOOK ABOVE, LATIN SMALL LETTER
- 0x01B4: 'y', // WITH HOOK, LATIN SMALL LETTER
- 0x0233: 'y', // WITH MACRON, LATIN SMALL LETTER
- 0x1E99: 'y', // WITH RING ABOVE, LATIN SMALL LETTER
- 0x024F: 'y', // WITH STROKE, LATIN SMALL LETTER
- 0x1EF9: 'y', // WITH TILDE, LATIN SMALL LETTER
- 0x028E: 'y', // , LATIN SMALL LETTER TURNED
- 0x017A: 'z', // WITH ACUTE, LATIN SMALL LETTER
- 0x017E: 'z', // WITH CARON, LATIN SMALL LETTER
- 0x1E91: 'z', // WITH CIRCUMFLEX, LATIN SMALL LETTER
- 0x0291: 'z', // WITH CURL, LATIN SMALL LETTER
- 0x017C: 'z', // WITH DOT ABOVE, LATIN SMALL LETTER
- 0x1E93: 'z', // WITH DOT BELOW, LATIN SMALL LETTER
- 0x0225: 'z', // WITH HOOK, LATIN SMALL LETTER
- 0x1E95: 'z', // WITH LINE BELOW, LATIN SMALL LETTER
- 0x0290: 'z', // WITH RETROFLEX HOOK, LATIN SMALL LETTER
- 0x01B6: 'z', // WITH STROKE, LATIN SMALL LETTER
- 0x0240: 'z', // WITH SWASH TAIL, LATIN SMALL LETTER
- 0x0251: 'a', // , latin small letter script
- 0x00C1: 'A', // WITH ACUTE, LATIN CAPITAL LETTER
- 0x00C2: 'A', // WITH CIRCUMFLEX, LATIN CAPITAL LETTER
- 0x00C4: 'A', // WITH DIAERESIS, LATIN CAPITAL LETTER
- 0x00C0: 'A', // WITH GRAVE, LATIN CAPITAL LETTER
- 0x00C5: 'A', // WITH RING ABOVE, LATIN CAPITAL LETTER
- 0x023A: 'A', // WITH STROKE, LATIN CAPITAL LETTER
- 0x00C3: 'A', // WITH TILDE, LATIN CAPITAL LETTER
- 0x1D00: 'A', // , LATIN LETTER SMALL CAPITAL
- 0x0181: 'B', // WITH HOOK, LATIN CAPITAL LETTER
- 0x0243: 'B', // WITH STROKE, LATIN CAPITAL LETTER
- 0x0299: 'B', // , LATIN LETTER SMALL CAPITAL
- 0x1D03: 'B', // , LATIN LETTER SMALL CAPITAL BARRED
- 0x00C7: 'C', // WITH CEDILLA, LATIN CAPITAL LETTER
- 0x023B: 'C', // WITH STROKE, LATIN CAPITAL LETTER
- 0x1D04: 'C', // , LATIN LETTER SMALL CAPITAL
- 0x018A: 'D', // WITH HOOK, LATIN CAPITAL LETTER
- 0x0189: 'D', // , LATIN CAPITAL LETTER AFRICAN
- 0x1D05: 'D', // , LATIN LETTER SMALL CAPITAL
- 0x00C9: 'E', // WITH ACUTE, LATIN CAPITAL LETTER
- 0x00CA: 'E', // WITH CIRCUMFLEX, LATIN CAPITAL LETTER
- 0x00CB: 'E', // WITH DIAERESIS, LATIN CAPITAL LETTER
- 0x00C8: 'E', // WITH GRAVE, LATIN CAPITAL LETTER
- 0x0246: 'E', // WITH STROKE, LATIN CAPITAL LETTER
- 0x0190: 'E', // , LATIN CAPITAL LETTER OPEN
- 0x018E: 'E', // , LATIN CAPITAL LETTER REVERSED
- 0x1D07: 'E', // , LATIN LETTER SMALL CAPITAL
- 0x0193: 'G', // WITH HOOK, LATIN CAPITAL LETTER
- 0x029B: 'G', // WITH HOOK, LATIN LETTER SMALL CAPITAL
- 0x0262: 'G', // , LATIN LETTER SMALL CAPITAL
- 0x029C: 'H', // , LATIN LETTER SMALL CAPITAL
- 0x00CD: 'I', // WITH ACUTE, LATIN CAPITAL LETTER
- 0x00CE: 'I', // WITH CIRCUMFLEX, LATIN CAPITAL LETTER
- 0x00CF: 'I', // WITH DIAERESIS, LATIN CAPITAL LETTER
- 0x0130: 'I', // WITH DOT ABOVE, LATIN CAPITAL LETTER
- 0x00CC: 'I', // WITH GRAVE, LATIN CAPITAL LETTER
- 0x0197: 'I', // WITH STROKE, LATIN CAPITAL LETTER
- 0x026A: 'I', // , LATIN LETTER SMALL CAPITAL
- 0x0248: 'J', // WITH STROKE, LATIN CAPITAL LETTER
- 0x1D0A: 'J', // , LATIN LETTER SMALL CAPITAL
- 0x1D0B: 'K', // , LATIN LETTER SMALL CAPITAL
- 0x023D: 'L', // WITH BAR, LATIN CAPITAL LETTER
- 0x1D0C: 'L', // WITH STROKE, LATIN LETTER SMALL CAPITAL
- 0x029F: 'L', // , LATIN LETTER SMALL CAPITAL
- 0x019C: 'M', // , LATIN CAPITAL LETTER TURNED
- 0x1D0D: 'M', // , LATIN LETTER SMALL CAPITAL
- 0x019D: 'N', // WITH LEFT HOOK, LATIN CAPITAL LETTER
- 0x0220: 'N', // WITH LONG RIGHT LEG, LATIN CAPITAL LETTER
- 0x00D1: 'N', // WITH TILDE, LATIN CAPITAL LETTER
- 0x0274: 'N', // , LATIN LETTER SMALL CAPITAL
- 0x1D0E: 'N', // , LATIN LETTER SMALL CAPITAL REVERSED
- 0x00D3: 'O', // WITH ACUTE, LATIN CAPITAL LETTER
- 0x00D4: 'O', // WITH CIRCUMFLEX, LATIN CAPITAL LETTER
- 0x00D6: 'O', // WITH DIAERESIS, LATIN CAPITAL LETTER
- 0x00D2: 'O', // WITH GRAVE, LATIN CAPITAL LETTER
- 0x019F: 'O', // WITH MIDDLE TILDE, LATIN CAPITAL LETTER
- 0x00D8: 'O', // WITH STROKE, LATIN CAPITAL LETTER
- 0x00D5: 'O', // WITH TILDE, LATIN CAPITAL LETTER
- 0x0186: 'O', // , LATIN CAPITAL LETTER OPEN
- 0x1D0F: 'O', // , LATIN LETTER SMALL CAPITAL
- 0x1D10: 'O', // , LATIN LETTER SMALL CAPITAL OPEN
- 0x1D18: 'P', // , LATIN LETTER SMALL CAPITAL
- 0x024A: 'Q', // WITH HOOK TAIL, LATIN CAPITAL LETTER SMALL
- 0x024C: 'R', // WITH STROKE, LATIN CAPITAL LETTER
- 0x0280: 'R', // , LATIN LETTER SMALL CAPITAL
- 0x0281: 'R', // , LATIN LETTER SMALL CAPITAL INVERTED
- 0x1D19: 'R', // , LATIN LETTER SMALL CAPITAL REVERSED
- 0x1D1A: 'R', // , LATIN LETTER SMALL CAPITAL TURNED
- 0x023E: 'T', // WITH DIAGONAL STROKE, LATIN CAPITAL LETTER
- 0x01AE: 'T', // WITH RETROFLEX HOOK, LATIN CAPITAL LETTER
- 0x1D1B: 'T', // , LATIN LETTER SMALL CAPITAL
- 0x0244: 'U', // BAR, LATIN CAPITAL LETTER
- 0x00DA: 'U', // WITH ACUTE, LATIN CAPITAL LETTER
- 0x00DB: 'U', // WITH CIRCUMFLEX, LATIN CAPITAL LETTER
- 0x00DC: 'U', // WITH DIAERESIS, LATIN CAPITAL LETTER
- 0x00D9: 'U', // WITH GRAVE, LATIN CAPITAL LETTER
- 0x1D1C: 'U', // , LATIN LETTER SMALL CAPITAL
- 0x01B2: 'V', // WITH HOOK, LATIN CAPITAL LETTER
- 0x0245: 'V', // , LATIN CAPITAL LETTER TURNED
- 0x1D20: 'V', // , LATIN LETTER SMALL CAPITAL
- 0x1D21: 'W', // , LATIN LETTER SMALL CAPITAL
- 0x00DD: 'Y', // WITH ACUTE, LATIN CAPITAL LETTER
- 0x0178: 'Y', // WITH DIAERESIS, LATIN CAPITAL LETTER
- 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
-func NormalizeRunes(runes []rune) []rune {
- ret := make([]rune, len(runes))
- copy(ret, runes)
- for idx, r := range runes {
- if r < 0x00C0 || r > 0x2184 {
- continue
- }
- n := normalized[r]
- if n > 0 {
- ret[idx] = normalized[r]
- }
- }
- return ret
-}
diff --git a/.fzf/src/ansi.go b/.fzf/src/ansi.go
deleted file mode 100644
index 698bf89..0000000
--- a/.fzf/src/ansi.go
+++ /dev/null
@@ -1,409 +0,0 @@
-package fzf
-
-import (
- "strconv"
- "strings"
- "unicode/utf8"
-
- "github.com/junegunn/fzf/src/tui"
-)
-
-type ansiOffset struct {
- offset [2]int32
- color ansiState
-}
-
-type ansiState struct {
- fg tui.Color
- bg tui.Color
- attr tui.Attr
- lbg tui.Color
-}
-
-func (s *ansiState) colored() bool {
- return s.fg != -1 || s.bg != -1 || s.attr > 0 || s.lbg >= 0
-}
-
-func (s *ansiState) equals(t *ansiState) bool {
- if t == nil {
- return !s.colored()
- }
- return s.fg == t.fg && s.bg == t.bg && s.attr == t.attr && s.lbg == t.lbg
-}
-
-func (s *ansiState) ToString() string {
- if !s.colored() {
- return ""
- }
-
- ret := ""
- if s.attr&tui.Bold > 0 {
- ret += "1;"
- }
- if s.attr&tui.Dim > 0 {
- ret += "2;"
- }
- if s.attr&tui.Italic > 0 {
- ret += "3;"
- }
- if s.attr&tui.Underline > 0 {
- ret += "4;"
- }
- if s.attr&tui.Blink > 0 {
- ret += "5;"
- }
- if s.attr&tui.Reverse > 0 {
- ret += "7;"
- }
- ret += toAnsiString(s.fg, 30) + toAnsiString(s.bg, 40)
-
- return "\x1b[" + strings.TrimSuffix(ret, ";") + "m"
-}
-
-func toAnsiString(color tui.Color, offset int) string {
- col := int(color)
- ret := ""
- if col == -1 {
- ret += strconv.Itoa(offset + 9)
- } else if col < 8 {
- ret += strconv.Itoa(offset + col)
- } else if col < 16 {
- ret += strconv.Itoa(offset - 30 + 90 + col - 8)
- } else if col < 256 {
- ret += strconv.Itoa(offset+8) + ";5;" + strconv.Itoa(col)
- } else if col >= (1 << 24) {
- r := strconv.Itoa((col >> 16) & 0xff)
- g := strconv.Itoa((col >> 8) & 0xff)
- b := strconv.Itoa(col & 0xff)
- ret += strconv.Itoa(offset+8) + ";2;" + r + ";" + g + ";" + b
- }
- return ret + ";"
-}
-
-func isPrint(c uint8) bool {
- return '\x20' <= c && c <= '\x7e'
-}
-
-func matchOperatingSystemCommand(s string) int {
- // `\x1b][0-9];[[:print:]]+(?:\x1b\\\\|\x07)`
- // ^ match starting here
- //
- i := 5 // prefix matched in nextAnsiEscapeSequence()
- for ; i < len(s) && isPrint(s[i]); i++ {
- }
- if i < len(s) {
- if s[i] == '\x07' {
- return i + 1
- }
- if s[i] == '\x1b' && i < len(s)-1 && s[i+1] == '\\' {
- return i + 2
- }
- }
- return -1
-}
-
-func matchControlSequence(s string) int {
- // `\x1b[\\[()][0-9;?]*[a-zA-Z@]`
- // ^ match starting here
- //
- i := 2 // prefix matched in nextAnsiEscapeSequence()
- for ; i < len(s) && (isNumeric(s[i]) || s[i] == ';' || s[i] == '?'); i++ {
- }
- if i < len(s) {
- c := s[i]
- if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '@' {
- return i + 1
- }
- }
- return -1
-}
-
-func isCtrlSeqStart(c uint8) bool {
- return c == '\\' || c == '[' || c == '(' || c == ')'
-}
-
-// nextAnsiEscapeSequence returns the ANSI escape sequence and is equivalent to
-// calling FindStringIndex() on the below regex (which was originally used):
-//
-// "(?:\x1b[\\[()][0-9;?]*[a-zA-Z@]|\x1b][0-9];[[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08)"
-//
-func nextAnsiEscapeSequence(s string) (int, int) {
- // fast check for ANSI escape sequences
- i := 0
- for ; i < len(s); i++ {
- switch s[i] {
- case '\x0e', '\x0f', '\x1b', '\x08':
- // We ignore the fact that '\x08' cannot be the first char
- // in the string and be an escape sequence for the sake of
- // speed and simplicity.
- goto Loop
- }
- }
- return -1, -1
-
-Loop:
- for ; i < len(s); i++ {
- switch s[i] {
- case '\x08':
- // backtrack to match: `.\x08`
- if i > 0 && s[i-1] != '\n' {
- if s[i-1] < utf8.RuneSelf {
- return i - 1, i + 1
- }
- _, n := utf8.DecodeLastRuneInString(s[:i])
- return i - n, i + 1
- }
- case '\x1b':
- // match: `\x1b[\\[()][0-9;?]*[a-zA-Z@]`
- if i+2 < len(s) && isCtrlSeqStart(s[i+1]) {
- if j := matchControlSequence(s[i:]); j != -1 {
- return i, i + j
- }
- }
-
- // match: `\x1b][0-9];[[:print:]]+(?:\x1b\\\\|\x07)`
- if i+5 < len(s) && s[i+1] == ']' && isNumeric(s[i+2]) &&
- s[i+3] == ';' && isPrint(s[i+4]) {
-
- if j := matchOperatingSystemCommand(s[i:]); j != -1 {
- return i, i + j
- }
- }
-
- // match: `\x1b.`
- if i+1 < len(s) && s[i+1] != '\n' {
- if s[i+1] < utf8.RuneSelf {
- return i, i + 2
- }
- _, n := utf8.DecodeRuneInString(s[i+1:])
- return i, i + n + 1
- }
- case '\x0e', '\x0f':
- // match: `[\x0e\x0f]`
- return i, i + 1
- }
- }
- return -1, -1
-}
-
-func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {
- // We append to a stack allocated variable that we'll
- // later copy and return, to save on allocations.
- offsets := make([]ansiOffset, 0, 32)
-
- if state != nil {
- offsets = append(offsets, ansiOffset{[2]int32{0, 0}, *state})
- }
-
- var (
- pstate *ansiState // lazily allocated
- output strings.Builder
- prevIdx int
- runeCount int
- )
- for idx := 0; idx < len(str); {
- // Make sure that we found an ANSI code
- start, end := nextAnsiEscapeSequence(str[idx:])
- if start == -1 {
- break
- }
- start += idx
- idx += end
-
- // Check if we should continue
- prev := str[prevIdx:start]
- if proc != nil && !proc(prev, state) {
- return "", nil, nil
- }
- prevIdx = idx
-
- if len(prev) != 0 {
- runeCount += utf8.RuneCountInString(prev)
- // Grow the buffer size to the maximum possible length (string length
- // containing ansi codes) to avoid repetitive allocation
- if output.Cap() == 0 {
- output.Grow(len(str))
- }
- output.WriteString(prev)
- }
-
- newState := interpretCode(str[start:idx], state)
- if !newState.equals(state) {
- if state != nil {
- // Update last offset
- (&offsets[len(offsets)-1]).offset[1] = int32(runeCount)
- }
-
- if newState.colored() {
- // Append new offset
- if pstate == nil {
- pstate = &ansiState{}
- }
- *pstate = newState
- state = pstate
- offsets = append(offsets, ansiOffset{
- [2]int32{int32(runeCount), int32(runeCount)},
- newState,
- })
- } else {
- // Discard state
- state = nil
- }
- }
- }
-
- var rest string
- var trimmed string
- if prevIdx == 0 {
- // No ANSI code found
- rest = str
- trimmed = str
- } else {
- rest = str[prevIdx:]
- output.WriteString(rest)
- trimmed = output.String()
- }
- if proc != nil {
- proc(rest, state)
- }
- if len(offsets) > 0 {
- if len(rest) > 0 && state != nil {
- // Update last offset
- runeCount += utf8.RuneCountInString(rest)
- (&offsets[len(offsets)-1]).offset[1] = int32(runeCount)
- }
- // Return a copy of the offsets slice
- a := make([]ansiOffset, len(offsets))
- copy(a, offsets)
- return trimmed, &a, state
- }
- return trimmed, nil, state
-}
-
-func parseAnsiCode(s string) (int, string) {
- var remaining string
- if i := strings.IndexByte(s, ';'); i >= 0 {
- remaining = s[i+1:]
- s = s[:i]
- }
-
- if len(s) > 0 {
- // Inlined version of strconv.Atoi() that only handles positive
- // integers and does not allocate on error.
- code := 0
- for _, ch := range []byte(s) {
- ch -= '0'
- if ch > 9 {
- return -1, remaining
- }
- code = code*10 + int(ch)
- }
- return code, remaining
- }
-
- return -1, remaining
-}
-
-func interpretCode(ansiCode string, prevState *ansiState) ansiState {
- var state ansiState
- if prevState == nil {
- state = ansiState{-1, -1, 0, -1}
- } else {
- state = ansiState{prevState.fg, prevState.bg, prevState.attr, prevState.lbg}
- }
- if ansiCode[0] != '\x1b' || ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
- if prevState != nil && strings.HasSuffix(ansiCode, "0K") {
- state.lbg = prevState.bg
- }
- return state
- }
-
- if len(ansiCode) <= 3 {
- state.fg = -1
- state.bg = -1
- state.attr = 0
- return state
- }
- ansiCode = ansiCode[2 : len(ansiCode)-1]
-
- state256 := 0
- ptr := &state.fg
-
- for len(ansiCode) != 0 {
- var num int
- if num, ansiCode = parseAnsiCode(ansiCode); num != -1 {
- switch state256 {
- case 0:
- switch num {
- case 38:
- ptr = &state.fg
- state256++
- case 48:
- ptr = &state.bg
- state256++
- case 39:
- state.fg = -1
- case 49:
- state.bg = -1
- case 1:
- state.attr = state.attr | tui.Bold
- case 2:
- state.attr = state.attr | tui.Dim
- case 3:
- state.attr = state.attr | tui.Italic
- case 4:
- state.attr = state.attr | tui.Underline
- case 5:
- state.attr = state.attr | tui.Blink
- case 7:
- state.attr = state.attr | tui.Reverse
- case 23: // tput rmso
- state.attr = state.attr &^ tui.Italic
- case 24: // tput rmul
- state.attr = state.attr &^ tui.Underline
- case 0:
- state.fg = -1
- state.bg = -1
- state.attr = 0
- state256 = 0
- default:
- if num >= 30 && num <= 37 {
- state.fg = tui.Color(num - 30)
- } else if num >= 40 && num <= 47 {
- state.bg = tui.Color(num - 40)
- } else if num >= 90 && num <= 97 {
- state.fg = tui.Color(num - 90 + 8)
- } else if num >= 100 && num <= 107 {
- state.bg = tui.Color(num - 100 + 8)
- }
- }
- case 1:
- switch num {
- case 2:
- state256 = 10 // MAGIC
- case 5:
- state256++
- default:
- state256 = 0
- }
- case 2:
- *ptr = tui.Color(num)
- state256 = 0
- case 10:
- *ptr = tui.Color(1<<24) | tui.Color(num<<16)
- state256++
- case 11:
- *ptr = *ptr | tui.Color(num<<8)
- state256++
- case 12:
- *ptr = *ptr | tui.Color(num)
- state256 = 0
- }
- }
- }
-
- if state256 > 0 {
- *ptr = -1
- }
- return state
-}
diff --git a/.fzf/src/ansi_test.go b/.fzf/src/ansi_test.go
deleted file mode 100644
index cdccc10..0000000
--- a/.fzf/src/ansi_test.go
+++ /dev/null
@@ -1,427 +0,0 @@
-package fzf
-
-import (
- "math/rand"
- "regexp"
- "strings"
- "testing"
- "unicode/utf8"
-
- "github.com/junegunn/fzf/src/tui"
-)
-
-// The following regular expression will include not all but most of the
-// frequently used ANSI sequences. This regex is used as a reference for
-// testing nextAnsiEscapeSequence().
-//
-// References:
-// - https://github.com/gnachman/iTerm2
-// - https://web.archive.org/web/20090204053813/http://ascii-table.com/ansi-escape-sequences.php
-// (archived from http://ascii-table.com/ansi-escape-sequences.php)
-// - https://web.archive.org/web/20090227051140/http://ascii-table.com/ansi-escape-sequences-vt-100.php
-// (archived from http://ascii-table.com/ansi-escape-sequences-vt-100.php)
-// - http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
-// - https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
-var ansiRegexReference = regexp.MustCompile("(?:\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b][0-9];[[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08)")
-
-func testParserReference(t testing.TB, str string) {
- t.Helper()
-
- toSlice := func(start, end int) []int {
- if start == -1 {
- return nil
- }
- return []int{start, end}
- }
-
- s := str
- for i := 0; ; i++ {
- got := toSlice(nextAnsiEscapeSequence(s))
- exp := ansiRegexReference.FindStringIndex(s)
-
- equal := len(got) == len(exp)
- if equal {
- for i := 0; i < len(got); i++ {
- if got[i] != exp[i] {
- equal = false
- break
- }
- }
- }
- if !equal {
- var exps, gots []rune
- if len(got) == 2 {
- gots = []rune(s[got[0]:got[1]])
- }
- if len(exp) == 2 {
- exps = []rune(s[exp[0]:exp[1]])
- }
- t.Errorf("%d: %q: got: %v (%q) want: %v (%q)", i, s, got, gots, exp, exps)
- return
- }
- if len(exp) == 0 {
- return
- }
- s = s[exp[1]:]
- }
-}
-
-func TestNextAnsiEscapeSequence(t *testing.T) {
- testStrs := []string{
- "\x1b[0mhello world",
- "\x1b[1mhello world",
- "椙\x1b[1m椙",
- "椙\x1b[1椙m椙",
- "\x1b[1mhello \x1b[mw\x1b7o\x1b8r\x1b(Bl\x1b[2@d",
- "\x1b[1mhello \x1b[Kworld",
- "hello \x1b[34;45;1mworld",
- "hello \x1b[34;45;1mwor\x1b[34;45;1mld",
- "hello \x1b[34;45;1mwor\x1b[0mld",
- "hello \x1b[34;48;5;233;1mwo\x1b[38;5;161mr\x1b[0ml\x1b[38;5;161md",
- "hello \x1b[38;5;38;48;5;48;1mwor\x1b[38;5;48;48;5;38ml\x1b[0md",
- "hello \x1b[32;1mworld",
- "hello world",
- "hello \x1b[0;38;5;200;48;5;100mworld",
- "\x1b椙",
- "椙\x08",
- "\n\x08",
- "X\x08",
- "",
- "\x1b]4;3;rgb:aa/bb/cc\x07 ",
- "\x1b]4;3;rgb:aa/bb/cc\x1b\\ ",
- ansiBenchmarkString,
- }
-
- for _, s := range testStrs {
- testParserReference(t, s)
- }
-}
-
-func TestNextAnsiEscapeSequence_Fuzz_Modified(t *testing.T) {
- t.Parallel()
- if testing.Short() {
- t.Skip("short test")
- }
-
- testStrs := []string{
- "\x1b[0mhello world",
- "\x1b[1mhello world",
- "椙\x1b[1m椙",
- "椙\x1b[1椙m椙",
- "\x1b[1mhello \x1b[mw\x1b7o\x1b8r\x1b(Bl\x1b[2@d",
- "\x1b[1mhello \x1b[Kworld",
- "hello \x1b[34;45;1mworld",
- "hello \x1b[34;45;1mwor\x1b[34;45;1mld",
- "hello \x1b[34;45;1mwor\x1b[0mld",
- "hello \x1b[34;48;5;233;1mwo\x1b[38;5;161mr\x1b[0ml\x1b[38;5;161md",
- "hello \x1b[38;5;38;48;5;48;1mwor\x1b[38;5;48;48;5;38ml\x1b[0md",
- "hello \x1b[32;1mworld",
- "hello world",
- "hello \x1b[0;38;5;200;48;5;100mworld",
- ansiBenchmarkString,
- }
-
- replacementBytes := [...]rune{'\x0e', '\x0f', '\x1b', '\x08'}
-
- modifyString := func(s string, rr *rand.Rand) string {
- n := rr.Intn(len(s))
- b := []rune(s)
- for ; n >= 0 && len(b) != 0; n-- {
- i := rr.Intn(len(b))
- switch x := rr.Intn(4); x {
- case 0:
- b = append(b[:i], b[i+1:]...)
- case 1:
- j := rr.Intn(len(replacementBytes) - 1)
- b[i] = replacementBytes[j]
- case 2:
- x := rune(rr.Intn(utf8.MaxRune))
- for !utf8.ValidRune(x) {
- x = rune(rr.Intn(utf8.MaxRune))
- }
- b[i] = x
- case 3:
- b[i] = rune(rr.Intn(utf8.MaxRune)) // potentially invalid
- default:
- t.Fatalf("unsupported value: %d", x)
- }
- }
- return string(b)
- }
-
- rr := rand.New(rand.NewSource(1))
- for _, s := range testStrs {
- for i := 1_000; i >= 0; i-- {
- testParserReference(t, modifyString(s, rr))
- }
- }
-}
-
-func TestNextAnsiEscapeSequence_Fuzz_Random(t *testing.T) {
- t.Parallel()
-
- if testing.Short() {
- t.Skip("short test")
- }
-
- randomString := func(rr *rand.Rand) string {
- numChars := rand.Intn(50)
- codePoints := make([]rune, numChars)
- for i := 0; i < len(codePoints); i++ {
- var r rune
- for n := 0; n < 1000; n++ {
- r = rune(rr.Intn(utf8.MaxRune))
- // Allow 10% of runes to be invalid
- if utf8.ValidRune(r) || rr.Float64() < 0.10 {
- break
- }
- }
- codePoints[i] = r
- }
- return string(codePoints)
- }
-
- rr := rand.New(rand.NewSource(1))
- for i := 0; i < 100_000; i++ {
- testParserReference(t, randomString(rr))
- }
-}
-
-func TestExtractColor(t *testing.T) {
- assert := func(offset ansiOffset, b int32, e int32, fg tui.Color, bg tui.Color, bold bool) {
- var attr tui.Attr
- if bold {
- attr = tui.Bold
- }
- if offset.offset[0] != b || offset.offset[1] != e ||
- offset.color.fg != fg || offset.color.bg != bg || offset.color.attr != attr {
- t.Error(offset, b, e, fg, bg, attr)
- }
- }
-
- src := "hello world"
- var state *ansiState
- clean := "\x1b[0m"
- check := func(assertion func(ansiOffsets *[]ansiOffset, state *ansiState)) {
- output, ansiOffsets, newState := extractColor(src, state, nil)
- state = newState
- if output != "hello world" {
- t.Errorf("Invalid output: %s %v", output, []rune(output))
- }
- t.Log(src, ansiOffsets, clean)
- assertion(ansiOffsets, state)
- }
-
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if offsets != nil {
- t.Fail()
- }
- })
-
- state = nil
- src = "\x1b[0mhello world"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if offsets != nil {
- t.Fail()
- }
- })
-
- state = nil
- src = "\x1b[1mhello world"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 1 {
- t.Fail()
- }
- assert((*offsets)[0], 0, 11, -1, -1, true)
- })
-
- state = nil
- src = "\x1b[1mhello \x1b[mw\x1b7o\x1b8r\x1b(Bl\x1b[2@d"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 1 {
- t.Fail()
- }
- assert((*offsets)[0], 0, 6, -1, -1, true)
- })
-
- state = nil
- src = "\x1b[1mhello \x1b[Kworld"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 1 {
- t.Fail()
- }
- assert((*offsets)[0], 0, 11, -1, -1, true)
- })
-
- state = nil
- src = "hello \x1b[34;45;1mworld"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 1 {
- t.Fail()
- }
- assert((*offsets)[0], 6, 11, 4, 5, true)
- })
-
- state = nil
- src = "hello \x1b[34;45;1mwor\x1b[34;45;1mld"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 1 {
- t.Fail()
- }
- assert((*offsets)[0], 6, 11, 4, 5, true)
- })
-
- state = nil
- src = "hello \x1b[34;45;1mwor\x1b[0mld"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 1 {
- t.Fail()
- }
- assert((*offsets)[0], 6, 9, 4, 5, true)
- })
-
- state = nil
- src = "hello \x1b[34;48;5;233;1mwo\x1b[38;5;161mr\x1b[0ml\x1b[38;5;161md"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 3 {
- t.Fail()
- }
- assert((*offsets)[0], 6, 8, 4, 233, true)
- assert((*offsets)[1], 8, 9, 161, 233, true)
- assert((*offsets)[2], 10, 11, 161, -1, false)
- })
-
- // {38,48};5;{38,48}
- state = nil
- src = "hello \x1b[38;5;38;48;5;48;1mwor\x1b[38;5;48;48;5;38ml\x1b[0md"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 2 {
- t.Fail()
- }
- assert((*offsets)[0], 6, 9, 38, 48, true)
- assert((*offsets)[1], 9, 10, 48, 38, true)
- })
-
- src = "hello \x1b[32;1mworld"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 1 {
- t.Fail()
- }
- if state.fg != 2 || state.bg != -1 || state.attr == 0 {
- t.Fail()
- }
- assert((*offsets)[0], 6, 11, 2, -1, true)
- })
-
- src = "hello world"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 1 {
- t.Fail()
- }
- if state.fg != 2 || state.bg != -1 || state.attr == 0 {
- t.Fail()
- }
- assert((*offsets)[0], 0, 11, 2, -1, true)
- })
-
- src = "hello \x1b[0;38;5;200;48;5;100mworld"
- check(func(offsets *[]ansiOffset, state *ansiState) {
- if len(*offsets) != 2 {
- t.Fail()
- }
- if state.fg != 200 || state.bg != 100 || state.attr > 0 {
- t.Fail()
- }
- assert((*offsets)[0], 0, 6, 2, -1, true)
- assert((*offsets)[1], 6, 11, 200, 100, false)
- })
-}
-
-func TestAnsiCodeStringConversion(t *testing.T) {
- assert := func(code string, prevState *ansiState, expected string) {
- state := interpretCode(code, prevState)
- if expected != state.ToString() {
- t.Errorf("expected: %s, actual: %s",
- strings.Replace(expected, "\x1b[", "\\x1b[", -1),
- strings.Replace(state.ToString(), "\x1b[", "\\x1b[", -1))
- }
- }
- assert("\x1b[m", nil, "")
- assert("\x1b[m", &ansiState{attr: tui.Blink, lbg: -1}, "")
-
- assert("\x1b[31m", nil, "\x1b[31;49m")
- assert("\x1b[41m", nil, "\x1b[39;41m")
-
- assert("\x1b[92m", nil, "\x1b[92;49m")
- assert("\x1b[102m", nil, "\x1b[39;102m")
-
- assert("\x1b[31m", &ansiState{fg: 4, bg: 4, lbg: -1}, "\x1b[31;44m")
- assert("\x1b[1;2;31m", &ansiState{fg: 2, bg: -1, attr: tui.Reverse, lbg: -1}, "\x1b[1;2;7;31;49m")
- assert("\x1b[38;5;100;48;5;200m", nil, "\x1b[38;5;100;48;5;200m")
- assert("\x1b[48;5;100;38;5;200m", nil, "\x1b[38;5;200;48;5;100m")
- assert("\x1b[48;5;100;38;2;10;20;30;1m", nil, "\x1b[1;38;2;10;20;30;48;5;100m")
- assert("\x1b[48;5;100;38;2;10;20;30;7m",
- &ansiState{attr: tui.Dim | tui.Italic, fg: 1, bg: 1},
- "\x1b[2;3;7;38;2;10;20;30;48;5;100m")
-}
-
-func TestParseAnsiCode(t *testing.T) {
- tests := []struct {
- In, Exp string
- N int
- }{
- {"123", "", 123},
- {"1a", "", -1},
- {"1a;12", "12", -1},
- {"12;a", "a", 12},
- {"-2", "", -1},
- }
- for _, x := range tests {
- n, s := parseAnsiCode(x.In)
- if n != x.N || s != x.Exp {
- t.Fatalf("%q: got: (%d %q) want: (%d %q)", x.In, n, s, x.N, x.Exp)
- }
- }
-}
-
-// kernel/bpf/preload/iterators/README
-const ansiBenchmarkString = "\x1b[38;5;81m\x1b[01;31m\x1b[Kkernel/\x1b[0m\x1b[38;5;81mbpf/" +
- "\x1b[0m\x1b[38;5;81mpreload/\x1b[0m\x1b[38;5;81miterators/" +
- "\x1b[0m\x1b[38;5;149mMakefile\x1b[m\x1b[K\x1b[0m"
-
-func BenchmarkNextAnsiEscapeSequence(b *testing.B) {
- b.SetBytes(int64(len(ansiBenchmarkString)))
- for i := 0; i < b.N; i++ {
- s := ansiBenchmarkString
- for {
- _, o := nextAnsiEscapeSequence(s)
- if o == -1 {
- break
- }
- s = s[o:]
- }
- }
-}
-
-// Baseline test to compare the speed of nextAnsiEscapeSequence() to the
-// previously used regex based implementation.
-func BenchmarkNextAnsiEscapeSequence_Regex(b *testing.B) {
- b.SetBytes(int64(len(ansiBenchmarkString)))
- for i := 0; i < b.N; i++ {
- s := ansiBenchmarkString
- for {
- a := ansiRegexReference.FindStringIndex(s)
- if len(a) == 0 {
- break
- }
- s = s[a[1]:]
- }
- }
-}
-
-func BenchmarkExtractColor(b *testing.B) {
- b.SetBytes(int64(len(ansiBenchmarkString)))
- for i := 0; i < b.N; i++ {
- extractColor(ansiBenchmarkString, nil, nil)
- }
-}
diff --git a/.fzf/src/cache.go b/.fzf/src/cache.go
deleted file mode 100644
index df1a6ab..0000000
--- a/.fzf/src/cache.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package fzf
-
-import "sync"
-
-// queryCache associates strings to lists of items
-type queryCache map[string][]Result
-
-// ChunkCache associates Chunk and query string to lists of items
-type ChunkCache struct {
- mutex sync.Mutex
- cache map[*Chunk]*queryCache
-}
-
-// NewChunkCache returns a new ChunkCache
-func NewChunkCache() ChunkCache {
- return ChunkCache{sync.Mutex{}, make(map[*Chunk]*queryCache)}
-}
-
-// Add adds the list to the cache
-func (cc *ChunkCache) Add(chunk *Chunk, key string, list []Result) {
- if len(key) == 0 || !chunk.IsFull() || len(list) > queryCacheMax {
- return
- }
-
- cc.mutex.Lock()
- defer cc.mutex.Unlock()
-
- qc, ok := cc.cache[chunk]
- if !ok {
- cc.cache[chunk] = &queryCache{}
- qc = cc.cache[chunk]
- }
- (*qc)[key] = list
-}
-
-// Lookup is called to lookup ChunkCache
-func (cc *ChunkCache) Lookup(chunk *Chunk, key string) []Result {
- if len(key) == 0 || !chunk.IsFull() {
- return nil
- }
-
- cc.mutex.Lock()
- defer cc.mutex.Unlock()
-
- qc, ok := cc.cache[chunk]
- if ok {
- list, ok := (*qc)[key]
- if ok {
- return list
- }
- }
- return nil
-}
-
-func (cc *ChunkCache) Search(chunk *Chunk, key string) []Result {
- if len(key) == 0 || !chunk.IsFull() {
- return nil
- }
-
- cc.mutex.Lock()
- defer cc.mutex.Unlock()
-
- qc, ok := cc.cache[chunk]
- if !ok {
- return nil
- }
-
- for idx := 1; idx < len(key); idx++ {
- // [---------| ] | [ |---------]
- // [--------| ] | [ |--------]
- // [-------| ] | [ |-------]
- prefix := key[:len(key)-idx]
- suffix := key[idx:]
- for _, substr := range [2]string{prefix, suffix} {
- if cached, found := (*qc)[substr]; found {
- return cached
- }
- }
- }
- return nil
-}
diff --git a/.fzf/src/cache_test.go b/.fzf/src/cache_test.go
deleted file mode 100644
index 0242534..0000000
--- a/.fzf/src/cache_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package fzf
-
-import "testing"
-
-func TestChunkCache(t *testing.T) {
- cache := NewChunkCache()
- chunk1p := &Chunk{}
- chunk2p := &Chunk{count: chunkSize}
- items1 := []Result{{}}
- items2 := []Result{{}, {}}
- cache.Add(chunk1p, "foo", items1)
- cache.Add(chunk2p, "foo", items1)
- cache.Add(chunk2p, "bar", items2)
-
- { // chunk1 is not full
- cached := cache.Lookup(chunk1p, "foo")
- if cached != nil {
- t.Error("Cached disabled for non-empty chunks", cached)
- }
- }
- {
- cached := cache.Lookup(chunk2p, "foo")
- if cached == nil || len(cached) != 1 {
- t.Error("Expected 1 item cached", cached)
- }
- }
- {
- cached := cache.Lookup(chunk2p, "bar")
- if cached == nil || len(cached) != 2 {
- t.Error("Expected 2 items cached", cached)
- }
- }
- {
- cached := cache.Lookup(chunk1p, "foobar")
- if cached != nil {
- t.Error("Expected 0 item cached", cached)
- }
- }
-}
diff --git a/.fzf/src/chunklist.go b/.fzf/src/chunklist.go
deleted file mode 100644
index cd635c2..0000000
--- a/.fzf/src/chunklist.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package fzf
-
-import "sync"
-
-// Chunk is a list of Items whose size has the upper limit of chunkSize
-type Chunk struct {
- items [chunkSize]Item
- count int
-}
-
-// ItemBuilder is a closure type that builds Item object from byte array
-type ItemBuilder func(*Item, []byte) bool
-
-// ChunkList is a list of Chunks
-type ChunkList struct {
- chunks []*Chunk
- mutex sync.Mutex
- trans ItemBuilder
-}
-
-// NewChunkList returns a new ChunkList
-func NewChunkList(trans ItemBuilder) *ChunkList {
- return &ChunkList{
- chunks: []*Chunk{},
- mutex: sync.Mutex{},
- trans: trans}
-}
-
-func (c *Chunk) push(trans ItemBuilder, data []byte) bool {
- if trans(&c.items[c.count], data) {
- c.count++
- return true
- }
- return false
-}
-
-// IsFull returns true if the Chunk is full
-func (c *Chunk) IsFull() bool {
- return c.count == chunkSize
-}
-
-func (cl *ChunkList) lastChunk() *Chunk {
- return cl.chunks[len(cl.chunks)-1]
-}
-
-// CountItems returns the total number of Items
-func CountItems(cs []*Chunk) int {
- if len(cs) == 0 {
- return 0
- }
- return chunkSize*(len(cs)-1) + cs[len(cs)-1].count
-}
-
-// Push adds the item to the list
-func (cl *ChunkList) Push(data []byte) bool {
- cl.mutex.Lock()
-
- if len(cl.chunks) == 0 || cl.lastChunk().IsFull() {
- cl.chunks = append(cl.chunks, &Chunk{})
- }
-
- ret := cl.lastChunk().push(cl.trans, data)
- cl.mutex.Unlock()
- return ret
-}
-
-// Clear clears the data
-func (cl *ChunkList) Clear() {
- cl.mutex.Lock()
- cl.chunks = nil
- cl.mutex.Unlock()
-}
-
-// Snapshot returns immutable snapshot of the ChunkList
-func (cl *ChunkList) Snapshot() ([]*Chunk, int) {
- cl.mutex.Lock()
-
- ret := make([]*Chunk, len(cl.chunks))
- copy(ret, cl.chunks)
-
- // Duplicate the last chunk
- if cnt := len(ret); cnt > 0 {
- newChunk := *ret[cnt-1]
- ret[cnt-1] = &newChunk
- }
-
- cl.mutex.Unlock()
- return ret, CountItems(ret)
-}
diff --git a/.fzf/src/chunklist_test.go b/.fzf/src/chunklist_test.go
deleted file mode 100644
index 6c1d09e..0000000
--- a/.fzf/src/chunklist_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package fzf
-
-import (
- "fmt"
- "testing"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-func TestChunkList(t *testing.T) {
- // FIXME global
- sortCriteria = []criterion{byScore, byLength}
-
- cl := NewChunkList(func(item *Item, s []byte) bool {
- item.text = util.ToChars(s)
- return true
- })
-
- // Snapshot
- snapshot, count := cl.Snapshot()
- if len(snapshot) > 0 || count > 0 {
- t.Error("Snapshot should be empty now")
- }
-
- // Add some data
- cl.Push([]byte("hello"))
- cl.Push([]byte("world"))
-
- // Previously created snapshot should remain the same
- if len(snapshot) > 0 {
- t.Error("Snapshot should not have changed")
- }
-
- // But the new snapshot should contain the added items
- snapshot, count = cl.Snapshot()
- if len(snapshot) != 1 && count != 2 {
- t.Error("Snapshot should not be empty now")
- }
-
- // Check the content of the ChunkList
- chunk1 := snapshot[0]
- if chunk1.count != 2 {
- t.Error("Snapshot should contain only two items")
- }
- if chunk1.items[0].text.ToString() != "hello" ||
- chunk1.items[1].text.ToString() != "world" {
- t.Error("Invalid data")
- }
- if chunk1.IsFull() {
- t.Error("Chunk should not have been marked full yet")
- }
-
- // Add more data
- for i := 0; i < chunkSize*2; i++ {
- cl.Push([]byte(fmt.Sprintf("item %d", i)))
- }
-
- // Previous snapshot should remain the same
- if len(snapshot) != 1 {
- t.Error("Snapshot should stay the same")
- }
-
- // New snapshot
- snapshot, count = cl.Snapshot()
- if len(snapshot) != 3 || !snapshot[0].IsFull() ||
- !snapshot[1].IsFull() || snapshot[2].IsFull() || count != chunkSize*2+2 {
- t.Error("Expected two full chunks and one more chunk")
- }
- if snapshot[2].count != 2 {
- t.Error("Unexpected number of items")
- }
-
- cl.Push([]byte("hello"))
- cl.Push([]byte("world"))
-
- lastChunkCount := snapshot[len(snapshot)-1].count
- if lastChunkCount != 2 {
- t.Error("Unexpected number of items:", lastChunkCount)
- }
-}
diff --git a/.fzf/src/constants.go b/.fzf/src/constants.go
deleted file mode 100644
index 96d9821..0000000
--- a/.fzf/src/constants.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package fzf
-
-import (
- "math"
- "os"
- "time"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-const (
- // Core
- coordinatorDelayMax time.Duration = 100 * time.Millisecond
- coordinatorDelayStep time.Duration = 10 * time.Millisecond
-
- // Reader
- readerBufferSize = 64 * 1024
- readerPollIntervalMin = 10 * time.Millisecond
- readerPollIntervalStep = 5 * time.Millisecond
- readerPollIntervalMax = 50 * time.Millisecond
-
- // Terminal
- initialDelay = 20 * time.Millisecond
- initialDelayTac = 100 * time.Millisecond
- spinnerDuration = 100 * time.Millisecond
- previewCancelWait = 500 * time.Millisecond
- previewChunkDelay = 100 * time.Millisecond
- previewDelayed = 500 * time.Millisecond
- maxPatternLength = 300
- maxMulti = math.MaxInt32
-
- // Matcher
- numPartitionsMultiplier = 8
- maxPartitions = 32
- progressMinDuration = 200 * time.Millisecond
-
- // Capacity of each chunk
- chunkSize int = 100
-
- // Pre-allocated memory slices to minimize GC
- slab16Size int = 100 * 1024 // 200KB * 32 = 12.8MB
- slab32Size int = 2048 // 8KB * 32 = 256KB
-
- // Do not cache results of low selectivity queries
- queryCacheMax int = chunkSize / 5
-
- // Not to cache mergers with large lists
- mergerCacheMax int = 100000
-
- // History
- defaultHistoryMax int = 1000
-
- // Jump labels
- defaultJumpLabels string = "asdfghjklqwertyuiopzxcvbnm1234567890ASDFGHJKLQWERTYUIOPZXCVBNM`~;:,<.>/?'\"!@#$%^&*()[{]}-_=+"
-)
-
-var defaultCommand string
-
-func init() {
- if !util.IsWindows() {
- defaultCommand = `set -o pipefail; command find -L . -mindepth 1 \( -path '*/\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \) -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-`
- } else if os.Getenv("TERM") == "cygwin" {
- defaultCommand = `sh -c "command find -L . -mindepth 1 -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-"`
- }
-}
-
-// fzf events
-const (
- EvtReadNew util.EventType = iota
- EvtReadFin
- EvtSearchNew
- EvtSearchProgress
- EvtSearchFin
- EvtHeader
- EvtReady
- EvtQuit
-)
-
-const (
- exitCancel = -1
- exitOk = 0
- exitNoMatch = 1
- exitError = 2
- exitInterrupt = 130
-)
diff --git a/.fzf/src/core.go b/.fzf/src/core.go
deleted file mode 100644
index 6244c99..0000000
--- a/.fzf/src/core.go
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
-Package fzf implements fzf, a command-line fuzzy finder.
-
-The MIT License (MIT)
-
-Copyright (c) 2013-2021 Junegunn Choi
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-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.
-*/
-package fzf
-
-import (
- "fmt"
- "os"
- "time"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-/*
-Reader -> EvtReadFin
-Reader -> EvtReadNew -> Matcher (restart)
-Terminal -> EvtSearchNew:bool -> Matcher (restart)
-Matcher -> EvtSearchProgress -> Terminal (update info)
-Matcher -> EvtSearchFin -> Terminal (update list)
-Matcher -> EvtHeader -> Terminal (update header)
-*/
-
-// Run starts fzf
-func Run(opts *Options, version string, revision string) {
- sort := opts.Sort > 0
- sortCriteria = opts.Criteria
-
- if opts.Version {
- if len(revision) > 0 {
- fmt.Printf("%s (%s)\n", version, revision)
- } else {
- fmt.Println(version)
- }
- os.Exit(exitOk)
- }
-
- // Event channel
- eventBox := util.NewEventBox()
-
- // ANSI code processor
- ansiProcessor := func(data []byte) (util.Chars, *[]ansiOffset) {
- return util.ToChars(data), nil
- }
-
- var lineAnsiState, prevLineAnsiState *ansiState
- if opts.Ansi {
- if opts.Theme.Colored {
- ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
- prevLineAnsiState = lineAnsiState
- trimmed, offsets, newState := extractColor(string(data), lineAnsiState, nil)
- lineAnsiState = newState
- return util.ToChars([]byte(trimmed)), offsets
- }
- } else {
- // When color is disabled but ansi option is given,
- // we simply strip out ANSI codes from the input
- ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
- trimmed, _, _ := extractColor(string(data), nil, nil)
- return util.ToChars([]byte(trimmed)), nil
- }
- }
- }
-
- // Chunk list
- var chunkList *ChunkList
- var itemIndex int32
- header := make([]string, 0, opts.HeaderLines)
- if len(opts.WithNth) == 0 {
- chunkList = NewChunkList(func(item *Item, data []byte) bool {
- if len(header) < opts.HeaderLines {
- header = append(header, string(data))
- eventBox.Set(EvtHeader, header)
- return false
- }
- item.text, item.colors = ansiProcessor(data)
- item.text.Index = itemIndex
- itemIndex++
- return true
- })
- } else {
- chunkList = NewChunkList(func(item *Item, data []byte) bool {
- tokens := Tokenize(string(data), opts.Delimiter)
- if opts.Ansi && opts.Theme.Colored && len(tokens) > 1 {
- var ansiState *ansiState
- if prevLineAnsiState != nil {
- ansiStateDup := *prevLineAnsiState
- ansiState = &ansiStateDup
- }
- for _, token := range tokens {
- prevAnsiState := ansiState
- _, _, ansiState = extractColor(token.text.ToString(), ansiState, nil)
- if prevAnsiState != nil {
- token.text.Prepend("\x1b[m" + prevAnsiState.ToString())
- } else {
- token.text.Prepend("\x1b[m")
- }
- }
- }
- trans := Transform(tokens, opts.WithNth)
- transformed := joinTokens(trans)
- if len(header) < opts.HeaderLines {
- header = append(header, transformed)
- eventBox.Set(EvtHeader, header)
- return false
- }
- item.text, item.colors = ansiProcessor([]byte(transformed))
- item.text.TrimTrailingWhitespaces()
- item.text.Index = itemIndex
- item.origText = &data
- itemIndex++
- return true
- })
- }
-
- // Reader
- streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
- var reader *Reader
- if !streamingFilter {
- reader = NewReader(func(data []byte) bool {
- return chunkList.Push(data)
- }, eventBox, opts.ReadZero, opts.Filter == nil)
- go reader.ReadSource()
- }
-
- // Matcher
- forward := true
- for _, cri := range opts.Criteria[1:] {
- if cri == byEnd {
- forward = false
- break
- }
- if cri == byBegin {
- break
- }
- }
- patternBuilder := func(runes []rune) *Pattern {
- return BuildPattern(
- opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward,
- opts.Filter == nil, opts.Nth, opts.Delimiter, runes)
- }
- matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox)
-
- // Filtering mode
- if opts.Filter != nil {
- if opts.PrintQuery {
- opts.Printer(*opts.Filter)
- }
-
- pattern := patternBuilder([]rune(*opts.Filter))
- matcher.sort = pattern.sortable
-
- found := false
- if streamingFilter {
- slab := util.MakeSlab(slab16Size, slab32Size)
- reader := NewReader(
- func(runes []byte) bool {
- item := Item{}
- if chunkList.trans(&item, runes) {
- if result, _, _ := pattern.MatchItem(&item, false, slab); result != nil {
- opts.Printer(item.text.ToString())
- found = true
- }
- }
- return false
- }, eventBox, opts.ReadZero, false)
- reader.ReadSource()
- } else {
- eventBox.Unwatch(EvtReadNew)
- eventBox.WaitFor(EvtReadFin)
-
- snapshot, _ := chunkList.Snapshot()
- merger, _ := matcher.scan(MatchRequest{
- chunks: snapshot,
- pattern: pattern})
- for i := 0; i < merger.Length(); i++ {
- opts.Printer(merger.Get(i).item.AsString(opts.Ansi))
- found = true
- }
- }
- if found {
- os.Exit(exitOk)
- }
- os.Exit(exitNoMatch)
- }
-
- // Synchronous search
- if opts.Sync {
- eventBox.Unwatch(EvtReadNew)
- eventBox.WaitFor(EvtReadFin)
- }
-
- // Go interactive
- go matcher.Loop()
-
- // Terminal I/O
- terminal := NewTerminal(opts, eventBox)
- deferred := opts.Select1 || opts.Exit0
- go terminal.Loop()
- if !deferred {
- terminal.startChan <- true
- }
-
- // Event coordination
- reading := true
- clearCache := util.Once(false)
- clearSelection := util.Once(false)
- ticks := 0
- var nextCommand *string
- restart := func(command string) {
- reading = true
- clearCache = util.Once(true)
- clearSelection = util.Once(true)
- chunkList.Clear()
- itemIndex = 0
- header = make([]string, 0, opts.HeaderLines)
- go reader.restart(command)
- }
- eventBox.Watch(EvtReadNew)
- query := []rune{}
- for {
- delay := true
- ticks++
- input := func() []rune {
- paused, input := terminal.Input()
- if !paused {
- query = input
- }
- return query
- }
- eventBox.Wait(func(events *util.Events) {
- if _, fin := (*events)[EvtReadFin]; fin {
- delete(*events, EvtReadNew)
- }
- for evt, value := range *events {
- switch evt {
- case EvtQuit:
- if reading {
- reader.terminate()
- }
- os.Exit(value.(int))
- case EvtReadNew, EvtReadFin:
- if evt == EvtReadFin && nextCommand != nil {
- restart(*nextCommand)
- nextCommand = nil
- break
- } else {
- reading = reading && evt == EvtReadNew
- }
- snapshot, count := chunkList.Snapshot()
- terminal.UpdateCount(count, !reading, value.(*string))
- if opts.Sync {
- opts.Sync = false
- terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false)
- }
- matcher.Reset(snapshot, input(), false, !reading, sort, clearCache())
-
- case EvtSearchNew:
- var command *string
- switch val := value.(type) {
- case searchRequest:
- sort = val.sort
- command = val.command
- }
- if command != nil {
- if reading {
- reader.terminate()
- nextCommand = command
- } else {
- restart(*command)
- }
- break
- }
- snapshot, _ := chunkList.Snapshot()
- matcher.Reset(snapshot, input(), true, !reading, sort, clearCache())
- delay = false
-
- case EvtSearchProgress:
- switch val := value.(type) {
- case float32:
- terminal.UpdateProgress(val)
- }
-
- case EvtHeader:
- headerPadded := make([]string, opts.HeaderLines)
- copy(headerPadded, value.([]string))
- terminal.UpdateHeader(headerPadded)
-
- case EvtSearchFin:
- switch val := value.(type) {
- case *Merger:
- if deferred {
- count := val.Length()
- if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 {
- deferred = false
- terminal.startChan <- true
- } else if val.final {
- if opts.Exit0 && count == 0 || opts.Select1 && count == 1 {
- if opts.PrintQuery {
- opts.Printer(opts.Query)
- }
- if len(opts.Expect) > 0 {
- opts.Printer("")
- }
- for i := 0; i < count; i++ {
- opts.Printer(val.Get(i).item.AsString(opts.Ansi))
- }
- if count > 0 {
- os.Exit(exitOk)
- }
- os.Exit(exitNoMatch)
- }
- deferred = false
- terminal.startChan <- true
- }
- }
- terminal.UpdateList(val, clearSelection())
- }
- }
- }
- events.Clear()
- })
- if delay && reading {
- dur := util.DurWithin(
- time.Duration(ticks)*coordinatorDelayStep,
- 0, coordinatorDelayMax)
- time.Sleep(dur)
- }
- }
-}
diff --git a/.fzf/src/history.go b/.fzf/src/history.go
deleted file mode 100644
index 45728d4..0000000
--- a/.fzf/src/history.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package fzf
-
-import (
- "errors"
- "io/ioutil"
- "os"
- "strings"
-)
-
-// History struct represents input history
-type History struct {
- path string
- lines []string
- modified map[int]string
- maxSize int
- cursor int
-}
-
-// NewHistory returns the pointer to a new History struct
-func NewHistory(path string, maxSize int) (*History, error) {
- fmtError := func(e error) error {
- if os.IsPermission(e) {
- return errors.New("permission denied: " + path)
- }
- return errors.New("invalid history file: " + e.Error())
- }
-
- // Read history file
- data, err := ioutil.ReadFile(path)
- if err != nil {
- // If it doesn't exist, check if we can create a file with the name
- if os.IsNotExist(err) {
- data = []byte{}
- if err := ioutil.WriteFile(path, data, 0600); err != nil {
- return nil, fmtError(err)
- }
- } else {
- return nil, fmtError(err)
- }
- }
- // Split lines and limit the maximum number of lines
- lines := strings.Split(strings.Trim(string(data), "\n"), "\n")
- if len(lines[len(lines)-1]) > 0 {
- lines = append(lines, "")
- }
- return &History{
- path: path,
- maxSize: maxSize,
- lines: lines,
- modified: make(map[int]string),
- cursor: len(lines) - 1}, nil
-}
-
-func (h *History) append(line string) error {
- // We don't append empty lines
- if len(line) == 0 {
- return nil
- }
-
- lines := append(h.lines[:len(h.lines)-1], line)
- if len(lines) > h.maxSize {
- lines = lines[len(lines)-h.maxSize:]
- }
- h.lines = append(lines, "")
- return ioutil.WriteFile(h.path, []byte(strings.Join(h.lines, "\n")), 0600)
-}
-
-func (h *History) override(str string) {
- // You can update the history but they're not written to the file
- if h.cursor == len(h.lines)-1 {
- h.lines[h.cursor] = str
- } else if h.cursor < len(h.lines)-1 {
- h.modified[h.cursor] = str
- }
-}
-
-func (h *History) current() string {
- if str, prs := h.modified[h.cursor]; prs {
- return str
- }
- return h.lines[h.cursor]
-}
-
-func (h *History) previous() string {
- if h.cursor > 0 {
- h.cursor--
- }
- return h.current()
-}
-
-func (h *History) next() string {
- if h.cursor < len(h.lines)-1 {
- h.cursor++
- }
- return h.current()
-}
diff --git a/.fzf/src/history_test.go b/.fzf/src/history_test.go
deleted file mode 100644
index 6294bde..0000000
--- a/.fzf/src/history_test.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package fzf
-
-import (
- "io/ioutil"
- "os"
- "runtime"
- "testing"
-)
-
-func TestHistory(t *testing.T) {
- maxHistory := 50
-
- // Invalid arguments
- var paths []string
- if runtime.GOOS == "windows" {
- // GOPATH should exist, so we shouldn't be able to override it
- paths = []string{os.Getenv("GOPATH")}
- } else {
- paths = []string{"/etc", "/proc"}
- }
-
- for _, path := range paths {
- if _, e := NewHistory(path, maxHistory); e == nil {
- t.Error("Error expected for: " + path)
- }
- }
-
- f, _ := ioutil.TempFile("", "fzf-history")
- f.Close()
-
- { // Append lines
- h, _ := NewHistory(f.Name(), maxHistory)
- for i := 0; i < maxHistory+10; i++ {
- h.append("foobar")
- }
- }
- { // Read lines
- h, _ := NewHistory(f.Name(), maxHistory)
- if len(h.lines) != maxHistory+1 {
- t.Errorf("Expected: %d, actual: %d\n", maxHistory+1, len(h.lines))
- }
- for i := 0; i < maxHistory; i++ {
- if h.lines[i] != "foobar" {
- t.Error("Expected: foobar, actual: " + h.lines[i])
- }
- }
- }
- { // Append lines
- h, _ := NewHistory(f.Name(), maxHistory)
- h.append("barfoo")
- h.append("")
- h.append("foobarbaz")
- }
- { // Read lines again
- h, _ := NewHistory(f.Name(), maxHistory)
- if len(h.lines) != maxHistory+1 {
- t.Errorf("Expected: %d, actual: %d\n", maxHistory+1, len(h.lines))
- }
- compare := func(idx int, exp string) {
- if h.lines[idx] != exp {
- t.Errorf("Expected: %s, actual: %s\n", exp, h.lines[idx])
- }
- }
- compare(maxHistory-3, "foobar")
- compare(maxHistory-2, "barfoo")
- compare(maxHistory-1, "foobarbaz")
- }
-}
diff --git a/.fzf/src/item.go b/.fzf/src/item.go
deleted file mode 100644
index cb778cb..0000000
--- a/.fzf/src/item.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package fzf
-
-import (
- "github.com/junegunn/fzf/src/util"
-)
-
-// Item represents each input line. 56 bytes.
-type Item struct {
- text util.Chars // 32 = 24 + 1 + 1 + 2 + 4
- transformed *[]Token // 8
- origText *[]byte // 8
- colors *[]ansiOffset // 8
-}
-
-// Index returns ordinal index of the Item
-func (item *Item) Index() int32 {
- return item.text.Index
-}
-
-var minItem = Item{text: util.Chars{Index: -1}}
-
-func (item *Item) TrimLength() uint16 {
- return item.text.TrimLength()
-}
-
-// Colors returns ansiOffsets of the Item
-func (item *Item) Colors() []ansiOffset {
- if item.colors == nil {
- return []ansiOffset{}
- }
- return *item.colors
-}
-
-// AsString returns the original string
-func (item *Item) AsString(stripAnsi bool) string {
- if item.origText != nil {
- if stripAnsi {
- trimmed, _, _ := extractColor(string(*item.origText), nil, nil)
- return trimmed
- }
- return string(*item.origText)
- }
- return item.text.ToString()
-}
diff --git a/.fzf/src/item_test.go b/.fzf/src/item_test.go
deleted file mode 100644
index 1efb5f1..0000000
--- a/.fzf/src/item_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package fzf
-
-import (
- "testing"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-func TestStringPtr(t *testing.T) {
- orig := []byte("\x1b[34mfoo")
- text := []byte("\x1b[34mbar")
- item := Item{origText: &orig, text: util.ToChars(text)}
- if item.AsString(true) != "foo" || item.AsString(false) != string(orig) {
- t.Fail()
- }
- if item.AsString(true) != "foo" {
- t.Fail()
- }
- item.origText = nil
- if item.AsString(true) != string(text) || item.AsString(false) != string(text) {
- t.Fail()
- }
-}
diff --git a/.fzf/src/matcher.go b/.fzf/src/matcher.go
deleted file mode 100644
index 22aa819..0000000
--- a/.fzf/src/matcher.go
+++ /dev/null
@@ -1,235 +0,0 @@
-package fzf
-
-import (
- "fmt"
- "runtime"
- "sort"
- "sync"
- "time"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-// MatchRequest represents a search request
-type MatchRequest struct {
- chunks []*Chunk
- pattern *Pattern
- final bool
- sort bool
- clearCache bool
-}
-
-// Matcher is responsible for performing search
-type Matcher struct {
- patternBuilder func([]rune) *Pattern
- sort bool
- tac bool
- eventBox *util.EventBox
- reqBox *util.EventBox
- partitions int
- slab []*util.Slab
- mergerCache map[string]*Merger
-}
-
-const (
- reqRetry util.EventType = iota
- reqReset
-)
-
-// NewMatcher returns a new Matcher
-func NewMatcher(patternBuilder func([]rune) *Pattern,
- sort bool, tac bool, eventBox *util.EventBox) *Matcher {
- partitions := util.Min(numPartitionsMultiplier*runtime.NumCPU(), maxPartitions)
- return &Matcher{
- patternBuilder: patternBuilder,
- sort: sort,
- tac: tac,
- eventBox: eventBox,
- reqBox: util.NewEventBox(),
- partitions: partitions,
- slab: make([]*util.Slab, partitions),
- mergerCache: make(map[string]*Merger)}
-}
-
-// Loop puts Matcher in action
-func (m *Matcher) Loop() {
- prevCount := 0
-
- for {
- var request MatchRequest
-
- m.reqBox.Wait(func(events *util.Events) {
- for _, val := range *events {
- switch val := val.(type) {
- case MatchRequest:
- request = val
- default:
- panic(fmt.Sprintf("Unexpected type: %T", val))
- }
- }
- events.Clear()
- })
-
- if request.sort != m.sort || request.clearCache {
- m.sort = request.sort
- m.mergerCache = make(map[string]*Merger)
- clearChunkCache()
- }
-
- // Restart search
- patternString := request.pattern.AsString()
- var merger *Merger
- cancelled := false
- count := CountItems(request.chunks)
-
- foundCache := false
- if count == prevCount {
- // Look up mergerCache
- if cached, found := m.mergerCache[patternString]; found {
- foundCache = true
- merger = cached
- }
- } else {
- // Invalidate mergerCache
- prevCount = count
- m.mergerCache = make(map[string]*Merger)
- }
-
- if !foundCache {
- merger, cancelled = m.scan(request)
- }
-
- if !cancelled {
- if merger.cacheable() {
- m.mergerCache[patternString] = merger
- }
- merger.final = request.final
- m.eventBox.Set(EvtSearchFin, merger)
- }
- }
-}
-
-func (m *Matcher) sliceChunks(chunks []*Chunk) [][]*Chunk {
- partitions := m.partitions
- perSlice := len(chunks) / partitions
-
- if perSlice == 0 {
- partitions = len(chunks)
- perSlice = 1
- }
-
- slices := make([][]*Chunk, partitions)
- for i := 0; i < partitions; i++ {
- start := i * perSlice
- end := start + perSlice
- if i == partitions-1 {
- end = len(chunks)
- }
- slices[i] = chunks[start:end]
- }
- return slices
-}
-
-type partialResult struct {
- index int
- matches []Result
-}
-
-func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
- startedAt := time.Now()
-
- numChunks := len(request.chunks)
- if numChunks == 0 {
- return EmptyMerger, false
- }
- pattern := request.pattern
- if pattern.IsEmpty() {
- return PassMerger(&request.chunks, m.tac), false
- }
-
- cancelled := util.NewAtomicBool(false)
-
- slices := m.sliceChunks(request.chunks)
- numSlices := len(slices)
- resultChan := make(chan partialResult, numSlices)
- countChan := make(chan int, numChunks)
- waitGroup := sync.WaitGroup{}
-
- for idx, chunks := range slices {
- waitGroup.Add(1)
- if m.slab[idx] == nil {
- m.slab[idx] = util.MakeSlab(slab16Size, slab32Size)
- }
- go func(idx int, slab *util.Slab, chunks []*Chunk) {
- defer func() { waitGroup.Done() }()
- count := 0
- allMatches := make([][]Result, len(chunks))
- for idx, chunk := range chunks {
- matches := request.pattern.Match(chunk, slab)
- allMatches[idx] = matches
- count += len(matches)
- if cancelled.Get() {
- return
- }
- countChan <- len(matches)
- }
- sliceMatches := make([]Result, 0, count)
- for _, matches := range allMatches {
- sliceMatches = append(sliceMatches, matches...)
- }
- if m.sort {
- if m.tac {
- sort.Sort(ByRelevanceTac(sliceMatches))
- } else {
- sort.Sort(ByRelevance(sliceMatches))
- }
- }
- resultChan <- partialResult{idx, sliceMatches}
- }(idx, m.slab[idx], chunks)
- }
-
- wait := func() bool {
- cancelled.Set(true)
- waitGroup.Wait()
- return true
- }
-
- count := 0
- matchCount := 0
- for matchesInChunk := range countChan {
- count++
- matchCount += matchesInChunk
-
- if count == numChunks {
- break
- }
-
- if m.reqBox.Peek(reqReset) {
- return nil, wait()
- }
-
- if time.Since(startedAt) > progressMinDuration {
- m.eventBox.Set(EvtSearchProgress, float32(count)/float32(numChunks))
- }
- }
-
- partialResults := make([][]Result, numSlices)
- for range slices {
- partialResult := <-resultChan
- partialResults[partialResult.index] = partialResult.matches
- }
- return NewMerger(pattern, partialResults, m.sort, m.tac), false
-}
-
-// Reset is called to interrupt/signal the ongoing search
-func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final bool, sort bool, clearCache bool) {
- pattern := m.patternBuilder(patternRunes)
-
- var event util.EventType
- if cancel {
- event = reqReset
- } else {
- event = reqRetry
- }
- m.reqBox.Set(event, MatchRequest{chunks, pattern, final, sort && pattern.sortable, clearCache})
-}
diff --git a/.fzf/src/merger.go b/.fzf/src/merger.go
deleted file mode 100644
index 8e6a884..0000000
--- a/.fzf/src/merger.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package fzf
-
-import "fmt"
-
-// EmptyMerger is a Merger with no data
-var EmptyMerger = NewMerger(nil, [][]Result{}, false, false)
-
-// Merger holds a set of locally sorted lists of items and provides the view of
-// a single, globally-sorted list
-type Merger struct {
- pattern *Pattern
- lists [][]Result
- merged []Result
- chunks *[]*Chunk
- cursors []int
- sorted bool
- tac bool
- final bool
- count int
-}
-
-// PassMerger returns a new Merger that simply returns the items in the
-// original order
-func PassMerger(chunks *[]*Chunk, tac bool) *Merger {
- mg := Merger{
- pattern: nil,
- chunks: chunks,
- tac: tac,
- count: 0}
-
- for _, chunk := range *mg.chunks {
- mg.count += chunk.count
- }
- return &mg
-}
-
-// NewMerger returns a new Merger
-func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool) *Merger {
- mg := Merger{
- pattern: pattern,
- lists: lists,
- merged: []Result{},
- chunks: nil,
- cursors: make([]int, len(lists)),
- sorted: sorted,
- tac: tac,
- final: false,
- count: 0}
-
- for _, list := range mg.lists {
- mg.count += len(list)
- }
- return &mg
-}
-
-// Length returns the number of items
-func (mg *Merger) Length() int {
- return mg.count
-}
-
-// Get returns the pointer to the Result object indexed by the given integer
-func (mg *Merger) Get(idx int) Result {
- if mg.chunks != nil {
- if mg.tac {
- idx = mg.count - idx - 1
- }
- chunk := (*mg.chunks)[idx/chunkSize]
- return Result{item: &chunk.items[idx%chunkSize]}
- }
-
- if mg.sorted {
- return mg.mergedGet(idx)
- }
-
- if mg.tac {
- idx = mg.count - idx - 1
- }
- for _, list := range mg.lists {
- numItems := len(list)
- if idx < numItems {
- return list[idx]
- }
- idx -= numItems
- }
- panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count))
-}
-
-func (mg *Merger) cacheable() bool {
- return mg.count < mergerCacheMax
-}
-
-func (mg *Merger) mergedGet(idx int) Result {
- for i := len(mg.merged); i <= idx; i++ {
- minRank := minRank()
- minIdx := -1
- for listIdx, list := range mg.lists {
- cursor := mg.cursors[listIdx]
- if cursor < 0 || cursor == len(list) {
- mg.cursors[listIdx] = -1
- continue
- }
- if cursor >= 0 {
- rank := list[cursor]
- if minIdx < 0 || compareRanks(rank, minRank, mg.tac) {
- minRank = rank
- minIdx = listIdx
- }
- }
- }
-
- if minIdx >= 0 {
- chosen := mg.lists[minIdx]
- mg.merged = append(mg.merged, chosen[mg.cursors[minIdx]])
- mg.cursors[minIdx]++
- } else {
- panic(fmt.Sprintf("Index out of bounds (sorted, %d/%d)", i, mg.count))
- }
- }
- return mg.merged[idx]
-}
diff --git a/.fzf/src/merger_test.go b/.fzf/src/merger_test.go
deleted file mode 100644
index c6af4f6..0000000
--- a/.fzf/src/merger_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package fzf
-
-import (
- "fmt"
- "math/rand"
- "sort"
- "testing"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-func assert(t *testing.T, cond bool, msg ...string) {
- if !cond {
- t.Error(msg)
- }
-}
-
-func randResult() Result {
- str := fmt.Sprintf("%d", rand.Uint32())
- chars := util.ToChars([]byte(str))
- chars.Index = rand.Int31()
- return Result{item: &Item{text: chars}}
-}
-
-func TestEmptyMerger(t *testing.T) {
- assert(t, EmptyMerger.Length() == 0, "Not empty")
- assert(t, EmptyMerger.count == 0, "Invalid count")
- assert(t, len(EmptyMerger.lists) == 0, "Invalid lists")
- assert(t, len(EmptyMerger.merged) == 0, "Invalid merged list")
-}
-
-func buildLists(partiallySorted bool) ([][]Result, []Result) {
- numLists := 4
- lists := make([][]Result, numLists)
- cnt := 0
- for i := 0; i < numLists; i++ {
- numResults := rand.Int() % 20
- cnt += numResults
- lists[i] = make([]Result, numResults)
- for j := 0; j < numResults; j++ {
- item := randResult()
- lists[i][j] = item
- }
- if partiallySorted {
- sort.Sort(ByRelevance(lists[i]))
- }
- }
- items := []Result{}
- for _, list := range lists {
- items = append(items, list...)
- }
- return lists, items
-}
-
-func TestMergerUnsorted(t *testing.T) {
- lists, items := buildLists(false)
- cnt := len(items)
-
- // Not sorted: same order
- mg := NewMerger(nil, lists, false, false)
- assert(t, cnt == mg.Length(), "Invalid Length")
- for i := 0; i < cnt; i++ {
- assert(t, items[i] == mg.Get(i), "Invalid Get")
- }
-}
-
-func TestMergerSorted(t *testing.T) {
- lists, items := buildLists(true)
- cnt := len(items)
-
- // Sorted sorted order
- mg := NewMerger(nil, lists, true, false)
- assert(t, cnt == mg.Length(), "Invalid Length")
- sort.Sort(ByRelevance(items))
- for i := 0; i < cnt; i++ {
- if items[i] != mg.Get(i) {
- t.Error("Not sorted", items[i], mg.Get(i))
- }
- }
-
- // Inverse order
- mg2 := NewMerger(nil, lists, true, false)
- for i := cnt - 1; i >= 0; i-- {
- if items[i] != mg2.Get(i) {
- t.Error("Not sorted", items[i], mg2.Get(i))
- }
- }
-}
diff --git a/.fzf/src/options.go b/.fzf/src/options.go
deleted file mode 100644
index b93fd97..0000000
--- a/.fzf/src/options.go
+++ /dev/null
@@ -1,1691 +0,0 @@
-package fzf
-
-import (
- "fmt"
- "os"
- "regexp"
- "strconv"
- "strings"
- "unicode"
-
- "github.com/junegunn/fzf/src/algo"
- "github.com/junegunn/fzf/src/tui"
-
- "github.com/mattn/go-runewidth"
- "github.com/mattn/go-shellwords"
-)
-
-const usage = `usage: fzf [options]
-
- Search
- -x, --extended Extended-search mode
- (enabled by default; +x or --no-extended to disable)
- -e, --exact Enable Exact-match
- --algo=TYPE Fuzzy matching algorithm: [v1|v2] (default: v2)
- -i Case-insensitive match (default: smart-case match)
- +i Case-sensitive match
- --literal Do not normalize latin script letters before matching
- -n, --nth=N[,..] Comma-separated list of field index expressions
- for limiting search scope. Each can be a non-zero
- integer or a range expression ([BEGIN]..[END]).
- --with-nth=N[,..] Transform the presentation of each line using
- field index expressions
- -d, --delimiter=STR Field delimiter regex (default: AWK-style)
- +s, --no-sort Do not sort the result
- --tac Reverse the order of the input
- --disabled Do not perform search
- --tiebreak=CRI[,..] Comma-separated list of sort criteria to apply
- when the scores are tied [length|begin|end|index]
- (default: length)
-
- Interface
- -m, --multi[=MAX] Enable multi-select with tab/shift-tab
- --no-mouse Disable mouse
- --bind=KEYBINDS Custom key bindings. Refer to the man page.
- --cycle Enable cyclic scroll
- --keep-right Keep the right end of the line visible on overflow
- --scroll-off=LINES Number of screen lines to keep above or below when
- scrolling to the top or to the bottom (default: 0)
- --no-hscroll Disable horizontal scroll
- --hscroll-off=COLS Number of screen columns to keep to the right of the
- highlighted substring (default: 10)
- --filepath-word Make word-wise movements respect path separators
- --jump-labels=CHARS Label characters for jump and jump-accept
-
- Layout
- --height=HEIGHT[%] Display fzf window below the cursor with the given
- height instead of using fullscreen
- --min-height=HEIGHT Minimum height when --height is given in percent
- (default: 10)
- --layout=LAYOUT Choose layout: [default|reverse|reverse-list]
- --border[=STYLE] Draw border around the finder
- [rounded|sharp|horizontal|vertical|
- top|bottom|left|right|none] (default: rounded)
- --margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
- --padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
- --info=STYLE Finder info style [default|inline|hidden]
- --prompt=STR Input prompt (default: '> ')
- --pointer=STR Pointer to the current line (default: '>')
- --marker=STR Multi-select marker (default: '>')
- --header=STR String to print as header
- --header-lines=N The first N lines of the input are treated as header
- --header-first Print header before the prompt line
-
- Display
- --ansi Enable processing of ANSI color codes
- --tabstop=SPACES Number of spaces for a tab character (default: 8)
- --color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors
- --no-bold Do not use bold text
-
- History
- --history=FILE History file
- --history-size=N Maximum number of history entries (default: 1000)
-
- Preview
- --preview=COMMAND Command to preview highlighted line ({})
- --preview-window=OPT Preview window layout (default: right:50%)
- [up|down|left|right][,SIZE[%]]
- [,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden]
- [,border-BORDER_OPT]
- [,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
- [,default]
-
- Scripting
- -q, --query=STR Start the finder with the given query
- -1, --select-1 Automatically select the only match
- -0, --exit-0 Exit immediately when there's no match
- -f, --filter=STR Filter mode. Do not start interactive finder.
- --print-query Print query as the first line
- --expect=KEYS Comma-separated list of keys to complete fzf
- --read0 Read input delimited by ASCII NUL characters
- --print0 Print output delimited by ASCII NUL characters
- --sync Synchronous search for multi-staged filtering
- --version Display version information and exit
-
- Environment variables
- FZF_DEFAULT_COMMAND Default command to use when input is tty
- FZF_DEFAULT_OPTS Default options
- (e.g. '--layout=reverse --inline-info')
-
-`
-
-// Case denotes case-sensitivity of search
-type Case int
-
-// Case-sensitivities
-const (
- CaseSmart Case = iota
- CaseIgnore
- CaseRespect
-)
-
-// Sort criteria
-type criterion int
-
-const (
- byScore criterion = iota
- byLength
- byBegin
- byEnd
-)
-
-type sizeSpec struct {
- size float64
- percent bool
-}
-
-func defaultMargin() [4]sizeSpec {
- return [4]sizeSpec{}
-}
-
-type windowPosition int
-
-const (
- posUp windowPosition = iota
- posDown
- posLeft
- posRight
-)
-
-type layoutType int
-
-const (
- layoutDefault layoutType = iota
- layoutReverse
- layoutReverseList
-)
-
-type infoStyle int
-
-const (
- infoDefault infoStyle = iota
- infoInline
- infoHidden
-)
-
-type previewOpts struct {
- command string
- position windowPosition
- size sizeSpec
- scroll string
- hidden bool
- wrap bool
- cycle bool
- follow bool
- border tui.BorderShape
- headerLines int
-}
-
-// Options stores the values of command-line options
-type Options struct {
- Fuzzy bool
- FuzzyAlgo algo.Algo
- Extended bool
- Phony bool
- Case Case
- Normalize bool
- Nth []Range
- WithNth []Range
- Delimiter Delimiter
- Sort int
- Tac bool
- Criteria []criterion
- Multi int
- Ansi bool
- Mouse bool
- Theme *tui.ColorTheme
- Black bool
- Bold bool
- Height sizeSpec
- MinHeight int
- Layout layoutType
- Cycle bool
- KeepRight bool
- Hscroll bool
- HscrollOff int
- ScrollOff int
- FileWord bool
- InfoStyle infoStyle
- JumpLabels string
- Prompt string
- Pointer string
- Marker string
- Query string
- Select1 bool
- Exit0 bool
- Filter *string
- ToggleSort bool
- Expect map[tui.Event]string
- Keymap map[tui.Event][]action
- Preview previewOpts
- PrintQuery bool
- ReadZero bool
- Printer func(string)
- PrintSep string
- Sync bool
- History *History
- Header []string
- HeaderLines int
- HeaderFirst bool
- Margin [4]sizeSpec
- Padding [4]sizeSpec
- BorderShape tui.BorderShape
- Unicode bool
- Tabstop int
- ClearOnExit bool
- Version bool
-}
-
-func defaultPreviewOpts(command string) previewOpts {
- return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.BorderRounded, 0}
-}
-
-func defaultOptions() *Options {
- return &Options{
- Fuzzy: true,
- FuzzyAlgo: algo.FuzzyMatchV2,
- Extended: true,
- Phony: false,
- Case: CaseSmart,
- Normalize: true,
- Nth: make([]Range, 0),
- WithNth: make([]Range, 0),
- Delimiter: Delimiter{},
- Sort: 1000,
- Tac: false,
- Criteria: []criterion{byScore, byLength},
- Multi: 0,
- Ansi: false,
- Mouse: true,
- Theme: tui.EmptyTheme(),
- Black: false,
- Bold: true,
- MinHeight: 10,
- Layout: layoutDefault,
- Cycle: false,
- KeepRight: false,
- Hscroll: true,
- HscrollOff: 10,
- ScrollOff: 0,
- FileWord: false,
- InfoStyle: infoDefault,
- JumpLabels: defaultJumpLabels,
- Prompt: "> ",
- Pointer: ">",
- Marker: ">",
- Query: "",
- Select1: false,
- Exit0: false,
- Filter: nil,
- ToggleSort: false,
- Expect: make(map[tui.Event]string),
- Keymap: make(map[tui.Event][]action),
- Preview: defaultPreviewOpts(""),
- PrintQuery: false,
- ReadZero: false,
- Printer: func(str string) { fmt.Println(str) },
- PrintSep: "\n",
- Sync: false,
- History: nil,
- Header: make([]string, 0),
- HeaderLines: 0,
- HeaderFirst: false,
- Margin: defaultMargin(),
- Padding: defaultMargin(),
- Unicode: true,
- Tabstop: 8,
- ClearOnExit: true,
- Version: false}
-}
-
-func help(code int) {
- os.Stdout.WriteString(usage)
- os.Exit(code)
-}
-
-func errorExit(msg string) {
- os.Stderr.WriteString(msg + "\n")
- os.Exit(exitError)
-}
-
-func optString(arg string, prefixes ...string) (bool, string) {
- for _, prefix := range prefixes {
- if strings.HasPrefix(arg, prefix) {
- return true, arg[len(prefix):]
- }
- }
- return false, ""
-}
-
-func nextString(args []string, i *int, message string) string {
- if len(args) > *i+1 {
- *i++
- } else {
- errorExit(message)
- }
- return args[*i]
-}
-
-func optionalNextString(args []string, i *int) (bool, string) {
- if len(args) > *i+1 && !strings.HasPrefix(args[*i+1], "-") && !strings.HasPrefix(args[*i+1], "+") {
- *i++
- return true, args[*i]
- }
- return false, ""
-}
-
-func atoi(str string) int {
- num, err := strconv.Atoi(str)
- if err != nil {
- errorExit("not a valid integer: " + str)
- }
- return num
-}
-
-func atof(str string) float64 {
- num, err := strconv.ParseFloat(str, 64)
- if err != nil {
- errorExit("not a valid number: " + str)
- }
- return num
-}
-
-func nextInt(args []string, i *int, message string) int {
- if len(args) > *i+1 {
- *i++
- } else {
- errorExit(message)
- }
- return atoi(args[*i])
-}
-
-func optionalNumeric(args []string, i *int, defaultValue int) int {
- if len(args) > *i+1 {
- if strings.IndexAny(args[*i+1], "0123456789") == 0 {
- *i++
- return atoi(args[*i])
- }
- }
- return defaultValue
-}
-
-func splitNth(str string) []Range {
- if match, _ := regexp.MatchString("^[0-9,-.]+$", str); !match {
- errorExit("invalid format: " + str)
- }
-
- tokens := strings.Split(str, ",")
- ranges := make([]Range, len(tokens))
- for idx, s := range tokens {
- r, ok := ParseRange(&s)
- if !ok {
- errorExit("invalid format: " + str)
- }
- ranges[idx] = r
- }
- return ranges
-}
-
-func delimiterRegexp(str string) Delimiter {
- // Special handling of \t
- str = strings.Replace(str, "\\t", "\t", -1)
-
- // 1. Pattern does not contain any special character
- if regexp.QuoteMeta(str) == str {
- return Delimiter{str: &str}
- }
-
- rx, e := regexp.Compile(str)
- // 2. Pattern is not a valid regular expression
- if e != nil {
- return Delimiter{str: &str}
- }
-
- // 3. Pattern as regular expression. Slow.
- return Delimiter{regex: rx}
-}
-
-func isAlphabet(char uint8) bool {
- return char >= 'a' && char <= 'z'
-}
-
-func isNumeric(char uint8) bool {
- return char >= '0' && char <= '9'
-}
-
-func parseAlgo(str string) algo.Algo {
- switch str {
- case "v1":
- return algo.FuzzyMatchV1
- case "v2":
- return algo.FuzzyMatchV2
- default:
- errorExit("invalid algorithm (expected: v1 or v2)")
- }
- return algo.FuzzyMatchV2
-}
-
-func parseBorder(str string, optional bool) tui.BorderShape {
- switch str {
- case "rounded":
- return tui.BorderRounded
- case "sharp":
- return tui.BorderSharp
- case "horizontal":
- return tui.BorderHorizontal
- case "vertical":
- return tui.BorderVertical
- case "top":
- return tui.BorderTop
- case "bottom":
- return tui.BorderBottom
- case "left":
- return tui.BorderLeft
- case "right":
- return tui.BorderRight
- case "none":
- return tui.BorderNone
- default:
- if optional && str == "" {
- return tui.BorderRounded
- }
- errorExit("invalid border style (expected: rounded|sharp|horizontal|vertical|top|bottom|left|right|none)")
- }
- return tui.BorderNone
-}
-
-func parseKeyChords(str string, message string) map[tui.Event]string {
- if len(str) == 0 {
- errorExit(message)
- }
-
- str = regexp.MustCompile("(?i)(alt-),").ReplaceAllString(str, "$1"+string([]rune{escapedComma}))
- tokens := strings.Split(str, ",")
- if str == "," || strings.HasPrefix(str, ",,") || strings.HasSuffix(str, ",,") || strings.Contains(str, ",,,") {
- tokens = append(tokens, ",")
- }
-
- chords := make(map[tui.Event]string)
- for _, key := range tokens {
- if len(key) == 0 {
- continue // ignore
- }
- key = strings.ReplaceAll(key, string([]rune{escapedComma}), ",")
- lkey := strings.ToLower(key)
- add := func(e tui.EventType) {
- chords[e.AsEvent()] = key
- }
- switch lkey {
- case "up":
- add(tui.Up)
- case "down":
- add(tui.Down)
- case "left":
- add(tui.Left)
- case "right":
- add(tui.Right)
- case "enter", "return":
- add(tui.CtrlM)
- case "space":
- chords[tui.Key(' ')] = key
- case "bspace", "bs":
- add(tui.BSpace)
- case "ctrl-space":
- add(tui.CtrlSpace)
- case "ctrl-^", "ctrl-6":
- add(tui.CtrlCaret)
- case "ctrl-/", "ctrl-_":
- add(tui.CtrlSlash)
- case "ctrl-\\":
- add(tui.CtrlBackSlash)
- case "ctrl-]":
- add(tui.CtrlRightBracket)
- case "change":
- add(tui.Change)
- case "backward-eof":
- add(tui.BackwardEOF)
- case "alt-enter", "alt-return":
- chords[tui.CtrlAltKey('m')] = key
- case "alt-space":
- chords[tui.AltKey(' ')] = key
- case "alt-bs", "alt-bspace":
- add(tui.AltBS)
- case "alt-up":
- add(tui.AltUp)
- case "alt-down":
- add(tui.AltDown)
- case "alt-left":
- add(tui.AltLeft)
- case "alt-right":
- add(tui.AltRight)
- case "tab":
- add(tui.Tab)
- case "btab", "shift-tab":
- add(tui.BTab)
- case "esc":
- add(tui.ESC)
- case "del":
- add(tui.Del)
- case "home":
- add(tui.Home)
- case "end":
- add(tui.End)
- case "insert":
- add(tui.Insert)
- case "pgup", "page-up":
- add(tui.PgUp)
- case "pgdn", "page-down":
- add(tui.PgDn)
- case "alt-shift-up", "shift-alt-up":
- add(tui.AltSUp)
- case "alt-shift-down", "shift-alt-down":
- add(tui.AltSDown)
- case "alt-shift-left", "shift-alt-left":
- add(tui.AltSLeft)
- case "alt-shift-right", "shift-alt-right":
- add(tui.AltSRight)
- case "shift-up":
- add(tui.SUp)
- case "shift-down":
- add(tui.SDown)
- case "shift-left":
- add(tui.SLeft)
- case "shift-right":
- add(tui.SRight)
- case "left-click":
- add(tui.LeftClick)
- case "right-click":
- add(tui.RightClick)
- case "double-click":
- add(tui.DoubleClick)
- case "f10":
- add(tui.F10)
- case "f11":
- add(tui.F11)
- case "f12":
- add(tui.F12)
- default:
- runes := []rune(key)
- if len(key) == 10 && strings.HasPrefix(lkey, "ctrl-alt-") && isAlphabet(lkey[9]) {
- chords[tui.CtrlAltKey(rune(key[9]))] = key
- } else if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
- add(tui.EventType(tui.CtrlA.Int() + int(lkey[5]) - 'a'))
- } else if len(runes) == 5 && strings.HasPrefix(lkey, "alt-") {
- r := runes[4]
- switch r {
- case escapedColon:
- r = ':'
- case escapedComma:
- r = ','
- case escapedPlus:
- r = '+'
- }
- chords[tui.AltKey(r)] = key
- } else if len(key) == 2 && strings.HasPrefix(lkey, "f") && key[1] >= '1' && key[1] <= '9' {
- add(tui.EventType(tui.F1.Int() + int(key[1]) - '1'))
- } else if len(runes) == 1 {
- chords[tui.Key(runes[0])] = key
- } else {
- errorExit("unsupported key: " + key)
- }
- }
- }
- return chords
-}
-
-func parseTiebreak(str string) []criterion {
- criteria := []criterion{byScore}
- hasIndex := false
- hasLength := false
- hasBegin := false
- hasEnd := false
- check := func(notExpected *bool, name string) {
- if *notExpected {
- errorExit("duplicate sort criteria: " + name)
- }
- if hasIndex {
- errorExit("index should be the last criterion")
- }
- *notExpected = true
- }
- for _, str := range strings.Split(strings.ToLower(str), ",") {
- switch str {
- case "index":
- check(&hasIndex, "index")
- case "length":
- check(&hasLength, "length")
- criteria = append(criteria, byLength)
- case "begin":
- check(&hasBegin, "begin")
- criteria = append(criteria, byBegin)
- case "end":
- check(&hasEnd, "end")
- criteria = append(criteria, byEnd)
- default:
- errorExit("invalid sort criterion: " + str)
- }
- }
- return criteria
-}
-
-func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
- dupe := *theme
- return &dupe
-}
-
-func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
- theme := dupeTheme(defaultTheme)
- rrggbb := regexp.MustCompile("^#[0-9a-fA-F]{6}$")
- for _, str := range strings.Split(strings.ToLower(str), ",") {
- switch str {
- case "dark":
- theme = dupeTheme(tui.Dark256)
- case "light":
- theme = dupeTheme(tui.Light256)
- case "16":
- theme = dupeTheme(tui.Default16)
- case "bw", "no":
- theme = tui.NoColorTheme()
- default:
- fail := func() {
- errorExit("invalid color specification: " + str)
- }
- // Color is disabled
- if theme == nil {
- continue
- }
-
- components := strings.Split(str, ":")
- if len(components) < 2 {
- fail()
- }
-
- mergeAttr := func(cattr *tui.ColorAttr) {
- for _, component := range components[1:] {
- switch component {
- case "regular":
- cattr.Attr = tui.AttrRegular
- case "bold", "strong":
- cattr.Attr |= tui.Bold
- case "dim":
- cattr.Attr |= tui.Dim
- case "italic":
- cattr.Attr |= tui.Italic
- case "underline":
- cattr.Attr |= tui.Underline
- case "blink":
- cattr.Attr |= tui.Blink
- case "reverse":
- cattr.Attr |= tui.Reverse
- case "black":
- cattr.Color = tui.Color(0)
- case "red":
- cattr.Color = tui.Color(1)
- case "green":
- cattr.Color = tui.Color(2)
- case "yellow":
- cattr.Color = tui.Color(3)
- case "blue":
- cattr.Color = tui.Color(4)
- case "magenta":
- cattr.Color = tui.Color(5)
- case "cyan":
- cattr.Color = tui.Color(6)
- case "white":
- cattr.Color = tui.Color(7)
- case "bright-black", "gray", "grey":
- cattr.Color = tui.Color(8)
- case "bright-red":
- cattr.Color = tui.Color(9)
- case "bright-green":
- cattr.Color = tui.Color(10)
- case "bright-yellow":
- cattr.Color = tui.Color(11)
- case "bright-blue":
- cattr.Color = tui.Color(12)
- case "bright-magenta":
- cattr.Color = tui.Color(13)
- case "bright-cyan":
- cattr.Color = tui.Color(14)
- case "bright-white":
- cattr.Color = tui.Color(15)
- case "":
- default:
- if rrggbb.MatchString(component) {
- cattr.Color = tui.HexToColor(component)
- } else {
- ansi32, err := strconv.Atoi(component)
- if err != nil || ansi32 < -1 || ansi32 > 255 {
- fail()
- }
- cattr.Color = tui.Color(ansi32)
- }
- }
- }
- }
- switch components[0] {
- case "query", "input":
- mergeAttr(&theme.Input)
- case "disabled":
- mergeAttr(&theme.Disabled)
- case "fg":
- mergeAttr(&theme.Fg)
- case "bg":
- mergeAttr(&theme.Bg)
- case "preview-fg":
- mergeAttr(&theme.PreviewFg)
- case "preview-bg":
- mergeAttr(&theme.PreviewBg)
- case "fg+":
- mergeAttr(&theme.Current)
- case "bg+":
- mergeAttr(&theme.DarkBg)
- case "gutter":
- mergeAttr(&theme.Gutter)
- case "hl":
- mergeAttr(&theme.Match)
- case "hl+":
- mergeAttr(&theme.CurrentMatch)
- case "border":
- mergeAttr(&theme.Border)
- case "prompt":
- mergeAttr(&theme.Prompt)
- case "spinner":
- mergeAttr(&theme.Spinner)
- case "info":
- mergeAttr(&theme.Info)
- case "pointer":
- mergeAttr(&theme.Cursor)
- case "marker":
- mergeAttr(&theme.Selected)
- case "header":
- mergeAttr(&theme.Header)
- default:
- fail()
- }
- }
- }
- return theme
-}
-
-var executeRegexp *regexp.Regexp
-
-func firstKey(keymap map[tui.Event]string) tui.Event {
- for k := range keymap {
- return k
- }
- return tui.EventType(0).AsEvent()
-}
-
-const (
- escapedColon = 0
- escapedComma = 1
- escapedPlus = 2
-)
-
-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|preview|change-prompt|unbind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|unbind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
-}
-
-func parseKeymap(keymap map[tui.Event][]action, str string) {
- masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
- symbol := ":"
- if strings.HasPrefix(src, "+") {
- symbol = "+"
- }
- prefix := symbol + "execute"
- if strings.HasPrefix(src[1:], "reload") {
- prefix = symbol + "reload"
- } else if strings.HasPrefix(src[1:], "preview") {
- prefix = symbol + "preview"
- } else if strings.HasPrefix(src[1:], "unbind") {
- prefix = symbol + "unbind"
- } else if strings.HasPrefix(src[1:], "change-prompt") {
- prefix = symbol + "change-prompt"
- } else if src[len(prefix)] == '-' {
- c := src[len(prefix)+1]
- if c == 's' || c == 'S' {
- prefix += "-silent"
- } else {
- prefix += "-multi"
- }
- }
- return prefix + "(" + strings.Repeat(" ", len(src)-len(prefix)-2) + ")"
- })
- masked = strings.Replace(masked, "::", string([]rune{escapedColon, ':'}), -1)
- masked = strings.Replace(masked, ",:", string([]rune{escapedComma, ':'}), -1)
- masked = strings.Replace(masked, "+:", string([]rune{escapedPlus, ':'}), -1)
-
- idx := 0
- for _, pairStr := range strings.Split(masked, ",") {
- origPairStr := str[idx : idx+len(pairStr)]
- idx += len(pairStr) + 1
-
- pair := strings.SplitN(pairStr, ":", 2)
- if len(pair) < 2 {
- errorExit("bind action not specified: " + origPairStr)
- }
- var key tui.Event
- if len(pair[0]) == 1 && pair[0][0] == escapedColon {
- key = tui.Key(':')
- } else if len(pair[0]) == 1 && pair[0][0] == escapedComma {
- key = tui.Key(',')
- } else if len(pair[0]) == 1 && pair[0][0] == escapedPlus {
- key = tui.Key('+')
- } else {
- keys := parseKeyChords(pair[0], "key name required")
- key = firstKey(keys)
- }
-
- idx2 := len(pair[0]) + 1
- specs := strings.Split(pair[1], "+")
- actions := make([]action, 0, len(specs))
- appendAction := func(types ...actionType) {
- actions = append(actions, toActions(types...)...)
- }
- prevSpec := ""
- for specIndex, maskedSpec := range specs {
- spec := origPairStr[idx2 : idx2+len(maskedSpec)]
- idx2 += len(maskedSpec) + 1
- spec = prevSpec + spec
- specLower := strings.ToLower(spec)
- switch specLower {
- case "ignore":
- appendAction(actIgnore)
- case "beginning-of-line":
- appendAction(actBeginningOfLine)
- case "abort":
- appendAction(actAbort)
- case "accept":
- appendAction(actAccept)
- case "accept-non-empty":
- appendAction(actAcceptNonEmpty)
- case "print-query":
- appendAction(actPrintQuery)
- case "refresh-preview":
- appendAction(actRefreshPreview)
- case "replace-query":
- appendAction(actReplaceQuery)
- case "backward-char":
- appendAction(actBackwardChar)
- case "backward-delete-char":
- appendAction(actBackwardDeleteChar)
- case "backward-delete-char/eof":
- appendAction(actBackwardDeleteCharEOF)
- case "backward-word":
- appendAction(actBackwardWord)
- case "clear-screen":
- appendAction(actClearScreen)
- case "delete-char":
- appendAction(actDeleteChar)
- case "delete-char/eof":
- appendAction(actDeleteCharEOF)
- case "deselect":
- appendAction(actDeselect)
- case "end-of-line":
- appendAction(actEndOfLine)
- case "cancel":
- appendAction(actCancel)
- case "clear-query":
- appendAction(actClearQuery)
- case "clear-selection":
- appendAction(actClearSelection)
- case "forward-char":
- appendAction(actForwardChar)
- case "forward-word":
- appendAction(actForwardWord)
- case "jump":
- appendAction(actJump)
- case "jump-accept":
- appendAction(actJumpAccept)
- case "kill-line":
- appendAction(actKillLine)
- case "kill-word":
- appendAction(actKillWord)
- case "unix-line-discard", "line-discard":
- appendAction(actUnixLineDiscard)
- case "unix-word-rubout", "word-rubout":
- appendAction(actUnixWordRubout)
- case "yank":
- appendAction(actYank)
- case "backward-kill-word":
- appendAction(actBackwardKillWord)
- case "toggle-down":
- appendAction(actToggle, actDown)
- case "toggle-up":
- appendAction(actToggle, actUp)
- case "toggle-in":
- appendAction(actToggleIn)
- case "toggle-out":
- appendAction(actToggleOut)
- case "toggle-all":
- appendAction(actToggleAll)
- case "toggle-search":
- appendAction(actToggleSearch)
- case "select":
- appendAction(actSelect)
- case "select-all":
- appendAction(actSelectAll)
- case "deselect-all":
- appendAction(actDeselectAll)
- case "close":
- appendAction(actClose)
- case "toggle":
- appendAction(actToggle)
- case "down":
- appendAction(actDown)
- case "up":
- appendAction(actUp)
- case "first", "top":
- appendAction(actFirst)
- case "last":
- appendAction(actLast)
- case "page-up":
- appendAction(actPageUp)
- case "page-down":
- appendAction(actPageDown)
- case "half-page-up":
- appendAction(actHalfPageUp)
- case "half-page-down":
- appendAction(actHalfPageDown)
- case "previous-history":
- appendAction(actPreviousHistory)
- case "next-history":
- appendAction(actNextHistory)
- case "toggle-preview":
- appendAction(actTogglePreview)
- case "toggle-preview-wrap":
- appendAction(actTogglePreviewWrap)
- case "toggle-sort":
- appendAction(actToggleSort)
- case "preview-top":
- appendAction(actPreviewTop)
- case "preview-bottom":
- appendAction(actPreviewBottom)
- case "preview-up":
- appendAction(actPreviewUp)
- case "preview-down":
- appendAction(actPreviewDown)
- case "preview-page-up":
- appendAction(actPreviewPageUp)
- case "preview-page-down":
- appendAction(actPreviewPageDown)
- case "preview-half-page-up":
- appendAction(actPreviewHalfPageUp)
- case "preview-half-page-down":
- appendAction(actPreviewHalfPageDown)
- case "enable-search":
- appendAction(actEnableSearch)
- case "disable-search":
- appendAction(actDisableSearch)
- case "put":
- if key.Type == tui.Rune && unicode.IsGraphic(key.Char) {
- appendAction(actRune)
- } else {
- errorExit("unable to put non-printable character: " + pair[0])
- }
- default:
- t := isExecuteAction(specLower)
- if t == actIgnore {
- if specIndex == 0 && specLower == "" {
- actions = append(keymap[key], actions...)
- } else {
- errorExit("unknown action: " + spec)
- }
- } else {
- var offset int
- switch t {
- case actReload:
- offset = len("reload")
- case actPreview:
- offset = len("preview")
- case actChangePrompt:
- offset = len("change-prompt")
- case actUnbind:
- offset = len("unbind")
- case actExecuteSilent:
- offset = len("execute-silent")
- case actExecuteMulti:
- offset = len("execute-multi")
- default:
- offset = len("execute")
- }
- var actionArg string
- if spec[offset] == ':' {
- if specIndex == len(specs)-1 {
- actionArg = spec[offset+1:]
- actions = append(actions, action{t: t, a: actionArg})
- } else {
- prevSpec = spec + "+"
- continue
- }
- } else {
- actionArg = spec[offset+1 : len(spec)-1]
- actions = append(actions, action{t: t, a: actionArg})
- }
- if t == actUnbind {
- parseKeyChords(actionArg, "unbind target required")
- }
- }
- }
- prevSpec = ""
- }
- keymap[key] = actions
- }
-}
-
-func isExecuteAction(str string) actionType {
- matches := executeRegexp.FindAllStringSubmatch(":"+str, -1)
- if matches == nil || len(matches) != 1 {
- return actIgnore
- }
- prefix := matches[0][1]
- if len(prefix) == 0 {
- prefix = matches[0][2]
- }
- switch prefix {
- case "reload":
- return actReload
- case "unbind":
- return actUnbind
- case "preview":
- return actPreview
- case "change-prompt":
- return actChangePrompt
- case "execute":
- return actExecute
- case "execute-silent":
- return actExecuteSilent
- case "execute-multi":
- return actExecuteMulti
- }
- return actIgnore
-}
-
-func parseToggleSort(keymap map[tui.Event][]action, str string) {
- keys := parseKeyChords(str, "key name required")
- if len(keys) != 1 {
- errorExit("multiple keys specified")
- }
- keymap[firstKey(keys)] = toActions(actToggleSort)
-}
-
-func strLines(str string) []string {
- return strings.Split(strings.TrimSuffix(str, "\n"), "\n")
-}
-
-func parseSize(str string, maxPercent float64, label string) sizeSpec {
- var val float64
- percent := strings.HasSuffix(str, "%")
- if percent {
- val = atof(str[:len(str)-1])
- if val < 0 {
- errorExit(label + " must be non-negative")
- }
- if val > maxPercent {
- errorExit(fmt.Sprintf("%s too large (max: %d%%)", label, int(maxPercent)))
- }
- } else {
- if strings.Contains(str, ".") {
- errorExit(label + " (without %) must be a non-negative integer")
- }
-
- val = float64(atoi(str))
- if val < 0 {
- errorExit(label + " must be non-negative")
- }
- }
- return sizeSpec{val, percent}
-}
-
-func parseHeight(str string) sizeSpec {
- size := parseSize(str, 100, "height")
- return size
-}
-
-func parseLayout(str string) layoutType {
- switch str {
- case "default":
- return layoutDefault
- case "reverse":
- return layoutReverse
- case "reverse-list":
- return layoutReverseList
- default:
- errorExit("invalid layout (expected: default / reverse / reverse-list)")
- }
- return layoutDefault
-}
-
-func parseInfoStyle(str string) infoStyle {
- switch str {
- case "default":
- return infoDefault
- case "inline":
- return infoInline
- case "hidden":
- return infoHidden
- default:
- errorExit("invalid info style (expected: default / inline / hidden)")
- }
- return infoDefault
-}
-
-func parsePreviewWindow(opts *previewOpts, input string) {
- delimRegex := regexp.MustCompile("[:,]") // : for backward compatibility
- sizeRegex := regexp.MustCompile("^[0-9]+%?$")
- offsetRegex := regexp.MustCompile(`^(\+{-?[0-9]+})?([+-][0-9]+)*(-?/[1-9][0-9]*)?$`)
- headerRegex := regexp.MustCompile("^~(0|[1-9][0-9]*)$")
- tokens := delimRegex.Split(input, -1)
- for _, token := range tokens {
- switch token {
- case "":
- case "default":
- *opts = defaultPreviewOpts(opts.command)
- case "hidden":
- opts.hidden = true
- case "nohidden":
- opts.hidden = false
- case "wrap":
- opts.wrap = true
- case "nowrap":
- opts.wrap = false
- case "cycle":
- opts.cycle = true
- case "nocycle":
- opts.cycle = false
- case "up", "top":
- opts.position = posUp
- case "down", "bottom":
- opts.position = posDown
- case "left":
- opts.position = posLeft
- case "right":
- opts.position = posRight
- case "rounded", "border", "border-rounded":
- opts.border = tui.BorderRounded
- case "sharp", "border-sharp":
- opts.border = tui.BorderSharp
- case "noborder", "border-none":
- opts.border = tui.BorderNone
- case "border-horizontal":
- opts.border = tui.BorderHorizontal
- case "border-vertical":
- opts.border = tui.BorderVertical
- case "border-top":
- opts.border = tui.BorderTop
- case "border-bottom":
- opts.border = tui.BorderBottom
- case "border-left":
- opts.border = tui.BorderLeft
- case "border-right":
- opts.border = tui.BorderRight
- case "follow":
- opts.follow = true
- case "nofollow":
- opts.follow = false
- default:
- if headerRegex.MatchString(token) {
- opts.headerLines = atoi(token[1:])
- } else if sizeRegex.MatchString(token) {
- opts.size = parseSize(token, 99, "window size")
- } else if offsetRegex.MatchString(token) {
- opts.scroll = token
- } else {
- errorExit("invalid preview window option: " + token)
- }
- }
- }
-}
-
-func parseMargin(opt string, margin string) [4]sizeSpec {
- margins := strings.Split(margin, ",")
- checked := func(str string) sizeSpec {
- return parseSize(str, 49, opt)
- }
- switch len(margins) {
- case 1:
- m := checked(margins[0])
- return [4]sizeSpec{m, m, m, m}
- case 2:
- tb := checked(margins[0])
- rl := checked(margins[1])
- return [4]sizeSpec{tb, rl, tb, rl}
- case 3:
- t := checked(margins[0])
- rl := checked(margins[1])
- b := checked(margins[2])
- return [4]sizeSpec{t, rl, b, rl}
- case 4:
- return [4]sizeSpec{
- checked(margins[0]), checked(margins[1]),
- checked(margins[2]), checked(margins[3])}
- default:
- errorExit("invalid " + opt + ": " + margin)
- }
- return defaultMargin()
-}
-
-func parseOptions(opts *Options, allArgs []string) {
- var historyMax int
- if opts.History == nil {
- historyMax = defaultHistoryMax
- } else {
- historyMax = opts.History.maxSize
- }
- setHistory := func(path string) {
- h, e := NewHistory(path, historyMax)
- if e != nil {
- errorExit(e.Error())
- }
- opts.History = h
- }
- setHistoryMax := func(max int) {
- historyMax = max
- if historyMax < 1 {
- errorExit("history max must be a positive integer")
- }
- if opts.History != nil {
- opts.History.maxSize = historyMax
- }
- }
- validateJumpLabels := false
- validatePointer := false
- validateMarker := false
- for i := 0; i < len(allArgs); i++ {
- arg := allArgs[i]
- switch arg {
- case "-h", "--help":
- help(exitOk)
- case "-x", "--extended":
- opts.Extended = true
- case "-e", "--exact":
- opts.Fuzzy = false
- case "--extended-exact":
- // Note that we now don't have --no-extended-exact
- opts.Fuzzy = false
- opts.Extended = true
- case "+x", "--no-extended":
- opts.Extended = false
- case "+e", "--no-exact":
- opts.Fuzzy = true
- case "-q", "--query":
- opts.Query = nextString(allArgs, &i, "query string required")
- case "-f", "--filter":
- filter := nextString(allArgs, &i, "query string required")
- opts.Filter = &filter
- case "--literal":
- opts.Normalize = false
- case "--no-literal":
- opts.Normalize = true
- case "--algo":
- opts.FuzzyAlgo = parseAlgo(nextString(allArgs, &i, "algorithm required (v1|v2)"))
- case "--expect":
- for k, v := range parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required") {
- opts.Expect[k] = v
- }
- case "--no-expect":
- opts.Expect = make(map[tui.Event]string)
- case "--enabled", "--no-phony":
- opts.Phony = false
- case "--disabled", "--phony":
- opts.Phony = true
- case "--tiebreak":
- opts.Criteria = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
- case "--bind":
- parseKeymap(opts.Keymap, nextString(allArgs, &i, "bind expression required"))
- case "--color":
- _, spec := optionalNextString(allArgs, &i)
- if len(spec) == 0 {
- opts.Theme = tui.EmptyTheme()
- } else {
- opts.Theme = parseTheme(opts.Theme, spec)
- }
- case "--toggle-sort":
- parseToggleSort(opts.Keymap, nextString(allArgs, &i, "key name required"))
- case "-d", "--delimiter":
- opts.Delimiter = delimiterRegexp(nextString(allArgs, &i, "delimiter required"))
- case "-n", "--nth":
- opts.Nth = splitNth(nextString(allArgs, &i, "nth expression required"))
- case "--with-nth":
- opts.WithNth = splitNth(nextString(allArgs, &i, "nth expression required"))
- case "-s", "--sort":
- opts.Sort = optionalNumeric(allArgs, &i, 1)
- case "+s", "--no-sort":
- opts.Sort = 0
- case "--tac":
- opts.Tac = true
- case "--no-tac":
- opts.Tac = false
- case "-i":
- opts.Case = CaseIgnore
- case "+i":
- opts.Case = CaseRespect
- case "-m", "--multi":
- opts.Multi = optionalNumeric(allArgs, &i, maxMulti)
- case "+m", "--no-multi":
- opts.Multi = 0
- case "--ansi":
- opts.Ansi = true
- case "--no-ansi":
- opts.Ansi = false
- case "--no-mouse":
- opts.Mouse = false
- case "+c", "--no-color":
- opts.Theme = tui.NoColorTheme()
- case "+2", "--no-256":
- opts.Theme = tui.Default16
- case "--black":
- opts.Black = true
- case "--no-black":
- opts.Black = false
- case "--bold":
- opts.Bold = true
- case "--no-bold":
- opts.Bold = false
- case "--layout":
- opts.Layout = parseLayout(
- nextString(allArgs, &i, "layout required (default / reverse / reverse-list)"))
- case "--reverse":
- opts.Layout = layoutReverse
- case "--no-reverse":
- opts.Layout = layoutDefault
- case "--cycle":
- opts.Cycle = true
- case "--no-cycle":
- opts.Cycle = false
- case "--keep-right":
- opts.KeepRight = true
- case "--no-keep-right":
- opts.KeepRight = false
- case "--hscroll":
- opts.Hscroll = true
- case "--no-hscroll":
- opts.Hscroll = false
- case "--hscroll-off":
- opts.HscrollOff = nextInt(allArgs, &i, "hscroll offset required")
- case "--scroll-off":
- opts.ScrollOff = nextInt(allArgs, &i, "scroll offset required")
- case "--filepath-word":
- opts.FileWord = true
- case "--no-filepath-word":
- opts.FileWord = false
- case "--info":
- opts.InfoStyle = parseInfoStyle(
- nextString(allArgs, &i, "info style required"))
- case "--no-info":
- opts.InfoStyle = infoHidden
- case "--inline-info":
- opts.InfoStyle = infoInline
- case "--no-inline-info":
- opts.InfoStyle = infoDefault
- case "--jump-labels":
- opts.JumpLabels = nextString(allArgs, &i, "label characters required")
- validateJumpLabels = true
- case "-1", "--select-1":
- opts.Select1 = true
- case "+1", "--no-select-1":
- opts.Select1 = false
- case "-0", "--exit-0":
- opts.Exit0 = true
- case "+0", "--no-exit-0":
- opts.Exit0 = false
- case "--read0":
- opts.ReadZero = true
- case "--no-read0":
- opts.ReadZero = false
- case "--print0":
- opts.Printer = func(str string) { fmt.Print(str, "\x00") }
- opts.PrintSep = "\x00"
- case "--no-print0":
- opts.Printer = func(str string) { fmt.Println(str) }
- opts.PrintSep = "\n"
- case "--print-query":
- opts.PrintQuery = true
- case "--no-print-query":
- opts.PrintQuery = false
- case "--prompt":
- opts.Prompt = nextString(allArgs, &i, "prompt string required")
- case "--pointer":
- opts.Pointer = nextString(allArgs, &i, "pointer sign string required")
- validatePointer = true
- case "--marker":
- opts.Marker = nextString(allArgs, &i, "selected sign string required")
- validateMarker = true
- case "--sync":
- opts.Sync = true
- case "--no-sync":
- opts.Sync = false
- case "--async":
- opts.Sync = false
- case "--no-history":
- opts.History = nil
- case "--history":
- setHistory(nextString(allArgs, &i, "history file path required"))
- case "--history-size":
- setHistoryMax(nextInt(allArgs, &i, "history max size required"))
- case "--no-header":
- opts.Header = []string{}
- case "--no-header-lines":
- opts.HeaderLines = 0
- case "--header":
- opts.Header = strLines(nextString(allArgs, &i, "header string required"))
- case "--header-lines":
- opts.HeaderLines = atoi(
- nextString(allArgs, &i, "number of header lines required"))
- case "--header-first":
- opts.HeaderFirst = true
- case "--no-header-first":
- opts.HeaderFirst = false
- case "--preview":
- opts.Preview.command = nextString(allArgs, &i, "preview command required")
- case "--no-preview":
- opts.Preview.command = ""
- case "--preview-window":
- parsePreviewWindow(&opts.Preview,
- nextString(allArgs, &i, "preview window layout required: [up|down|left|right][,SIZE[%]][,border-BORDER_OPT][,wrap][,cycle][,hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default]"))
- case "--height":
- opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
- case "--min-height":
- opts.MinHeight = nextInt(allArgs, &i, "height required: HEIGHT")
- case "--no-height":
- opts.Height = sizeSpec{}
- case "--no-margin":
- opts.Margin = defaultMargin()
- case "--no-padding":
- opts.Padding = defaultMargin()
- case "--no-border":
- opts.BorderShape = tui.BorderNone
- case "--border":
- hasArg, arg := optionalNextString(allArgs, &i)
- opts.BorderShape = parseBorder(arg, !hasArg)
- case "--no-unicode":
- opts.Unicode = false
- case "--unicode":
- opts.Unicode = true
- case "--margin":
- opts.Margin = parseMargin(
- "margin",
- nextString(allArgs, &i, "margin required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))
- case "--padding":
- opts.Padding = parseMargin(
- "padding",
- nextString(allArgs, &i, "padding required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))
- case "--tabstop":
- opts.Tabstop = nextInt(allArgs, &i, "tab stop required")
- case "--clear":
- opts.ClearOnExit = true
- case "--no-clear":
- opts.ClearOnExit = false
- case "--version":
- opts.Version = true
- default:
- if match, value := optString(arg, "--algo="); match {
- opts.FuzzyAlgo = parseAlgo(value)
- } else if match, value := optString(arg, "-q", "--query="); match {
- opts.Query = value
- } else if match, value := optString(arg, "-f", "--filter="); match {
- opts.Filter = &value
- } else if match, value := optString(arg, "-d", "--delimiter="); match {
- opts.Delimiter = delimiterRegexp(value)
- } else if match, value := optString(arg, "--border="); match {
- opts.BorderShape = parseBorder(value, false)
- } else if match, value := optString(arg, "--prompt="); match {
- opts.Prompt = value
- } else if match, value := optString(arg, "--pointer="); match {
- opts.Pointer = value
- validatePointer = true
- } else if match, value := optString(arg, "--marker="); match {
- opts.Marker = value
- validateMarker = true
- } else if match, value := optString(arg, "-n", "--nth="); match {
- opts.Nth = splitNth(value)
- } else if match, value := optString(arg, "--with-nth="); match {
- opts.WithNth = splitNth(value)
- } else if match, _ := optString(arg, "-s", "--sort="); match {
- opts.Sort = 1 // Don't care
- } else if match, value := optString(arg, "-m", "--multi="); match {
- opts.Multi = atoi(value)
- } else if match, value := optString(arg, "--height="); match {
- opts.Height = parseHeight(value)
- } else if match, value := optString(arg, "--min-height="); match {
- opts.MinHeight = atoi(value)
- } else if match, value := optString(arg, "--layout="); match {
- opts.Layout = parseLayout(value)
- } else if match, value := optString(arg, "--info="); match {
- opts.InfoStyle = parseInfoStyle(value)
- } else if match, value := optString(arg, "--toggle-sort="); match {
- parseToggleSort(opts.Keymap, value)
- } else if match, value := optString(arg, "--expect="); match {
- for k, v := range parseKeyChords(value, "key names required") {
- opts.Expect[k] = v
- }
- } else if match, value := optString(arg, "--tiebreak="); match {
- opts.Criteria = parseTiebreak(value)
- } else if match, value := optString(arg, "--color="); match {
- opts.Theme = parseTheme(opts.Theme, value)
- } else if match, value := optString(arg, "--bind="); match {
- parseKeymap(opts.Keymap, value)
- } else if match, value := optString(arg, "--history="); match {
- setHistory(value)
- } else if match, value := optString(arg, "--history-size="); match {
- setHistoryMax(atoi(value))
- } else if match, value := optString(arg, "--header="); match {
- opts.Header = strLines(value)
- } else if match, value := optString(arg, "--header-lines="); match {
- opts.HeaderLines = atoi(value)
- } else if match, value := optString(arg, "--preview="); match {
- opts.Preview.command = value
- } else if match, value := optString(arg, "--preview-window="); match {
- parsePreviewWindow(&opts.Preview, value)
- } else if match, value := optString(arg, "--margin="); match {
- opts.Margin = parseMargin("margin", value)
- } else if match, value := optString(arg, "--padding="); match {
- opts.Padding = parseMargin("padding", value)
- } else if match, value := optString(arg, "--tabstop="); match {
- opts.Tabstop = atoi(value)
- } else if match, value := optString(arg, "--hscroll-off="); match {
- opts.HscrollOff = atoi(value)
- } else if match, value := optString(arg, "--scroll-off="); match {
- opts.ScrollOff = atoi(value)
- } else if match, value := optString(arg, "--jump-labels="); match {
- opts.JumpLabels = value
- validateJumpLabels = true
- } else {
- errorExit("unknown option: " + arg)
- }
- }
- }
-
- if opts.HeaderLines < 0 {
- errorExit("header lines must be a non-negative integer")
- }
-
- if opts.HscrollOff < 0 {
- errorExit("hscroll offset must be a non-negative integer")
- }
-
- if opts.ScrollOff < 0 {
- errorExit("scroll offset must be a non-negative integer")
- }
-
- if opts.Tabstop < 1 {
- errorExit("tab stop must be a positive integer")
- }
-
- if len(opts.JumpLabels) == 0 {
- errorExit("empty jump labels")
- }
-
- if validateJumpLabels {
- for _, r := range opts.JumpLabels {
- if r < 32 || r > 126 {
- errorExit("non-ascii jump labels are not allowed")
- }
- }
- }
-
- if validatePointer {
- if err := validateSign(opts.Pointer, "pointer"); err != nil {
- errorExit(err.Error())
- }
- }
-
- if validateMarker {
- if err := validateSign(opts.Marker, "marker"); err != nil {
- errorExit(err.Error())
- }
- }
-}
-
-func validateSign(sign string, signOptName string) error {
- if sign == "" {
- return fmt.Errorf("%v cannot be empty", signOptName)
- }
- for _, r := range sign {
- if !unicode.IsGraphic(r) {
- return fmt.Errorf("invalid character in %v", signOptName)
- }
- }
- if runewidth.StringWidth(sign) > 2 {
- return fmt.Errorf("%v display width should be up to 2", signOptName)
- }
- return nil
-}
-
-func postProcessOptions(opts *Options) {
- if !opts.Version && !tui.IsLightRendererSupported() && opts.Height.size > 0 {
- errorExit("--height option is currently not supported on this platform")
- }
- // Default actions for CTRL-N / CTRL-P when --history is set
- if opts.History != nil {
- if _, prs := opts.Keymap[tui.CtrlP.AsEvent()]; !prs {
- opts.Keymap[tui.CtrlP.AsEvent()] = toActions(actPreviousHistory)
- }
- if _, prs := opts.Keymap[tui.CtrlN.AsEvent()]; !prs {
- opts.Keymap[tui.CtrlN.AsEvent()] = toActions(actNextHistory)
- }
- }
-
- // Extend the default key map
- keymap := defaultKeymap()
- for key, actions := range opts.Keymap {
- for _, act := range actions {
- if act.t == actToggleSort {
- opts.ToggleSort = true
- }
- }
- keymap[key] = actions
- }
- opts.Keymap = keymap
-
- // If we're not using extended search mode, --nth option becomes irrelevant
- // if it contains the whole range
- if !opts.Extended || len(opts.Nth) == 1 {
- for _, r := range opts.Nth {
- if r.begin == rangeEllipsis && r.end == rangeEllipsis {
- opts.Nth = make([]Range, 0)
- return
- }
- }
- }
-
- if opts.Bold {
- theme := opts.Theme
- boldify := func(c tui.ColorAttr) tui.ColorAttr {
- dup := c
- if !theme.Colored {
- dup.Attr |= tui.Bold
- } else if (c.Attr & tui.AttrRegular) == 0 {
- dup.Attr |= tui.Bold
- }
- return dup
- }
- theme.Current = boldify(theme.Current)
- theme.CurrentMatch = boldify(theme.CurrentMatch)
- theme.Prompt = boldify(theme.Prompt)
- theme.Input = boldify(theme.Input)
- theme.Cursor = boldify(theme.Cursor)
- theme.Spinner = boldify(theme.Spinner)
- }
-}
-
-// ParseOptions parses command-line options
-func ParseOptions() *Options {
- opts := defaultOptions()
-
- // Options from Env var
- words, _ := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS"))
- if len(words) > 0 {
- parseOptions(opts, words)
- }
-
- // Options from command-line arguments
- parseOptions(opts, os.Args[1:])
-
- postProcessOptions(opts)
- return opts
-}
diff --git a/.fzf/src/options_test.go b/.fzf/src/options_test.go
deleted file mode 100644
index bb94623..0000000
--- a/.fzf/src/options_test.go
+++ /dev/null
@@ -1,457 +0,0 @@
-package fzf
-
-import (
- "fmt"
- "io/ioutil"
- "testing"
-
- "github.com/junegunn/fzf/src/tui"
-)
-
-func TestDelimiterRegex(t *testing.T) {
- // Valid regex
- delim := delimiterRegexp(".")
- if delim.regex == nil || delim.str != nil {
- t.Error(delim)
- }
- // Broken regex -> string
- delim = delimiterRegexp("[0-9")
- if delim.regex != nil || *delim.str != "[0-9" {
- t.Error(delim)
- }
- // Valid regex
- delim = delimiterRegexp("[0-9]")
- if delim.regex.String() != "[0-9]" || delim.str != nil {
- t.Error(delim)
- }
- // Tab character
- delim = delimiterRegexp("\t")
- if delim.regex != nil || *delim.str != "\t" {
- t.Error(delim)
- }
- // Tab expression
- delim = delimiterRegexp("\\t")
- if delim.regex != nil || *delim.str != "\t" {
- t.Error(delim)
- }
- // Tabs -> regex
- delim = delimiterRegexp("\t+")
- if delim.regex == nil || delim.str != nil {
- t.Error(delim)
- }
-}
-
-func TestDelimiterRegexString(t *testing.T) {
- delim := delimiterRegexp("*")
- tokens := Tokenize("-*--*---**---", delim)
- if delim.regex != nil ||
- tokens[0].text.ToString() != "-*" ||
- tokens[1].text.ToString() != "--*" ||
- tokens[2].text.ToString() != "---*" ||
- tokens[3].text.ToString() != "*" ||
- tokens[4].text.ToString() != "---" {
- t.Errorf("%s %v %d", delim, tokens, len(tokens))
- }
-}
-
-func TestDelimiterRegexRegex(t *testing.T) {
- delim := delimiterRegexp("--\\*")
- tokens := Tokenize("-*--*---**---", delim)
- if delim.str != nil ||
- tokens[0].text.ToString() != "-*--*" ||
- tokens[1].text.ToString() != "---*" ||
- tokens[2].text.ToString() != "*---" {
- t.Errorf("%s %d", tokens, len(tokens))
- }
-}
-
-func TestSplitNth(t *testing.T) {
- {
- ranges := splitNth("..")
- if len(ranges) != 1 ||
- ranges[0].begin != rangeEllipsis ||
- ranges[0].end != rangeEllipsis {
- t.Errorf("%v", ranges)
- }
- }
- {
- ranges := splitNth("..3,1..,2..3,4..-1,-3..-2,..,2,-2,2..-2,1..-1")
- if len(ranges) != 10 ||
- ranges[0].begin != rangeEllipsis || ranges[0].end != 3 ||
- ranges[1].begin != rangeEllipsis || ranges[1].end != rangeEllipsis ||
- ranges[2].begin != 2 || ranges[2].end != 3 ||
- ranges[3].begin != 4 || ranges[3].end != rangeEllipsis ||
- ranges[4].begin != -3 || ranges[4].end != -2 ||
- ranges[5].begin != rangeEllipsis || ranges[5].end != rangeEllipsis ||
- ranges[6].begin != 2 || ranges[6].end != 2 ||
- ranges[7].begin != -2 || ranges[7].end != -2 ||
- ranges[8].begin != 2 || ranges[8].end != -2 ||
- ranges[9].begin != rangeEllipsis || ranges[9].end != rangeEllipsis {
- t.Errorf("%v", ranges)
- }
- }
-}
-
-func TestIrrelevantNth(t *testing.T) {
- {
- opts := defaultOptions()
- words := []string{"--nth", "..", "-x"}
- parseOptions(opts, words)
- postProcessOptions(opts)
- if len(opts.Nth) != 0 {
- t.Errorf("nth should be empty: %v", opts.Nth)
- }
- }
- for _, words := range [][]string{{"--nth", "..,3", "+x"}, {"--nth", "3,1..", "+x"}, {"--nth", "..-1,1", "+x"}} {
- {
- opts := defaultOptions()
- parseOptions(opts, words)
- postProcessOptions(opts)
- if len(opts.Nth) != 0 {
- t.Errorf("nth should be empty: %v", opts.Nth)
- }
- }
- {
- opts := defaultOptions()
- words = append(words, "-x")
- parseOptions(opts, words)
- postProcessOptions(opts)
- if len(opts.Nth) != 2 {
- t.Errorf("nth should not be empty: %v", opts.Nth)
- }
- }
- }
-}
-
-func TestParseKeys(t *testing.T) {
- pairs := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g,ctrl-alt-a,ALT-enter,alt-SPACE", "")
- checkEvent := func(e tui.Event, s string) {
- if pairs[e] != s {
- t.Errorf("%s != %s", pairs[e], s)
- }
- }
- check := func(et tui.EventType, s string) {
- checkEvent(et.AsEvent(), s)
- }
- if len(pairs) != 12 {
- t.Error(12)
- }
- check(tui.CtrlZ, "ctrl-z")
- check(tui.F2, "f2")
- check(tui.CtrlG, "ctrl-G")
- checkEvent(tui.AltKey('z'), "alt-z")
- checkEvent(tui.Key('@'), "@")
- checkEvent(tui.AltKey('a'), "Alt-a")
- checkEvent(tui.Key('!'), "!")
- checkEvent(tui.Key('J'), "J")
- checkEvent(tui.Key('g'), "g")
- checkEvent(tui.CtrlAltKey('a'), "ctrl-alt-a")
- checkEvent(tui.CtrlAltKey('m'), "ALT-enter")
- checkEvent(tui.AltKey(' '), "alt-SPACE")
-
- // Synonyms
- pairs = parseKeyChords("enter,Return,space,tab,btab,esc,up,down,left,right", "")
- if len(pairs) != 9 {
- t.Error(9)
- }
- check(tui.CtrlM, "Return")
- checkEvent(tui.Key(' '), "space")
- check(tui.Tab, "tab")
- check(tui.BTab, "btab")
- check(tui.ESC, "esc")
- check(tui.Up, "up")
- check(tui.Down, "down")
- check(tui.Left, "left")
- check(tui.Right, "right")
-
- pairs = parseKeyChords("Tab,Ctrl-I,PgUp,page-up,pgdn,Page-Down,Home,End,Alt-BS,Alt-BSpace,shift-left,shift-right,btab,shift-tab,return,Enter,bspace", "")
- if len(pairs) != 11 {
- t.Error(11)
- }
- check(tui.Tab, "Ctrl-I")
- check(tui.PgUp, "page-up")
- check(tui.PgDn, "Page-Down")
- check(tui.Home, "Home")
- check(tui.End, "End")
- check(tui.AltBS, "Alt-BSpace")
- check(tui.SLeft, "shift-left")
- check(tui.SRight, "shift-right")
- check(tui.BTab, "shift-tab")
- check(tui.CtrlM, "Enter")
- check(tui.BSpace, "bspace")
-}
-
-func TestParseKeysWithComma(t *testing.T) {
- checkN := func(a int, b int) {
- if a != b {
- t.Errorf("%d != %d", a, b)
- }
- }
- check := func(pairs map[tui.Event]string, e tui.Event, s string) {
- if pairs[e] != s {
- t.Errorf("%s != %s", pairs[e], s)
- }
- }
-
- pairs := parseKeyChords(",", "")
- checkN(len(pairs), 1)
- check(pairs, tui.Key(','), ",")
-
- pairs = parseKeyChords(",,a,b", "")
- checkN(len(pairs), 3)
- check(pairs, tui.Key('a'), "a")
- check(pairs, tui.Key('b'), "b")
- check(pairs, tui.Key(','), ",")
-
- pairs = parseKeyChords("a,b,,", "")
- checkN(len(pairs), 3)
- check(pairs, tui.Key('a'), "a")
- check(pairs, tui.Key('b'), "b")
- check(pairs, tui.Key(','), ",")
-
- pairs = parseKeyChords("a,,,b", "")
- checkN(len(pairs), 3)
- check(pairs, tui.Key('a'), "a")
- check(pairs, tui.Key('b'), "b")
- check(pairs, tui.Key(','), ",")
-
- pairs = parseKeyChords("a,,,b,c", "")
- checkN(len(pairs), 4)
- check(pairs, tui.Key('a'), "a")
- check(pairs, tui.Key('b'), "b")
- check(pairs, tui.Key('c'), "c")
- check(pairs, tui.Key(','), ",")
-
- pairs = parseKeyChords(",,,", "")
- checkN(len(pairs), 1)
- check(pairs, tui.Key(','), ",")
-
- pairs = parseKeyChords(",ALT-,,", "")
- checkN(len(pairs), 1)
- check(pairs, tui.AltKey(','), "ALT-,")
-}
-
-func TestBind(t *testing.T) {
- keymap := defaultKeymap()
- check := func(event tui.Event, arg1 string, types ...actionType) {
- if len(keymap[event]) != len(types) {
- t.Errorf("invalid number of actions for %v (%d != %d)",
- event, len(types), len(keymap[event]))
- return
- }
- for idx, action := range keymap[event] {
- if types[idx] != action.t {
- t.Errorf("invalid action type (%d != %d)", types[idx], action.t)
- }
- }
- if len(arg1) > 0 && keymap[event][0].a != arg1 {
- t.Errorf("invalid action argument: (%s != %s)", arg1, keymap[event][0].a)
- }
- }
- check(tui.CtrlA.AsEvent(), "", actBeginningOfLine)
- parseKeymap(keymap,
- "ctrl-a:kill-line,ctrl-b:toggle-sort+up+down,c:page-up,alt-z:page-down,"+
- "f1:execute(ls {+})+abort+execute(echo {+})+select-all,f2:execute/echo {}, {}, {}/,f3:execute[echo '({})'],f4:execute;less {};,"+
- "alt-a:execute-Multi@echo (,),[,],/,:,;,%,{}@,alt-b:execute;echo (,),[,],/,:,@,%,{};,"+
- "x:Execute(foo+bar),X:execute/bar+baz/"+
- ",f1:+first,f1:+top"+
- ",,:abort,::accept,+:execute:++\nfoobar,Y:execute(baz)+up")
- check(tui.CtrlA.AsEvent(), "", actKillLine)
- check(tui.CtrlB.AsEvent(), "", actToggleSort, actUp, actDown)
- check(tui.Key('c'), "", actPageUp)
- check(tui.Key(','), "", actAbort)
- check(tui.Key(':'), "", actAccept)
- check(tui.AltKey('z'), "", actPageDown)
- check(tui.F1.AsEvent(), "ls {+}", actExecute, actAbort, actExecute, actSelectAll, actFirst, actFirst)
- check(tui.F2.AsEvent(), "echo {}, {}, {}", actExecute)
- check(tui.F3.AsEvent(), "echo '({})'", actExecute)
- check(tui.F4.AsEvent(), "less {}", actExecute)
- check(tui.Key('x'), "foo+bar", actExecute)
- check(tui.Key('X'), "bar+baz", actExecute)
- check(tui.AltKey('a'), "echo (,),[,],/,:,;,%,{}", actExecuteMulti)
- check(tui.AltKey('b'), "echo (,),[,],/,:,@,%,{}", actExecute)
- check(tui.Key('+'), "++\nfoobar,Y:execute(baz)+up", actExecute)
-
- for idx, char := range []rune{'~', '!', '@', '#', '$', '%', '^', '&', '*', '|', ';', '/'} {
- parseKeymap(keymap, fmt.Sprintf("%d:execute%cfoobar%c", idx%10, char, char))
- check(tui.Key([]rune(fmt.Sprintf("%d", idx%10))[0]), "foobar", actExecute)
- }
-
- parseKeymap(keymap, "f1:abort")
- check(tui.F1.AsEvent(), "", actAbort)
-}
-
-func TestColorSpec(t *testing.T) {
- theme := tui.Dark256
- dark := parseTheme(theme, "dark")
- if *dark != *theme {
- t.Errorf("colors should be equivalent")
- }
- if dark == theme {
- t.Errorf("point should not be equivalent")
- }
-
- light := parseTheme(theme, "dark,light")
- if *light == *theme {
- t.Errorf("should not be equivalent")
- }
- if *light != *tui.Light256 {
- t.Errorf("colors should be equivalent")
- }
- if light == theme {
- t.Errorf("point should not be equivalent")
- }
-
- customized := parseTheme(theme, "fg:231,bg:232")
- if customized.Fg.Color != 231 || customized.Bg.Color != 232 {
- t.Errorf("color not customized")
- }
- if *tui.Dark256 == *customized {
- t.Errorf("colors should not be equivalent")
- }
- customized.Fg = tui.Dark256.Fg
- customized.Bg = tui.Dark256.Bg
- if *tui.Dark256 != *customized {
- t.Errorf("colors should now be equivalent: %v, %v", tui.Dark256, customized)
- }
-
- customized = parseTheme(theme, "fg:231,dark,bg:232")
- if customized.Fg != tui.Dark256.Fg || customized.Bg == tui.Dark256.Bg {
- t.Errorf("color not customized")
- }
-}
-
-func TestDefaultCtrlNP(t *testing.T) {
- check := func(words []string, et tui.EventType, expected actionType) {
- e := et.AsEvent()
- opts := defaultOptions()
- parseOptions(opts, words)
- postProcessOptions(opts)
- if opts.Keymap[e][0].t != expected {
- t.Error()
- }
- }
- check([]string{}, tui.CtrlN, actDown)
- check([]string{}, tui.CtrlP, actUp)
-
- check([]string{"--bind=ctrl-n:accept"}, tui.CtrlN, actAccept)
- check([]string{"--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)
-
- f, _ := ioutil.TempFile("", "fzf-history")
- f.Close()
- hist := "--history=" + f.Name()
- check([]string{hist}, tui.CtrlN, actNextHistory)
- check([]string{hist}, tui.CtrlP, actPreviousHistory)
-
- check([]string{hist, "--bind=ctrl-n:accept"}, tui.CtrlN, actAccept)
- check([]string{hist, "--bind=ctrl-n:accept"}, tui.CtrlP, actPreviousHistory)
-
- check([]string{hist, "--bind=ctrl-p:accept"}, tui.CtrlN, actNextHistory)
- check([]string{hist, "--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)
-}
-
-func optsFor(words ...string) *Options {
- opts := defaultOptions()
- parseOptions(opts, words)
- postProcessOptions(opts)
- return opts
-}
-
-func TestToggle(t *testing.T) {
- opts := optsFor()
- if opts.ToggleSort {
- t.Error()
- }
-
- opts = optsFor("--bind=a:toggle-sort")
- if !opts.ToggleSort {
- t.Error()
- }
-
- opts = optsFor("--bind=a:toggle-sort", "--bind=a:up")
- if opts.ToggleSort {
- t.Error()
- }
-}
-
-func TestPreviewOpts(t *testing.T) {
- opts := optsFor()
- if !(opts.Preview.command == "" &&
- opts.Preview.hidden == false &&
- opts.Preview.wrap == false &&
- opts.Preview.position == posRight &&
- opts.Preview.size.percent == true &&
- opts.Preview.size.size == 50) {
- t.Error()
- }
- opts = optsFor("--preview", "cat {}", "--preview-window=left:15,hidden,wrap:+{1}-/2")
- if !(opts.Preview.command == "cat {}" &&
- opts.Preview.hidden == true &&
- opts.Preview.wrap == true &&
- opts.Preview.position == posLeft &&
- opts.Preview.scroll == "+{1}-/2" &&
- opts.Preview.size.percent == false &&
- opts.Preview.size.size == 15) {
- t.Error(opts.Preview)
- }
- opts = optsFor("--preview-window=up,15,wrap,hidden,+{1}+3-1-2/2", "--preview-window=down", "--preview-window=cycle")
- if !(opts.Preview.command == "" &&
- opts.Preview.hidden == true &&
- opts.Preview.wrap == true &&
- opts.Preview.cycle == true &&
- opts.Preview.position == posDown &&
- opts.Preview.scroll == "+{1}+3-1-2/2" &&
- opts.Preview.size.percent == false &&
- opts.Preview.size.size == 15) {
- t.Error(opts.Preview.size.size)
- }
- opts = optsFor("--preview-window=up:15:wrap:hidden")
- if !(opts.Preview.command == "" &&
- opts.Preview.hidden == true &&
- opts.Preview.wrap == true &&
- opts.Preview.position == posUp &&
- opts.Preview.size.percent == false &&
- opts.Preview.size.size == 15) {
- t.Error(opts.Preview)
- }
- opts = optsFor("--preview=foo", "--preview-window=up", "--preview-window=default:70%")
- if !(opts.Preview.command == "foo" &&
- opts.Preview.position == posRight &&
- opts.Preview.size.percent == true &&
- opts.Preview.size.size == 70) {
- t.Error(opts.Preview)
- }
-}
-
-func TestAdditiveExpect(t *testing.T) {
- opts := optsFor("--expect=a", "--expect", "b", "--expect=c")
- if len(opts.Expect) != 3 {
- t.Error(opts.Expect)
- }
-}
-
-func TestValidateSign(t *testing.T) {
- testCases := []struct {
- inputSign string
- isValid bool
- }{
- {"> ", true},
- {"아", true},
- {"😀", true},
- {"", false},
- {">>>", false},
- {"\n", false},
- {"\t", false},
- }
-
- for _, testCase := range testCases {
- err := validateSign(testCase.inputSign, "")
- if testCase.isValid && err != nil {
- t.Errorf("Input sign `%s` caused error", testCase.inputSign)
- }
-
- if !testCase.isValid && err == nil {
- t.Errorf("Input sign `%s` did not cause error", testCase.inputSign)
- }
- }
-}
diff --git a/.fzf/src/pattern.go b/.fzf/src/pattern.go
deleted file mode 100644
index 4a7a87a..0000000
--- a/.fzf/src/pattern.go
+++ /dev/null
@@ -1,425 +0,0 @@
-package fzf
-
-import (
- "fmt"
- "regexp"
- "strings"
-
- "github.com/junegunn/fzf/src/algo"
- "github.com/junegunn/fzf/src/util"
-)
-
-// fuzzy
-// 'exact
-// ^prefix-exact
-// suffix-exact$
-// !inverse-exact
-// !'inverse-fuzzy
-// !^inverse-prefix-exact
-// !inverse-suffix-exact$
-
-type termType int
-
-const (
- termFuzzy termType = iota
- termExact
- termPrefix
- termSuffix
- termEqual
-)
-
-type term struct {
- typ termType
- inv bool
- text []rune
- caseSensitive bool
- normalize bool
-}
-
-// String returns the string representation of a term.
-func (t term) String() string {
- return fmt.Sprintf("term{typ: %d, inv: %v, text: []rune(%q), caseSensitive: %v}", t.typ, t.inv, string(t.text), t.caseSensitive)
-}
-
-type termSet []term
-
-// Pattern represents search pattern
-type Pattern struct {
- fuzzy bool
- fuzzyAlgo algo.Algo
- extended bool
- caseSensitive bool
- normalize bool
- forward bool
- text []rune
- termSets []termSet
- sortable bool
- cacheable bool
- cacheKey string
- delimiter Delimiter
- nth []Range
- procFun map[termType]algo.Algo
-}
-
-var (
- _patternCache map[string]*Pattern
- _splitRegex *regexp.Regexp
- _cache ChunkCache
-)
-
-func init() {
- _splitRegex = regexp.MustCompile(" +")
- clearPatternCache()
- clearChunkCache()
-}
-
-func clearPatternCache() {
- // We can uniquely identify the pattern for a given string since
- // search mode and caseMode do not change while the program is running
- _patternCache = make(map[string]*Pattern)
-}
-
-func clearChunkCache() {
- _cache = NewChunkCache()
-}
-
-// BuildPattern builds Pattern object from the given arguments
-func BuildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case, normalize bool, forward bool,
- cacheable bool, nth []Range, delimiter Delimiter, runes []rune) *Pattern {
-
- var asString string
- if extended {
- asString = strings.TrimLeft(string(runes), " ")
- for strings.HasSuffix(asString, " ") && !strings.HasSuffix(asString, "\\ ") {
- asString = asString[:len(asString)-1]
- }
- } else {
- asString = string(runes)
- }
-
- cached, found := _patternCache[asString]
- if found {
- return cached
- }
-
- caseSensitive := true
- sortable := true
- termSets := []termSet{}
-
- if extended {
- termSets = parseTerms(fuzzy, caseMode, normalize, asString)
- // We should not sort the result if there are only inverse search terms
- sortable = false
- Loop:
- for _, termSet := range termSets {
- for idx, term := range termSet {
- if !term.inv {
- sortable = true
- }
- // If the query contains inverse search terms or OR operators,
- // we cannot cache the search scope
- if !cacheable || idx > 0 || term.inv || fuzzy && term.typ != termFuzzy || !fuzzy && term.typ != termExact {
- cacheable = false
- if sortable {
- // Can't break until we see at least one non-inverse term
- break Loop
- }
- }
- }
- }
- } else {
- lowerString := strings.ToLower(asString)
- normalize = normalize &&
- lowerString == string(algo.NormalizeRunes([]rune(lowerString)))
- caseSensitive = caseMode == CaseRespect ||
- caseMode == CaseSmart && lowerString != asString
- if !caseSensitive {
- asString = lowerString
- }
- }
-
- ptr := &Pattern{
- fuzzy: fuzzy,
- fuzzyAlgo: fuzzyAlgo,
- extended: extended,
- caseSensitive: caseSensitive,
- normalize: normalize,
- forward: forward,
- text: []rune(asString),
- termSets: termSets,
- sortable: sortable,
- cacheable: cacheable,
- nth: nth,
- delimiter: delimiter,
- procFun: make(map[termType]algo.Algo)}
-
- ptr.cacheKey = ptr.buildCacheKey()
- ptr.procFun[termFuzzy] = fuzzyAlgo
- ptr.procFun[termEqual] = algo.EqualMatch
- ptr.procFun[termExact] = algo.ExactMatchNaive
- ptr.procFun[termPrefix] = algo.PrefixMatch
- ptr.procFun[termSuffix] = algo.SuffixMatch
-
- _patternCache[asString] = ptr
- return ptr
-}
-
-func parseTerms(fuzzy bool, caseMode Case, normalize bool, str string) []termSet {
- str = strings.Replace(str, "\\ ", "\t", -1)
- tokens := _splitRegex.Split(str, -1)
- sets := []termSet{}
- set := termSet{}
- switchSet := false
- afterBar := false
- for _, token := range tokens {
- typ, inv, text := termFuzzy, false, strings.Replace(token, "\t", " ", -1)
- lowerText := strings.ToLower(text)
- caseSensitive := caseMode == CaseRespect ||
- caseMode == CaseSmart && text != lowerText
- normalizeTerm := normalize &&
- lowerText == string(algo.NormalizeRunes([]rune(lowerText)))
- if !caseSensitive {
- text = lowerText
- }
- if !fuzzy {
- typ = termExact
- }
-
- if len(set) > 0 && !afterBar && text == "|" {
- switchSet = false
- afterBar = true
- continue
- }
- afterBar = false
-
- if strings.HasPrefix(text, "!") {
- inv = true
- typ = termExact
- text = text[1:]
- }
-
- if text != "$" && strings.HasSuffix(text, "$") {
- typ = termSuffix
- text = text[:len(text)-1]
- }
-
- if strings.HasPrefix(text, "'") {
- // Flip exactness
- if fuzzy && !inv {
- typ = termExact
- text = text[1:]
- } else {
- typ = termFuzzy
- text = text[1:]
- }
- } else if strings.HasPrefix(text, "^") {
- if typ == termSuffix {
- typ = termEqual
- } else {
- typ = termPrefix
- }
- text = text[1:]
- }
-
- if len(text) > 0 {
- if switchSet {
- sets = append(sets, set)
- set = termSet{}
- }
- textRunes := []rune(text)
- if normalizeTerm {
- textRunes = algo.NormalizeRunes(textRunes)
- }
- set = append(set, term{
- typ: typ,
- inv: inv,
- text: textRunes,
- caseSensitive: caseSensitive,
- normalize: normalizeTerm})
- switchSet = true
- }
- }
- if len(set) > 0 {
- sets = append(sets, set)
- }
- return sets
-}
-
-// IsEmpty returns true if the pattern is effectively empty
-func (p *Pattern) IsEmpty() bool {
- if !p.extended {
- return len(p.text) == 0
- }
- return len(p.termSets) == 0
-}
-
-// AsString returns the search query in string type
-func (p *Pattern) AsString() string {
- return string(p.text)
-}
-
-func (p *Pattern) buildCacheKey() string {
- if !p.extended {
- return p.AsString()
- }
- cacheableTerms := []string{}
- for _, termSet := range p.termSets {
- if len(termSet) == 1 && !termSet[0].inv && (p.fuzzy || termSet[0].typ == termExact) {
- cacheableTerms = append(cacheableTerms, string(termSet[0].text))
- }
- }
- return strings.Join(cacheableTerms, "\t")
-}
-
-// CacheKey is used to build string to be used as the key of result cache
-func (p *Pattern) CacheKey() string {
- return p.cacheKey
-}
-
-// Match returns the list of matches Items in the given Chunk
-func (p *Pattern) Match(chunk *Chunk, slab *util.Slab) []Result {
- // ChunkCache: Exact match
- cacheKey := p.CacheKey()
- if p.cacheable {
- if cached := _cache.Lookup(chunk, cacheKey); cached != nil {
- return cached
- }
- }
-
- // Prefix/suffix cache
- space := _cache.Search(chunk, cacheKey)
-
- matches := p.matchChunk(chunk, space, slab)
-
- if p.cacheable {
- _cache.Add(chunk, cacheKey, matches)
- }
- return matches
-}
-
-func (p *Pattern) matchChunk(chunk *Chunk, space []Result, slab *util.Slab) []Result {
- matches := []Result{}
-
- if space == nil {
- for idx := 0; idx < chunk.count; idx++ {
- if match, _, _ := p.MatchItem(&chunk.items[idx], false, slab); match != nil {
- matches = append(matches, *match)
- }
- }
- } else {
- for _, result := range space {
- if match, _, _ := p.MatchItem(result.item, false, slab); match != nil {
- matches = append(matches, *match)
- }
- }
- }
- return matches
-}
-
-// MatchItem returns true if the Item is a match
-func (p *Pattern) MatchItem(item *Item, withPos bool, slab *util.Slab) (*Result, []Offset, *[]int) {
- if p.extended {
- if offsets, bonus, pos := p.extendedMatch(item, withPos, slab); len(offsets) == len(p.termSets) {
- result := buildResult(item, offsets, bonus)
- return &result, offsets, pos
- }
- return nil, nil, nil
- }
- offset, bonus, pos := p.basicMatch(item, withPos, slab)
- if sidx := offset[0]; sidx >= 0 {
- offsets := []Offset{offset}
- result := buildResult(item, offsets, bonus)
- return &result, offsets, pos
- }
- return nil, nil, nil
-}
-
-func (p *Pattern) basicMatch(item *Item, withPos bool, slab *util.Slab) (Offset, int, *[]int) {
- var input []Token
- if len(p.nth) == 0 {
- input = []Token{{text: &item.text, prefixLength: 0}}
- } else {
- input = p.transformInput(item)
- }
- if p.fuzzy {
- return p.iter(p.fuzzyAlgo, input, p.caseSensitive, p.normalize, p.forward, p.text, withPos, slab)
- }
- return p.iter(algo.ExactMatchNaive, input, p.caseSensitive, p.normalize, p.forward, p.text, withPos, slab)
-}
-
-func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Offset, int, *[]int) {
- var input []Token
- if len(p.nth) == 0 {
- input = []Token{{text: &item.text, prefixLength: 0}}
- } else {
- input = p.transformInput(item)
- }
- offsets := []Offset{}
- var totalScore int
- var allPos *[]int
- if withPos {
- allPos = &[]int{}
- }
- for _, termSet := range p.termSets {
- var offset Offset
- var currentScore int
- matched := false
- for _, term := range termSet {
- pfun := p.procFun[term.typ]
- 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
- }
- offset, currentScore = off, score
- matched = true
- if withPos {
- if pos != nil {
- *allPos = append(*allPos, *pos...)
- } else {
- for idx := off[0]; idx < off[1]; idx++ {
- *allPos = append(*allPos, int(idx))
- }
- }
- }
- break
- } else if term.inv {
- offset, currentScore = Offset{0, 0}, 0
- matched = true
- continue
- }
- }
- if matched {
- offsets = append(offsets, offset)
- totalScore += currentScore
- }
- }
- return offsets, totalScore, allPos
-}
-
-func (p *Pattern) transformInput(item *Item) []Token {
- if item.transformed != nil {
- return *item.transformed
- }
-
- tokens := Tokenize(item.text.ToString(), p.delimiter)
- ret := Transform(tokens, p.nth)
- item.transformed = &ret
- return ret
-}
-
-func (p *Pattern) iter(pfun algo.Algo, tokens []Token, caseSensitive bool, normalize bool, forward bool, pattern []rune, withPos bool, slab *util.Slab) (Offset, int, *[]int) {
- for _, part := range tokens {
- if res, pos := pfun(caseSensitive, normalize, forward, part.text, pattern, withPos, slab); res.Start >= 0 {
- sidx := int32(res.Start) + part.prefixLength
- eidx := int32(res.End) + part.prefixLength
- if pos != nil {
- for idx := range *pos {
- (*pos)[idx] += int(part.prefixLength)
- }
- }
- return Offset{sidx, eidx}, res.Score, pos
- }
- }
- return Offset{-1, -1}, 0, nil
-}
diff --git a/.fzf/src/pattern_test.go b/.fzf/src/pattern_test.go
deleted file mode 100644
index b95d151..0000000
--- a/.fzf/src/pattern_test.go
+++ /dev/null
@@ -1,209 +0,0 @@
-package fzf
-
-import (
- "reflect"
- "testing"
-
- "github.com/junegunn/fzf/src/algo"
- "github.com/junegunn/fzf/src/util"
-)
-
-var slab *util.Slab
-
-func init() {
- slab = util.MakeSlab(slab16Size, slab32Size)
-}
-
-func TestParseTermsExtended(t *testing.T) {
- terms := parseTerms(true, CaseSmart, false,
- "aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$ | ^iii$ ^xxx | 'yyy | zzz$ | !ZZZ |")
- if len(terms) != 9 ||
- terms[0][0].typ != termFuzzy || terms[0][0].inv ||
- terms[1][0].typ != termExact || terms[1][0].inv ||
- terms[2][0].typ != termPrefix || terms[2][0].inv ||
- terms[3][0].typ != termSuffix || terms[3][0].inv ||
- terms[4][0].typ != termExact || !terms[4][0].inv ||
- terms[5][0].typ != termFuzzy || !terms[5][0].inv ||
- terms[6][0].typ != termPrefix || !terms[6][0].inv ||
- terms[7][0].typ != termSuffix || !terms[7][0].inv ||
- terms[7][1].typ != termEqual || terms[7][1].inv ||
- terms[8][0].typ != termPrefix || terms[8][0].inv ||
- terms[8][1].typ != termExact || terms[8][1].inv ||
- terms[8][2].typ != termSuffix || terms[8][2].inv ||
- terms[8][3].typ != termExact || !terms[8][3].inv {
- t.Errorf("%v", terms)
- }
- for _, termSet := range terms[:8] {
- term := termSet[0]
- if len(term.text) != 3 {
- t.Errorf("%v", term)
- }
- }
-}
-
-func TestParseTermsExtendedExact(t *testing.T) {
- terms := parseTerms(false, CaseSmart, false,
- "aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$")
- if len(terms) != 8 ||
- terms[0][0].typ != termExact || terms[0][0].inv || len(terms[0][0].text) != 3 ||
- terms[1][0].typ != termFuzzy || terms[1][0].inv || len(terms[1][0].text) != 3 ||
- terms[2][0].typ != termPrefix || terms[2][0].inv || len(terms[2][0].text) != 3 ||
- terms[3][0].typ != termSuffix || terms[3][0].inv || len(terms[3][0].text) != 3 ||
- terms[4][0].typ != termExact || !terms[4][0].inv || len(terms[4][0].text) != 3 ||
- terms[5][0].typ != termFuzzy || !terms[5][0].inv || len(terms[5][0].text) != 3 ||
- terms[6][0].typ != termPrefix || !terms[6][0].inv || len(terms[6][0].text) != 3 ||
- terms[7][0].typ != termSuffix || !terms[7][0].inv || len(terms[7][0].text) != 3 {
- t.Errorf("%v", terms)
- }
-}
-
-func TestParseTermsEmpty(t *testing.T) {
- terms := parseTerms(true, CaseSmart, false, "' ^ !' !^")
- if len(terms) != 0 {
- t.Errorf("%v", terms)
- }
-}
-
-func TestExact(t *testing.T) {
- defer clearPatternCache()
- clearPatternCache()
- pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true,
- []Range{}, Delimiter{}, []rune("'abc"))
- chars := util.ToChars([]byte("aabbcc abc"))
- res, pos := algo.ExactMatchNaive(
- pattern.caseSensitive, pattern.normalize, pattern.forward, &chars, pattern.termSets[0][0].text, true, nil)
- if res.Start != 7 || res.End != 10 {
- t.Errorf("%v / %d / %d", pattern.termSets, res.Start, res.End)
- }
- if pos != nil {
- t.Errorf("pos is expected to be nil")
- }
-}
-
-func TestEqual(t *testing.T) {
- defer clearPatternCache()
- clearPatternCache()
- pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("^AbC$"))
-
- match := func(str string, sidxExpected int, eidxExpected int) {
- chars := util.ToChars([]byte(str))
- res, pos := algo.EqualMatch(
- pattern.caseSensitive, pattern.normalize, pattern.forward, &chars, pattern.termSets[0][0].text, true, nil)
- if res.Start != sidxExpected || res.End != eidxExpected {
- t.Errorf("%v / %d / %d", pattern.termSets, res.Start, res.End)
- }
- if pos != nil {
- t.Errorf("pos is expected to be nil")
- }
- }
- match("ABC", -1, -1)
- match("AbC", 0, 3)
- match("AbC ", 0, 3)
- match(" AbC ", 1, 4)
- match(" AbC", 2, 5)
-}
-
-func TestCaseSensitivity(t *testing.T) {
- defer clearPatternCache()
- clearPatternCache()
- pat1 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
- clearPatternCache()
- pat2 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
- clearPatternCache()
- pat3 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseIgnore, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
- clearPatternCache()
- pat4 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseIgnore, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
- clearPatternCache()
- pat5 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseRespect, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
- clearPatternCache()
- pat6 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseRespect, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
-
- if string(pat1.text) != "abc" || pat1.caseSensitive != false ||
- string(pat2.text) != "Abc" || pat2.caseSensitive != true ||
- string(pat3.text) != "abc" || pat3.caseSensitive != false ||
- string(pat4.text) != "abc" || pat4.caseSensitive != false ||
- string(pat5.text) != "abc" || pat5.caseSensitive != true ||
- string(pat6.text) != "Abc" || pat6.caseSensitive != true {
- t.Error("Invalid case conversion")
- }
-}
-
-func TestOrigTextAndTransformed(t *testing.T) {
- pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("jg"))
- tokens := Tokenize("junegunn", Delimiter{})
- trans := Transform(tokens, []Range{{1, 1}})
-
- origBytes := []byte("junegunn.choi")
- for _, extended := range []bool{false, true} {
- chunk := Chunk{count: 1}
- chunk.items[0] = Item{
- text: util.ToChars([]byte("junegunn")),
- origText: &origBytes,
- transformed: &trans}
- pattern.extended = extended
- matches := pattern.matchChunk(&chunk, nil, slab) // No cache
- if !(matches[0].item.text.ToString() == "junegunn" &&
- string(*matches[0].item.origText) == "junegunn.choi" &&
- reflect.DeepEqual(*matches[0].item.transformed, trans)) {
- t.Error("Invalid match result", matches)
- }
-
- match, offsets, pos := pattern.MatchItem(&chunk.items[0], true, slab)
- if !(match.item.text.ToString() == "junegunn" &&
- string(*match.item.origText) == "junegunn.choi" &&
- offsets[0][0] == 0 && offsets[0][1] == 5 &&
- reflect.DeepEqual(*match.item.transformed, trans)) {
- t.Error("Invalid match result", match, offsets, extended)
- }
- if !((*pos)[0] == 4 && (*pos)[1] == 0) {
- t.Error("Invalid pos array", *pos)
- }
- }
-}
-
-func TestCacheKey(t *testing.T) {
- test := func(extended bool, patStr string, expected string, cacheable bool) {
- clearPatternCache()
- pat := BuildPattern(true, algo.FuzzyMatchV2, extended, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune(patStr))
- if pat.CacheKey() != expected {
- t.Errorf("Expected: %s, actual: %s", expected, pat.CacheKey())
- }
- if pat.cacheable != cacheable {
- t.Errorf("Expected: %t, actual: %t (%s)", cacheable, pat.cacheable, patStr)
- }
- clearPatternCache()
- }
- test(false, "foo !bar", "foo !bar", true)
- test(false, "foo | bar !baz", "foo | bar !baz", true)
- test(true, "foo bar baz", "foo\tbar\tbaz", true)
- test(true, "foo !bar", "foo", false)
- test(true, "foo !bar baz", "foo\tbaz", false)
- test(true, "foo | bar baz", "baz", false)
- test(true, "foo | bar | baz", "", false)
- test(true, "foo | bar !baz", "", false)
- test(true, "| | foo", "", false)
- test(true, "| | | foo", "foo", false)
-}
-
-func TestCacheable(t *testing.T) {
- test := func(fuzzy bool, str string, expected string, cacheable bool) {
- clearPatternCache()
- pat := BuildPattern(fuzzy, algo.FuzzyMatchV2, true, CaseSmart, true, true, true, []Range{}, Delimiter{}, []rune(str))
- if pat.CacheKey() != expected {
- t.Errorf("Expected: %s, actual: %s", expected, pat.CacheKey())
- }
- if cacheable != pat.cacheable {
- t.Errorf("Invalid Pattern.cacheable for \"%s\": %v (expected: %v)", str, pat.cacheable, cacheable)
- }
- clearPatternCache()
- }
- test(true, "foo bar", "foo\tbar", true)
- test(true, "foo 'bar", "foo\tbar", false)
- test(true, "foo !bar", "foo", false)
-
- test(false, "foo bar", "foo\tbar", true)
- test(false, "foo 'bar", "foo", false)
- test(false, "foo '", "foo", true)
- test(false, "foo 'bar", "foo", false)
- test(false, "foo !bar", "foo", false)
-}
diff --git a/.fzf/src/protector/protector.go b/.fzf/src/protector/protector.go
deleted file mode 100644
index 2739c01..0000000
--- a/.fzf/src/protector/protector.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// +build !openbsd
-
-package protector
-
-// Protect calls OS specific protections like pledge on OpenBSD
-func Protect() {
- return
-}
diff --git a/.fzf/src/protector/protector_openbsd.go b/.fzf/src/protector/protector_openbsd.go
deleted file mode 100644
index 84a5ded..0000000
--- a/.fzf/src/protector/protector_openbsd.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// +build openbsd
-
-package protector
-
-import "golang.org/x/sys/unix"
-
-// Protect calls OS specific protections like pledge on OpenBSD
-func Protect() {
- unix.PledgePromises("stdio rpath tty proc exec")
-}
diff --git a/.fzf/src/reader.go b/.fzf/src/reader.go
deleted file mode 100644
index 06e9b73..0000000
--- a/.fzf/src/reader.go
+++ /dev/null
@@ -1,201 +0,0 @@
-package fzf
-
-import (
- "bufio"
- "context"
- "io"
- "os"
- "os/exec"
- "path/filepath"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/junegunn/fzf/src/util"
- "github.com/saracen/walker"
-)
-
-// Reader reads from command or standard input
-type Reader struct {
- pusher func([]byte) bool
- eventBox *util.EventBox
- delimNil bool
- event int32
- finChan chan bool
- mutex sync.Mutex
- exec *exec.Cmd
- command *string
- killed bool
- wait bool
-}
-
-// NewReader returns new Reader object
-func NewReader(pusher func([]byte) bool, eventBox *util.EventBox, delimNil bool, wait bool) *Reader {
- return &Reader{pusher, eventBox, delimNil, int32(EvtReady), make(chan bool, 1), sync.Mutex{}, nil, nil, false, wait}
-}
-
-func (r *Reader) startEventPoller() {
- go func() {
- ptr := &r.event
- pollInterval := readerPollIntervalMin
- for {
- if atomic.CompareAndSwapInt32(ptr, int32(EvtReadNew), int32(EvtReady)) {
- r.eventBox.Set(EvtReadNew, (*string)(nil))
- pollInterval = readerPollIntervalMin
- } else if atomic.LoadInt32(ptr) == int32(EvtReadFin) {
- if r.wait {
- r.finChan <- true
- }
- return
- } else {
- pollInterval += readerPollIntervalStep
- if pollInterval > readerPollIntervalMax {
- pollInterval = readerPollIntervalMax
- }
- }
- time.Sleep(pollInterval)
- }
- }()
-}
-
-func (r *Reader) fin(success bool) {
- atomic.StoreInt32(&r.event, int32(EvtReadFin))
- if r.wait {
- <-r.finChan
- }
-
- r.mutex.Lock()
- ret := r.command
- if success || r.killed {
- ret = nil
- }
- r.mutex.Unlock()
-
- r.eventBox.Set(EvtReadFin, ret)
-}
-
-func (r *Reader) terminate() {
- r.mutex.Lock()
- defer func() { r.mutex.Unlock() }()
-
- r.killed = true
- if r.exec != nil && r.exec.Process != nil {
- util.KillCommand(r.exec)
- } else if defaultCommand != "" {
- os.Stdin.Close()
- }
-}
-
-func (r *Reader) restart(command string) {
- r.event = int32(EvtReady)
- r.startEventPoller()
- success := r.readFromCommand(nil, command)
- r.fin(success)
-}
-
-// ReadSource reads data from the default command or from standard input
-func (r *Reader) ReadSource() {
- r.startEventPoller()
- var success bool
- if util.IsTty() {
- // The default command for *nix requires bash
- shell := "bash"
- cmd := os.Getenv("FZF_DEFAULT_COMMAND")
- if len(cmd) == 0 {
- if defaultCommand != "" {
- success = r.readFromCommand(&shell, defaultCommand)
- } else {
- success = r.readFiles()
- }
- } else {
- success = r.readFromCommand(nil, cmd)
- }
- } else {
- success = r.readFromStdin()
- }
- r.fin(success)
-}
-
-func (r *Reader) feed(src io.Reader) {
- delim := byte('\n')
- if r.delimNil {
- delim = '\000'
- }
- reader := bufio.NewReaderSize(src, readerBufferSize)
- for {
- // ReadBytes returns err != nil if and only if the returned data does not
- // end in delim.
- bytea, err := reader.ReadBytes(delim)
- byteaLen := len(bytea)
- if byteaLen > 0 {
- if err == nil {
- // get rid of carriage return if under Windows:
- if util.IsWindows() && byteaLen >= 2 && bytea[byteaLen-2] == byte('\r') {
- bytea = bytea[:byteaLen-2]
- } else {
- bytea = bytea[:byteaLen-1]
- }
- }
- if r.pusher(bytea) {
- atomic.StoreInt32(&r.event, int32(EvtReadNew))
- }
- }
- if err != nil {
- break
- }
- }
-}
-
-func (r *Reader) readFromStdin() bool {
- r.feed(os.Stdin)
- return true
-}
-
-func (r *Reader) readFiles() bool {
- r.killed = false
- fn := func(path string, mode os.FileInfo) error {
- path = filepath.Clean(path)
- if path != "." {
- isDir := mode.Mode().IsDir()
- if isDir && filepath.Base(path)[0] == '.' {
- return filepath.SkipDir
- }
- if !isDir && r.pusher([]byte(path)) {
- atomic.StoreInt32(&r.event, int32(EvtReadNew))
- }
- }
- r.mutex.Lock()
- defer r.mutex.Unlock()
- if r.killed {
- return context.Canceled
- }
- return nil
- }
- cb := walker.WithErrorCallback(func(pathname string, err error) error {
- return nil
- })
- return walker.Walk(".", fn, cb) == nil
-}
-
-func (r *Reader) readFromCommand(shell *string, command string) bool {
- r.mutex.Lock()
- r.killed = false
- r.command = &command
- if shell != nil {
- r.exec = util.ExecCommandWith(*shell, command, true)
- } else {
- r.exec = util.ExecCommand(command, true)
- }
- out, err := r.exec.StdoutPipe()
- if err != nil {
- r.mutex.Unlock()
- return false
- }
- err = r.exec.Start()
- r.mutex.Unlock()
- if err != nil {
- return false
- }
- r.feed(out)
- return r.exec.Wait() == nil
-}
diff --git a/.fzf/src/reader_test.go b/.fzf/src/reader_test.go
deleted file mode 100644
index feb45fc..0000000
--- a/.fzf/src/reader_test.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package fzf
-
-import (
- "testing"
- "time"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-func TestReadFromCommand(t *testing.T) {
- strs := []string{}
- eb := util.NewEventBox()
- reader := NewReader(
- func(s []byte) bool { strs = append(strs, string(s)); return true },
- eb, false, true)
-
- reader.startEventPoller()
-
- // Check EventBox
- if eb.Peek(EvtReadNew) {
- t.Error("EvtReadNew should not be set yet")
- }
-
- // Normal command
- reader.fin(reader.readFromCommand(nil, `echo abc&&echo def`))
- if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" {
- t.Errorf("%s", strs)
- }
-
- // Check EventBox again
- eb.WaitFor(EvtReadFin)
-
- // Wait should return immediately
- eb.Wait(func(events *util.Events) {
- events.Clear()
- })
-
- // EventBox is cleared
- if eb.Peek(EvtReadNew) {
- t.Error("EvtReadNew should not be set yet")
- }
-
- // Make sure that event poller is finished
- time.Sleep(readerPollIntervalMax)
-
- // Restart event poller
- reader.startEventPoller()
-
- // Failing command
- reader.fin(reader.readFromCommand(nil, `no-such-command`))
- strs = []string{}
- if len(strs) > 0 {
- t.Errorf("%s", strs)
- }
-
- // Check EventBox again
- if eb.Peek(EvtReadNew) {
- t.Error("Command failed. EvtReadNew should not be set")
- }
- if !eb.Peek(EvtReadFin) {
- t.Error("EvtReadFin should be set")
- }
-}
diff --git a/.fzf/src/result.go b/.fzf/src/result.go
deleted file mode 100644
index 8abe0d3..0000000
--- a/.fzf/src/result.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package fzf
-
-import (
- "math"
- "sort"
- "unicode"
-
- "github.com/junegunn/fzf/src/tui"
- "github.com/junegunn/fzf/src/util"
-)
-
-// Offset holds two 32-bit integers denoting the offsets of a matched substring
-type Offset [2]int32
-
-type colorOffset struct {
- offset [2]int32
- color tui.ColorPair
-}
-
-type Result struct {
- item *Item
- points [4]uint16
-}
-
-func buildResult(item *Item, offsets []Offset, score int) Result {
- if len(offsets) > 1 {
- sort.Sort(ByOrder(offsets))
- }
-
- result := Result{item: item}
- numChars := item.text.Length()
- minBegin := math.MaxUint16
- minEnd := math.MaxUint16
- maxEnd := 0
- validOffsetFound := false
- for _, offset := range offsets {
- b, e := int(offset[0]), int(offset[1])
- if b < e {
- minBegin = util.Min(b, minBegin)
- minEnd = util.Min(e, minEnd)
- maxEnd = util.Max(e, maxEnd)
- validOffsetFound = true
- }
- }
-
- for idx, criterion := range sortCriteria {
- val := uint16(math.MaxUint16)
- switch criterion {
- case byScore:
- // Higher is better
- val = math.MaxUint16 - util.AsUint16(score)
- case byLength:
- val = item.TrimLength()
- case byBegin, byEnd:
- if validOffsetFound {
- whitePrefixLen := 0
- for idx := 0; idx < numChars; idx++ {
- r := item.text.Get(idx)
- whitePrefixLen = idx
- if idx == minBegin || !unicode.IsSpace(r) {
- break
- }
- }
- if criterion == byBegin {
- val = util.AsUint16(minEnd - whitePrefixLen)
- } else {
- val = util.AsUint16(math.MaxUint16 - math.MaxUint16*(maxEnd-whitePrefixLen)/int(item.TrimLength()))
- }
- }
- }
- result.points[3-idx] = val
- }
-
- return result
-}
-
-// Sort criteria to use. Never changes once fzf is started.
-var sortCriteria []criterion
-
-// Index returns ordinal index of the Item
-func (result *Result) Index() int32 {
- return result.item.Index()
-}
-
-func minRank() Result {
- return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
-}
-
-func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, colBase tui.ColorPair, colMatch tui.ColorPair, current bool) []colorOffset {
- itemColors := result.item.Colors()
-
- // No ANSI codes
- if len(itemColors) == 0 {
- var offsets []colorOffset
- for _, off := range matchOffsets {
- offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: colMatch})
- }
- return offsets
- }
-
- // Find max column
- var maxCol int32
- for _, off := range matchOffsets {
- if off[1] > maxCol {
- maxCol = off[1]
- }
- }
- for _, ansi := range itemColors {
- if ansi.offset[1] > maxCol {
- maxCol = ansi.offset[1]
- }
- }
-
- cols := make([]int, maxCol)
- for colorIndex, ansi := range itemColors {
- for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
- cols[i] = colorIndex + 1 // 1-based index of itemColors
- }
- }
-
- for _, off := range matchOffsets {
- for i := off[0]; i < off[1]; i++ {
- // Negative of 1-based index of itemColors
- // - The extra -1 means highlighted
- cols[i] = cols[i]*-1 - 1
- }
- }
-
- // sort.Sort(ByOrder(offsets))
-
- // Merge offsets
- // ------------ ---- -- ----
- // ++++++++ ++++++++++
- // --++++++++-- --++++++++++---
- curr := 0
- start := 0
- ansiToColorPair := func(ansi ansiOffset, base tui.ColorPair) tui.ColorPair {
- fg := ansi.color.fg
- bg := ansi.color.bg
- if fg == -1 {
- if current {
- fg = theme.Current.Color
- } else {
- fg = theme.Fg.Color
- }
- }
- if bg == -1 {
- if current {
- bg = theme.DarkBg.Color
- } else {
- bg = theme.Bg.Color
- }
- }
- return tui.NewColorPair(fg, bg, ansi.color.attr).MergeAttr(base)
- }
- var colors []colorOffset
- add := func(idx int) {
- if curr != 0 && idx > start {
- if curr < 0 {
- color := colMatch
- if curr < -1 && theme.Colored {
- origColor := ansiToColorPair(itemColors[-curr-2], colMatch)
- // hl or hl+ only sets the foreground color, so colMatch is the
- // combination of either [hl and bg] or [hl+ and bg+].
- //
- // If the original text already has background color, and the
- // foreground color of colMatch is -1, we shouldn't only apply the
- // background color of colMatch.
- // e.g. echo -e "\x1b[32;7mfoo\x1b[mbar" | fzf --ansi --color bg+:1,hl+:-1:underline
- // echo -e "\x1b[42mfoo\x1b[mbar" | fzf --ansi --color bg+:1,hl+:-1:underline
- if color.Fg().IsDefault() && origColor.HasBg() {
- color = origColor
- } else {
- color = origColor.MergeNonDefault(color)
- }
- }
- colors = append(colors, colorOffset{
- offset: [2]int32{int32(start), int32(idx)}, color: color})
- } else {
- ansi := itemColors[curr-1]
- colors = append(colors, colorOffset{
- offset: [2]int32{int32(start), int32(idx)},
- color: ansiToColorPair(ansi, colBase)})
- }
- }
- }
- for idx, col := range cols {
- if col != curr {
- add(idx)
- start = idx
- curr = col
- }
- }
- add(int(maxCol))
- return colors
-}
-
-// ByOrder is for sorting substring offsets
-type ByOrder []Offset
-
-func (a ByOrder) Len() int {
- return len(a)
-}
-
-func (a ByOrder) Swap(i, j int) {
- a[i], a[j] = a[j], a[i]
-}
-
-func (a ByOrder) Less(i, j int) bool {
- ioff := a[i]
- joff := a[j]
- return (ioff[0] < joff[0]) || (ioff[0] == joff[0]) && (ioff[1] <= joff[1])
-}
-
-// ByRelevance is for sorting Items
-type ByRelevance []Result
-
-func (a ByRelevance) Len() int {
- return len(a)
-}
-
-func (a ByRelevance) Swap(i, j int) {
- a[i], a[j] = a[j], a[i]
-}
-
-func (a ByRelevance) Less(i, j int) bool {
- return compareRanks(a[i], a[j], false)
-}
-
-// ByRelevanceTac is for sorting Items
-type ByRelevanceTac []Result
-
-func (a ByRelevanceTac) Len() int {
- return len(a)
-}
-
-func (a ByRelevanceTac) Swap(i, j int) {
- a[i], a[j] = a[j], a[i]
-}
-
-func (a ByRelevanceTac) Less(i, j int) bool {
- return compareRanks(a[i], a[j], true)
-}
diff --git a/.fzf/src/result_others.go b/.fzf/src/result_others.go
deleted file mode 100644
index e3363a8..0000000
--- a/.fzf/src/result_others.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// +build !386,!amd64
-
-package fzf
-
-func compareRanks(irank Result, jrank Result, tac bool) bool {
- for idx := 3; idx >= 0; idx-- {
- left := irank.points[idx]
- right := jrank.points[idx]
- if left < right {
- return true
- } else if left > right {
- return false
- }
- }
- return (irank.item.Index() <= jrank.item.Index()) != tac
-}
diff --git a/.fzf/src/result_test.go b/.fzf/src/result_test.go
deleted file mode 100644
index 4084fdb..0000000
--- a/.fzf/src/result_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// +build !tcell
-
-package fzf
-
-import (
- "math"
- "sort"
- "testing"
-
- "github.com/junegunn/fzf/src/tui"
- "github.com/junegunn/fzf/src/util"
-)
-
-func withIndex(i *Item, index int) *Item {
- (*i).text.Index = int32(index)
- return i
-}
-
-func TestOffsetSort(t *testing.T) {
- offsets := []Offset{
- {3, 5}, {2, 7},
- {1, 3}, {2, 9}}
- sort.Sort(ByOrder(offsets))
-
- if offsets[0][0] != 1 || offsets[0][1] != 3 ||
- offsets[1][0] != 2 || offsets[1][1] != 7 ||
- offsets[2][0] != 2 || offsets[2][1] != 9 ||
- offsets[3][0] != 3 || offsets[3][1] != 5 {
- t.Error("Invalid order:", offsets)
- }
-}
-
-func TestRankComparison(t *testing.T) {
- rank := func(vals ...uint16) Result {
- return Result{
- points: [4]uint16{vals[0], vals[1], vals[2], vals[3]},
- item: &Item{text: util.Chars{Index: int32(vals[4])}}}
- }
- if compareRanks(rank(3, 0, 0, 0, 5), rank(2, 0, 0, 0, 7), false) ||
- !compareRanks(rank(3, 0, 0, 0, 5), rank(3, 0, 0, 0, 6), false) ||
- !compareRanks(rank(1, 2, 0, 0, 3), rank(1, 3, 0, 0, 2), false) ||
- !compareRanks(rank(0, 0, 0, 0, 0), rank(0, 0, 0, 0, 0), false) {
- t.Error("Invalid order")
- }
-
- if compareRanks(rank(3, 0, 0, 0, 5), rank(2, 0, 0, 0, 7), true) ||
- !compareRanks(rank(3, 0, 0, 0, 5), rank(3, 0, 0, 0, 6), false) ||
- !compareRanks(rank(1, 2, 0, 0, 3), rank(1, 3, 0, 0, 2), true) ||
- !compareRanks(rank(0, 0, 0, 0, 0), rank(0, 0, 0, 0, 0), false) {
- t.Error("Invalid order (tac)")
- }
-}
-
-// Match length, string length, index
-func TestResultRank(t *testing.T) {
- // FIXME global
- sortCriteria = []criterion{byScore, byLength}
-
- strs := [][]rune{[]rune("foo"), []rune("foobar"), []rune("bar"), []rune("baz")}
- item1 := buildResult(
- withIndex(&Item{text: util.RunesToChars(strs[0])}, 1), []Offset{}, 2)
- if item1.points[3] != math.MaxUint16-2 || // Bonus
- item1.points[2] != 3 || // Length
- item1.points[1] != 0 || // Unused
- item1.points[0] != 0 || // Unused
- item1.item.Index() != 1 {
- t.Error(item1)
- }
- // Only differ in index
- item2 := buildResult(&Item{text: util.RunesToChars(strs[0])}, []Offset{}, 2)
-
- items := []Result{item1, item2}
- sort.Sort(ByRelevance(items))
- if items[0] != item2 || items[1] != item1 {
- t.Error(items)
- }
-
- items = []Result{item2, item1, item1, item2}
- sort.Sort(ByRelevance(items))
- if items[0] != item2 || items[1] != item2 ||
- items[2] != item1 || items[3] != item1 {
- t.Error(items, item1, item1.item.Index(), item2, item2.item.Index())
- }
-
- // Sort by relevance
- item3 := buildResult(
- withIndex(&Item{}, 2), []Offset{{1, 3}, {5, 7}}, 3)
- item4 := buildResult(
- withIndex(&Item{}, 2), []Offset{{1, 2}, {6, 7}}, 4)
- item5 := buildResult(
- withIndex(&Item{}, 2), []Offset{{1, 3}, {5, 7}}, 5)
- item6 := buildResult(
- withIndex(&Item{}, 2), []Offset{{1, 2}, {6, 7}}, 6)
- items = []Result{item1, item2, item3, item4, item5, item6}
- sort.Sort(ByRelevance(items))
- if !(items[0] == item6 && items[1] == item5 &&
- items[2] == item4 && items[3] == item3 &&
- items[4] == item2 && items[5] == item1) {
- t.Error(items, item1, item2, item3, item4, item5, item6)
- }
-}
-
-func TestColorOffset(t *testing.T) {
- // ------------ 20 ---- -- ----
- // ++++++++ ++++++++++
- // --++++++++-- --++++++++++---
-
- offsets := []Offset{{5, 15}, {25, 35}}
- item := Result{
- item: &Item{
- colors: &[]ansiOffset{
- {[2]int32{0, 20}, ansiState{1, 5, 0, -1}},
- {[2]int32{22, 27}, ansiState{2, 6, tui.Bold, -1}},
- {[2]int32{30, 32}, ansiState{3, 7, 0, -1}},
- {[2]int32{33, 40}, ansiState{4, 8, tui.Bold, -1}}}}}
-
- colBase := tui.NewColorPair(89, 189, tui.AttrUndefined)
- colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined)
- colors := item.colorOffsets(offsets, tui.Dark256, colBase, colMatch, true)
- assert := func(idx int, b int32, e int32, c tui.ColorPair) {
- o := colors[idx]
- if o.offset[0] != b || o.offset[1] != e || o.color != c {
- t.Error(o, b, e, c)
- }
- }
- // [{[0 5] {1 5 0}} {[5 15] {99 199 0}} {[15 20] {1 5 0}}
- // {[22 25] {2 6 1}} {[25 27] {99 199 1}} {[27 30] {99 199 0}}
- // {[30 32] {99 199 0}} {[32 33] {99 199 0}} {[33 35] {99 199 1}}
- // {[35 40] {4 8 1}}]
- assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
- assert(1, 5, 15, colMatch)
- assert(2, 15, 20, tui.NewColorPair(1, 5, tui.AttrUndefined))
- assert(3, 22, 25, tui.NewColorPair(2, 6, tui.Bold))
- assert(4, 25, 27, colMatch.WithAttr(tui.Bold))
- assert(5, 27, 30, colMatch)
- assert(6, 30, 32, colMatch)
- assert(7, 32, 33, colMatch) // TODO: Should we merge consecutive blocks?
- assert(8, 33, 35, colMatch.WithAttr(tui.Bold))
- assert(9, 35, 40, tui.NewColorPair(4, 8, tui.Bold))
-
- colRegular := tui.NewColorPair(-1, -1, tui.AttrUndefined)
- colUnderline := tui.NewColorPair(-1, -1, tui.Underline)
- colors = item.colorOffsets(offsets, tui.Dark256, colRegular, colUnderline, true)
-
- // [{[0 5] {1 5 0}} {[5 15] {1 5 8}} {[15 20] {1 5 0}}
- // {[22 25] {2 6 1}} {[25 27] {2 6 9}} {[27 30] {-1 -1 8}}
- // {[30 32] {3 7 8}} {[32 33] {-1 -1 8}} {[33 35] {4 8 9}}
- // {[35 40] {4 8 1}}]
- assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
- assert(1, 5, 15, tui.NewColorPair(1, 5, tui.Underline))
- assert(2, 15, 20, tui.NewColorPair(1, 5, tui.AttrUndefined))
- assert(3, 22, 25, tui.NewColorPair(2, 6, tui.Bold))
- assert(4, 25, 27, tui.NewColorPair(2, 6, tui.Bold|tui.Underline))
- assert(5, 27, 30, colUnderline)
- assert(6, 30, 32, tui.NewColorPair(3, 7, tui.Underline))
- assert(7, 32, 33, colUnderline)
- assert(8, 33, 35, tui.NewColorPair(4, 8, tui.Bold|tui.Underline))
- assert(9, 35, 40, tui.NewColorPair(4, 8, tui.Bold))
-}
diff --git a/.fzf/src/result_x86.go b/.fzf/src/result_x86.go
deleted file mode 100644
index 60e26e9..0000000
--- a/.fzf/src/result_x86.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// +build 386 amd64
-
-package fzf
-
-import "unsafe"
-
-func compareRanks(irank Result, jrank Result, tac bool) bool {
- left := *(*uint64)(unsafe.Pointer(&irank.points[0]))
- right := *(*uint64)(unsafe.Pointer(&jrank.points[0]))
- if left < right {
- return true
- } else if left > right {
- return false
- }
- return (irank.item.Index() <= jrank.item.Index()) != tac
-}
diff --git a/.fzf/src/terminal.go b/.fzf/src/terminal.go
deleted file mode 100644
index 2bad5d7..0000000
--- a/.fzf/src/terminal.go
+++ /dev/null
@@ -1,2832 +0,0 @@
-package fzf
-
-import (
- "bufio"
- "fmt"
- "io/ioutil"
- "os"
- "os/signal"
- "regexp"
- "sort"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "time"
-
- "github.com/mattn/go-runewidth"
- "github.com/rivo/uniseg"
-
- "github.com/junegunn/fzf/src/tui"
- "github.com/junegunn/fzf/src/util"
-)
-
-// import "github.com/pkg/profile"
-
-/*
- Placeholder regex is used to extract placeholders from fzf's template
- strings. Acts as input validation for parsePlaceholder function.
- Describes the syntax, but it is fairly lenient.
-
- The following pseudo regex has been reverse engineered from the
- implementation. It is overly strict, but better describes whats possible.
- As such it is not useful for validation, but rather to generate test
- cases for example.
-
- \\?(?: # escaped type
- {\+?s?f?RANGE(?:,RANGE)*} # token type
- |{q} # query type
- |{\+?n?f?} # item type (notice no mandatory element inside brackets)
- )
- RANGE = (?:
- (?:-?[0-9]+)?\.\.(?:-?[0-9]+)? # ellipsis syntax for token range (x..y)
- |-?[0-9]+ # shorthand syntax (x..x)
- )
-*/
-var placeholder *regexp.Regexp
-var whiteSuffix *regexp.Regexp
-var offsetComponentRegex *regexp.Regexp
-var offsetTrimCharsRegex *regexp.Regexp
-var activeTempFiles []string
-
-const ellipsis string = ".."
-const clearCode string = "\x1b[2J"
-
-func init() {
- placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
- whiteSuffix = regexp.MustCompile(`\s*$`)
- offsetComponentRegex = regexp.MustCompile(`([+-][0-9]+)|(-?/[1-9][0-9]*)`)
- offsetTrimCharsRegex = regexp.MustCompile(`[^0-9/+-]`)
- activeTempFiles = []string{}
-}
-
-type jumpMode int
-
-const (
- jumpDisabled jumpMode = iota
- jumpEnabled
- jumpAcceptEnabled
-)
-
-type previewer struct {
- version int64
- lines []string
- offset int
- enabled bool
- scrollable bool
- final bool
- following bool
- spinner string
-}
-
-type previewed struct {
- version int64
- numLines int
- offset int
- filled bool
-}
-
-type eachLine struct {
- line string
- err error
-}
-
-type itemLine struct {
- current bool
- selected bool
- label string
- queryLen int
- width int
- result Result
-}
-
-var emptyLine = itemLine{}
-
-// Terminal represents terminal input/output
-type Terminal struct {
- initDelay time.Duration
- infoStyle infoStyle
- spinner []string
- prompt func()
- promptLen int
- pointer string
- pointerLen int
- pointerEmpty string
- marker string
- markerLen int
- markerEmpty string
- queryLen [2]int
- layout layoutType
- fullscreen bool
- keepRight bool
- hscroll bool
- hscrollOff int
- scrollOff int
- wordRubout string
- wordNext string
- cx int
- cy int
- offset int
- xoffset int
- yanked []rune
- input []rune
- multi int
- sort bool
- toggleSort bool
- delimiter Delimiter
- expect map[tui.Event]string
- keymap map[tui.Event][]action
- pressed string
- printQuery bool
- history *History
- cycle bool
- headerFirst bool
- headerLines int
- header []string
- header0 []string
- ansi bool
- tabstop int
- margin [4]sizeSpec
- padding [4]sizeSpec
- strong tui.Attr
- unicode bool
- borderShape tui.BorderShape
- cleanExit bool
- paused bool
- border tui.Window
- window tui.Window
- pborder tui.Window
- pwindow tui.Window
- count int
- progress int
- reading bool
- running bool
- failed *string
- jumping jumpMode
- jumpLabels string
- printer func(string)
- printsep string
- merger *Merger
- selected map[int32]selectedItem
- version int64
- reqBox *util.EventBox
- previewOpts previewOpts
- previewer previewer
- previewed previewed
- previewBox *util.EventBox
- eventBox *util.EventBox
- mutex sync.Mutex
- initFunc func()
- prevLines []itemLine
- suppress bool
- sigstop bool
- startChan chan bool
- killChan chan int
- slab *util.Slab
- theme *tui.ColorTheme
- tui tui.Renderer
- executing *util.AtomicBool
-}
-
-type selectedItem struct {
- at time.Time
- item *Item
-}
-
-type byTimeOrder []selectedItem
-
-func (a byTimeOrder) Len() int {
- return len(a)
-}
-
-func (a byTimeOrder) Swap(i, j int) {
- a[i], a[j] = a[j], a[i]
-}
-
-func (a byTimeOrder) Less(i, j int) bool {
- return a[i].at.Before(a[j].at)
-}
-
-const (
- reqPrompt util.EventType = iota
- reqInfo
- reqHeader
- reqList
- reqJump
- reqRefresh
- reqReinit
- reqRedraw
- reqClose
- reqPrintQuery
- reqPreviewEnqueue
- reqPreviewDisplay
- reqPreviewRefresh
- reqPreviewDelayed
- reqQuit
-)
-
-type action struct {
- t actionType
- a string
-}
-
-type actionType int
-
-const (
- actIgnore actionType = iota
- actInvalid
- actRune
- actMouse
- actBeginningOfLine
- actAbort
- actAccept
- actAcceptNonEmpty
- actBackwardChar
- actBackwardDeleteChar
- actBackwardDeleteCharEOF
- actBackwardWord
- actCancel
- actChangePrompt
- actClearScreen
- actClearQuery
- actClearSelection
- actClose
- actDeleteChar
- actDeleteCharEOF
- actEndOfLine
- actForwardChar
- actForwardWord
- actKillLine
- actKillWord
- actUnixLineDiscard
- actUnixWordRubout
- actYank
- actBackwardKillWord
- actSelectAll
- actDeselectAll
- actToggle
- actToggleSearch
- actToggleAll
- actToggleDown
- actToggleUp
- actToggleIn
- actToggleOut
- actDown
- actUp
- actPageUp
- actPageDown
- actHalfPageUp
- actHalfPageDown
- actJump
- actJumpAccept
- actPrintQuery
- actRefreshPreview
- actReplaceQuery
- actToggleSort
- actTogglePreview
- actTogglePreviewWrap
- actPreview
- actPreviewTop
- actPreviewBottom
- actPreviewUp
- actPreviewDown
- actPreviewPageUp
- actPreviewPageDown
- actPreviewHalfPageUp
- actPreviewHalfPageDown
- actPreviousHistory
- actNextHistory
- actExecute
- actExecuteSilent
- actExecuteMulti // Deprecated
- actSigStop
- actFirst
- actLast
- actReload
- actDisableSearch
- actEnableSearch
- actSelect
- actDeselect
- actUnbind
-)
-
-type placeholderFlags struct {
- plus bool
- preserveSpace bool
- number bool
- query bool
- file bool
-}
-
-type searchRequest struct {
- sort bool
- command *string
-}
-
-type previewRequest struct {
- template string
- pwindow tui.Window
- list []*Item
-}
-
-type previewResult struct {
- version int64
- lines []string
- offset int
- spinner string
-}
-
-func toActions(types ...actionType) []action {
- actions := make([]action, len(types))
- for idx, t := range types {
- actions[idx] = action{t: t, a: ""}
- }
- return actions
-}
-
-func defaultKeymap() map[tui.Event][]action {
- keymap := make(map[tui.Event][]action)
- add := func(e tui.EventType, a actionType) {
- keymap[e.AsEvent()] = toActions(a)
- }
- addEvent := func(e tui.Event, a actionType) {
- keymap[e] = toActions(a)
- }
-
- add(tui.Invalid, actInvalid)
- add(tui.Resize, actClearScreen)
- add(tui.CtrlA, actBeginningOfLine)
- add(tui.CtrlB, actBackwardChar)
- add(tui.CtrlC, actAbort)
- add(tui.CtrlG, actAbort)
- add(tui.CtrlQ, actAbort)
- add(tui.ESC, actAbort)
- add(tui.CtrlD, actDeleteCharEOF)
- add(tui.CtrlE, actEndOfLine)
- add(tui.CtrlF, actForwardChar)
- add(tui.CtrlH, actBackwardDeleteChar)
- add(tui.BSpace, actBackwardDeleteChar)
- add(tui.Tab, actToggleDown)
- add(tui.BTab, actToggleUp)
- add(tui.CtrlJ, actDown)
- add(tui.CtrlK, actUp)
- add(tui.CtrlL, actClearScreen)
- add(tui.CtrlM, actAccept)
- add(tui.CtrlN, actDown)
- add(tui.CtrlP, actUp)
- add(tui.CtrlU, actUnixLineDiscard)
- add(tui.CtrlW, actUnixWordRubout)
- add(tui.CtrlY, actYank)
- if !util.IsWindows() {
- add(tui.CtrlZ, actSigStop)
- }
-
- addEvent(tui.AltKey('b'), actBackwardWord)
- add(tui.SLeft, actBackwardWord)
- addEvent(tui.AltKey('f'), actForwardWord)
- add(tui.SRight, actForwardWord)
- addEvent(tui.AltKey('d'), actKillWord)
- add(tui.AltBS, actBackwardKillWord)
-
- add(tui.Up, actUp)
- add(tui.Down, actDown)
- add(tui.Left, actBackwardChar)
- add(tui.Right, actForwardChar)
-
- add(tui.Home, actBeginningOfLine)
- add(tui.End, actEndOfLine)
- add(tui.Del, actDeleteChar)
- add(tui.PgUp, actPageUp)
- add(tui.PgDn, actPageDown)
-
- add(tui.SUp, actPreviewUp)
- add(tui.SDown, actPreviewDown)
-
- add(tui.Mouse, actMouse)
- add(tui.DoubleClick, actAccept)
- add(tui.LeftClick, actIgnore)
- add(tui.RightClick, actToggle)
- return keymap
-}
-
-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
-}
-
-func makeSpinner(unicode bool) []string {
- if unicode {
- return []string{`⠋`, `⠙`, `⠹`, `⠸`, `⠼`, `⠴`, `⠦`, `⠧`, `⠇`, `⠏`}
- }
- return []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
-}
-
-// NewTerminal returns new Terminal object
-func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
- input := trimQuery(opts.Query)
- var header []string
- switch opts.Layout {
- case layoutDefault, layoutReverseList:
- header = reverseStringArray(opts.Header)
- default:
- header = opts.Header
- }
- var delay time.Duration
- if opts.Tac {
- delay = initialDelayTac
- } else {
- delay = initialDelay
- }
- var previewBox *util.EventBox
- showPreviewWindow := len(opts.Preview.command) > 0 && !opts.Preview.hidden
- if len(opts.Preview.command) > 0 || hasPreviewAction(opts) {
- previewBox = util.NewEventBox()
- }
- strongAttr := tui.Bold
- if !opts.Bold {
- strongAttr = tui.AttrRegular
- }
- var renderer tui.Renderer
- fullscreen := opts.Height.size == 0 || opts.Height.percent && opts.Height.size == 100
- if fullscreen {
- if tui.HasFullscreenRenderer() {
- renderer = tui.NewFullscreenRenderer(opts.Theme, opts.Black, opts.Mouse)
- } else {
- renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit,
- true, func(h int) int { return h })
- }
- } else {
- maxHeightFunc := func(termHeight int) int {
- var maxHeight int
- if opts.Height.percent {
- maxHeight = util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight)
- } else {
- maxHeight = int(opts.Height.size)
- }
-
- effectiveMinHeight := minHeight
- if previewBox != nil && (opts.Preview.position == posUp || opts.Preview.position == posDown) {
- effectiveMinHeight *= 2
- }
- if opts.InfoStyle != infoDefault {
- effectiveMinHeight--
- }
- if opts.BorderShape != tui.BorderNone {
- effectiveMinHeight += 2
- }
- return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight))
- }
- renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit, false, maxHeightFunc)
- }
- 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)
- wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
- }
- t := Terminal{
- initDelay: delay,
- infoStyle: opts.InfoStyle,
- spinner: makeSpinner(opts.Unicode),
- queryLen: [2]int{0, 0},
- layout: opts.Layout,
- fullscreen: fullscreen,
- keepRight: opts.KeepRight,
- hscroll: opts.Hscroll,
- hscrollOff: opts.HscrollOff,
- scrollOff: opts.ScrollOff,
- wordRubout: wordRubout,
- wordNext: wordNext,
- cx: len(input),
- cy: 0,
- offset: 0,
- xoffset: 0,
- yanked: []rune{},
- input: input,
- multi: opts.Multi,
- sort: opts.Sort > 0,
- toggleSort: opts.ToggleSort,
- delimiter: opts.Delimiter,
- expect: opts.Expect,
- keymap: opts.Keymap,
- pressed: "",
- printQuery: opts.PrintQuery,
- history: opts.History,
- margin: opts.Margin,
- padding: opts.Padding,
- unicode: opts.Unicode,
- borderShape: opts.BorderShape,
- cleanExit: opts.ClearOnExit,
- paused: opts.Phony,
- strong: strongAttr,
- cycle: opts.Cycle,
- headerFirst: opts.HeaderFirst,
- headerLines: opts.HeaderLines,
- header: header,
- header0: header,
- ansi: opts.Ansi,
- tabstop: opts.Tabstop,
- reading: true,
- running: true,
- failed: nil,
- jumping: jumpDisabled,
- jumpLabels: opts.JumpLabels,
- printer: opts.Printer,
- printsep: opts.PrintSep,
- merger: EmptyMerger,
- selected: make(map[int32]selectedItem),
- reqBox: util.NewEventBox(),
- previewOpts: opts.Preview,
- previewer: previewer{0, []string{}, 0, showPreviewWindow, false, true, false, ""},
- previewed: previewed{0, 0, 0, false},
- previewBox: previewBox,
- eventBox: eventBox,
- mutex: sync.Mutex{},
- suppress: true,
- sigstop: false,
- slab: util.MakeSlab(slab16Size, slab32Size),
- theme: opts.Theme,
- startChan: make(chan bool, 1),
- killChan: make(chan int),
- tui: renderer,
- initFunc: func() { renderer.Init() },
- executing: util.NewAtomicBool(false)}
- 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
- t.pointerEmpty = strings.Repeat(" ", t.pointerLen)
- t.markerEmpty = strings.Repeat(" ", t.markerLen)
-
- 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}
-
- // "Prompt> "
- // ------- // Do not apply ANSI attributes to the trailing whitespaces
- // // unless the part has a non-default ANSI state
- loc := whiteSuffix.FindStringIndex(trimmed)
- if loc != nil {
- blankState := ansiOffset{[2]int32{int32(loc[0]), int32(loc[1])}, ansiState{-1, -1, tui.AttrClear, -1}}
- if item.colors != nil {
- lastColor := (*item.colors)[len(*item.colors)-1]
- if lastColor.offset[1] < int32(loc[1]) {
- blankState.offset[0] = lastColor.offset[1]
- colors := append(*item.colors, blankState)
- item.colors = &colors
- }
- } else {
- colors := []ansiOffset{blankState}
- item.colors = &colors
- }
- }
- output := func() {
- t.printHighlighted(
- Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false)
- }
- _, promptLen := t.processTabs([]rune(trimmed), 0)
-
- return output, promptLen
-}
-
-func (t *Terminal) noInfoLine() bool {
- return t.infoStyle != infoDefault
-}
-
-// Input returns current query string
-func (t *Terminal) Input() (bool, []rune) {
- t.mutex.Lock()
- defer t.mutex.Unlock()
- return t.paused, copySlice(t.input)
-}
-
-// UpdateCount updates the count information
-func (t *Terminal) UpdateCount(cnt int, final bool, failedCommand *string) {
- t.mutex.Lock()
- t.count = cnt
- t.reading = !final
- t.failed = failedCommand
- t.mutex.Unlock()
- t.reqBox.Set(reqInfo, nil)
- if final {
- t.reqBox.Set(reqRefresh, nil)
- }
-}
-
-func reverseStringArray(input []string) []string {
- size := len(input)
- reversed := make([]string, size)
- for idx, str := range input {
- reversed[size-idx-1] = str
- }
- return reversed
-}
-
-// UpdateHeader updates the header
-func (t *Terminal) UpdateHeader(header []string) {
- t.mutex.Lock()
- t.header = append(append([]string{}, t.header0...), header...)
- t.mutex.Unlock()
- t.reqBox.Set(reqHeader, nil)
-}
-
-// UpdateProgress updates the search progress
-func (t *Terminal) UpdateProgress(progress float32) {
- t.mutex.Lock()
- newProgress := int(progress * 100)
- changed := t.progress != newProgress
- t.progress = newProgress
- t.mutex.Unlock()
-
- if changed {
- t.reqBox.Set(reqInfo, nil)
- }
-}
-
-// UpdateList updates Merger to display the list
-func (t *Terminal) UpdateList(merger *Merger, reset bool) {
- t.mutex.Lock()
- t.progress = 100
- t.merger = merger
- if reset {
- t.selected = make(map[int32]selectedItem)
- }
- t.mutex.Unlock()
- t.reqBox.Set(reqInfo, nil)
- t.reqBox.Set(reqList, nil)
-}
-
-func (t *Terminal) output() bool {
- if t.printQuery {
- t.printer(string(t.input))
- }
- if len(t.expect) > 0 {
- t.printer(t.pressed)
- }
- found := len(t.selected) > 0
- if !found {
- current := t.currentItem()
- if current != nil {
- t.printer(current.AsString(t.ansi))
- found = true
- }
- } else {
- for _, sel := range t.sortSelected() {
- t.printer(sel.item.AsString(t.ansi))
- }
- }
- return found
-}
-
-func (t *Terminal) sortSelected() []selectedItem {
- sels := make([]selectedItem, 0, len(t.selected))
- for _, sel := range t.selected {
- sels = append(sels, sel)
- }
- sort.Sort(byTimeOrder(sels))
- return sels
-}
-
-func (t *Terminal) displayWidth(runes []rune) int {
- width, _ := util.RunesWidth(runes, 0, t.tabstop, 0)
- return width
-}
-
-const (
- minWidth = 4
- minHeight = 4
-)
-
-func calculateSize(base int, size sizeSpec, occupied int, minSize int, pad int) int {
- max := base - occupied
- if size.percent {
- return util.Constrain(int(float64(base)*0.01*size.size), minSize, max)
- }
- return util.Constrain(int(size.size)+pad, minSize, max)
-}
-
-func (t *Terminal) resizeWindows() {
- screenWidth := t.tui.MaxX()
- screenHeight := t.tui.MaxY()
- t.prevLines = make([]itemLine, screenHeight)
-
- marginInt := [4]int{} // TRBL
- paddingInt := [4]int{} // TRBL
- sizeSpecToInt := func(index int, spec sizeSpec) int {
- if spec.percent {
- var max float64
- if index%2 == 0 {
- max = float64(screenHeight)
- } else {
- max = float64(screenWidth)
- }
- return int(max * spec.size * 0.01)
- }
- return int(spec.size)
- }
- for idx, sizeSpec := range t.padding {
- paddingInt[idx] = sizeSpecToInt(idx, sizeSpec)
- }
-
- extraMargin := [4]int{} // TRBL
- for idx, sizeSpec := range t.margin {
- switch t.borderShape {
- case tui.BorderHorizontal:
- extraMargin[idx] += 1 - idx%2
- case tui.BorderVertical:
- extraMargin[idx] += 2 * (idx % 2)
- case tui.BorderTop:
- if idx == 0 {
- extraMargin[idx]++
- }
- case tui.BorderRight:
- if idx == 1 {
- extraMargin[idx] += 2
- }
- case tui.BorderBottom:
- if idx == 2 {
- extraMargin[idx]++
- }
- case tui.BorderLeft:
- if idx == 3 {
- extraMargin[idx] += 2
- }
- case tui.BorderRounded, tui.BorderSharp:
- extraMargin[idx] += 1 + idx%2
- }
- marginInt[idx] = sizeSpecToInt(idx, sizeSpec) + extraMargin[idx]
- }
-
- adjust := func(idx1 int, idx2 int, max int, min int) {
- if max >= min {
- margin := marginInt[idx1] + marginInt[idx2] + paddingInt[idx1] + paddingInt[idx2]
- if max-margin < min {
- desired := max - min
- paddingInt[idx1] = desired * paddingInt[idx1] / margin
- paddingInt[idx2] = desired * paddingInt[idx2] / margin
- marginInt[idx1] = util.Max(extraMargin[idx1], desired*marginInt[idx1]/margin)
- marginInt[idx2] = util.Max(extraMargin[idx2], desired*marginInt[idx2]/margin)
- }
- }
- }
-
- previewVisible := t.isPreviewEnabled() && t.previewOpts.size.size > 0
- minAreaWidth := minWidth
- minAreaHeight := minHeight
- if previewVisible {
- switch t.previewOpts.position {
- case posUp, posDown:
- minAreaHeight *= 2
- case posLeft, posRight:
- minAreaWidth *= 2
- }
- }
- adjust(1, 3, screenWidth, minAreaWidth)
- adjust(0, 2, screenHeight, minAreaHeight)
- if t.border != nil {
- t.border.Close()
- }
- if t.window != nil {
- t.window.Close()
- }
- if t.pborder != nil {
- t.pborder.Close()
- }
- if t.pwindow != nil {
- t.pwindow.Close()
- }
- // Reset preview version so that full redraw occurs
- t.previewed.version = 0
-
- width := screenWidth - marginInt[1] - marginInt[3]
- height := screenHeight - marginInt[0] - marginInt[2]
- switch t.borderShape {
- case tui.BorderHorizontal:
- t.border = t.tui.NewWindow(
- marginInt[0]-1, marginInt[3], width, height+2,
- false, tui.MakeBorderStyle(tui.BorderHorizontal, t.unicode))
- case tui.BorderVertical:
- t.border = t.tui.NewWindow(
- marginInt[0], marginInt[3]-2, width+4, height,
- false, tui.MakeBorderStyle(tui.BorderVertical, t.unicode))
- case tui.BorderTop:
- t.border = t.tui.NewWindow(
- marginInt[0]-1, marginInt[3], width, height+1,
- false, tui.MakeBorderStyle(tui.BorderTop, t.unicode))
- case tui.BorderBottom:
- t.border = t.tui.NewWindow(
- marginInt[0], marginInt[3], width, height+1,
- false, tui.MakeBorderStyle(tui.BorderBottom, t.unicode))
- case tui.BorderLeft:
- t.border = t.tui.NewWindow(
- marginInt[0], marginInt[3]-2, width+2, height,
- false, tui.MakeBorderStyle(tui.BorderLeft, t.unicode))
- case tui.BorderRight:
- t.border = t.tui.NewWindow(
- marginInt[0], marginInt[3], width+2, height,
- false, tui.MakeBorderStyle(tui.BorderRight, t.unicode))
- case tui.BorderRounded, tui.BorderSharp:
- t.border = t.tui.NewWindow(
- marginInt[0]-1, marginInt[3]-2, width+4, height+2,
- false, tui.MakeBorderStyle(t.borderShape, t.unicode))
- }
-
- // Add padding
- for idx, val := range paddingInt {
- marginInt[idx] += val
- }
- width = screenWidth - marginInt[1] - marginInt[3]
- height = screenHeight - marginInt[0] - marginInt[2]
-
- noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
- if previewVisible {
- createPreviewWindow := func(y int, x int, w int, h int) {
- pwidth := w
- pheight := h
- var previewBorder tui.BorderStyle
- if t.previewOpts.border == tui.BorderNone {
- previewBorder = tui.MakeTransparentBorder()
- } else {
- previewBorder = tui.MakeBorderStyle(t.previewOpts.border, t.unicode)
- }
- t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
- switch t.previewOpts.border {
- case tui.BorderSharp, tui.BorderRounded:
- pwidth -= 4
- pheight -= 2
- x += 2
- y += 1
- case tui.BorderLeft:
- pwidth -= 2
- x += 2
- case tui.BorderRight:
- pwidth -= 2
- case tui.BorderTop:
- pheight -= 1
- y += 1
- case tui.BorderBottom:
- pheight -= 1
- case tui.BorderHorizontal:
- pheight -= 2
- y += 1
- case tui.BorderVertical:
- pwidth -= 4
- x += 2
- }
- t.pwindow = t.tui.NewWindow(y, x, pwidth, pheight, true, noBorder)
- }
- verticalPad := 2
- minPreviewHeight := 3
- switch t.previewOpts.border {
- case tui.BorderNone, tui.BorderVertical, tui.BorderLeft, tui.BorderRight:
- verticalPad = 0
- minPreviewHeight = 1
- case tui.BorderTop, tui.BorderBottom:
- verticalPad = 1
- minPreviewHeight = 2
- }
- switch t.previewOpts.position {
- case posUp:
- pheight := calculateSize(height, t.previewOpts.size, minHeight, minPreviewHeight, verticalPad)
- t.window = t.tui.NewWindow(
- marginInt[0]+pheight, marginInt[3], width, height-pheight, false, noBorder)
- createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
- case posDown:
- pheight := calculateSize(height, t.previewOpts.size, minHeight, minPreviewHeight, verticalPad)
- t.window = t.tui.NewWindow(
- marginInt[0], marginInt[3], width, height-pheight, false, noBorder)
- createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
- case posLeft:
- pwidth := calculateSize(width, t.previewOpts.size, minWidth, 5, 4)
- t.window = t.tui.NewWindow(
- marginInt[0], marginInt[3]+pwidth, width-pwidth, height, false, noBorder)
- createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
- case posRight:
- pwidth := calculateSize(width, t.previewOpts.size, minWidth, 5, 4)
- t.window = t.tui.NewWindow(
- marginInt[0], marginInt[3], width-pwidth, height, false, noBorder)
- createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height)
- }
- } else {
- t.window = t.tui.NewWindow(
- marginInt[0],
- marginInt[3],
- width,
- height, false, noBorder)
- }
- for i := 0; i < t.window.Height(); i++ {
- t.window.MoveAndClear(i, 0)
- }
-}
-
-func (t *Terminal) move(y int, x int, clear bool) {
- h := t.window.Height()
-
- switch t.layout {
- case layoutDefault:
- y = h - y - 1
- case layoutReverseList:
- n := 2 + len(t.header)
- if t.noInfoLine() {
- n--
- }
- if y < n {
- y = h - y - 1
- } else {
- y -= n
- }
- }
-
- if clear {
- t.window.MoveAndClear(y, x)
- } else {
- t.window.Move(y, x)
- }
-}
-
-func (t *Terminal) truncateQuery() {
- t.input, _ = t.trimRight(t.input, maxPatternLength)
- t.cx = util.Constrain(t.cx, 0, len(t.input))
-}
-
-func (t *Terminal) updatePromptOffset() ([]rune, []rune) {
- maxWidth := util.Max(1, t.window.Width()-t.promptLen-1)
-
- _, overflow := t.trimLeft(t.input[:t.cx], maxWidth)
- minOffset := int(overflow)
- maxOffset := util.Min(util.Min(len(t.input), minOffset+maxWidth), t.cx)
-
- t.xoffset = util.Constrain(t.xoffset, minOffset, maxOffset)
- before, _ := t.trimLeft(t.input[t.xoffset:t.cx], maxWidth)
- beforeLen := t.displayWidth(before)
- after, _ := t.trimRight(t.input[t.cx:], maxWidth-beforeLen)
- afterLen := t.displayWidth(after)
- t.queryLen = [2]int{beforeLen, afterLen}
- return before, after
-}
-
-func (t *Terminal) promptLine() int {
- if t.headerFirst {
- max := t.window.Height() - 1
- if !t.noInfoLine() {
- max--
- }
- return util.Min(len(t.header0)+t.headerLines, max)
- }
- return 0
-}
-
-func (t *Terminal) placeCursor() {
- t.move(t.promptLine(), t.promptLen+t.queryLen[0], false)
-}
-
-func (t *Terminal) printPrompt() {
- t.move(t.promptLine(), 0, true)
- t.prompt()
-
- before, after := t.updatePromptOffset()
- color := tui.ColInput
- if t.paused {
- color = tui.ColDisabled
- }
- t.window.CPrint(color, string(before))
- t.window.CPrint(color, string(after))
-}
-
-func (t *Terminal) trimMessage(message string, maxWidth int) string {
- if len(message) <= maxWidth {
- return message
- }
- runes, _ := t.trimRight([]rune(message), maxWidth-2)
- return string(runes) + strings.Repeat(".", util.Constrain(maxWidth, 0, 2))
-}
-
-func (t *Terminal) printInfo() {
- pos := 0
- line := t.promptLine()
- switch t.infoStyle {
- case infoDefault:
- t.move(line+1, 0, true)
- if t.reading {
- duration := int64(spinnerDuration)
- idx := (time.Now().UnixNano() % (duration * int64(len(t.spinner)))) / duration
- t.window.CPrint(tui.ColSpinner, t.spinner[idx])
- }
- t.move(line+1, 2, false)
- pos = 2
- case infoInline:
- pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
- if pos+len(" < ") > t.window.Width() {
- return
- }
- t.move(line, pos, true)
- if t.reading {
- t.window.CPrint(tui.ColSpinner, " < ")
- } else {
- t.window.CPrint(tui.ColPrompt, " < ")
- }
- pos += len(" < ")
- case infoHidden:
- return
- }
-
- found := t.merger.Length()
- total := util.Max(found, t.count)
- output := fmt.Sprintf("%d/%d", found, total)
- if t.toggleSort {
- if t.sort {
- output += " +S"
- } else {
- output += " -S"
- }
- }
- if t.multi > 0 {
- if t.multi == maxMulti {
- output += fmt.Sprintf(" (%d)", len(t.selected))
- } else {
- output += fmt.Sprintf(" (%d/%d)", len(t.selected), t.multi)
- }
- }
- if t.progress > 0 && t.progress < 100 {
- output += fmt.Sprintf(" (%d%%)", t.progress)
- }
- if t.failed != nil && t.count == 0 {
- output = fmt.Sprintf("[Command failed: %s]", *t.failed)
- }
- output = t.trimMessage(output, t.window.Width()-pos)
- t.window.CPrint(tui.ColInfo, output)
-}
-
-func (t *Terminal) printHeader() {
- if len(t.header) == 0 {
- return
- }
- max := t.window.Height()
- if t.headerFirst {
- max--
- if !t.noInfoLine() {
- max--
- }
- }
- var state *ansiState
- for idx, lineStr := range t.header {
- line := idx
- if !t.headerFirst {
- line++
- if !t.noInfoLine() {
- line++
- }
- }
- if line >= max {
- continue
- }
- trimmed, colors, newState := extractColor(lineStr, state, nil)
- state = newState
- item := &Item{
- text: util.ToChars([]byte(trimmed)),
- colors: colors}
-
- t.move(line, 2, true)
- t.printHighlighted(Result{item: item},
- tui.ColHeader, tui.ColHeader, false, false)
- }
-}
-
-func (t *Terminal) printList() {
- t.constrain()
-
- maxy := t.maxItems()
- count := t.merger.Length() - t.offset
- for j := 0; j < maxy; j++ {
- i := j
- if t.layout == layoutDefault {
- i = maxy - 1 - j
- }
- line := i + 2 + len(t.header)
- if t.noInfoLine() {
- line--
- }
- if i < count {
- t.printItem(t.merger.Get(i+t.offset), line, i, i == t.cy-t.offset)
- } else if t.prevLines[i] != emptyLine {
- t.prevLines[i] = emptyLine
- t.move(line, 0, true)
- }
- }
-}
-
-func (t *Terminal) printItem(result Result, line int, i int, current bool) {
- item := result.item
- _, selected := t.selected[item.Index()]
- label := ""
- if t.jumping != jumpDisabled {
- if i < len(t.jumpLabels) {
- // Striped
- current = i%2 == 0
- label = t.jumpLabels[i:i+1] + strings.Repeat(" ", t.pointerLen-1)
- }
- } else if current {
- label = t.pointer
- }
-
- // Avoid unnecessary redraw
- newLine := itemLine{current: current, selected: selected, label: label,
- result: result, queryLen: len(t.input), width: 0}
- prevLine := t.prevLines[i]
- if prevLine.current == newLine.current &&
- prevLine.selected == newLine.selected &&
- prevLine.label == newLine.label &&
- prevLine.queryLen == newLine.queryLen &&
- prevLine.result == newLine.result {
- return
- }
-
- t.move(line, 0, false)
- if current {
- if len(label) == 0 {
- t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty)
- } else {
- t.window.CPrint(tui.ColCurrentCursor, label)
- }
- if selected {
- t.window.CPrint(tui.ColCurrentSelected, t.marker)
- } else {
- t.window.CPrint(tui.ColCurrentSelectedEmpty, t.markerEmpty)
- }
- newLine.width = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true)
- } else {
- if len(label) == 0 {
- t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty)
- } else {
- t.window.CPrint(tui.ColCursor, label)
- }
- if selected {
- t.window.CPrint(tui.ColSelected, t.marker)
- } else {
- t.window.Print(t.markerEmpty)
- }
- newLine.width = t.printHighlighted(result, tui.ColNormal, tui.ColMatch, false, true)
- }
- fillSpaces := prevLine.width - newLine.width
- if fillSpaces > 0 {
- t.window.Print(strings.Repeat(" ", fillSpaces))
- }
- t.prevLines[i] = newLine
-}
-
-func (t *Terminal) trimRight(runes []rune, width int) ([]rune, bool) {
- // We start from the beginning to handle tab characters
- width, overflowIdx := util.RunesWidth(runes, 0, t.tabstop, width)
- if overflowIdx >= 0 {
- return runes[:overflowIdx], true
- }
- return runes, false
-}
-
-func (t *Terminal) displayWidthWithLimit(runes []rune, prefixWidth int, limit int) int {
- width, _ := util.RunesWidth(runes, prefixWidth, t.tabstop, limit)
- return width
-}
-
-func (t *Terminal) trimLeft(runes []rune, width int) ([]rune, int32) {
- width = util.Max(0, width)
- var trimmed int32
- // Assume that each rune takes at least one column on screen
- if len(runes) > width+2 {
- diff := len(runes) - width - 2
- trimmed = int32(diff)
- runes = runes[diff:]
- }
-
- currentWidth := t.displayWidth(runes)
-
- for currentWidth > width && len(runes) > 0 {
- runes = runes[1:]
- trimmed++
- currentWidth = t.displayWidthWithLimit(runes, 2, width)
- }
- return runes, trimmed
-}
-
-func (t *Terminal) overflow(runes []rune, max int) bool {
- return t.displayWidthWithLimit(runes, 0, max) > max
-}
-
-func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool) int {
- item := result.item
-
- // Overflow
- text := make([]rune, item.text.Length())
- copy(text, item.text.ToRunes())
- matchOffsets := []Offset{}
- var pos *[]int
- if match && t.merger.pattern != nil {
- _, matchOffsets, pos = t.merger.pattern.MatchItem(item, true, t.slab)
- }
- charOffsets := matchOffsets
- if pos != nil {
- charOffsets = make([]Offset, len(*pos))
- for idx, p := range *pos {
- offset := Offset{int32(p), int32(p + 1)}
- charOffsets[idx] = offset
- }
- sort.Sort(ByOrder(charOffsets))
- }
- var maxe int
- for _, offset := range charOffsets {
- maxe = util.Max(maxe, int(offset[1]))
- }
-
- offsets := result.colorOffsets(charOffsets, t.theme, colBase, colMatch, current)
- maxWidth := t.window.Width() - (t.pointerLen + t.markerLen + 1)
- maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
- displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
- if displayWidth > maxWidth {
- transformOffsets := func(diff int32) {
- for idx, offset := range offsets {
- b, e := offset.offset[0], offset.offset[1]
- b += 2 - diff
- e += 2 - diff
- b = util.Max32(b, 2)
- offsets[idx].offset[0] = b
- offsets[idx].offset[1] = util.Max32(b, e)
- }
- }
- if t.hscroll {
- if t.keepRight && pos == nil {
- trimmed, diff := t.trimLeft(text, maxWidth-2)
- transformOffsets(diff)
- text = append([]rune(ellipsis), trimmed...)
- } else if !t.overflow(text[:maxe], maxWidth-2) {
- // Stri..
- text, _ = t.trimRight(text, maxWidth-2)
- text = append(text, []rune(ellipsis)...)
- } else {
- // Stri..
- if t.overflow(text[maxe:], 2) {
- text = append(text[:maxe], []rune(ellipsis)...)
- }
- // ..ri..
- var diff int32
- text, diff = t.trimLeft(text, maxWidth-2)
-
- // Transform offsets
- transformOffsets(diff)
- text = append([]rune(ellipsis), text...)
- }
- } else {
- text, _ = t.trimRight(text, maxWidth-2)
- text = append(text, []rune(ellipsis)...)
-
- for idx, offset := range offsets {
- offsets[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-2))
- offsets[idx].offset[1] = util.Min32(offset.offset[1], int32(maxWidth))
- }
- }
- displayWidth = t.displayWidthWithLimit(text, 0, displayWidth)
- }
-
- var index int32
- var substr string
- var prefixWidth int
- maxOffset := int32(len(text))
- for _, offset := range offsets {
- b := util.Constrain32(offset.offset[0], index, maxOffset)
- e := util.Constrain32(offset.offset[1], index, maxOffset)
-
- substr, prefixWidth = t.processTabs(text[index:b], prefixWidth)
- t.window.CPrint(colBase, substr)
-
- if b < e {
- substr, prefixWidth = t.processTabs(text[b:e], prefixWidth)
- t.window.CPrint(offset.color, substr)
- }
-
- index = e
- if index >= maxOffset {
- break
- }
- }
- if index < maxOffset {
- substr, _ = t.processTabs(text[index:], prefixWidth)
- t.window.CPrint(colBase, substr)
- }
- return displayWidth
-}
-
-func (t *Terminal) renderPreviewSpinner() {
- numLines := len(t.previewer.lines)
- spin := t.previewer.spinner
- if len(spin) > 0 || t.previewer.scrollable {
- maxWidth := t.pwindow.Width()
- if !t.previewer.scrollable {
- if maxWidth > 0 {
- t.pwindow.Move(0, maxWidth-1)
- t.pwindow.CPrint(tui.ColSpinner, spin)
- }
- } else {
- offsetString := fmt.Sprintf("%d/%d", t.previewer.offset+1, numLines)
- if len(spin) > 0 {
- spin += " "
- maxWidth -= 2
- }
- offsetRunes, _ := t.trimRight([]rune(offsetString), maxWidth)
- pos := maxWidth - t.displayWidth(offsetRunes)
- t.pwindow.Move(0, pos)
- if maxWidth > 0 {
- t.pwindow.CPrint(tui.ColSpinner, spin)
- t.pwindow.CPrint(tui.ColInfo.WithAttr(tui.Reverse), string(offsetRunes))
- }
- }
- }
-}
-
-func (t *Terminal) renderPreviewArea(unchanged bool) {
- if unchanged {
- t.pwindow.MoveAndClear(0, 0) // Clear scroll offset display
- } else {
- t.previewed.filled = false
- t.pwindow.Erase()
- }
-
- height := t.pwindow.Height()
- header := []string{}
- body := t.previewer.lines
- headerLines := t.previewOpts.headerLines
- // Do not enable preview header lines if it's value is too large
- if headerLines > 0 && headerLines < util.Min(len(body), height) {
- header = t.previewer.lines[0:headerLines]
- body = t.previewer.lines[headerLines:]
- // Always redraw header
- t.renderPreviewText(height, header, 0, false)
- t.pwindow.MoveAndClear(t.pwindow.Y(), 0)
- }
- t.renderPreviewText(height, body, -t.previewer.offset+headerLines, unchanged)
-
- if !unchanged {
- t.pwindow.FinishFill()
- }
-}
-
-func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unchanged bool) {
- maxWidth := t.pwindow.Width()
- var ansi *ansiState
- for _, line := range lines {
- var lbg tui.Color = -1
- if ansi != nil {
- ansi.lbg = -1
- }
- line = strings.TrimSuffix(line, "\n")
- if lineNo >= height || t.pwindow.Y() == height-1 && t.pwindow.X() > 0 {
- t.previewed.filled = true
- break
- } else if lineNo >= 0 {
- var fillRet tui.FillReturn
- prefixWidth := 0
- _, _, ansi = extractColor(line, ansi, func(str string, ansi *ansiState) bool {
- trimmed := []rune(str)
- isTrimmed := false
- if !t.previewOpts.wrap {
- trimmed, isTrimmed = t.trimRight(trimmed, maxWidth-t.pwindow.X())
- }
- str, width := t.processTabs(trimmed, prefixWidth)
- prefixWidth += width
- if t.theme.Colored && ansi != nil && ansi.colored() {
- lbg = ansi.lbg
- fillRet = t.pwindow.CFill(ansi.fg, ansi.bg, ansi.attr, str)
- } else {
- fillRet = t.pwindow.CFill(tui.ColPreview.Fg(), tui.ColPreview.Bg(), tui.AttrRegular, str)
- }
- return !isTrimmed &&
- (fillRet == tui.FillContinue || t.previewOpts.wrap && fillRet == tui.FillNextLine)
- })
- t.previewer.scrollable = t.previewer.scrollable || t.pwindow.Y() == height-1 && t.pwindow.X() == t.pwindow.Width()
- if fillRet == tui.FillNextLine {
- continue
- } else if fillRet == tui.FillSuspend {
- t.previewed.filled = true
- break
- }
- if unchanged && lineNo == 0 {
- break
- }
- if lbg >= 0 {
- t.pwindow.CFill(-1, lbg, tui.AttrRegular,
- strings.Repeat(" ", t.pwindow.Width()-t.pwindow.X())+"\n")
- } else {
- t.pwindow.Fill("\n")
- }
- }
- lineNo++
- }
-}
-
-func (t *Terminal) printPreview() {
- if !t.hasPreviewWindow() {
- return
- }
- numLines := len(t.previewer.lines)
- height := t.pwindow.Height()
- unchanged := (t.previewed.filled || numLines == t.previewed.numLines) &&
- t.previewer.version == t.previewed.version &&
- t.previewer.offset == t.previewed.offset
- t.previewer.scrollable = t.previewer.offset > 0 || numLines > height
- t.renderPreviewArea(unchanged)
- t.renderPreviewSpinner()
- t.previewed.numLines = numLines
- t.previewed.version = t.previewer.version
- t.previewed.offset = t.previewer.offset
-}
-
-func (t *Terminal) printPreviewDelayed() {
- if !t.hasPreviewWindow() || len(t.previewer.lines) > 0 && t.previewed.version == t.previewer.version {
- return
- }
-
- t.previewer.scrollable = false
- t.renderPreviewArea(true)
-
- message := t.trimMessage("Loading ..", t.pwindow.Width())
- pos := t.pwindow.Width() - len(message)
- t.pwindow.Move(0, pos)
- t.pwindow.CPrint(tui.ColInfo.WithAttr(tui.Reverse), message)
-}
-
-func (t *Terminal) processTabs(runes []rune, prefixWidth int) (string, int) {
- var strbuf strings.Builder
- l := prefixWidth
- gr := uniseg.NewGraphemes(string(runes))
- for gr.Next() {
- rs := gr.Runes()
- str := string(rs)
- var w int
- if len(rs) == 1 && rs[0] == '\t' {
- w = t.tabstop - l%t.tabstop
- strbuf.WriteString(strings.Repeat(" ", w))
- } else {
- w = runewidth.StringWidth(str)
- strbuf.WriteString(str)
- }
- l += w
- }
- return strbuf.String(), l
-}
-
-func (t *Terminal) printAll() {
- t.resizeWindows()
- t.printList()
- t.printPrompt()
- t.printInfo()
- t.printHeader()
- t.printPreview()
-}
-
-func (t *Terminal) refresh() {
- t.placeCursor()
- if !t.suppress {
- windows := make([]tui.Window, 0, 4)
- if t.borderShape != tui.BorderNone {
- windows = append(windows, t.border)
- }
- if t.hasPreviewWindow() {
- if t.pborder != nil {
- windows = append(windows, t.pborder)
- }
- windows = append(windows, t.pwindow)
- }
- windows = append(windows, t.window)
- t.tui.RefreshWindows(windows)
- }
-}
-
-func (t *Terminal) delChar() bool {
- if len(t.input) > 0 && t.cx < len(t.input) {
- t.input = append(t.input[:t.cx], t.input[t.cx+1:]...)
- return true
- }
- return false
-}
-
-func findLastMatch(pattern string, str string) int {
- rx, err := regexp.Compile(pattern)
- if err != nil {
- return -1
- }
- locs := rx.FindAllStringIndex(str, -1)
- if locs == nil {
- return -1
- }
- prefix := []rune(str[:locs[len(locs)-1][0]])
- return len(prefix)
-}
-
-func findFirstMatch(pattern string, str string) int {
- rx, err := regexp.Compile(pattern)
- if err != nil {
- return -1
- }
- loc := rx.FindStringIndex(str)
- if loc == nil {
- return -1
- }
- prefix := []rune(str[:loc[0]])
- return len(prefix)
-}
-
-func copySlice(slice []rune) []rune {
- ret := make([]rune, len(slice))
- copy(ret, slice)
- return ret
-}
-
-func (t *Terminal) rubout(pattern string) {
- pcx := t.cx
- after := t.input[t.cx:]
- t.cx = findLastMatch(pattern, string(t.input[:t.cx])) + 1
- t.yanked = copySlice(t.input[t.cx:pcx])
- t.input = append(t.input[:t.cx], after...)
-}
-
-func keyMatch(key tui.Event, event tui.Event) bool {
- return event.Type == key.Type && event.Char == key.Char ||
- key.Type == tui.DoubleClick && event.Type == tui.Mouse && event.MouseEvent.Double
-}
-
-func parsePlaceholder(match string) (bool, string, placeholderFlags) {
- flags := placeholderFlags{}
-
- if match[0] == '\\' {
- // Escaped placeholder pattern
- return true, match[1:], flags
- }
-
- skipChars := 1
- for _, char := range match[1:] {
- switch char {
- case '+':
- flags.plus = true
- skipChars++
- case 's':
- flags.preserveSpace = true
- skipChars++
- case 'n':
- flags.number = true
- skipChars++
- case 'f':
- flags.file = true
- skipChars++
- case 'q':
- flags.query = true
- // query flag is not skipped
- default:
- break
- }
- }
-
- matchWithoutFlags := "{" + match[skipChars:]
-
- return false, matchWithoutFlags, flags
-}
-
-func hasPreviewFlags(template string) (slot bool, plus bool, query bool) {
- for _, match := range placeholder.FindAllString(template, -1) {
- _, _, flags := parsePlaceholder(match)
- if flags.plus {
- plus = true
- }
- if flags.query {
- query = true
- }
- slot = true
- }
- return
-}
-
-func writeTemporaryFile(data []string, printSep string) string {
- f, err := ioutil.TempFile("", "fzf-preview-*")
- if err != nil {
- errorExit("Unable to create temporary file")
- }
- defer f.Close()
-
- f.WriteString(strings.Join(data, printSep))
- f.WriteString(printSep)
- activeTempFiles = append(activeTempFiles, f.Name())
- return f.Name()
-}
-
-func cleanTemporaryFiles() {
- for _, filename := range activeTempFiles {
- os.Remove(filename)
- }
- 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)
-}
-
-func (t *Terminal) evaluateScrollOffset(list []*Item, height int) int {
- offsetExpr := offsetTrimCharsRegex.ReplaceAllString(
- t.replacePlaceholder(t.previewOpts.scroll, false, "", list), "")
-
- atoi := func(s string) int {
- n, e := strconv.Atoi(s)
- if e != nil {
- return 0
- }
- return n
- }
-
- base := -1
- for _, component := range offsetComponentRegex.FindAllString(offsetExpr, -1) {
- if strings.HasPrefix(component, "-/") {
- component = component[1:]
- }
- if component[0] == '/' {
- denom := atoi(component[1:])
- if denom == 0 {
- return base
- }
- return base - height/denom
- }
- base += atoi(component)
- }
- return base
-}
-
-func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems []*Item) string {
- current := allItems[:1]
- selected := allItems[1:]
- if current[0] == nil {
- current = []*Item{}
- }
- if selected[0] == nil {
- selected = []*Item{}
- }
-
- // replace placeholders one by one
- return placeholder.ReplaceAllStringFunc(template, func(match string) string {
- escaped, match, flags := parsePlaceholder(match)
-
- // this function implements the effects a placeholder has on items
- var replace func(*Item) string
-
- // placeholder types (escaped, query type, item type, token type)
- switch {
- case escaped:
- return match
- case match == "{q}":
- return quoteEntry(query)
- case match == "{}":
- replace = func(item *Item) string {
- switch {
- case flags.number:
- n := int(item.text.Index)
- if n < 0 {
- return ""
- }
- return strconv.Itoa(n)
- case flags.file:
- return item.AsString(stripAnsi)
- default:
- return quoteEntry(item.AsString(stripAnsi))
- }
- }
- default:
- // token type and also failover (below)
- rangeExpressions := strings.Split(match[1:len(match)-1], ",")
- ranges := make([]Range, len(rangeExpressions))
- for idx, s := range rangeExpressions {
- r, ok := ParseRange(&s) // ellipsis (x..y) and shorthand (x..x) range syntax
- if !ok {
- // Invalid expression, just return the original string in the template
- return match
- }
- ranges[idx] = r
- }
-
- replace = func(item *Item) string {
- tokens := Tokenize(item.AsString(stripAnsi), delimiter)
- trans := Transform(tokens, ranges)
- str := joinTokens(trans)
-
- // trim the last delimiter
- if delimiter.str != nil {
- str = strings.TrimSuffix(str, *delimiter.str)
- } else if delimiter.regex != nil {
- delims := delimiter.regex.FindAllStringIndex(str, -1)
- // make sure the delimiter is at the very end of the string
- if len(delims) > 0 && delims[len(delims)-1][1] == len(str) {
- str = str[:delims[len(delims)-1][0]]
- }
- }
-
- if !flags.preserveSpace {
- str = strings.TrimSpace(str)
- }
- if !flags.file {
- str = quoteEntry(str)
- }
- return str
- }
- }
-
- // apply 'replace' function over proper set of items and return result
-
- items := current
- if flags.plus || forcePlus {
- items = selected
- }
- replacements := make([]string, len(items))
-
- for idx, item := range items {
- replacements[idx] = replace(item)
- }
-
- if flags.file {
- return writeTemporaryFile(replacements, printsep)
- }
- return strings.Join(replacements, " ")
- })
-}
-
-func (t *Terminal) redraw() {
- t.tui.Clear()
- t.tui.Refresh()
- t.printAll()
-}
-
-func (t *Terminal) executeCommand(template string, forcePlus bool, background bool) {
- valid, list := t.buildPlusList(template, forcePlus)
- if !valid {
- return
- }
- command := t.replacePlaceholder(template, forcePlus, string(t.input), list)
- cmd := util.ExecCommand(command, false)
- t.executing.Set(true)
- if !background {
- cmd.Stdin = tui.TtyIn()
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- t.tui.Pause(true)
- cmd.Run()
- t.tui.Resume(true, false)
- t.redraw()
- t.refresh()
- } else {
- t.tui.Pause(false)
- cmd.Run()
- t.tui.Resume(false, false)
- }
- t.executing.Set(false)
- cleanTemporaryFiles()
-}
-
-func (t *Terminal) hasPreviewer() bool {
- return t.previewBox != nil
-}
-
-func (t *Terminal) isPreviewEnabled() bool {
- return t.hasPreviewer() && t.previewer.enabled
-}
-
-func (t *Terminal) hasPreviewWindow() bool {
- return t.pwindow != nil && t.isPreviewEnabled()
-}
-
-func (t *Terminal) currentItem() *Item {
- cnt := t.merger.Length()
- if t.cy >= 0 && cnt > 0 && cnt > t.cy {
- return t.merger.Get(t.cy).item
- }
- return nil
-}
-
-func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) {
- current := t.currentItem()
- slot, plus, query := hasPreviewFlags(template)
- if !(!slot || query && len(t.input) > 0 || (forcePlus || plus) && len(t.selected) > 0) {
- return current != nil, []*Item{current, current}
- }
-
- // We would still want to update preview window even if there is no match if
- // 1. command template contains {q} and the query string is not empty
- // 2. or it contains {+} and we have more than one item already selected.
- // To do so, we pass an empty Item instead of nil to trigger an update.
- if current == nil {
- current = &minItem
- }
-
- var sels []*Item
- if len(t.selected) == 0 {
- sels = []*Item{current, current}
- } else {
- sels = make([]*Item, len(t.selected)+1)
- sels[0] = current
- for i, sel := range t.sortSelected() {
- sels[i+1] = sel.item
- }
- }
- return true, sels
-}
-
-func (t *Terminal) selectItem(item *Item) bool {
- if len(t.selected) >= t.multi {
- return false
- }
- if _, found := t.selected[item.Index()]; found {
- return true
- }
-
- t.selected[item.Index()] = selectedItem{time.Now(), item}
- t.version++
-
- return true
-}
-
-func (t *Terminal) selectItemChanged(item *Item) bool {
- if _, found := t.selected[item.Index()]; found {
- return false
- }
- return t.selectItem(item)
-}
-
-func (t *Terminal) deselectItem(item *Item) {
- delete(t.selected, item.Index())
- t.version++
-}
-
-func (t *Terminal) deselectItemChanged(item *Item) bool {
- if _, found := t.selected[item.Index()]; found {
- t.deselectItem(item)
- return true
- }
- return false
-}
-
-func (t *Terminal) toggleItem(item *Item) bool {
- if _, found := t.selected[item.Index()]; !found {
- return t.selectItem(item)
- }
- t.deselectItem(item)
- return true
-}
-
-func (t *Terminal) killPreview(code int) {
- select {
- case t.killChan <- code:
- default:
- if code != exitCancel {
- t.eventBox.Set(EvtQuit, code)
- }
- }
-}
-
-func (t *Terminal) cancelPreview() {
- t.killPreview(exitCancel)
-}
-
-// Loop is called to start Terminal I/O
-func (t *Terminal) Loop() {
- // prof := profile.Start(profile.ProfilePath("/tmp/"))
- <-t.startChan
- { // Late initialization
- intChan := make(chan os.Signal, 1)
- signal.Notify(intChan, os.Interrupt, syscall.SIGTERM)
- go func() {
- for s := range intChan {
- // Don't quit by SIGINT while executing because it should be for the executing command and not for fzf itself
- if !(s == os.Interrupt && t.executing.Get()) {
- t.reqBox.Set(reqQuit, nil)
- }
- }
- }()
-
- contChan := make(chan os.Signal, 1)
- notifyOnCont(contChan)
- go func() {
- for {
- <-contChan
- t.reqBox.Set(reqReinit, nil)
- }
- }()
-
- resizeChan := make(chan os.Signal, 1)
- notifyOnResize(resizeChan) // Non-portable
- go func() {
- for {
- <-resizeChan
- t.reqBox.Set(reqRedraw, nil)
- }
- }()
-
- t.mutex.Lock()
- t.initFunc()
- t.resizeWindows()
- t.printPrompt()
- t.printInfo()
- t.printHeader()
- t.refresh()
- t.mutex.Unlock()
- go func() {
- timer := time.NewTimer(t.initDelay)
- <-timer.C
- t.reqBox.Set(reqRefresh, nil)
- }()
-
- // Keep the spinner spinning
- go func() {
- for {
- t.mutex.Lock()
- reading := t.reading
- t.mutex.Unlock()
- time.Sleep(spinnerDuration)
- if reading {
- t.reqBox.Set(reqInfo, nil)
- }
- }
- }()
- }
-
- if t.hasPreviewer() {
- go func() {
- var version int64
- for {
- var items []*Item
- var commandTemplate string
- var pwindow tui.Window
- t.previewBox.Wait(func(events *util.Events) {
- for req, value := range *events {
- switch req {
- case reqPreviewEnqueue:
- request := value.(previewRequest)
- commandTemplate = request.template
- items = request.list
- pwindow = request.pwindow
- }
- }
- events.Clear()
- })
- version++
- // We don't display preview window if no match
- if items[0] != nil {
- _, query := t.Input()
- command := t.replacePlaceholder(commandTemplate, false, string(query), items)
- initialOffset := 0
- cmd := util.ExecCommand(command, true)
- if pwindow != nil {
- height := pwindow.Height()
- initialOffset = util.Max(0, t.evaluateScrollOffset(items, util.Max(0, height-t.previewOpts.headerLines)))
- env := os.Environ()
- lines := fmt.Sprintf("LINES=%d", height)
- columns := fmt.Sprintf("COLUMNS=%d", pwindow.Width())
- env = append(env, lines)
- env = append(env, "FZF_PREVIEW_"+lines)
- env = append(env, columns)
- env = append(env, "FZF_PREVIEW_"+columns)
- cmd.Env = env
- }
-
- out, _ := cmd.StdoutPipe()
- cmd.Stderr = cmd.Stdout
- reader := bufio.NewReader(out)
- eofChan := make(chan bool)
- finishChan := make(chan bool, 1)
- err := cmd.Start()
- if err == nil {
- reapChan := make(chan bool)
- lineChan := make(chan eachLine)
- // Goroutine 1 reads process output
- go func() {
- for {
- line, err := reader.ReadString('\n')
- lineChan <- eachLine{line, err}
- if err != nil {
- break
- }
- }
- eofChan <- true
- }()
-
- // Goroutine 2 periodically requests rendering
- rendered := util.NewAtomicBool(false)
- go func(version int64) {
- lines := []string{}
- spinner := makeSpinner(t.unicode)
- spinnerIndex := -1 // Delay initial rendering by an extra tick
- ticker := time.NewTicker(previewChunkDelay)
- offset := initialOffset
- Loop:
- for {
- select {
- case <-ticker.C:
- if len(lines) > 0 && len(lines) >= initialOffset {
- if spinnerIndex >= 0 {
- spin := spinner[spinnerIndex%len(spinner)]
- t.reqBox.Set(reqPreviewDisplay, previewResult{version, lines, offset, spin})
- rendered.Set(true)
- offset = -1
- }
- spinnerIndex++
- }
- case eachLine := <-lineChan:
- line := eachLine.line
- err := eachLine.err
- if len(line) > 0 {
- clearIndex := strings.Index(line, clearCode)
- if clearIndex >= 0 {
- lines = []string{}
- line = line[clearIndex+len(clearCode):]
- version--
- offset = 0
- }
- lines = append(lines, line)
- }
- if err != nil {
- t.reqBox.Set(reqPreviewDisplay, previewResult{version, lines, offset, ""})
- rendered.Set(true)
- break Loop
- }
- }
- }
- ticker.Stop()
- reapChan <- true
- }(version)
-
- // Goroutine 3 is responsible for cancelling running preview command
- go func(version int64) {
- timer := time.NewTimer(previewDelayed)
- Loop:
- for {
- select {
- case <-timer.C:
- t.reqBox.Set(reqPreviewDelayed, version)
- case code := <-t.killChan:
- if code != exitCancel {
- util.KillCommand(cmd)
- t.eventBox.Set(EvtQuit, code)
- } else {
- // We can immediately kill a long-running preview program
- // once we started rendering its partial output
- delay := previewCancelWait
- if rendered.Get() {
- delay = 0
- }
- timer := time.NewTimer(delay)
- select {
- case <-timer.C:
- util.KillCommand(cmd)
- case <-finishChan:
- }
- timer.Stop()
- }
- break Loop
- case <-finishChan:
- break Loop
- }
- }
- timer.Stop()
- reapChan <- true
- }(version)
-
- <-eofChan // Goroutine 1 finished
- cmd.Wait() // NOTE: We should not call Wait before EOF
- finishChan <- true // Tell Goroutine 3 to stop
- <-reapChan // Goroutine 2 and 3 finished
- <-reapChan
- } else {
- // Failed to start the command. Report the error immediately.
- t.reqBox.Set(reqPreviewDisplay, previewResult{version, []string{err.Error()}, 0, ""})
- }
-
- cleanTemporaryFiles()
- } else {
- t.reqBox.Set(reqPreviewDisplay, previewResult{version, nil, 0, ""})
- }
- }
- }()
- }
-
- refreshPreview := func(command string) {
- if len(command) > 0 && t.isPreviewEnabled() {
- _, list := t.buildPlusList(command, false)
- t.cancelPreview()
- t.previewBox.Set(reqPreviewEnqueue, previewRequest{command, t.pwindow, list})
- }
- }
-
- go func() {
- var focusedIndex int32 = minItem.Index()
- var version int64 = -1
- running := true
- code := exitError
- exit := func(getCode func() int) {
- t.tui.Close()
- code = getCode()
- if code <= exitNoMatch && t.history != nil {
- t.history.append(string(t.input))
- }
- running = false
- t.mutex.Unlock()
- }
-
- for running {
- t.reqBox.Wait(func(events *util.Events) {
- defer events.Clear()
- t.mutex.Lock()
- for req, value := range *events {
- switch req {
- case reqPrompt:
- t.printPrompt()
- if t.noInfoLine() {
- t.printInfo()
- }
- case reqInfo:
- t.printInfo()
- case reqList:
- t.printList()
- var currentIndex int32 = minItem.Index()
- currentItem := t.currentItem()
- if currentItem != nil {
- currentIndex = currentItem.Index()
- }
- if focusedIndex != currentIndex || version != t.version {
- version = t.version
- focusedIndex = currentIndex
- refreshPreview(t.previewOpts.command)
- }
- case reqJump:
- if t.merger.Length() == 0 {
- t.jumping = jumpDisabled
- }
- t.printList()
- case reqHeader:
- t.printHeader()
- case reqRefresh:
- t.suppress = false
- case reqReinit:
- t.tui.Resume(t.fullscreen, t.sigstop)
- t.redraw()
- case reqRedraw:
- t.redraw()
- case reqClose:
- exit(func() int {
- if t.output() {
- return exitOk
- }
- return exitNoMatch
- })
- return
- case reqPreviewDisplay:
- result := value.(previewResult)
- if t.previewer.version != result.version {
- t.previewer.version = result.version
- t.previewer.following = t.previewOpts.follow
- }
- t.previewer.lines = result.lines
- t.previewer.spinner = result.spinner
- if t.previewer.following {
- t.previewer.offset = len(t.previewer.lines) - t.pwindow.Height()
- } else if result.offset >= 0 {
- t.previewer.offset = util.Constrain(result.offset, t.previewOpts.headerLines, len(t.previewer.lines)-1)
- }
- t.printPreview()
- case reqPreviewRefresh:
- t.printPreview()
- case reqPreviewDelayed:
- t.previewer.version = value.(int64)
- t.printPreviewDelayed()
- case reqPrintQuery:
- exit(func() int {
- t.printer(string(t.input))
- return exitOk
- })
- return
- case reqQuit:
- exit(func() int { return exitInterrupt })
- return
- }
- }
- t.refresh()
- t.mutex.Unlock()
- })
- }
- // prof.Stop()
- t.killPreview(code)
- }()
-
- looping := true
- for looping {
- var newCommand *string
- changed := false
- beof := false
- queryChanged := false
-
- event := t.tui.GetChar()
-
- t.mutex.Lock()
- previousInput := t.input
- previousCx := t.cx
- events := []util.EventType{}
- req := func(evts ...util.EventType) {
- for _, event := range evts {
- events = append(events, event)
- if event == reqClose || event == reqQuit {
- looping = false
- }
- }
- }
- 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 {
- current := t.currentItem()
- if current != nil && t.toggleItem(current) {
- req(reqInfo)
- return true
- }
- return false
- }
- scrollPreviewTo := func(newOffset int) {
- if !t.previewer.scrollable {
- return
- }
- t.previewer.following = false
- numLines := len(t.previewer.lines)
- if t.previewOpts.cycle {
- newOffset = (newOffset + numLines) % numLines
- }
- newOffset = util.Constrain(newOffset, t.previewOpts.headerLines, numLines-1)
- if t.previewer.offset != newOffset {
- t.previewer.offset = newOffset
- req(reqPreviewRefresh)
- }
- }
- scrollPreviewBy := func(amount int) {
- scrollPreviewTo(t.previewer.offset + amount)
- }
- for key, ret := range t.expect {
- if keyMatch(key, event) {
- t.pressed = ret
- t.reqBox.Set(reqClose, nil)
- t.mutex.Unlock()
- return
- }
- }
-
- actionsFor := func(eventType tui.EventType) []action {
- return t.keymap[eventType.AsEvent()]
- }
-
- var doAction func(action) bool
- doActions := func(actions []action) bool {
- for _, action := range actions {
- if !doAction(action) {
- return false
- }
- }
- return true
- }
- doAction = func(a action) bool {
- switch a.t {
- case actIgnore:
- case actExecute, actExecuteSilent:
- t.executeCommand(a.a, false, a.t == actExecuteSilent)
- case actExecuteMulti:
- t.executeCommand(a.a, true, false)
- case actInvalid:
- t.mutex.Unlock()
- return false
- case actTogglePreview:
- if t.hasPreviewer() {
- togglePreview(!t.previewer.enabled)
- if t.previewer.enabled {
- valid, list := t.buildPlusList(t.previewOpts.command, false)
- if valid {
- t.cancelPreview()
- t.previewBox.Set(reqPreviewEnqueue,
- previewRequest{t.previewOpts.command, t.pwindow, list})
- }
- }
- }
- case actTogglePreviewWrap:
- if t.hasPreviewWindow() {
- t.previewOpts.wrap = !t.previewOpts.wrap
- // Reset preview version so that full redraw occurs
- t.previewed.version = 0
- req(reqPreviewRefresh)
- }
- case actToggleSort:
- t.sort = !t.sort
- changed = true
- case actPreviewTop:
- if t.hasPreviewWindow() {
- scrollPreviewTo(0)
- }
- case actPreviewBottom:
- if t.hasPreviewWindow() {
- scrollPreviewTo(len(t.previewer.lines) - t.pwindow.Height())
- }
- case actPreviewUp:
- if t.hasPreviewWindow() {
- scrollPreviewBy(-1)
- }
- case actPreviewDown:
- if t.hasPreviewWindow() {
- scrollPreviewBy(1)
- }
- case actPreviewPageUp:
- if t.hasPreviewWindow() {
- scrollPreviewBy(-t.pwindow.Height())
- }
- case actPreviewPageDown:
- if t.hasPreviewWindow() {
- scrollPreviewBy(t.pwindow.Height())
- }
- case actPreviewHalfPageUp:
- if t.hasPreviewWindow() {
- scrollPreviewBy(-t.pwindow.Height() / 2)
- }
- case actPreviewHalfPageDown:
- if t.hasPreviewWindow() {
- scrollPreviewBy(t.pwindow.Height() / 2)
- }
- case actBeginningOfLine:
- t.cx = 0
- case actBackwardChar:
- if t.cx > 0 {
- t.cx--
- }
- case actPrintQuery:
- req(reqPrintQuery)
- case actChangePrompt:
- t.prompt, t.promptLen = t.parsePrompt(a.a)
- req(reqPrompt)
- case actPreview:
- togglePreview(true)
- refreshPreview(a.a)
- case actRefreshPreview:
- refreshPreview(t.previewOpts.command)
- case actReplaceQuery:
- current := t.currentItem()
- if current != nil {
- t.input = current.text.ToRunes()
- t.cx = len(t.input)
- }
- case actAbort:
- req(reqQuit)
- case actDeleteChar:
- t.delChar()
- case actDeleteCharEOF:
- if !t.delChar() && t.cx == 0 {
- req(reqQuit)
- }
- case actEndOfLine:
- t.cx = len(t.input)
- case actCancel:
- if len(t.input) == 0 {
- req(reqQuit)
- } else {
- t.yanked = t.input
- t.input = []rune{}
- t.cx = 0
- }
- case actBackwardDeleteCharEOF:
- if len(t.input) == 0 {
- req(reqQuit)
- } else if t.cx > 0 {
- t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
- t.cx--
- }
- case actForwardChar:
- if t.cx < len(t.input) {
- 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--
- }
- case actSelectAll:
- if t.multi > 0 {
- for i := 0; i < t.merger.Length(); i++ {
- if !t.selectItem(t.merger.Get(i).item) {
- break
- }
- }
- req(reqList, reqInfo)
- }
- case actDeselectAll:
- if t.multi > 0 {
- for i := 0; i < t.merger.Length() && len(t.selected) > 0; i++ {
- t.deselectItem(t.merger.Get(i).item)
- }
- req(reqList, reqInfo)
- }
- case actClose:
- if t.isPreviewEnabled() {
- togglePreview(false)
- } else {
- req(reqQuit)
- }
- case actSelect:
- current := t.currentItem()
- if t.multi > 0 && current != nil && t.selectItemChanged(current) {
- req(reqList, reqInfo)
- }
- case actDeselect:
- current := t.currentItem()
- if t.multi > 0 && current != nil && t.deselectItemChanged(current) {
- req(reqList, reqInfo)
- }
- case actToggle:
- if t.multi > 0 && t.merger.Length() > 0 && toggle() {
- req(reqList)
- }
- case actToggleAll:
- if t.multi > 0 {
- prevIndexes := make(map[int]struct{})
- for i := 0; i < t.merger.Length() && len(t.selected) > 0; i++ {
- item := t.merger.Get(i).item
- if _, found := t.selected[item.Index()]; found {
- prevIndexes[i] = struct{}{}
- t.deselectItem(item)
- }
- }
-
- for i := 0; i < t.merger.Length(); i++ {
- if _, found := prevIndexes[i]; !found {
- item := t.merger.Get(i).item
- if !t.selectItem(item) {
- break
- }
- }
- }
- req(reqList, reqInfo)
- }
- case actToggleIn:
- if t.layout != layoutDefault {
- return doAction(action{t: actToggleUp})
- }
- return doAction(action{t: actToggleDown})
- case actToggleOut:
- if t.layout != layoutDefault {
- return doAction(action{t: actToggleDown})
- }
- return doAction(action{t: actToggleUp})
- case actToggleDown:
- if t.multi > 0 && t.merger.Length() > 0 && toggle() {
- t.vmove(-1, true)
- req(reqList)
- }
- case actToggleUp:
- if t.multi > 0 && t.merger.Length() > 0 && toggle() {
- t.vmove(1, true)
- req(reqList)
- }
- case actDown:
- t.vmove(-1, true)
- req(reqList)
- case actUp:
- t.vmove(1, true)
- req(reqList)
- case actAccept:
- req(reqClose)
- case actAcceptNonEmpty:
- if len(t.selected) > 0 || t.merger.Length() > 0 || !t.reading && t.count == 0 {
- req(reqClose)
- }
- case actClearScreen:
- req(reqRedraw)
- case actClearQuery:
- t.input = []rune{}
- t.cx = 0
- case actClearSelection:
- if t.multi > 0 {
- t.selected = make(map[int32]selectedItem)
- t.version++
- req(reqList, reqInfo)
- }
- case actFirst:
- t.vset(0)
- req(reqList)
- case actLast:
- t.vset(t.merger.Length() - 1)
- 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)
- }
- case actYank:
- suffix := copySlice(t.input[t.cx:])
- t.input = append(append(t.input[:t.cx], t.yanked...), suffix...)
- t.cx += len(t.yanked)
- case actPageUp:
- t.vmove(t.maxItems()-1, false)
- req(reqList)
- case actPageDown:
- t.vmove(-(t.maxItems() - 1), false)
- req(reqList)
- case actHalfPageUp:
- t.vmove(t.maxItems()/2, false)
- req(reqList)
- case actHalfPageDown:
- t.vmove(-(t.maxItems() / 2), false)
- req(reqList)
- case actJump:
- t.jumping = jumpEnabled
- req(reqJump)
- case actJumpAccept:
- t.jumping = jumpAcceptEnabled
- req(reqJump)
- case actBackwardWord:
- t.cx = findLastMatch(t.wordRubout, string(t.input[:t.cx])) + 1
- case actForwardWord:
- t.cx += findFirstMatch(t.wordNext, string(t.input[t.cx:])) + 1
- case actKillWord:
- ncx := t.cx +
- findFirstMatch(t.wordNext, string(t.input[t.cx:])) + 1
- if ncx > t.cx {
- t.yanked = copySlice(t.input[t.cx:ncx])
- t.input = append(t.input[:t.cx], t.input[ncx:]...)
- }
- case actKillLine:
- if t.cx < len(t.input) {
- t.yanked = copySlice(t.input[t.cx:])
- t.input = t.input[:t.cx]
- }
- case actRune:
- prefix := copySlice(t.input[:t.cx])
- t.input = append(append(prefix, event.Char), t.input[t.cx:]...)
- t.cx++
- case actPreviousHistory:
- if t.history != nil {
- t.history.override(string(t.input))
- t.input = trimQuery(t.history.previous())
- t.cx = len(t.input)
- }
- case actNextHistory:
- if t.history != nil {
- t.history.override(string(t.input))
- t.input = trimQuery(t.history.next())
- t.cx = len(t.input)
- }
- case actToggleSearch:
- t.paused = !t.paused
- changed = !t.paused
- req(reqPrompt)
- case actEnableSearch:
- t.paused = false
- changed = true
- req(reqPrompt)
- case actDisableSearch:
- t.paused = true
- req(reqPrompt)
- case actSigStop:
- p, err := os.FindProcess(os.Getpid())
- if err == nil {
- t.sigstop = true
- t.tui.Clear()
- t.tui.Pause(t.fullscreen)
- notifyStop(p)
- t.mutex.Unlock()
- return false
- }
- case actMouse:
- me := event.MouseEvent
- mx, my := me.X, me.Y
- if me.S != 0 {
- // Scroll
- if t.window.Enclose(my, mx) && t.merger.Length() > 0 {
- if t.multi > 0 && me.Mod {
- toggle()
- }
- t.vmove(me.S, true)
- req(reqList)
- } else if t.hasPreviewWindow() && t.pwindow.Enclose(my, mx) {
- scrollPreviewBy(-me.S)
- }
- } else if t.window.Enclose(my, mx) {
- mx -= t.window.Left()
- my -= t.window.Top()
- mx = util.Constrain(mx-t.promptLen, 0, len(t.input))
- min := 2 + len(t.header)
- if t.noInfoLine() {
- min--
- }
- h := t.window.Height()
- switch t.layout {
- case layoutDefault:
- my = h - my - 1
- case layoutReverseList:
- if my < h-min {
- my += min
- } else {
- my = h - my - 1
- }
- }
- if me.Double {
- // Double-click
- if my >= min {
- if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
- return doActions(actionsFor(tui.DoubleClick))
- }
- }
- } else if me.Down {
- if my == t.promptLine() && mx >= 0 {
- // Prompt
- t.cx = mx + t.xoffset
- } else if my >= min {
- // List
- if t.vset(t.offset+my-min) && t.multi > 0 && me.Mod {
- toggle()
- }
- req(reqList)
- if me.Left {
- return doActions(actionsFor(tui.LeftClick))
- }
- return doActions(actionsFor(tui.RightClick))
- }
- }
- }
- case actReload:
- t.failed = nil
-
- valid, list := t.buildPlusList(a.a, false)
- if !valid {
- // We run the command even when there's no match
- // 1. If the template doesn't have any slots
- // 2. If the template has {q}
- slot, _, query := hasPreviewFlags(a.a)
- valid = !slot || query
- }
- if valid {
- command := t.replacePlaceholder(a.a, false, string(t.input), list)
- newCommand = &command
- t.reading = true
- t.version++
- }
- case actUnbind:
- keys := parseKeyChords(a.a, "PANIC")
- for key := range keys {
- delete(t.keymap, key)
- }
- }
- return true
- }
-
- if t.jumping == jumpDisabled {
- actions := t.keymap[event.Comparable()]
- if len(actions) == 0 && event.Type == tui.Rune {
- doAction(action{t: actRune})
- } else if !doActions(actions) {
- continue
- }
- t.truncateQuery()
- queryChanged = string(previousInput) != string(t.input)
- changed = changed || queryChanged
- if onChanges, prs := t.keymap[tui.Change.AsEvent()]; queryChanged && prs {
- if !doActions(onChanges) {
- continue
- }
- }
- if onEOFs, prs := t.keymap[tui.BackwardEOF.AsEvent()]; beof && prs {
- if !doActions(onEOFs) {
- continue
- }
- }
- } else {
- if event.Type == tui.Rune {
- if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() {
- t.cy = idx + t.offset
- if t.jumping == jumpAcceptEnabled {
- req(reqClose)
- }
- }
- }
- t.jumping = jumpDisabled
- req(reqList)
- }
-
- if queryChanged {
- if t.isPreviewEnabled() {
- _, _, q := hasPreviewFlags(t.previewOpts.command)
- if q {
- t.version++
- }
- }
- }
-
- if queryChanged || t.cx != previousCx {
- req(reqPrompt)
- }
-
- t.mutex.Unlock() // Must be unlocked before touching reqBox
-
- if changed || newCommand != nil {
- t.eventBox.Set(EvtSearchNew, searchRequest{sort: t.sort, command: newCommand})
- }
- for _, event := range events {
- t.reqBox.Set(event, nil)
- }
- }
-}
-
-func (t *Terminal) constrain() {
- // count of items to display allowed by filtering
- count := t.merger.Length()
- // count of lines can be displayed
- height := t.maxItems()
-
- t.cy = util.Constrain(t.cy, 0, count-1)
-
- minOffset := util.Max(t.cy-height+1, 0)
- maxOffset := util.Max(util.Min(count-height, t.cy), 0)
- t.offset = util.Constrain(t.offset, minOffset, maxOffset)
- if t.scrollOff == 0 {
- return
- }
-
- scrollOff := util.Min(height/2, t.scrollOff)
- for {
- prevOffset := t.offset
- if t.cy-t.offset < scrollOff {
- t.offset = util.Max(minOffset, t.offset-1)
- }
- if t.cy-t.offset >= height-scrollOff {
- t.offset = util.Min(maxOffset, t.offset+1)
- }
- if t.offset == prevOffset {
- break
- }
- }
-}
-
-func (t *Terminal) vmove(o int, allowCycle bool) {
- if t.layout != layoutDefault {
- o *= -1
- }
- dest := t.cy + o
- if t.cycle && allowCycle {
- max := t.merger.Length() - 1
- if dest > max {
- if t.cy == max {
- dest = 0
- }
- } else if dest < 0 {
- if t.cy == 0 {
- dest = max
- }
- }
- }
- t.vset(dest)
-}
-
-func (t *Terminal) vset(o int) bool {
- t.cy = util.Constrain(o, 0, t.merger.Length()-1)
- return t.cy == o
-}
-
-func (t *Terminal) maxItems() int {
- max := t.window.Height() - 2 - len(t.header)
- if t.noInfoLine() {
- max++
- }
- return util.Max(max, 0)
-}
diff --git a/.fzf/src/terminal_test.go b/.fzf/src/terminal_test.go
deleted file mode 100644
index ee19b67..0000000
--- a/.fzf/src/terminal_test.go
+++ /dev/null
@@ -1,638 +0,0 @@
-package fzf
-
-import (
- "bytes"
- "io"
- "os"
- "regexp"
- "strings"
- "testing"
- "text/template"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-func TestReplacePlaceholder(t *testing.T) {
- item1 := newItem(" foo'bar \x1b[31mbaz\x1b[m")
- items1 := []*Item{item1, item1}
- items2 := []*Item{
- newItem("foo'bar \x1b[31mbaz\x1b[m"),
- newItem("foo'bar \x1b[31mbaz\x1b[m"),
- newItem("FOO'BAR \x1b[31mBAZ\x1b[m")}
-
- delim := "'"
- var regex *regexp.Regexp
-
- var result string
- check := func(expected string) {
- if result != expected {
- t.Errorf("expected: %s, actual: %s", expected, result)
- }
- }
- // helper function that converts template format into string and carries out the check()
- checkFormat := func(format string) {
- type quotes struct{ O, I, S string } // outer, inner quotes, print separator
- unixStyle := quotes{`'`, `'\''`, "\n"}
- windowsStyle := quotes{`^"`, `'`, "\n"}
- var effectiveStyle quotes
-
- if util.IsWindows() {
- effectiveStyle = windowsStyle
- } else {
- effectiveStyle = unixStyle
- }
-
- expected := templateToString(format, effectiveStyle)
- check(expected)
- }
- printsep := "\n"
-
- /*
- Test multiple placeholders and the function parameters.
- */
-
- // {}, preserve ansi
- result = replacePlaceholder("echo {}", false, Delimiter{}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} foo{{.I}}bar \x1b[31mbaz\x1b[m{{.O}}")
-
- // {}, strip ansi
- result = replacePlaceholder("echo {}", true, Delimiter{}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} foo{{.I}}bar baz{{.O}}")
-
- // {}, with multiple items
- result = replacePlaceholder("echo {}", true, Delimiter{}, printsep, false, "query", items2)
- checkFormat("echo {{.O}}foo{{.I}}bar baz{{.O}}")
-
- // {..}, strip leading whitespaces, preserve ansi
- result = replacePlaceholder("echo {..}", false, Delimiter{}, printsep, false, "query", items1)
- checkFormat("echo {{.O}}foo{{.I}}bar \x1b[31mbaz\x1b[m{{.O}}")
-
- // {..}, strip leading whitespaces, strip ansi
- result = replacePlaceholder("echo {..}", true, Delimiter{}, printsep, false, "query", items1)
- checkFormat("echo {{.O}}foo{{.I}}bar baz{{.O}}")
-
- // {q}
- result = replacePlaceholder("echo {} {q}", true, Delimiter{}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} foo{{.I}}bar baz{{.O}} {{.O}}query{{.O}}")
-
- // {q}, multiple items
- result = replacePlaceholder("echo {+}{q}{+}", true, Delimiter{}, printsep, false, "query 'string'", items2)
- checkFormat("echo {{.O}}foo{{.I}}bar baz{{.O}} {{.O}}FOO{{.I}}BAR BAZ{{.O}}{{.O}}query {{.I}}string{{.I}}{{.O}}{{.O}}foo{{.I}}bar baz{{.O}} {{.O}}FOO{{.I}}BAR BAZ{{.O}}")
-
- result = replacePlaceholder("echo {}{q}{}", true, Delimiter{}, printsep, false, "query 'string'", items2)
- checkFormat("echo {{.O}}foo{{.I}}bar baz{{.O}}{{.O}}query {{.I}}string{{.I}}{{.O}}{{.O}}foo{{.I}}bar baz{{.O}}")
-
- result = replacePlaceholder("echo {1}/{2}/{2,1}/{-1}/{-2}/{}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, printsep, false, "query", items1)
- checkFormat("echo {{.O}}foo{{.I}}bar{{.O}}/{{.O}}baz{{.O}}/{{.O}}bazfoo{{.I}}bar{{.O}}/{{.O}}baz{{.O}}/{{.O}}foo{{.I}}bar{{.O}}/{{.O}} foo{{.I}}bar baz{{.O}}/{{.O}}foo{{.I}}bar baz{{.O}}/{n.t}/{}/{1}/{q}/{{.O}}{{.O}}")
-
- result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, printsep, false, "query", items2)
- checkFormat("echo {{.O}}foo{{.I}}bar{{.O}}/{{.O}}baz{{.O}}/{{.O}}baz{{.O}}/{{.O}}foo{{.I}}bar{{.O}}/{{.O}}foo{{.I}}bar baz{{.O}}/{n.t}/{}/{1}/{q}/{{.O}}{{.O}}")
-
- result = replacePlaceholder("echo {+1}/{+2}/{+-1}/{+-2}/{+..}/{n.t}/\\{}/\\{1}/\\{q}/{+3}", true, Delimiter{}, printsep, false, "query", items2)
- checkFormat("echo {{.O}}foo{{.I}}bar{{.O}} {{.O}}FOO{{.I}}BAR{{.O}}/{{.O}}baz{{.O}} {{.O}}BAZ{{.O}}/{{.O}}baz{{.O}} {{.O}}BAZ{{.O}}/{{.O}}foo{{.I}}bar{{.O}} {{.O}}FOO{{.I}}BAR{{.O}}/{{.O}}foo{{.I}}bar baz{{.O}} {{.O}}FOO{{.I}}BAR BAZ{{.O}}/{n.t}/{}/{1}/{q}/{{.O}}{{.O}} {{.O}}{{.O}}")
-
- // forcePlus
- result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, printsep, true, "query", items2)
- checkFormat("echo {{.O}}foo{{.I}}bar{{.O}} {{.O}}FOO{{.I}}BAR{{.O}}/{{.O}}baz{{.O}} {{.O}}BAZ{{.O}}/{{.O}}baz{{.O}} {{.O}}BAZ{{.O}}/{{.O}}foo{{.I}}bar{{.O}} {{.O}}FOO{{.I}}BAR{{.O}}/{{.O}}foo{{.I}}bar baz{{.O}} {{.O}}FOO{{.I}}BAR BAZ{{.O}}/{n.t}/{}/{1}/{q}/{{.O}}{{.O}} {{.O}}{{.O}}")
-
- // Whitespace preserving flag with "'" delimiter
- result = replacePlaceholder("echo {s1}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} foo{{.O}}")
-
- result = replacePlaceholder("echo {s2}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
- checkFormat("echo {{.O}}bar baz{{.O}}")
-
- result = replacePlaceholder("echo {s}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} foo{{.I}}bar baz{{.O}}")
-
- result = replacePlaceholder("echo {s..}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} foo{{.I}}bar baz{{.O}}")
-
- // Whitespace preserving flag with regex delimiter
- regex = regexp.MustCompile(`\w+`)
-
- result = replacePlaceholder("echo {s1}", true, Delimiter{regex: regex}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} {{.O}}")
-
- result = replacePlaceholder("echo {s2}", true, Delimiter{regex: regex}, printsep, false, "query", items1)
- checkFormat("echo {{.O}}{{.I}}{{.O}}")
-
- result = replacePlaceholder("echo {s3}", true, Delimiter{regex: regex}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} {{.O}}")
-
- // No match
- result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, printsep, false, "query", []*Item{nil, nil})
- check("echo /")
-
- // No match, but with selections
- result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, printsep, false, "query", []*Item{nil, item1})
- checkFormat("echo /{{.O}} foo{{.I}}bar baz{{.O}}")
-
- // String delimiter
- result = replacePlaceholder("echo {}/{1}/{2}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} foo{{.I}}bar baz{{.O}}/{{.O}}foo{{.O}}/{{.O}}bar baz{{.O}}")
-
- // Regex delimiter
- regex = regexp.MustCompile("[oa]+")
- // foo'bar baz
- result = replacePlaceholder("echo {}/{1}/{3}/{2..3}", true, Delimiter{regex: regex}, printsep, false, "query", items1)
- checkFormat("echo {{.O}} foo{{.I}}bar baz{{.O}}/{{.O}}f{{.O}}/{{.O}}r b{{.O}}/{{.O}}{{.I}}bar b{{.O}}")
-
- /*
- Test single placeholders, but focus on the placeholders' parameters (e.g. flags).
- see: TestParsePlaceholder
- */
- items3 := []*Item{
- // single line
- newItem("1a 1b 1c 1d 1e 1f"),
- // multi line
- newItem("1a 1b 1c 1d 1e 1f"),
- newItem("2a 2b 2c 2d 2e 2f"),
- newItem("3a 3b 3c 3d 3e 3f"),
- newItem("4a 4b 4c 4d 4e 4f"),
- newItem("5a 5b 5c 5d 5e 5f"),
- newItem("6a 6b 6c 6d 6e 6f"),
- newItem("7a 7b 7c 7d 7e 7f"),
- }
- stripAnsi := false
- printsep = "\n"
- forcePlus := false
- query := "sample query"
-
- templateToOutput := make(map[string]string)
- templateToFile := make(map[string]string) // same as above, but the file contents will be matched
- // I. item type placeholder
- templateToOutput[`{}`] = `{{.O}}1a 1b 1c 1d 1e 1f{{.O}}`
- templateToOutput[`{+}`] = `{{.O}}1a 1b 1c 1d 1e 1f{{.O}} {{.O}}2a 2b 2c 2d 2e 2f{{.O}} {{.O}}3a 3b 3c 3d 3e 3f{{.O}} {{.O}}4a 4b 4c 4d 4e 4f{{.O}} {{.O}}5a 5b 5c 5d 5e 5f{{.O}} {{.O}}6a 6b 6c 6d 6e 6f{{.O}} {{.O}}7a 7b 7c 7d 7e 7f{{.O}}`
- templateToOutput[`{n}`] = `0`
- templateToOutput[`{+n}`] = `0 0 0 0 0 0 0`
- templateToFile[`{f}`] = `1a 1b 1c 1d 1e 1f{{.S}}`
- templateToFile[`{+f}`] = `1a 1b 1c 1d 1e 1f{{.S}}2a 2b 2c 2d 2e 2f{{.S}}3a 3b 3c 3d 3e 3f{{.S}}4a 4b 4c 4d 4e 4f{{.S}}5a 5b 5c 5d 5e 5f{{.S}}6a 6b 6c 6d 6e 6f{{.S}}7a 7b 7c 7d 7e 7f{{.S}}`
- templateToFile[`{nf}`] = `0{{.S}}`
- templateToFile[`{+nf}`] = `0{{.S}}0{{.S}}0{{.S}}0{{.S}}0{{.S}}0{{.S}}0{{.S}}`
-
- // II. token type placeholders
- templateToOutput[`{..}`] = templateToOutput[`{}`]
- templateToOutput[`{1..}`] = templateToOutput[`{}`]
- templateToOutput[`{..2}`] = `{{.O}}1a 1b{{.O}}`
- templateToOutput[`{1..2}`] = templateToOutput[`{..2}`]
- templateToOutput[`{-2..-1}`] = `{{.O}}1e 1f{{.O}}`
- // shorthand for x..x range
- templateToOutput[`{1}`] = `{{.O}}1a{{.O}}`
- templateToOutput[`{1..1}`] = templateToOutput[`{1}`]
- templateToOutput[`{-6}`] = templateToOutput[`{1}`]
- // multiple ranges
- templateToOutput[`{1,2}`] = templateToOutput[`{1..2}`]
- templateToOutput[`{1,2,4}`] = `{{.O}}1a 1b 1d{{.O}}`
- templateToOutput[`{1,2..4}`] = `{{.O}}1a 1b 1c 1d{{.O}}`
- templateToOutput[`{1..2,-4..-3}`] = `{{.O}}1a 1b 1c 1d{{.O}}`
- // flags
- templateToOutput[`{+1}`] = `{{.O}}1a{{.O}} {{.O}}2a{{.O}} {{.O}}3a{{.O}} {{.O}}4a{{.O}} {{.O}}5a{{.O}} {{.O}}6a{{.O}} {{.O}}7a{{.O}}`
- templateToOutput[`{+-1}`] = `{{.O}}1f{{.O}} {{.O}}2f{{.O}} {{.O}}3f{{.O}} {{.O}}4f{{.O}} {{.O}}5f{{.O}} {{.O}}6f{{.O}} {{.O}}7f{{.O}}`
- templateToOutput[`{s1}`] = `{{.O}}1a {{.O}}`
- templateToFile[`{f1}`] = `1a{{.S}}`
- templateToOutput[`{+s1..2}`] = `{{.O}}1a 1b {{.O}} {{.O}}2a 2b {{.O}} {{.O}}3a 3b {{.O}} {{.O}}4a 4b {{.O}} {{.O}}5a 5b {{.O}} {{.O}}6a 6b {{.O}} {{.O}}7a 7b {{.O}}`
- templateToFile[`{+sf1..2}`] = `1a 1b {{.S}}2a 2b {{.S}}3a 3b {{.S}}4a 4b {{.S}}5a 5b {{.S}}6a 6b {{.S}}7a 7b {{.S}}`
-
- // III. query type placeholder
- // query flag is not removed after parsing, so it gets doubled
- // while the double q is invalid, it is useful here for testing purposes
- templateToOutput[`{q}`] = "{{.O}}" + query + "{{.O}}"
-
- // IV. escaping placeholder
- templateToOutput[`\{}`] = `{}`
- templateToOutput[`\{++}`] = `{++}`
- templateToOutput[`{++}`] = templateToOutput[`{+}`]
-
- for giveTemplate, wantOutput := range templateToOutput {
- result = replacePlaceholder(giveTemplate, stripAnsi, Delimiter{}, printsep, forcePlus, query, items3)
- checkFormat(wantOutput)
- }
- for giveTemplate, wantOutput := range templateToFile {
- path := replacePlaceholder(giveTemplate, stripAnsi, Delimiter{}, printsep, forcePlus, query, items3)
-
- data, err := readFile(path)
- if err != nil {
- t.Errorf("Cannot read the content of the temp file %s.", path)
- }
- result = string(data)
-
- checkFormat(wantOutput)
- }
-}
-
-func TestQuoteEntry(t *testing.T) {
- type quotes struct{ E, O, SQ, DQ, BS string } // standalone escape, outer, single and double quotes, backslash
- unixStyle := quotes{``, `'`, `'\''`, `"`, `\`}
- windowsStyle := quotes{`^`, `^"`, `'`, `\^"`, `\\`}
- var effectiveStyle quotes
-
- if util.IsWindows() {
- effectiveStyle = windowsStyle
- } else {
- effectiveStyle = unixStyle
- }
-
- tests := map[string]string{
- `'`: `{{.O}}{{.SQ}}{{.O}}`,
- `"`: `{{.O}}{{.DQ}}{{.O}}`,
- `\`: `{{.O}}{{.BS}}{{.O}}`,
- `\"`: `{{.O}}{{.BS}}{{.DQ}}{{.O}}`,
- `"\\\"`: `{{.O}}{{.DQ}}{{.BS}}{{.BS}}{{.BS}}{{.DQ}}{{.O}}`,
-
- `$`: `{{.O}}${{.O}}`,
- `$HOME`: `{{.O}}$HOME{{.O}}`,
- `'$HOME'`: `{{.O}}{{.SQ}}$HOME{{.SQ}}{{.O}}`,
-
- `&`: `{{.O}}{{.E}}&{{.O}}`,
- `|`: `{{.O}}{{.E}}|{{.O}}`,
- `<`: `{{.O}}{{.E}}<{{.O}}`,
- `>`: `{{.O}}{{.E}}>{{.O}}`,
- `(`: `{{.O}}{{.E}}({{.O}}`,
- `)`: `{{.O}}{{.E}}){{.O}}`,
- `@`: `{{.O}}{{.E}}@{{.O}}`,
- `^`: `{{.O}}{{.E}}^{{.O}}`,
- `%`: `{{.O}}{{.E}}%{{.O}}`,
- `!`: `{{.O}}{{.E}}!{{.O}}`,
- `%USERPROFILE%`: `{{.O}}{{.E}}%USERPROFILE{{.E}}%{{.O}}`,
- `C:\Program Files (x86)\`: `{{.O}}C:{{.BS}}Program Files {{.E}}(x86{{.E}}){{.BS}}{{.O}}`,
- `"C:\Program Files"`: `{{.O}}{{.DQ}}C:{{.BS}}Program Files{{.DQ}}{{.O}}`,
- }
-
- for input, expected := range tests {
- escaped := quoteEntry(input)
- expected = templateToString(expected, effectiveStyle)
- if escaped != expected {
- t.Errorf("Input: %s, expected: %s, actual %s", input, expected, escaped)
- }
- }
-}
-
-// purpose of this test is to demonstrate some shortcomings of fzf's templating system on Unix
-func TestUnixCommands(t *testing.T) {
- if util.IsWindows() {
- t.SkipNow()
- }
- tests := []testCase{
- // reference: give{template, query, items}, want{output OR match}
-
- // 1) working examples
-
- // paths that does not have to evaluated will work fine, when quoted
- {give{`grep foo {}`, ``, newItems(`test`)}, want{output: `grep foo 'test'`}},
- {give{`grep foo {}`, ``, newItems(`/home/user/test`)}, want{output: `grep foo '/home/user/test'`}},
- {give{`grep foo {}`, ``, newItems(`./test`)}, want{output: `grep foo './test'`}},
-
- // only placeholders are escaped as data, this will lookup tilde character in a test file in your home directory
- // quoting the tilde is required (to be treated as string)
- {give{`grep {} ~/test`, ``, newItems(`~`)}, want{output: `grep '~' ~/test`}},
-
- // 2) problematic examples
- // (not necessarily unexpected)
-
- // paths that need to expand some part of it won't work (special characters and variables)
- {give{`cat {}`, ``, newItems(`~/test`)}, want{output: `cat '~/test'`}},
- {give{`cat {}`, ``, newItems(`$HOME/test`)}, want{output: `cat '$HOME/test'`}},
- }
- testCommands(t, tests)
-}
-
-// purpose of this test is to demonstrate some shortcomings of fzf's templating system on Windows
-func TestWindowsCommands(t *testing.T) {
- if !util.IsWindows() {
- t.SkipNow()
- }
- tests := []testCase{
- // reference: give{template, query, items}, want{output OR match}
-
- // 1) working examples
-
- // example of redundantly escaped backslash in the output, besides looking bit ugly, it won't cause any issue
- {give{`type {}`, ``, newItems(`C:\test.txt`)}, want{output: `type ^"C:\\test.txt^"`}},
- {give{`rg -- "package" {}`, ``, newItems(`.\test.go`)}, want{output: `rg -- "package" ^".\\test.go^"`}},
- // example of mandatorily escaped backslash in the output, otherwise `rg -- "C:\test.txt"` is matching for tabulator
- {give{`rg -- {}`, ``, newItems(`C:\test.txt`)}, want{output: `rg -- ^"C:\\test.txt^"`}},
- // example of mandatorily escaped double quote in the output, otherwise `rg -- ""C:\\test.txt""` is not matching for the double quotes around the path
- {give{`rg -- {}`, ``, newItems(`"C:\test.txt"`)}, want{output: `rg -- ^"\^"C:\\test.txt\^"^"`}},
-
- // 2) problematic examples
- // (not necessarily unexpected)
-
- // notepad++'s parser can't handle `-n"12"` generate by fzf, expects `-n12`
- {give{`notepad++ -n{1} {2}`, ``, newItems(`12 C:\Work\Test Folder\File.txt`)}, want{output: `notepad++ -n^"12^" ^"C:\\Work\\Test Folder\\File.txt^"`}},
-
- // cat is parsing `\"` as a part of the file path, double quote is illegal character for paths on Windows
- // cat: "C:\\test.txt: Invalid argument
- {give{`cat {}`, ``, newItems(`"C:\test.txt"`)}, want{output: `cat ^"\^"C:\\test.txt\^"^"`}},
- // cat: "C:\\test.txt": Invalid argument
- {give{`cmd /c {}`, ``, newItems(`cat "C:\test.txt"`)}, want{output: `cmd /c ^"cat \^"C:\\test.txt\^"^"`}},
-
- // the "file" flag in the pattern won't create *.bat or *.cmd file so the command in the output tries to edit the file, instead of executing it
- // the temp file contains: `cat "C:\test.txt"`
- // TODO this should actually work
- {give{`cmd /c {f}`, ``, newItems(`cat "C:\test.txt"`)}, want{match: `^cmd /c .*\fzf-preview-[0-9]{9}$`}},
- }
- testCommands(t, tests)
-}
-
-// purpose of this test is to demonstrate some shortcomings of fzf's templating system on Windows in Powershell
-func TestPowershellCommands(t *testing.T) {
- if !util.IsWindows() {
- t.SkipNow()
- }
-
- tests := []testCase{
- // reference: give{template, query, items}, want{output OR match}
-
- /*
- You can read each line in the following table as a pipeline that
- consist of series of parsers that act upon your input (col. 1) and
- each cell represents the output value.
-
- For example:
- - exec.Command("program.exe", `\''`)
- - goes to win32 api which will process it transparently as it contains no special characters, see [CommandLineToArgvW][].
- - powershell command will receive it as is, that is two arguments: a literal backslash and empty string in single quotes
- - native command run via/from powershell will receive only one argument: a literal backslash. Because extra parsing rules apply, see [NativeCallsFromPowershell][].
- - some¹ apps have internal parser, that requires one more level of escaping (yes, this is completely application-specific, but see terminal_test.go#TestWindowsCommands)
-
- Character⁰ CommandLineToArgvW Powershell commands Native commands from Powershell Apps requiring escapes¹ | Being tested below
- ---------- ------------------ ------------------------------ ------------------------------- -------------------------- | ------------------
- " empty string² missing argument error ... ... |
- \" literal " unbalanced quote error ... ... |
- '\"' literal '"' literal " empty string empty string (match all) | yes
- '\\\"' literal '\"' literal \" literal " literal " |
- ---------- ------------------ ------------------------------ ------------------------------- -------------------------- | ------------------
- \ transparent transparent transparent regex error |
- '\' transparent literal \ literal \ regex error | yes
- \\ transparent transparent transparent literal \ |
- '\\' transparent literal \\ literal \\ literal \ |
- ---------- ------------------ ------------------------------ ------------------------------- -------------------------- | ------------------
- ' transparent unbalanced quote error ... ... |
- \' transparent literal \ and unb. quote error ... ... |
- \'' transparent literal \ and empty string literal \ regex error | no, but given as example above
- ''' transparent unbalanced quote error ... ... |
- '''' transparent literal ' literal ' literal ' | yes
- ---------- ------------------ ------------------------------ ------------------------------- -------------------------- | ------------------
-
- ⁰: charatecter or characters 'x' as an argument to a program in go's call: exec.Command("program.exe", `x`)
- ¹: native commands like grep, git grep, ripgrep
- ²: interpreted as a grouping quote, affects argument parser and gets removed from the result
-
- [CommandLineToArgvW]: https://docs.microsoft.com/en-gb/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw#remarks
- [NativeCallsFromPowershell]: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing?view=powershell-7.1#passing-arguments-that-contain-quote-characters
- */
-
- // 1) working examples
-
- {give{`Get-Content {}`, ``, newItems(`C:\test.txt`)}, want{output: `Get-Content 'C:\test.txt'`}},
- {give{`rg -- "package" {}`, ``, newItems(`.\test.go`)}, want{output: `rg -- "package" '.\test.go'`}},
-
- // example of escaping single quotes
- {give{`rg -- {}`, ``, newItems(`'foobar'`)}, want{output: `rg -- '''foobar'''`}},
-
- // chaining powershells
- {give{`powershell -NoProfile -Command {}`, ``, newItems(`cat "C:\test.txt"`)}, want{output: `powershell -NoProfile -Command 'cat \"C:\test.txt\"'`}},
-
- // 2) problematic examples
- // (not necessarily unexpected)
-
- // looking for a path string will only work with escaped backslashes
- {give{`rg -- {}`, ``, newItems(`C:\test.txt`)}, want{output: `rg -- 'C:\test.txt'`}},
- // looking for a literal double quote will only work with triple escaped double quotes
- {give{`rg -- {}`, ``, newItems(`"C:\test.txt"`)}, want{output: `rg -- '\"C:\test.txt\"'`}},
-
- // Get-Content (i.e. cat alias) is parsing `"` as a part of the file path, returns an error:
- // Get-Content : Cannot find drive. A drive with the name '"C:' does not exist.
- {give{`cat {}`, ``, newItems(`"C:\test.txt"`)}, want{output: `cat '\"C:\test.txt\"'`}},
-
- // the "file" flag in the pattern won't create *.ps1 file so the powershell will offload this "unknown" filetype
- // to explorer, which will prompt user to pick editing program for the fzf-preview file
- // the temp file contains: `cat "C:\test.txt"`
- // TODO this should actually work
- {give{`powershell -NoProfile -Command {f}`, ``, newItems(`cat "C:\test.txt"`)}, want{match: `^powershell -NoProfile -Command .*\fzf-preview-[0-9]{9}$`}},
- }
-
- // to force powershell-style escaping we temporarily set environment variable that fzf honors
- shellBackup := os.Getenv("SHELL")
- os.Setenv("SHELL", "powershell")
- testCommands(t, tests)
- os.Setenv("SHELL", shellBackup)
-}
-
-/*
- Test typical valid placeholders and parsing of them.
-
- Also since the parser assumes the input is matched with `placeholder` regex,
- the regex is tested here as well.
-*/
-func TestParsePlaceholder(t *testing.T) {
- // give, want pairs
- templates := map[string]string{
- // I. item type placeholder
- `{}`: `{}`,
- `{+}`: `{+}`,
- `{n}`: `{n}`,
- `{+n}`: `{+n}`,
- `{f}`: `{f}`,
- `{+nf}`: `{+nf}`,
-
- // II. token type placeholders
- `{..}`: `{..}`,
- `{1..}`: `{1..}`,
- `{..2}`: `{..2}`,
- `{1..2}`: `{1..2}`,
- `{-2..-1}`: `{-2..-1}`,
- // shorthand for x..x range
- `{1}`: `{1}`,
- `{1..1}`: `{1..1}`,
- `{-6}`: `{-6}`,
- // multiple ranges
- `{1,2}`: `{1,2}`,
- `{1,2,4}`: `{1,2,4}`,
- `{1,2..4}`: `{1,2..4}`,
- `{1..2,-4..-3}`: `{1..2,-4..-3}`,
- // flags
- `{+1}`: `{+1}`,
- `{+-1}`: `{+-1}`,
- `{s1}`: `{s1}`,
- `{f1}`: `{f1}`,
- `{+s1..2}`: `{+s1..2}`,
- `{+sf1..2}`: `{+sf1..2}`,
-
- // III. query type placeholder
- // query flag is not removed after parsing, so it gets doubled
- // while the double q is invalid, it is useful here for testing purposes
- `{q}`: `{qq}`,
-
- // IV. escaping placeholder
- `\{}`: `{}`,
- `\{++}`: `{++}`,
- `{++}`: `{+}`,
- }
-
- for giveTemplate, wantTemplate := range templates {
- if !placeholder.MatchString(giveTemplate) {
- t.Errorf(`given placeholder %s does not match placeholder regex, so attempt to parse it is unexpected`, giveTemplate)
- continue
- }
-
- _, placeholderWithoutFlags, flags := parsePlaceholder(giveTemplate)
- gotTemplate := placeholderWithoutFlags[:1] + flags.encodePlaceholder() + placeholderWithoutFlags[1:]
-
- if gotTemplate != wantTemplate {
- t.Errorf(`parsed placeholder "%s" into "%s", but want "%s"`, giveTemplate, gotTemplate, wantTemplate)
- }
- }
-}
-
-/* utilities section */
-
-// Item represents one line in fzf UI. Usually it is relative path to files and folders.
-func newItem(str string) *Item {
- bytes := []byte(str)
- trimmed, _, _ := extractColor(str, nil, nil)
- return &Item{origText: &bytes, text: util.ToChars([]byte(trimmed))}
-}
-
-// Functions tested in this file require array of items (allItems). The array needs
-// to consist of at least two nils. This is helper function.
-func newItems(str ...string) []*Item {
- result := make([]*Item, util.Max(len(str), 2))
- for i, s := range str {
- result[i] = newItem(s)
- }
- return result
-}
-
-// (for logging purposes)
-func (item *Item) String() string {
- return item.AsString(true)
-}
-
-// Helper function to parse, execute and convert "text/template" to string. Panics on error.
-func templateToString(format string, data interface{}) string {
- bb := &bytes.Buffer{}
-
- err := template.Must(template.New("").Parse(format)).Execute(bb, data)
- if err != nil {
- panic(err)
- }
-
- return bb.String()
-}
-
-// ad hoc types for test cases
-type give struct {
- template string
- query string
- allItems []*Item
-}
-type want struct {
- /*
- Unix:
- The `want.output` string is supposed to be formatted for evaluation by
- `sh -c command` system call.
-
- Windows:
- The `want.output` string is supposed to be formatted for evaluation by
- `cmd.exe /s /c "command"` system call. The `/s` switch enables so called old
- behaviour, which is more favourable for nesting (possibly escaped)
- special characters. This is the relevant section of `help cmd`:
-
- ...old behavior is to see if the first character is
- a quote character and if so, strip the leading character and
- remove the last quote character on the command line, preserving
- any text after the last quote character.
- */
- output string // literal output
- match string // output is matched against this regex (when output is empty string)
-}
-type testCase struct {
- give
- want
-}
-
-func testCommands(t *testing.T, tests []testCase) {
- // common test parameters
- delim := "\t"
- delimiter := Delimiter{str: &delim}
- printsep := ""
- stripAnsi := false
- forcePlus := false
-
- // evaluate the test cases
- for idx, test := range tests {
- gotOutput := replacePlaceholder(
- test.give.template, stripAnsi, delimiter, printsep, forcePlus,
- test.give.query,
- test.give.allItems)
- switch {
- case test.want.output != "":
- if gotOutput != test.want.output {
- t.Errorf("tests[%v]:\ngave{\n\ttemplate: '%s',\n\tquery: '%s',\n\tallItems: %s}\nand got '%s',\nbut want '%s'",
- idx,
- test.give.template, test.give.query, test.give.allItems,
- gotOutput, test.want.output)
- }
- case test.want.match != "":
- wantMatch := strings.ReplaceAll(test.want.match, `\`, `\\`)
- wantRegex := regexp.MustCompile(wantMatch)
- if !wantRegex.MatchString(gotOutput) {
- t.Errorf("tests[%v]:\ngave{\n\ttemplate: '%s',\n\tquery: '%s',\n\tallItems: %s}\nand got '%s',\nbut want '%s'",
- idx,
- test.give.template, test.give.query, test.give.allItems,
- gotOutput, test.want.match)
- }
- default:
- t.Errorf("tests[%v]: test case does not describe 'want' property", idx)
- }
- }
-}
-
-// naive encoder of placeholder flags
-func (flags placeholderFlags) encodePlaceholder() string {
- encoded := ""
- if flags.plus {
- encoded += "+"
- }
- if flags.preserveSpace {
- encoded += "s"
- }
- if flags.number {
- encoded += "n"
- }
- if flags.file {
- encoded += "f"
- }
- if flags.query {
- encoded += "q"
- }
- return encoded
-}
-
-// can be replaced with os.ReadFile() in go 1.16+
-func readFile(path string) ([]byte, error) {
- file, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer file.Close()
-
- data := make([]byte, 0, 128)
- for {
- if len(data) >= cap(data) {
- d := append(data[:cap(data)], 0)
- data = d[:len(data)]
- }
-
- n, err := file.Read(data[len(data):cap(data)])
- data = data[:len(data)+n]
- if err != nil {
- if err == io.EOF {
- err = nil
- }
- return data, err
- }
- }
-}
diff --git a/.fzf/src/terminal_unix.go b/.fzf/src/terminal_unix.go
deleted file mode 100644
index b14cd68..0000000
--- a/.fzf/src/terminal_unix.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// +build !windows
-
-package fzf
-
-import (
- "os"
- "os/signal"
- "strings"
- "syscall"
-)
-
-func notifyOnResize(resizeChan chan<- os.Signal) {
- signal.Notify(resizeChan, syscall.SIGWINCH)
-}
-
-func notifyStop(p *os.Process) {
- p.Signal(syscall.SIGSTOP)
-}
-
-func notifyOnCont(resizeChan chan<- os.Signal) {
- signal.Notify(resizeChan, syscall.SIGCONT)
-}
-
-func quoteEntry(entry string) string {
- return "'" + strings.Replace(entry, "'", "'\\''", -1) + "'"
-}
diff --git a/.fzf/src/terminal_windows.go b/.fzf/src/terminal_windows.go
deleted file mode 100644
index 5e74873..0000000
--- a/.fzf/src/terminal_windows.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// +build windows
-
-package fzf
-
-import (
- "os"
- "regexp"
- "strings"
-)
-
-func notifyOnResize(resizeChan chan<- os.Signal) {
- // TODO
-}
-
-func notifyStop(p *os.Process) {
- // NOOP
-}
-
-func notifyOnCont(resizeChan chan<- os.Signal) {
- // NOOP
-}
-
-func quoteEntry(entry string) string {
- shell := os.Getenv("SHELL")
- if len(shell) == 0 {
- shell = "cmd"
- }
-
- if strings.Contains(shell, "cmd") {
- // backslash escaping is done here for applications
- // (see ripgrep test case in terminal_test.go#TestWindowsCommands)
- escaped := strings.Replace(entry, `\`, `\\`, -1)
- escaped = `"` + strings.Replace(escaped, `"`, `\"`, -1) + `"`
- // caret is the escape character for cmd shell
- r, _ := regexp.Compile(`[&|<>()@^%!"]`)
- return r.ReplaceAllStringFunc(escaped, func(match string) string {
- return "^" + match
- })
- } else if strings.Contains(shell, "pwsh") || strings.Contains(shell, "powershell") {
- escaped := strings.Replace(entry, `"`, `\"`, -1)
- return "'" + strings.Replace(escaped, "'", "''", -1) + "'"
- } else {
- return "'" + strings.Replace(entry, "'", "'\\''", -1) + "'"
- }
-}
diff --git a/.fzf/src/tokenizer.go b/.fzf/src/tokenizer.go
deleted file mode 100644
index 26f42d2..0000000
--- a/.fzf/src/tokenizer.go
+++ /dev/null
@@ -1,253 +0,0 @@
-package fzf
-
-import (
- "bytes"
- "fmt"
- "regexp"
- "strconv"
- "strings"
-
- "github.com/junegunn/fzf/src/util"
-)
-
-const rangeEllipsis = 0
-
-// Range represents nth-expression
-type Range struct {
- begin int
- end int
-}
-
-// Token contains the tokenized part of the strings and its prefix length
-type Token struct {
- text *util.Chars
- prefixLength int32
-}
-
-// String returns the string representation of a Token.
-func (t Token) String() string {
- return fmt.Sprintf("Token{text: %s, prefixLength: %d}", t.text, t.prefixLength)
-}
-
-// Delimiter for tokenizing the input
-type Delimiter struct {
- regex *regexp.Regexp
- str *string
-}
-
-// String returns the string representation of a Delimiter.
-func (d Delimiter) String() string {
- return fmt.Sprintf("Delimiter{regex: %v, str: &%q}", d.regex, *d.str)
-}
-
-func newRange(begin int, end int) Range {
- if begin == 1 {
- begin = rangeEllipsis
- }
- if end == -1 {
- end = rangeEllipsis
- }
- return Range{begin, end}
-}
-
-// ParseRange parses nth-expression and returns the corresponding Range object
-func ParseRange(str *string) (Range, bool) {
- if (*str) == ".." {
- return newRange(rangeEllipsis, rangeEllipsis), true
- } else if strings.HasPrefix(*str, "..") {
- end, err := strconv.Atoi((*str)[2:])
- if err != nil || end == 0 {
- return Range{}, false
- }
- return newRange(rangeEllipsis, end), true
- } else if strings.HasSuffix(*str, "..") {
- begin, err := strconv.Atoi((*str)[:len(*str)-2])
- if err != nil || begin == 0 {
- return Range{}, false
- }
- return newRange(begin, rangeEllipsis), true
- } else if strings.Contains(*str, "..") {
- ns := strings.Split(*str, "..")
- if len(ns) != 2 {
- return Range{}, false
- }
- begin, err1 := strconv.Atoi(ns[0])
- end, err2 := strconv.Atoi(ns[1])
- if err1 != nil || err2 != nil || begin == 0 || end == 0 {
- return Range{}, false
- }
- return newRange(begin, end), true
- }
-
- n, err := strconv.Atoi(*str)
- if err != nil || n == 0 {
- return Range{}, false
- }
- return newRange(n, n), true
-}
-
-func withPrefixLengths(tokens []string, begin int) []Token {
- ret := make([]Token, len(tokens))
-
- prefixLength := begin
- for idx := range tokens {
- chars := util.ToChars([]byte(tokens[idx]))
- ret[idx] = Token{&chars, int32(prefixLength)}
- prefixLength += chars.Length()
- }
- return ret
-}
-
-const (
- awkNil = iota
- awkBlack
- awkWhite
-)
-
-func awkTokenizer(input string) ([]string, int) {
- // 9, 32
- ret := []string{}
- prefixLength := 0
- state := awkNil
- begin := 0
- end := 0
- for idx := 0; idx < len(input); idx++ {
- r := input[idx]
- white := r == 9 || r == 32
- switch state {
- case awkNil:
- if white {
- prefixLength++
- } else {
- state, begin, end = awkBlack, idx, idx+1
- }
- case awkBlack:
- end = idx + 1
- if white {
- state = awkWhite
- }
- case awkWhite:
- if white {
- end = idx + 1
- } else {
- ret = append(ret, input[begin:end])
- state, begin, end = awkBlack, idx, idx+1
- }
- }
- }
- if begin < end {
- ret = append(ret, input[begin:end])
- }
- return ret, prefixLength
-}
-
-// Tokenize tokenizes the given string with the delimiter
-func Tokenize(text string, delimiter Delimiter) []Token {
- if delimiter.str == nil && delimiter.regex == nil {
- // AWK-style (\S+\s*)
- tokens, prefixLength := awkTokenizer(text)
- return withPrefixLengths(tokens, prefixLength)
- }
-
- if delimiter.str != nil {
- return withPrefixLengths(strings.SplitAfter(text, *delimiter.str), 0)
- }
-
- // FIXME performance
- var tokens []string
- if delimiter.regex != nil {
- for len(text) > 0 {
- loc := delimiter.regex.FindStringIndex(text)
- if len(loc) < 2 {
- loc = []int{0, len(text)}
- }
- last := util.Max(loc[1], 1)
- tokens = append(tokens, text[:last])
- text = text[last:]
- }
- }
- return withPrefixLengths(tokens, 0)
-}
-
-func joinTokens(tokens []Token) string {
- var output bytes.Buffer
- for _, token := range tokens {
- output.WriteString(token.text.ToString())
- }
- return output.String()
-}
-
-// Transform is used to transform the input when --with-nth option is given
-func Transform(tokens []Token, withNth []Range) []Token {
- transTokens := make([]Token, len(withNth))
- numTokens := len(tokens)
- for idx, r := range withNth {
- parts := []*util.Chars{}
- minIdx := 0
- if r.begin == r.end {
- idx := r.begin
- if idx == rangeEllipsis {
- chars := util.ToChars([]byte(joinTokens(tokens)))
- parts = append(parts, &chars)
- } else {
- if idx < 0 {
- idx += numTokens + 1
- }
- if idx >= 1 && idx <= numTokens {
- minIdx = idx - 1
- parts = append(parts, tokens[idx-1].text)
- }
- }
- } else {
- var begin, end int
- if r.begin == rangeEllipsis { // ..N
- begin, end = 1, r.end
- if end < 0 {
- end += numTokens + 1
- }
- } else if r.end == rangeEllipsis { // N..
- begin, end = r.begin, numTokens
- if begin < 0 {
- begin += numTokens + 1
- }
- } else {
- begin, end = r.begin, r.end
- if begin < 0 {
- begin += numTokens + 1
- }
- if end < 0 {
- end += numTokens + 1
- }
- }
- minIdx = util.Max(0, begin-1)
- for idx := begin; idx <= end; idx++ {
- if idx >= 1 && idx <= numTokens {
- parts = append(parts, tokens[idx-1].text)
- }
- }
- }
- // Merge multiple parts
- var merged util.Chars
- switch len(parts) {
- case 0:
- merged = util.ToChars([]byte{})
- case 1:
- merged = *parts[0]
- default:
- var output bytes.Buffer
- for _, part := range parts {
- output.WriteString(part.ToString())
- }
- merged = util.ToChars(output.Bytes())
- }
-
- var prefixLength int32
- if minIdx < numTokens {
- prefixLength = tokens[minIdx].prefixLength
- } else {
- prefixLength = 0
- }
- transTokens[idx] = Token{&merged, prefixLength}
- }
- return transTokens
-}
diff --git a/.fzf/src/tokenizer_test.go b/.fzf/src/tokenizer_test.go
deleted file mode 100644
index 985cef9..0000000
--- a/.fzf/src/tokenizer_test.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package fzf
-
-import (
- "testing"
-)
-
-func TestParseRange(t *testing.T) {
- {
- i := ".."
- r, _ := ParseRange(&i)
- if r.begin != rangeEllipsis || r.end != rangeEllipsis {
- t.Errorf("%v", r)
- }
- }
- {
- i := "3.."
- r, _ := ParseRange(&i)
- if r.begin != 3 || r.end != rangeEllipsis {
- t.Errorf("%v", r)
- }
- }
- {
- i := "3..5"
- r, _ := ParseRange(&i)
- if r.begin != 3 || r.end != 5 {
- t.Errorf("%v", r)
- }
- }
- {
- i := "-3..-5"
- r, _ := ParseRange(&i)
- if r.begin != -3 || r.end != -5 {
- t.Errorf("%v", r)
- }
- }
- {
- i := "3"
- r, _ := ParseRange(&i)
- if r.begin != 3 || r.end != 3 {
- t.Errorf("%v", r)
- }
- }
-}
-
-func TestTokenize(t *testing.T) {
- // AWK-style
- input := " abc: def: ghi "
- tokens := Tokenize(input, Delimiter{})
- if tokens[0].text.ToString() != "abc: " || tokens[0].prefixLength != 2 {
- t.Errorf("%s", tokens)
- }
-
- // With delimiter
- tokens = Tokenize(input, delimiterRegexp(":"))
- if tokens[0].text.ToString() != " abc:" || tokens[0].prefixLength != 0 {
- t.Error(tokens[0].text.ToString(), tokens[0].prefixLength)
- }
-
- // With delimiter regex
- tokens = Tokenize(input, delimiterRegexp("\\s+"))
- if tokens[0].text.ToString() != " " || tokens[0].prefixLength != 0 ||
- tokens[1].text.ToString() != "abc: " || tokens[1].prefixLength != 2 ||
- tokens[2].text.ToString() != "def: " || tokens[2].prefixLength != 8 ||
- tokens[3].text.ToString() != "ghi " || tokens[3].prefixLength != 14 {
- t.Errorf("%s", tokens)
- }
-}
-
-func TestTransform(t *testing.T) {
- input := " abc: def: ghi: jkl"
- {
- tokens := Tokenize(input, Delimiter{})
- {
- ranges := splitNth("1,2,3")
- tx := Transform(tokens, ranges)
- if joinTokens(tx) != "abc: def: ghi: " {
- t.Errorf("%s", tx)
- }
- }
- {
- ranges := splitNth("1..2,3,2..,1")
- tx := Transform(tokens, ranges)
- if string(joinTokens(tx)) != "abc: def: ghi: def: ghi: jklabc: " ||
- len(tx) != 4 ||
- tx[0].text.ToString() != "abc: def: " || tx[0].prefixLength != 2 ||
- tx[1].text.ToString() != "ghi: " || tx[1].prefixLength != 14 ||
- tx[2].text.ToString() != "def: ghi: jkl" || tx[2].prefixLength != 8 ||
- tx[3].text.ToString() != "abc: " || tx[3].prefixLength != 2 {
- t.Errorf("%s", tx)
- }
- }
- }
- {
- tokens := Tokenize(input, delimiterRegexp(":"))
- {
- ranges := splitNth("1..2,3,2..,1")
- tx := Transform(tokens, ranges)
- if joinTokens(tx) != " abc: def: ghi: def: ghi: jkl abc:" ||
- len(tx) != 4 ||
- tx[0].text.ToString() != " abc: def:" || tx[0].prefixLength != 0 ||
- tx[1].text.ToString() != " ghi:" || tx[1].prefixLength != 12 ||
- tx[2].text.ToString() != " def: ghi: jkl" || tx[2].prefixLength != 6 ||
- tx[3].text.ToString() != " abc:" || tx[3].prefixLength != 0 {
- t.Errorf("%s", tx)
- }
- }
- }
-}
-
-func TestTransformIndexOutOfBounds(t *testing.T) {
- Transform([]Token{}, splitNth("1"))
-}
diff --git a/.fzf/src/tui/dummy.go b/.fzf/src/tui/dummy.go
deleted file mode 100644
index af7e759..0000000
--- a/.fzf/src/tui/dummy.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// +build !ncurses
-// +build !tcell
-// +build !windows
-
-package tui
-
-type Attr int32
-
-func HasFullscreenRenderer() bool {
- return false
-}
-
-func (a Attr) Merge(b Attr) Attr {
- return a | b
-}
-
-const (
- AttrUndefined = Attr(0)
- AttrRegular = Attr(1 << 7)
- AttrClear = Attr(1 << 8)
-
- Bold = Attr(1)
- Dim = Attr(1 << 1)
- Italic = Attr(1 << 2)
- Underline = Attr(1 << 3)
- Blink = Attr(1 << 4)
- Blink2 = Attr(1 << 5)
- Reverse = Attr(1 << 6)
-)
-
-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) GetChar() Event { return Event{} }
-func (r *FullscreenRenderer) MaxX() int { return 0 }
-func (r *FullscreenRenderer) MaxY() int { return 0 }
-
-func (r *FullscreenRenderer) RefreshWindows(windows []Window) {}
-
-func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
- return nil
-}
diff --git a/.fzf/src/tui/light.go b/.fzf/src/tui/light.go
deleted file mode 100644
index d3e3fab..0000000
--- a/.fzf/src/tui/light.go
+++ /dev/null
@@ -1,987 +0,0 @@
-package tui
-
-import (
- "bytes"
- "fmt"
- "os"
- "regexp"
- "strconv"
- "strings"
- "time"
- "unicode/utf8"
-
- "github.com/mattn/go-runewidth"
- "github.com/rivo/uniseg"
-
- "golang.org/x/term"
-)
-
-const (
- defaultWidth = 80
- defaultHeight = 24
-
- defaultEscDelay = 100
- escPollInterval = 5
- offsetPollTries = 10
- maxInputBuffer = 10 * 1024
-)
-
-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)
-}
-
-// FIXME: Need better handling of non-displayable characters
-func (r *LightRenderer) stderrInternal(str string, allowNLCR bool) {
- bytes := []byte(str)
- runes := []rune{}
- for len(bytes) > 0 {
- r, sz := utf8.DecodeRune(bytes)
- nlcr := r == '\n' || r == '\r'
- if r >= 32 || r == '\x1b' || nlcr {
- if r == utf8.RuneError || nlcr && !allowNLCR {
- runes = append(runes, ' ')
- } else {
- runes = append(runes, r)
- }
- }
- bytes = bytes[sz:]
- }
- r.queued.WriteString(string(runes))
-}
-
-func (r *LightRenderer) csi(code string) {
- r.stderr("\x1b[" + code)
-}
-
-func (r *LightRenderer) flush() {
- if r.queued.Len() > 0 {
- fmt.Fprint(os.Stderr, r.queued.String())
- r.queued.Reset()
- }
-}
-
-// Light renderer
-type LightRenderer struct {
- theme *ColorTheme
- mouse bool
- forceBlack bool
- clearOnExit bool
- prevDownTime time.Time
- clickY []int
- ttyin *os.File
- buffer []byte
- origState *term.State
- width int
- height int
- yoffset int
- tabstop int
- escDelay int
- fullscreen bool
- upOneLine bool
- queued strings.Builder
- y int
- x int
- maxHeightFunc func(int) int
-
- // Windows only
- ttyinChannel chan byte
- inHandle uintptr
- outHandle uintptr
- origStateInput uint32
- origStateOutput uint32
-}
-
-type LightWindow struct {
- renderer *LightRenderer
- colored bool
- preview bool
- border BorderStyle
- top int
- left int
- width int
- height int
- posx int
- posy int
- tabstop int
- fg Color
- bg Color
-}
-
-func NewLightRenderer(theme *ColorTheme, forceBlack bool, mouse bool, tabstop int, clearOnExit bool, fullscreen bool, maxHeightFunc func(int) int) Renderer {
- r := LightRenderer{
- theme: theme,
- forceBlack: forceBlack,
- mouse: mouse,
- clearOnExit: clearOnExit,
- ttyin: openTtyIn(),
- yoffset: 0,
- tabstop: tabstop,
- fullscreen: fullscreen,
- upOneLine: false,
- maxHeightFunc: maxHeightFunc}
- return &r
-}
-
-func repeat(r rune, times int) string {
- if times > 0 {
- return strings.Repeat(string(r), times)
- }
- return ""
-}
-
-func atoi(s string, defaultValue int) int {
- value, err := strconv.Atoi(s)
- if err != nil {
- return defaultValue
- }
- return value
-}
-
-func (r *LightRenderer) Init() {
- r.escDelay = atoi(os.Getenv("ESCDELAY"), defaultEscDelay)
-
- if err := r.initPlatform(); err != nil {
- errorExit(err.Error())
- }
- r.updateTerminalSize()
- initTheme(r.theme, r.defaultTheme(), r.forceBlack)
-
- if r.fullscreen {
- r.smcup()
- } else {
- // We assume that --no-clear is used for repetitive relaunching of fzf.
- // So we do not clear the lower bottom of the screen.
- if r.clearOnExit {
- r.csi("J")
- }
- y, x := r.findOffset()
- r.mouse = r.mouse && y >= 0
- // When --no-clear is used for repetitive relaunching, there is a small
- // time frame between fzf processes where the user keystrokes are not
- // captured by either of fzf process which can cause x offset to be
- // increased and we're left with unwanted extra new line.
- if x > 0 && r.clearOnExit {
- r.upOneLine = true
- r.makeSpace()
- }
- for i := 1; i < r.MaxY(); i++ {
- r.makeSpace()
- }
- }
-
- if r.mouse {
- r.csi("?1000h")
- }
- r.csi(fmt.Sprintf("%dA", r.MaxY()-1))
- r.csi("G")
- r.csi("K")
- if !r.clearOnExit && !r.fullscreen {
- r.csi("s")
- }
- if !r.fullscreen && r.mouse {
- r.yoffset, _ = r.findOffset()
- }
-}
-
-func (r *LightRenderer) makeSpace() {
- r.stderr("\n")
- r.csi("G")
-}
-
-func (r *LightRenderer) move(y int, x int) {
- // w.csi("u")
- if r.y < y {
- r.csi(fmt.Sprintf("%dB", y-r.y))
- } else if r.y > y {
- r.csi(fmt.Sprintf("%dA", r.y-y))
- }
- r.stderr("\r")
- if x > 0 {
- r.csi(fmt.Sprintf("%dC", x))
- }
- r.y = y
- r.x = x
-}
-
-func (r *LightRenderer) origin() {
- r.move(0, 0)
-}
-
-func getEnv(name string, defaultValue int) int {
- env := os.Getenv(name)
- if len(env) == 0 {
- return defaultValue
- }
- return atoi(env, defaultValue)
-}
-
-func (r *LightRenderer) getBytes() []byte {
- return r.getBytesInternal(r.buffer, false)
-}
-
-func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte {
- c, ok := r.getch(nonblock)
- if !nonblock && !ok {
- r.Close()
- errorExit("Failed to read " + consoleDevice)
- }
-
- retries := 0
- if c == ESC.Int() || nonblock {
- retries = r.escDelay / escPollInterval
- }
- buffer = append(buffer, byte(c))
-
- pc := c
- for {
- c, ok = r.getch(true)
- if !ok {
- if retries > 0 {
- retries--
- time.Sleep(escPollInterval * time.Millisecond)
- continue
- }
- break
- } else if c == ESC.Int() && pc != c {
- retries = r.escDelay / escPollInterval
- } else {
- retries = 0
- }
- buffer = append(buffer, byte(c))
- pc = c
-
- // This should never happen under normal conditions,
- // so terminate fzf immediately.
- if len(buffer) > maxInputBuffer {
- r.Close()
- panic(fmt.Sprintf("Input buffer overflow (%d): %v", len(buffer), buffer))
- }
- }
-
- return buffer
-}
-
-func (r *LightRenderer) GetChar() Event {
- if len(r.buffer) == 0 {
- r.buffer = r.getBytes()
- }
- if len(r.buffer) == 0 {
- panic("Empty buffer")
- }
-
- sz := 1
- defer func() {
- r.buffer = r.buffer[sz:]
- }()
-
- switch r.buffer[0] {
- case CtrlC.Byte():
- return Event{CtrlC, 0, nil}
- case CtrlG.Byte():
- return Event{CtrlG, 0, nil}
- case CtrlQ.Byte():
- return Event{CtrlQ, 0, nil}
- case 127:
- return Event{BSpace, 0, nil}
- case 0:
- return Event{CtrlSpace, 0, nil}
- case 28:
- return Event{CtrlBackSlash, 0, nil}
- case 29:
- return Event{CtrlRightBracket, 0, nil}
- case 30:
- return Event{CtrlCaret, 0, nil}
- case 31:
- return Event{CtrlSlash, 0, nil}
- case ESC.Byte():
- ev := r.escSequence(&sz)
- // Second chance
- if ev.Type == Invalid {
- r.buffer = r.getBytes()
- ev = r.escSequence(&sz)
- }
- return ev
- }
-
- // CTRL-A ~ CTRL-Z
- if r.buffer[0] <= CtrlZ.Byte() {
- return Event{EventType(r.buffer[0]), 0, nil}
- }
- char, rsz := utf8.DecodeRune(r.buffer)
- if char == utf8.RuneError {
- return Event{ESC, 0, nil}
- }
- sz = rsz
- return Event{Rune, char, nil}
-}
-
-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 CtrlAltKey(rune(r.buffer[1] + 'a' - 1))
- }
- alt := false
- if len(r.buffer) > 2 && r.buffer[1] == ESC.Byte() {
- r.buffer = r.buffer[1:]
- alt = true
- }
- switch r.buffer[1] {
- case ESC.Byte():
- return Event{ESC, 0, nil}
- case 127:
- return Event{AltBS, 0, nil}
- case '[', 'O':
- if len(r.buffer) < 3 {
- return Event{Invalid, 0, nil}
- }
- *sz = 3
- switch r.buffer[2] {
- case 'D':
- if alt {
- return Event{AltLeft, 0, nil}
- }
- return Event{Left, 0, nil}
- case 'C':
- if alt {
- // Ugh..
- return Event{AltRight, 0, nil}
- }
- return Event{Right, 0, nil}
- case 'B':
- if alt {
- return Event{AltDown, 0, nil}
- }
- return Event{Down, 0, nil}
- case 'A':
- if alt {
- return Event{AltUp, 0, nil}
- }
- return Event{Up, 0, nil}
- case 'Z':
- return Event{BTab, 0, nil}
- case 'H':
- return Event{Home, 0, nil}
- case 'F':
- return Event{End, 0, nil}
- case 'M':
- return r.mouseSequence(sz)
- case 'P':
- return Event{F1, 0, nil}
- case 'Q':
- return Event{F2, 0, nil}
- case 'R':
- return Event{F3, 0, nil}
- case 'S':
- return Event{F4, 0, nil}
- case '1', '2', '3', '4', '5', '6':
- if len(r.buffer) < 4 {
- return Event{Invalid, 0, nil}
- }
- *sz = 4
- switch r.buffer[2] {
- case '2':
- if r.buffer[3] == '~' {
- return Event{Insert, 0, nil}
- }
- if len(r.buffer) > 4 && r.buffer[4] == '~' {
- *sz = 5
- switch r.buffer[3] {
- case '0':
- return Event{F9, 0, nil}
- case '1':
- return Event{F10, 0, nil}
- case '3':
- return Event{F11, 0, nil}
- case '4':
- return Event{F12, 0, nil}
- }
- }
- // Bracketed paste mode: \e[200~ ... \e[201~
- if len(r.buffer) > 5 && r.buffer[3] == '0' && (r.buffer[4] == '0' || r.buffer[4] == '1') && r.buffer[5] == '~' {
- // Immediately discard the sequence from the buffer and reread input
- r.buffer = r.buffer[6:]
- *sz = 0
- return r.GetChar()
- }
- return Event{Invalid, 0, nil} // INS
- case '3':
- return Event{Del, 0, nil}
- case '4':
- return Event{End, 0, nil}
- case '5':
- return Event{PgUp, 0, nil}
- case '6':
- return Event{PgDn, 0, nil}
- case '1':
- switch r.buffer[3] {
- case '~':
- return Event{Home, 0, nil}
- case '1', '2', '3', '4', '5', '7', '8', '9':
- if len(r.buffer) == 5 && r.buffer[4] == '~' {
- *sz = 5
- switch r.buffer[3] {
- case '1':
- return Event{F1, 0, nil}
- case '2':
- return Event{F2, 0, nil}
- case '3':
- return Event{F3, 0, nil}
- case '4':
- return Event{F4, 0, nil}
- case '5':
- return Event{F5, 0, nil}
- case '7':
- return Event{F6, 0, nil}
- case '8':
- return Event{F7, 0, nil}
- case '9':
- return Event{F8, 0, nil}
- }
- }
- return Event{Invalid, 0, nil}
- case ';':
- if len(r.buffer) < 6 {
- return Event{Invalid, 0, nil}
- }
- *sz = 6
- switch r.buffer[4] {
- case '1', '2', '3', '5':
- alt := r.buffer[4] == '3'
- altShift := r.buffer[4] == '1' && r.buffer[5] == '0'
- char := r.buffer[5]
- if altShift {
- if len(r.buffer) < 7 {
- return Event{Invalid, 0, nil}
- }
- *sz = 7
- char = r.buffer[6]
- }
- switch char {
- case 'A':
- if alt {
- return Event{AltUp, 0, nil}
- }
- if altShift {
- return Event{AltSUp, 0, nil}
- }
- return Event{SUp, 0, nil}
- case 'B':
- if alt {
- return Event{AltDown, 0, nil}
- }
- if altShift {
- return Event{AltSDown, 0, nil}
- }
- return Event{SDown, 0, nil}
- case 'C':
- if alt {
- return Event{AltRight, 0, nil}
- }
- if altShift {
- return Event{AltSRight, 0, nil}
- }
- return Event{SRight, 0, nil}
- case 'D':
- if alt {
- return Event{AltLeft, 0, nil}
- }
- if altShift {
- return Event{AltSLeft, 0, nil}
- }
- return Event{SLeft, 0, nil}
- }
- } // r.buffer[4]
- } // r.buffer[3]
- } // r.buffer[2]
- } // r.buffer[2]
- } // r.buffer[1]
- rest := bytes.NewBuffer(r.buffer[1:])
- c, size, err := rest.ReadRune()
- if err == nil {
- *sz = 1 + size
- return AltKey(c)
- }
- return Event{Invalid, 0, nil}
-}
-
-func (r *LightRenderer) mouseSequence(sz *int) Event {
- if len(r.buffer) < 6 || !r.mouse {
- return Event{Invalid, 0, nil}
- }
- *sz = 6
- switch r.buffer[3] {
- case 32, 34, 36, 40, 48, // mouse-down / shift / cmd / ctrl
- 35, 39, 43, 51: // mouse-up / shift / cmd / ctrl
- mod := r.buffer[3] >= 36
- left := r.buffer[3] == 32
- down := r.buffer[3]%2 == 0
- x := int(r.buffer[4] - 33)
- y := int(r.buffer[5]-33) - r.yoffset
- double := false
- if down {
- now := time.Now()
- if !left { // Right double click is not allowed
- r.clickY = []int{}
- } else if now.Sub(r.prevDownTime) < doubleClickDuration {
- r.clickY = append(r.clickY, y)
- } else {
- r.clickY = []int{y}
- }
- r.prevDownTime = now
- } else {
- if len(r.clickY) > 1 && r.clickY[0] == r.clickY[1] &&
- time.Since(r.prevDownTime) < doubleClickDuration {
- double = true
- }
- }
-
- return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, mod}}
- case 96, 100, 104, 112, // scroll-up / shift / cmd / ctrl
- 97, 101, 105, 113: // scroll-down / shift / cmd / ctrl
- mod := r.buffer[3] >= 100
- s := 1 - int(r.buffer[3]%2)*2
- x := int(r.buffer[4] - 33)
- y := int(r.buffer[5]-33) - r.yoffset
- return Event{Mouse, 0, &MouseEvent{y, x, s, false, false, false, mod}}
- }
- return Event{Invalid, 0, nil}
-}
-
-func (r *LightRenderer) smcup() {
- r.csi("?1049h")
-}
-
-func (r *LightRenderer) rmcup() {
- r.csi("?1049l")
-}
-
-func (r *LightRenderer) Pause(clear bool) {
- r.restoreTerminal()
- if clear {
- if r.fullscreen {
- r.rmcup()
- } else {
- r.smcup()
- r.csi("H")
- }
- r.flush()
- }
-}
-
-func (r *LightRenderer) Resume(clear bool, sigcont bool) {
- r.setupTerminal()
- if clear {
- if r.fullscreen {
- r.smcup()
- } else {
- r.rmcup()
- }
- r.flush()
- } 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
- }
-}
-
-func (r *LightRenderer) Clear() {
- if r.fullscreen {
- r.csi("H")
- }
- // r.csi("u")
- r.origin()
- r.csi("J")
- r.flush()
-}
-
-func (r *LightRenderer) RefreshWindows(windows []Window) {
- r.flush()
-}
-
-func (r *LightRenderer) Refresh() {
- r.updateTerminalSize()
-}
-
-func (r *LightRenderer) Close() {
- // r.csi("u")
- if r.clearOnExit {
- if r.fullscreen {
- r.rmcup()
- } else {
- r.origin()
- if r.upOneLine {
- r.csi("A")
- }
- r.csi("J")
- }
- } else if !r.fullscreen {
- r.csi("u")
- }
- if r.mouse {
- r.csi("?1000l")
- }
- r.flush()
- r.closePlatform()
- r.restoreTerminal()
-}
-
-func (r *LightRenderer) MaxX() int {
- return r.width
-}
-
-func (r *LightRenderer) MaxY() int {
- return r.height
-}
-
-func (r *LightRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
- w := &LightWindow{
- renderer: r,
- colored: r.theme.Colored,
- preview: preview,
- border: borderStyle,
- top: top,
- left: left,
- width: width,
- height: height,
- tabstop: r.tabstop,
- fg: colDefault,
- bg: colDefault}
- if preview {
- w.fg = r.theme.PreviewFg.Color
- w.bg = r.theme.PreviewBg.Color
- } else {
- w.fg = r.theme.Fg.Color
- w.bg = r.theme.Bg.Color
- }
- w.drawBorder()
- return w
-}
-
-func (w *LightWindow) drawBorder() {
- switch w.border.shape {
- case BorderRounded, BorderSharp:
- w.drawBorderAround()
- case BorderHorizontal:
- w.drawBorderHorizontal(true, true)
- case BorderVertical:
- w.drawBorderVertical(true, true)
- case BorderTop:
- w.drawBorderHorizontal(true, false)
- case BorderBottom:
- w.drawBorderHorizontal(false, true)
- case BorderLeft:
- w.drawBorderVertical(true, false)
- case BorderRight:
- w.drawBorderVertical(false, true)
- }
-}
-
-func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
- color := ColBorder
- if w.preview {
- color = ColPreviewBorder
- }
- if top {
- w.Move(0, 0)
- w.CPrint(color, repeat(w.border.horizontal, w.width))
- }
- if bottom {
- w.Move(w.height-1, 0)
- w.CPrint(color, repeat(w.border.horizontal, w.width))
- }
-}
-
-func (w *LightWindow) drawBorderVertical(left, right bool) {
- width := w.width - 2
- if !left || !right {
- width++
- }
- color := ColBorder
- if w.preview {
- color = ColPreviewBorder
- }
- for y := 0; y < w.height; y++ {
- w.Move(y, 0)
- if left {
- w.CPrint(color, string(w.border.vertical))
- }
- w.CPrint(color, repeat(' ', width))
- if right {
- w.CPrint(color, string(w.border.vertical))
- }
- }
-}
-
-func (w *LightWindow) drawBorderAround() {
- w.Move(0, 0)
- color := ColBorder
- if w.preview {
- color = ColPreviewBorder
- }
- w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
- for y := 1; y < w.height-1; y++ {
- w.Move(y, 0)
- w.CPrint(color, string(w.border.vertical))
- w.CPrint(color, repeat(' ', w.width-2))
- w.CPrint(color, string(w.border.vertical))
- }
- w.Move(w.height-1, 0)
- w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
-}
-
-func (w *LightWindow) csi(code string) {
- w.renderer.csi(code)
-}
-
-func (w *LightWindow) stderrInternal(str string, allowNLCR bool) {
- w.renderer.stderrInternal(str, allowNLCR)
-}
-
-func (w *LightWindow) Top() int {
- return w.top
-}
-
-func (w *LightWindow) Left() int {
- return w.left
-}
-
-func (w *LightWindow) Width() int {
- return w.width
-}
-
-func (w *LightWindow) Height() int {
- return w.height
-}
-
-func (w *LightWindow) Refresh() {
-}
-
-func (w *LightWindow) Close() {
-}
-
-func (w *LightWindow) X() int {
- return w.posx
-}
-
-func (w *LightWindow) Y() int {
- return w.posy
-}
-
-func (w *LightWindow) Enclose(y int, x int) bool {
- return x >= w.left && x < (w.left+w.width) &&
- y >= w.top && y < (w.top+w.height)
-}
-
-func (w *LightWindow) Move(y int, x int) {
- w.posx = x
- w.posy = y
-
- w.renderer.move(w.Top()+y, w.Left()+x)
-}
-
-func (w *LightWindow) MoveAndClear(y int, x int) {
- w.Move(y, x)
- // We should not delete preview window on the right
- // csi("K")
- w.Print(repeat(' ', w.width-x))
- w.Move(y, x)
-}
-
-func attrCodes(attr Attr) []string {
- codes := []string{}
- if (attr & AttrClear) > 0 {
- return codes
- }
- if (attr & Bold) > 0 {
- codes = append(codes, "1")
- }
- if (attr & Dim) > 0 {
- codes = append(codes, "2")
- }
- if (attr & Italic) > 0 {
- codes = append(codes, "3")
- }
- if (attr & Underline) > 0 {
- codes = append(codes, "4")
- }
- if (attr & Blink) > 0 {
- codes = append(codes, "5")
- }
- if (attr & Reverse) > 0 {
- codes = append(codes, "7")
- }
- return codes
-}
-
-func colorCodes(fg Color, bg Color) []string {
- codes := []string{}
- appendCode := func(c Color, offset int) {
- if c == colDefault {
- return
- }
- if c.is24() {
- r := (c >> 16) & 0xff
- g := (c >> 8) & 0xff
- b := (c) & 0xff
- codes = append(codes, fmt.Sprintf("%d;2;%d;%d;%d", 38+offset, r, g, b))
- } else if c >= colBlack && c <= colWhite {
- codes = append(codes, fmt.Sprintf("%d", int(c)+30+offset))
- } else if c > colWhite && c < 16 {
- codes = append(codes, fmt.Sprintf("%d", int(c)+90+offset-8))
- } else if c >= 16 && c < 256 {
- codes = append(codes, fmt.Sprintf("%d;5;%d", 38+offset, c))
- }
- }
- appendCode(fg, 0)
- appendCode(bg, 10)
- return codes
-}
-
-func (w *LightWindow) csiColor(fg Color, bg Color, attr Attr) bool {
- codes := append(attrCodes(attr), colorCodes(fg, bg)...)
- w.csi(";" + strings.Join(codes, ";") + "m")
- return len(codes) > 0
-}
-
-func (w *LightWindow) Print(text string) {
- w.cprint2(colDefault, w.bg, AttrRegular, text)
-}
-
-func cleanse(str string) string {
- return strings.Replace(str, "\x1b", "", -1)
-}
-
-func (w *LightWindow) CPrint(pair ColorPair, text string) {
- w.csiColor(pair.Fg(), pair.Bg(), pair.Attr())
- w.stderrInternal(cleanse(text), false)
- w.csi("m")
-}
-
-func (w *LightWindow) cprint2(fg Color, bg Color, attr Attr, text string) {
- if w.csiColor(fg, bg, attr) {
- defer w.csi("m")
- }
- w.stderrInternal(cleanse(text), false)
-}
-
-type wrappedLine struct {
- text string
- displayWidth int
-}
-
-func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLine {
- lines := []wrappedLine{}
- width := 0
- line := ""
- gr := uniseg.NewGraphemes(input)
- for gr.Next() {
- rs := gr.Runes()
- str := string(rs)
- var w int
- if len(rs) == 1 && rs[0] == '\t' {
- w = tabstop - (prefixLength+width)%tabstop
- str = repeat(' ', w)
- } else {
- w = runewidth.StringWidth(str)
- }
- width += w
-
- if prefixLength+width <= max {
- line += str
- } else {
- lines = append(lines, wrappedLine{string(line), width - w})
- line = str
- prefixLength = 0
- width = w
- }
- }
- lines = append(lines, wrappedLine{string(line), width})
- return lines
-}
-
-func (w *LightWindow) fill(str string, onMove func()) FillReturn {
- allLines := strings.Split(str, "\n")
- for i, line := range allLines {
- lines := wrapLine(line, w.posx, w.width, w.tabstop)
- for j, wl := range lines {
- w.stderrInternal(wl.text, false)
- w.posx += wl.displayWidth
-
- // Wrap line
- if j < len(lines)-1 || i < len(allLines)-1 {
- if w.posy+1 >= w.height {
- return FillSuspend
- }
- w.MoveAndClear(w.posy, w.posx)
- w.Move(w.posy+1, 0)
- onMove()
- }
- }
- }
- if w.posx+1 >= w.Width() {
- if w.posy+1 >= w.height {
- return FillSuspend
- }
- w.Move(w.posy+1, 0)
- onMove()
- return FillNextLine
- }
- return FillContinue
-}
-
-func (w *LightWindow) setBg() {
- if w.bg != colDefault {
- w.csiColor(colDefault, w.bg, AttrRegular)
- }
-}
-
-func (w *LightWindow) Fill(text string) FillReturn {
- w.Move(w.posy, w.posx)
- w.setBg()
- return w.fill(text, w.setBg)
-}
-
-func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) FillReturn {
- w.Move(w.posy, w.posx)
- if fg == colDefault {
- fg = w.fg
- }
- if bg == colDefault {
- bg = w.bg
- }
- if w.csiColor(fg, bg, attr) {
- defer w.csi("m")
- return w.fill(text, func() { w.csiColor(fg, bg, attr) })
- }
- return w.fill(text, w.setBg)
-}
-
-func (w *LightWindow) FinishFill() {
- w.MoveAndClear(w.posy, w.posx)
- for y := w.posy + 1; y < w.height; y++ {
- w.MoveAndClear(y, 0)
- }
-}
-
-func (w *LightWindow) Erase() {
- w.drawBorder()
- // We don't erase the window here to avoid flickering during scroll
- w.Move(0, 0)
-}
diff --git a/.fzf/src/tui/light_unix.go b/.fzf/src/tui/light_unix.go
deleted file mode 100644
index 936a13e..0000000
--- a/.fzf/src/tui/light_unix.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// +build !windows
-
-package tui
-
-import (
- "fmt"
- "os"
- "os/exec"
- "strings"
- "syscall"
-
- "github.com/junegunn/fzf/src/util"
- "golang.org/x/term"
-)
-
-func IsLightRendererSupported() bool {
- return true
-}
-
-func (r *LightRenderer) defaultTheme() *ColorTheme {
- if strings.Contains(os.Getenv("TERM"), "256") {
- return Dark256
- }
- colors, err := exec.Command("tput", "colors").Output()
- if err == nil && atoi(strings.TrimSpace(string(colors)), 16) > 16 {
- return Dark256
- }
- return Default16
-}
-
-func (r *LightRenderer) fd() int {
- return int(r.ttyin.Fd())
-}
-
-func (r *LightRenderer) initPlatform() error {
- fd := r.fd()
- origState, err := term.GetState(fd)
- if err != nil {
- return err
- }
- r.origState = origState
- term.MakeRaw(fd)
- return nil
-}
-
-func (r *LightRenderer) closePlatform() {
- // NOOP
-}
-
-func openTtyIn() *os.File {
- in, err := os.OpenFile(consoleDevice, syscall.O_RDONLY, 0)
- if err != nil {
- tty := ttyname()
- if len(tty) > 0 {
- if in, err := os.OpenFile(tty, syscall.O_RDONLY, 0); err == nil {
- return in
- }
- }
- fmt.Fprintln(os.Stderr, "Failed to open "+consoleDevice)
- os.Exit(2)
- }
- return in
-}
-
-func (r *LightRenderer) setupTerminal() {
- term.MakeRaw(r.fd())
-}
-
-func (r *LightRenderer) restoreTerminal() {
- term.Restore(r.fd(), r.origState)
-}
-
-func (r *LightRenderer) updateTerminalSize() {
- width, height, err := term.GetSize(r.fd())
-
- if err == nil {
- r.width = width
- r.height = r.maxHeightFunc(height)
- } else {
- r.width = getEnv("COLUMNS", defaultWidth)
- r.height = r.maxHeightFunc(getEnv("LINES", defaultHeight))
- }
-}
-
-func (r *LightRenderer) findOffset() (row int, col int) {
- r.csi("6n")
- r.flush()
- bytes := []byte{}
- for tries := 0; tries < offsetPollTries; tries++ {
- bytes = r.getBytesInternal(bytes, tries > 0)
- offsets := offsetRegexp.FindSubmatch(bytes)
- if len(offsets) > 3 {
- // Add anything we skipped over to the input buffer
- r.buffer = append(r.buffer, offsets[1]...)
- return atoi(string(offsets[2]), 0) - 1, atoi(string(offsets[3]), 0) - 1
- }
- }
- return -1, -1
-}
-
-func (r *LightRenderer) getch(nonblock bool) (int, bool) {
- b := make([]byte, 1)
- fd := r.fd()
- util.SetNonblock(r.ttyin, nonblock)
- _, err := util.Read(fd, b)
- if err != nil {
- return 0, false
- }
- return int(b[0]), true
-}
diff --git a/.fzf/src/tui/light_windows.go b/.fzf/src/tui/light_windows.go
deleted file mode 100644
index 875bf6f..0000000
--- a/.fzf/src/tui/light_windows.go
+++ /dev/null
@@ -1,145 +0,0 @@
-//+build windows
-
-package tui
-
-import (
- "os"
- "syscall"
- "time"
-
- "github.com/junegunn/fzf/src/util"
- "golang.org/x/sys/windows"
-)
-
-const (
- timeoutInterval = 10
-)
-
-var (
- consoleFlagsInput = uint32(windows.ENABLE_VIRTUAL_TERMINAL_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_EXTENDED_FLAGS)
- consoleFlagsOutput = uint32(windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | windows.ENABLE_PROCESSED_OUTPUT | windows.DISABLE_NEWLINE_AUTO_RETURN)
-)
-
-// IsLightRendererSupported checks to see if the Light renderer is supported
-func IsLightRendererSupported() bool {
- var oldState uint32
- // enable vt100 emulation (https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences)
- if windows.GetConsoleMode(windows.Stderr, &oldState) != nil {
- return false
- }
- // attempt to set mode to determine if we support VT 100 codes. This will work on newer Windows 10
- // version:
- canSetVt100 := windows.SetConsoleMode(windows.Stderr, oldState|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) == nil
- var checkState uint32
- if windows.GetConsoleMode(windows.Stderr, &checkState) != nil ||
- (checkState&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) != windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING {
- return false
- }
- windows.SetConsoleMode(windows.Stderr, oldState)
- return canSetVt100
-}
-
-func (r *LightRenderer) defaultTheme() *ColorTheme {
- // the getenv check is borrowed from here: https://github.com/gdamore/tcell/commit/0c473b86d82f68226a142e96cc5a34c5a29b3690#diff-b008fcd5e6934bf31bc3d33bf49f47d8R178:
- if !IsLightRendererSupported() || os.Getenv("ConEmuPID") != "" || os.Getenv("TCELL_TRUECOLOR") == "disable" {
- return Default16
- }
- return Dark256
-}
-
-func (r *LightRenderer) initPlatform() error {
- //outHandle := windows.Stdout
- outHandle, _ := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
- // enable vt100 emulation (https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences)
- if err := windows.GetConsoleMode(windows.Handle(outHandle), &r.origStateOutput); err != nil {
- return err
- }
- r.outHandle = uintptr(outHandle)
- inHandle, _ := syscall.Open("CONIN$", syscall.O_RDWR, 0)
- if err := windows.GetConsoleMode(windows.Handle(inHandle), &r.origStateInput); err != nil {
- return err
- }
- r.inHandle = uintptr(inHandle)
-
- r.setupTerminal()
-
- // channel for non-blocking reads. Buffer to make sure
- // we get the ESC sets:
- r.ttyinChannel = make(chan byte, 1024)
-
- // the following allows for non-blocking IO.
- // syscall.SetNonblock() is a NOOP under Windows.
- go func() {
- fd := int(r.inHandle)
- b := make([]byte, 1)
- for {
- // HACK: if run from PSReadline, something resets ConsoleMode to remove ENABLE_VIRTUAL_TERMINAL_INPUT.
- _ = windows.SetConsoleMode(windows.Handle(r.inHandle), consoleFlagsInput)
-
- _, err := util.Read(fd, b)
- if err == nil {
- r.ttyinChannel <- b[0]
- }
- }
- }()
-
- return nil
-}
-
-func (r *LightRenderer) closePlatform() {
- windows.SetConsoleMode(windows.Handle(r.outHandle), r.origStateOutput)
- windows.SetConsoleMode(windows.Handle(r.inHandle), r.origStateInput)
-}
-
-func openTtyIn() *os.File {
- // not used
- return nil
-}
-
-func (r *LightRenderer) setupTerminal() error {
- if err := windows.SetConsoleMode(windows.Handle(r.outHandle), consoleFlagsOutput); err != nil {
- return err
- }
- return windows.SetConsoleMode(windows.Handle(r.inHandle), consoleFlagsInput)
-}
-
-func (r *LightRenderer) restoreTerminal() error {
- if err := windows.SetConsoleMode(windows.Handle(r.inHandle), r.origStateInput); err != nil {
- return err
- }
- return windows.SetConsoleMode(windows.Handle(r.outHandle), r.origStateOutput)
-}
-
-func (r *LightRenderer) updateTerminalSize() {
- var bufferInfo windows.ConsoleScreenBufferInfo
- if err := windows.GetConsoleScreenBufferInfo(windows.Handle(r.outHandle), &bufferInfo); err != nil {
- r.width = getEnv("COLUMNS", defaultWidth)
- r.height = r.maxHeightFunc(getEnv("LINES", defaultHeight))
-
- } else {
- r.width = int(bufferInfo.Window.Right - bufferInfo.Window.Left)
- r.height = r.maxHeightFunc(int(bufferInfo.Window.Bottom - bufferInfo.Window.Top))
- }
-}
-
-func (r *LightRenderer) findOffset() (row int, col int) {
- var bufferInfo windows.ConsoleScreenBufferInfo
- if err := windows.GetConsoleScreenBufferInfo(windows.Handle(r.outHandle), &bufferInfo); err != nil {
- return -1, -1
- }
- return int(bufferInfo.CursorPosition.X), int(bufferInfo.CursorPosition.Y)
-}
-
-func (r *LightRenderer) getch(nonblock bool) (int, bool) {
- if nonblock {
- select {
- case bc := <-r.ttyinChannel:
- return int(bc), true
- case <-time.After(timeoutInterval * time.Millisecond):
- return 0, false
- }
- } else {
- bc := <-r.ttyinChannel
- return int(bc), true
- }
-}
diff --git a/.fzf/src/tui/tcell.go b/.fzf/src/tui/tcell.go
deleted file mode 100644
index 82c7566..0000000
--- a/.fzf/src/tui/tcell.go
+++ /dev/null
@@ -1,721 +0,0 @@
-// +build tcell windows
-
-package tui
-
-import (
- "os"
- "time"
-
- "runtime"
-
- "github.com/gdamore/tcell"
- "github.com/gdamore/tcell/encoding"
-
- "github.com/mattn/go-runewidth"
- "github.com/rivo/uniseg"
-)
-
-func HasFullscreenRenderer() bool {
- return true
-}
-
-func (p ColorPair) style() tcell.Style {
- style := tcell.StyleDefault
- return style.Foreground(tcell.Color(p.Fg())).Background(tcell.Color(p.Bg()))
-}
-
-type Attr tcell.Style
-
-type TcellWindow struct {
- color bool
- preview bool
- top int
- left int
- width int
- height int
- normal ColorPair
- lastX int
- lastY int
- moveCursor bool
- borderStyle BorderStyle
-}
-
-func (w *TcellWindow) Top() int {
- return w.top
-}
-
-func (w *TcellWindow) Left() int {
- return w.left
-}
-
-func (w *TcellWindow) Width() int {
- return w.width
-}
-
-func (w *TcellWindow) Height() int {
- return w.height
-}
-
-func (w *TcellWindow) Refresh() {
- if w.moveCursor {
- _screen.ShowCursor(w.left+w.lastX, w.top+w.lastY)
- w.moveCursor = false
- }
- w.lastX = 0
- w.lastY = 0
-
- w.drawBorder()
-}
-
-func (w *TcellWindow) FinishFill() {
- // NO-OP
-}
-
-const (
- Bold Attr = Attr(tcell.AttrBold)
- Dim = Attr(tcell.AttrDim)
- Blink = Attr(tcell.AttrBlink)
- Reverse = Attr(tcell.AttrReverse)
- Underline = Attr(tcell.AttrUnderline)
- Italic = Attr(tcell.AttrItalic)
-)
-
-const (
- AttrUndefined = Attr(0)
- AttrRegular = Attr(1 << 7)
- AttrClear = Attr(1 << 8)
-)
-
-func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
- if _screen.Colors() >= 256 {
- return Dark256
- }
- return Default16
-}
-
-var (
- _colorToAttribute = []tcell.Color{
- tcell.ColorBlack,
- tcell.ColorRed,
- tcell.ColorGreen,
- tcell.ColorYellow,
- tcell.ColorBlue,
- tcell.ColorDarkMagenta,
- tcell.ColorLightCyan,
- tcell.ColorWhite,
- }
-)
-
-func (c Color) Style() tcell.Color {
- if c <= colDefault {
- return tcell.ColorDefault
- } else if c >= colBlack && c <= colWhite {
- return _colorToAttribute[int(c)]
- } else {
- return tcell.Color(c)
- }
-}
-
-func (a Attr) Merge(b Attr) Attr {
- return a | b
-}
-
-// handle the following as private members of FullscreenRenderer instance
-// they are declared here to prevent introducing tcell library in non-windows builds
-var (
- _screen tcell.Screen
- _prevMouseButton tcell.ButtonMask
-)
-
-func (r *FullscreenRenderer) initScreen() {
- s, e := tcell.NewScreen()
- if e != nil {
- errorExit(e.Error())
- }
- if e = s.Init(); e != nil {
- errorExit(e.Error())
- }
- if r.mouse {
- s.EnableMouse()
- } else {
- s.DisableMouse()
- }
- _screen = s
-}
-
-func (r *FullscreenRenderer) Init() {
- if os.Getenv("TERM") == "cygwin" {
- os.Setenv("TERM", "")
- }
- encoding.Register()
-
- r.initScreen()
- initTheme(r.theme, r.defaultTheme(), r.forceBlack)
-}
-
-func (r *FullscreenRenderer) MaxX() int {
- ncols, _ := _screen.Size()
- return int(ncols)
-}
-
-func (r *FullscreenRenderer) MaxY() int {
- _, nlines := _screen.Size()
- return int(nlines)
-}
-
-func (w *TcellWindow) X() int {
- return w.lastX
-}
-
-func (w *TcellWindow) Y() int {
- return w.lastY
-}
-
-func (r *FullscreenRenderer) Clear() {
- _screen.Sync()
- _screen.Clear()
-}
-
-func (r *FullscreenRenderer) Refresh() {
- // noop
-}
-
-func (r *FullscreenRenderer) GetChar() Event {
- ev := _screen.PollEvent()
- switch ev := ev.(type) {
- case *tcell.EventResize:
- return Event{Resize, 0, nil}
-
- // process mouse events:
- case *tcell.EventMouse:
- // mouse down events have zeroed buttons, so we can't use them
- // mouse up event consists of two events, 1. (main) event with modifier and other metadata, 2. event with zeroed buttons
- // so mouse click is three consecutive events, but the first and last are indistinguishable from movement events (with released buttons)
- // dragging has same structure, it only repeats the middle (main) event appropriately
- x, y := ev.Position()
- mod := ev.Modifiers() != 0
-
- // since we dont have mouse down events (unlike LightRenderer), we need to track state in prevButton
- prevButton, button := _prevMouseButton, ev.Buttons()
- _prevMouseButton = button
- drag := prevButton == button
-
- switch {
- case button&tcell.WheelDown != 0:
- return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, false, mod}}
- case button&tcell.WheelUp != 0:
- return Event{Mouse, 0, &MouseEvent{y, x, +1, false, false, false, mod}}
- case button&tcell.Button1 != 0 && !drag:
- // all potential double click events put their 'line' coordinate in the clickY array
- // double click event has two conditions, temporal and spatial, the first is checked here
- now := time.Now()
- if now.Sub(r.prevDownTime) < doubleClickDuration {
- r.clickY = append(r.clickY, y)
- } else {
- r.clickY = []int{y}
- }
- r.prevDownTime = now
-
- // detect double clicks (also check for spatial condition)
- n := len(r.clickY)
- double := n > 1 && r.clickY[n-2] == r.clickY[n-1]
- if double {
- // make sure two consecutive double clicks require four clicks
- r.clickY = []int{}
- }
-
- // fire single or double click event
- return Event{Mouse, 0, &MouseEvent{y, x, 0, true, !double, double, mod}}
- case button&tcell.Button2 != 0 && !drag:
- return Event{Mouse, 0, &MouseEvent{y, x, 0, false, true, false, mod}}
- case runtime.GOOS != "windows":
-
- // double and single taps on Windows don't quite work due to
- // the console acting on the events and not allowing us
- // to consume them.
-
- left := button&tcell.Button1 != 0
- down := left || button&tcell.Button3 != 0
- double := false
- if down {
- now := time.Now()
- if !left {
- r.clickY = []int{}
- } else if now.Sub(r.prevDownTime) < doubleClickDuration {
- r.clickY = append(r.clickY, x)
- } else {
- r.clickY = []int{x}
- r.prevDownTime = now
- }
- } else {
- if len(r.clickY) > 1 && r.clickY[0] == r.clickY[1] &&
- time.Now().Sub(r.prevDownTime) < doubleClickDuration {
- double = true
- }
- }
-
- return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, mod}}
- }
-
- // process keyboard:
- case *tcell.EventKey:
- mods := ev.Modifiers()
- none := mods == tcell.ModNone
- alt := (mods & tcell.ModAlt) > 0
- ctrl := (mods & tcell.ModCtrl) > 0
- shift := (mods & tcell.ModShift) > 0
- ctrlAlt := ctrl && alt
- altShift := alt && shift
-
- keyfn := func(r rune) Event {
- if alt {
- return CtrlAltKey(r)
- }
- return EventType(CtrlA.Int() - 'a' + int(r)).AsEvent()
- }
- switch ev.Key() {
- // section 1: Ctrl+(Alt)+[a-z]
- case tcell.KeyCtrlA:
- return keyfn('a')
- case tcell.KeyCtrlB:
- return keyfn('b')
- case tcell.KeyCtrlC:
- return keyfn('c')
- case tcell.KeyCtrlD:
- return keyfn('d')
- case tcell.KeyCtrlE:
- return keyfn('e')
- case tcell.KeyCtrlF:
- return keyfn('f')
- case tcell.KeyCtrlG:
- return keyfn('g')
- case tcell.KeyCtrlH:
- switch ev.Rune() {
- case 0:
- if ctrl {
- return Event{BSpace, 0, nil}
- }
- case rune(tcell.KeyCtrlH):
- switch {
- case ctrl:
- return keyfn('h')
- case alt:
- return Event{AltBS, 0, nil}
- case none, shift:
- return Event{BSpace, 0, nil}
- }
- }
- case tcell.KeyCtrlI:
- return keyfn('i')
- case tcell.KeyCtrlJ:
- return keyfn('j')
- case tcell.KeyCtrlK:
- return keyfn('k')
- case tcell.KeyCtrlL:
- return keyfn('l')
- case tcell.KeyCtrlM:
- return keyfn('m')
- case tcell.KeyCtrlN:
- return keyfn('n')
- case tcell.KeyCtrlO:
- return keyfn('o')
- case tcell.KeyCtrlP:
- return keyfn('p')
- case tcell.KeyCtrlQ:
- return keyfn('q')
- case tcell.KeyCtrlR:
- return keyfn('r')
- case tcell.KeyCtrlS:
- return keyfn('s')
- case tcell.KeyCtrlT:
- return keyfn('t')
- case tcell.KeyCtrlU:
- return keyfn('u')
- case tcell.KeyCtrlV:
- return keyfn('v')
- case tcell.KeyCtrlW:
- return keyfn('w')
- case tcell.KeyCtrlX:
- return keyfn('x')
- case tcell.KeyCtrlY:
- return keyfn('y')
- case tcell.KeyCtrlZ:
- return keyfn('z')
- // section 2: Ctrl+[ \]_]
- case tcell.KeyCtrlSpace:
- return Event{CtrlSpace, 0, nil}
- case tcell.KeyCtrlBackslash:
- return Event{CtrlBackSlash, 0, nil}
- case tcell.KeyCtrlRightSq:
- return Event{CtrlRightBracket, 0, nil}
- case tcell.KeyCtrlCarat:
- return Event{CtrlCaret, 0, nil}
- case tcell.KeyCtrlUnderscore:
- return Event{CtrlSlash, 0, nil}
- // section 3: (Alt)+Backspace2
- case tcell.KeyBackspace2:
- if alt {
- return Event{AltBS, 0, nil}
- }
- return Event{BSpace, 0, nil}
-
- // section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
- case tcell.KeyUp:
- if altShift {
- return Event{AltSUp, 0, nil}
- }
- if shift {
- return Event{SUp, 0, nil}
- }
- if alt {
- return Event{AltUp, 0, nil}
- }
- return Event{Up, 0, nil}
- case tcell.KeyDown:
- if altShift {
- return Event{AltSDown, 0, nil}
- }
- if shift {
- return Event{SDown, 0, nil}
- }
- if alt {
- return Event{AltDown, 0, nil}
- }
- return Event{Down, 0, nil}
- case tcell.KeyLeft:
- if altShift {
- return Event{AltSLeft, 0, nil}
- }
- if shift {
- return Event{SLeft, 0, nil}
- }
- if alt {
- return Event{AltLeft, 0, nil}
- }
- return Event{Left, 0, nil}
- case tcell.KeyRight:
- if altShift {
- return Event{AltSRight, 0, nil}
- }
- if shift {
- return Event{SRight, 0, nil}
- }
- if alt {
- return Event{AltRight, 0, nil}
- }
- return Event{Right, 0, nil}
-
- // section 5: (Insert|Home|Delete|End|PgUp|PgDn|BackTab|F1-F12)
- case tcell.KeyInsert:
- return Event{Insert, 0, nil}
- case tcell.KeyHome:
- return Event{Home, 0, nil}
- case tcell.KeyDelete:
- return Event{Del, 0, nil}
- case tcell.KeyEnd:
- return Event{End, 0, nil}
- case tcell.KeyPgUp:
- return Event{PgUp, 0, nil}
- case tcell.KeyPgDn:
- return Event{PgDn, 0, nil}
- case tcell.KeyBacktab:
- return Event{BTab, 0, nil}
- case tcell.KeyF1:
- return Event{F1, 0, nil}
- case tcell.KeyF2:
- return Event{F2, 0, nil}
- case tcell.KeyF3:
- return Event{F3, 0, nil}
- case tcell.KeyF4:
- return Event{F4, 0, nil}
- case tcell.KeyF5:
- return Event{F5, 0, nil}
- case tcell.KeyF6:
- return Event{F6, 0, nil}
- case tcell.KeyF7:
- return Event{F7, 0, nil}
- case tcell.KeyF8:
- return Event{F8, 0, nil}
- case tcell.KeyF9:
- return Event{F9, 0, nil}
- case tcell.KeyF10:
- return Event{F10, 0, nil}
- case tcell.KeyF11:
- return Event{F11, 0, nil}
- case tcell.KeyF12:
- return Event{F12, 0, nil}
-
- // section 6: (Ctrl+Alt)+'rune'
- case tcell.KeyRune:
- r := ev.Rune()
-
- switch {
- // translate native key events to ascii control characters
- case r == ' ' && ctrl:
- return Event{CtrlSpace, 0, nil}
- // handle AltGr characters
- case ctrlAlt:
- return Event{Rune, r, nil} // dropping modifiers
- // simple characters (possibly with modifier)
- case alt:
- return AltKey(r)
- default:
- return Event{Rune, r, nil}
- }
-
- // section 7: Esc
- case tcell.KeyEsc:
- return Event{ESC, 0, nil}
- }
- }
-
- // section 8: Invalid
- return Event{Invalid, 0, nil}
-}
-
-func (r *FullscreenRenderer) Pause(clear bool) {
- if clear {
- _screen.Fini()
- }
-}
-
-func (r *FullscreenRenderer) Resume(clear bool, sigcont bool) {
- if clear {
- r.initScreen()
- }
-}
-
-func (r *FullscreenRenderer) Close() {
- _screen.Fini()
-}
-
-func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
- // TODO
- for _, w := range windows {
- w.Refresh()
- }
- _screen.Show()
-}
-
-func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
- normal := ColNormal
- if preview {
- normal = ColPreview
- }
- return &TcellWindow{
- color: r.theme.Colored,
- preview: preview,
- top: top,
- left: left,
- width: width,
- height: height,
- normal: normal,
- borderStyle: borderStyle}
-}
-
-func (w *TcellWindow) Close() {
- // TODO
-}
-
-func fill(x, y, w, h int, n ColorPair, r rune) {
- for ly := 0; ly <= h; ly++ {
- for lx := 0; lx <= w; lx++ {
- _screen.SetContent(x+lx, y+ly, r, nil, n.style())
- }
- }
-}
-
-func (w *TcellWindow) Erase() {
- fill(w.left-1, w.top, w.width+1, w.height, w.normal, ' ')
-}
-
-func (w *TcellWindow) Enclose(y int, x int) bool {
- return x >= w.left && x < (w.left+w.width) &&
- y >= w.top && y < (w.top+w.height)
-}
-
-func (w *TcellWindow) Move(y int, x int) {
- w.lastX = x
- w.lastY = y
- w.moveCursor = true
-}
-
-func (w *TcellWindow) MoveAndClear(y int, x int) {
- w.Move(y, x)
- for i := w.lastX; i < w.width; i++ {
- _screen.SetContent(i+w.left, w.lastY+w.top, rune(' '), nil, w.normal.style())
- }
- w.lastX = x
-}
-
-func (w *TcellWindow) Print(text string) {
- w.printString(text, w.normal)
-}
-
-func (w *TcellWindow) printString(text string, pair ColorPair) {
- lx := 0
- a := pair.Attr()
-
- style := pair.style()
- if a&AttrClear == 0 {
- style = style.
- Reverse(a&Attr(tcell.AttrReverse) != 0).
- Underline(a&Attr(tcell.AttrUnderline) != 0).
- Italic(a&Attr(tcell.AttrItalic) != 0).
- Blink(a&Attr(tcell.AttrBlink) != 0).
- Dim(a&Attr(tcell.AttrDim) != 0)
- }
-
- gr := uniseg.NewGraphemes(text)
- for gr.Next() {
- rs := gr.Runes()
-
- if len(rs) == 1 {
- r := rs[0]
- if r < rune(' ') { // ignore control characters
- continue
- } else if r == '\n' {
- w.lastY++
- lx = 0
- continue
- } else if r == '\u000D' { // skip carriage return
- continue
- }
- }
- var xPos = w.left + w.lastX + lx
- var yPos = w.top + w.lastY
- if xPos < (w.left+w.width) && yPos < (w.top+w.height) {
- _screen.SetContent(xPos, yPos, rs[0], rs[1:], style)
- }
- lx += runewidth.StringWidth(string(rs))
- }
- w.lastX += lx
-}
-
-func (w *TcellWindow) CPrint(pair ColorPair, text string) {
- w.printString(text, pair)
-}
-
-func (w *TcellWindow) fillString(text string, pair ColorPair) FillReturn {
- lx := 0
- a := pair.Attr()
-
- var style tcell.Style
- if w.color {
- style = pair.style()
- } else {
- style = w.normal.style()
- }
- style = style.
- Blink(a&Attr(tcell.AttrBlink) != 0).
- Bold(a&Attr(tcell.AttrBold) != 0).
- Dim(a&Attr(tcell.AttrDim) != 0).
- Reverse(a&Attr(tcell.AttrReverse) != 0).
- Underline(a&Attr(tcell.AttrUnderline) != 0).
- Italic(a&Attr(tcell.AttrItalic) != 0)
-
- gr := uniseg.NewGraphemes(text)
- for gr.Next() {
- rs := gr.Runes()
- if len(rs) == 1 && rs[0] == '\n' {
- w.lastY++
- w.lastX = 0
- lx = 0
- continue
- }
-
- // word wrap:
- xPos := w.left + w.lastX + lx
- if xPos >= (w.left + w.width) {
- w.lastY++
- w.lastX = 0
- lx = 0
- xPos = w.left
- }
-
- yPos := w.top + w.lastY
- if yPos >= (w.top + w.height) {
- return FillSuspend
- }
-
- _screen.SetContent(xPos, yPos, rs[0], rs[1:], style)
- lx += runewidth.StringWidth(string(rs))
- }
- w.lastX += lx
- if w.lastX == w.width {
- w.lastY++
- w.lastX = 0
- return FillNextLine
- }
-
- return FillContinue
-}
-
-func (w *TcellWindow) Fill(str string) FillReturn {
- return w.fillString(str, w.normal)
-}
-
-func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
- if fg == colDefault {
- fg = w.normal.Fg()
- }
- if bg == colDefault {
- bg = w.normal.Bg()
- }
- return w.fillString(str, NewColorPair(fg, bg, a))
-}
-
-func (w *TcellWindow) drawBorder() {
- shape := w.borderStyle.shape
- if shape == BorderNone {
- return
- }
-
- left := w.left
- right := left + w.width
- top := w.top
- bot := top + w.height
-
- var style tcell.Style
- if w.color {
- if w.preview {
- style = ColPreviewBorder.style()
- } else {
- style = ColBorder.style()
- }
- } else {
- style = w.normal.style()
- }
-
- switch shape {
- case BorderRounded, BorderSharp, BorderHorizontal, BorderTop:
- for x := left; x < right; x++ {
- _screen.SetContent(x, top, w.borderStyle.horizontal, nil, style)
- }
- }
- switch shape {
- case BorderRounded, BorderSharp, BorderHorizontal, BorderBottom:
- for x := left; x < right; x++ {
- _screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
- }
- }
- switch shape {
- case BorderRounded, BorderSharp, BorderVertical, BorderLeft:
- for y := top; y < bot; y++ {
- _screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
- }
- }
- switch shape {
- case BorderRounded, BorderSharp, BorderVertical, BorderRight:
- for y := top; y < bot; y++ {
- _screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)
- }
- }
- switch shape {
- case BorderRounded, BorderSharp:
- _screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
- _screen.SetContent(right-1, top, w.borderStyle.topRight, nil, style)
- _screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
- _screen.SetContent(right-1, bot-1, w.borderStyle.bottomRight, nil, style)
- }
-}
diff --git a/.fzf/src/tui/tcell_test.go b/.fzf/src/tui/tcell_test.go
deleted file mode 100644
index aa63b72..0000000
--- a/.fzf/src/tui/tcell_test.go
+++ /dev/null
@@ -1,392 +0,0 @@
-// +build tcell windows
-
-package tui
-
-import (
- "testing"
-
- "github.com/gdamore/tcell"
- "github.com/junegunn/fzf/src/util"
-)
-
-func assert(t *testing.T, context string, got interface{}, want interface{}) bool {
- if got == want {
- return true
- } else {
- t.Errorf("%s = (%T)%v, want (%T)%v", context, got, got, want, want)
- return false
- }
-}
-
-// Test the handling of the tcell keyboard events.
-func TestGetCharEventKey(t *testing.T) {
- if util.ToTty() {
- // This test is skipped when output goes to terminal, because it causes
- // some glitches:
- // - output lines may not start at the beginning of a row which makes
- // the output unreadable
- // - terminal may get cleared which prevents you from seeing results of
- // previous tests
- // Good ways to prevent the glitches are piping the output to a pager
- // or redirecting to a file. I've found `less +G` to be trouble-free.
- t.Skip("Skipped because this test misbehaves in terminal, pipe to a pager or redirect output to a file to run it safely.")
- } else if testing.Verbose() {
- // I have observed a behaviour when this test outputted more than 8192
- // bytes (32*256) into the 'less' pager, both the go's test executable
- // and the pager hanged. The go's executable was blocking on printing.
- // I was able to create minimal working example of that behaviour, but
- // that example hanged after 12256 bytes (32*(256+127)).
- t.Log("If you are piping this test to a pager and it hangs, make the pager greedy for input, e.g. 'less +G'.")
- }
-
- if !HasFullscreenRenderer() {
- t.Skip("Can't test FullscreenRenderer.")
- }
-
- // construct test cases
- type giveKey struct {
- Type tcell.Key
- Char rune
- Mods tcell.ModMask
- }
- type wantKey = Event
- type testCase struct {
- giveKey
- wantKey
- }
- /*
- Some test cases are marked "fabricated". It means that giveKey value
- is valid, but it is not what you get when you press the keys. For
- example Ctrl+C will NOT give you tcell.KeyCtrlC, but tcell.KeyETX
- (End-Of-Text character, causing SIGINT).
- I was trying to accompany the fabricated test cases with real ones.
-
- Some test cases are marked "unhandled". It means that giveKey.Type
- is not present in tcell.go source code. It can still be handled via
- implicit or explicit alias.
-
- If not said otherwise, test cases are for US keyboard.
-
- (tabstop=44)
- */
- tests := []testCase{
-
- // section 1: Ctrl+(Alt)+[a-z]
- {giveKey{tcell.KeyCtrlA, rune(tcell.KeyCtrlA), tcell.ModCtrl}, wantKey{CtrlA, 0, nil}},
- {giveKey{tcell.KeyCtrlC, rune(tcell.KeyCtrlC), tcell.ModCtrl}, wantKey{CtrlC, 0, nil}}, // fabricated
- {giveKey{tcell.KeyETX, rune(tcell.KeyETX), tcell.ModCtrl}, wantKey{CtrlC, 0, nil}}, // this is SIGINT (Ctrl+C)
- {giveKey{tcell.KeyCtrlZ, rune(tcell.KeyCtrlZ), tcell.ModCtrl}, wantKey{CtrlZ, 0, nil}}, // fabricated
- // KeyTab is alias for KeyTAB
- {giveKey{tcell.KeyCtrlI, rune(tcell.KeyCtrlI), tcell.ModCtrl}, wantKey{Tab, 0, nil}}, // fabricated
- {giveKey{tcell.KeyTab, rune(tcell.KeyTab), tcell.ModNone}, wantKey{Tab, 0, nil}}, // unhandled, actual "Tab" keystroke
- {giveKey{tcell.KeyTAB, rune(tcell.KeyTAB), tcell.ModNone}, wantKey{Tab, 0, nil}}, // fabricated, unhandled
- // KeyEnter is alias for KeyCR
- {giveKey{tcell.KeyCtrlM, rune(tcell.KeyCtrlM), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // actual "Enter" keystroke
- {giveKey{tcell.KeyCR, rune(tcell.KeyCR), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // fabricated, unhandled
- // Ctrl+Alt keys
- {giveKey{tcell.KeyCtrlA, rune(tcell.KeyCtrlA), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'a', nil}}, // fabricated
- {giveKey{tcell.KeyCtrlA, rune(tcell.KeyCtrlA), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAlt, 'a', nil}}, // fabricated
-
- // section 2: Ctrl+[ \]_]
- {giveKey{tcell.KeyCtrlSpace, rune(tcell.KeyCtrlSpace), tcell.ModCtrl}, wantKey{CtrlSpace, 0, nil}}, // fabricated
- {giveKey{tcell.KeyNUL, rune(tcell.KeyNUL), tcell.ModNone}, wantKey{CtrlSpace, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyRune, ' ', tcell.ModCtrl}, wantKey{CtrlSpace, 0, nil}}, // actual Ctrl+' '
- {giveKey{tcell.KeyCtrlBackslash, rune(tcell.KeyCtrlBackslash), tcell.ModCtrl}, wantKey{CtrlBackSlash, 0, nil}},
- {giveKey{tcell.KeyCtrlRightSq, rune(tcell.KeyCtrlRightSq), tcell.ModCtrl}, wantKey{CtrlRightBracket, 0, nil}},
- {giveKey{tcell.KeyCtrlCarat, rune(tcell.KeyCtrlCarat), tcell.ModShift | tcell.ModCtrl}, wantKey{CtrlCaret, 0, nil}}, // fabricated
- {giveKey{tcell.KeyRS, rune(tcell.KeyRS), tcell.ModShift | tcell.ModCtrl}, wantKey{CtrlCaret, 0, nil}}, // actual Ctrl+Shift+6 (i.e. Ctrl+^) keystroke
- {giveKey{tcell.KeyCtrlUnderscore, rune(tcell.KeyCtrlUnderscore), tcell.ModShift | tcell.ModCtrl}, wantKey{CtrlSlash, 0, nil}},
-
- // section 3: (Alt)+Backspace2
- // KeyBackspace2 is alias for KeyDEL = 0x7F (ASCII) (allegedly unused by Windows)
- // KeyDelete = 0x2E (VK_DELETE constant in Windows)
- // KeyBackspace is alias for KeyBS = 0x08 (ASCII) (implicit alias with KeyCtrlH)
- {giveKey{tcell.KeyBackspace2, 0, tcell.ModNone}, wantKey{BSpace, 0, nil}}, // fabricated
- {giveKey{tcell.KeyBackspace2, 0, tcell.ModAlt}, wantKey{AltBS, 0, nil}}, // fabricated
- {giveKey{tcell.KeyDEL, 0, tcell.ModNone}, wantKey{BSpace, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyDelete, 0, tcell.ModNone}, wantKey{Del, 0, nil}},
- {giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{Del, 0, nil}},
- {giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{BSpace, 0, nil}}, // actual "Backspace" keystroke
- {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBS, 0, nil}}, // actual "Alt+Backspace" keystroke
- {giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Backspace" keystroke
- {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{BSpace, 0, nil}}, // actual "Shift+Backspace" keystroke
- {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke
- {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke
- {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBS, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke
- {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke
- {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+H" keystroke
- {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Alt+H" keystroke
- {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+H" keystroke
- {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Shift+Alt+H" keystroke
-
- // section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
- {giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}},
- {giveKey{tcell.KeyDown, 0, tcell.ModAlt}, wantKey{AltDown, 0, nil}},
- {giveKey{tcell.KeyLeft, 0, tcell.ModShift}, wantKey{SLeft, 0, nil}},
- {giveKey{tcell.KeyRight, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltSRight, 0, nil}},
- {giveKey{tcell.KeyUpLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyUpRight, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyDownLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyDownRight, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyCenter, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- // section 5: (Insert|Home|Delete|End|PgUp|PgDn|BackTab|F1-F12)
- {giveKey{tcell.KeyInsert, 0, tcell.ModNone}, wantKey{Insert, 0, nil}},
- {giveKey{tcell.KeyF1, 0, tcell.ModNone}, wantKey{F1, 0, nil}},
- // section 6: (Ctrl+Alt)+'rune'
- {giveKey{tcell.KeyRune, 'a', tcell.ModNone}, wantKey{Rune, 'a', nil}},
- {giveKey{tcell.KeyRune, 'a', tcell.ModCtrl}, wantKey{Rune, 'a', nil}}, // fabricated
- {giveKey{tcell.KeyRune, 'a', tcell.ModAlt}, wantKey{Alt, 'a', nil}},
- {giveKey{tcell.KeyRune, 'A', tcell.ModAlt}, wantKey{Alt, 'A', nil}},
- {giveKey{tcell.KeyRune, '`', tcell.ModAlt}, wantKey{Alt, '`', nil}},
- /*
- "Input method" in Windows Language options:
- US: "US Keyboard" does not generate any characters (and thus any events) in Ctrl+Alt+[a-z] range
- CS: "Czech keyboard"
- DE: "German keyboard"
-
- Note that right Alt is not just `tcell.ModAlt` on foreign language keyboards, but it is the AltGr `tcell.ModCtrl|tcell.ModAlt`.
- */
- {giveKey{tcell.KeyRune, '{', tcell.ModCtrl | tcell.ModAlt}, wantKey{Rune, '{', nil}}, // CS: Ctrl+Alt+b = "{" // Note that this does not interfere with CtrlB, since the "b" is replaced with "{" on OS level
- {giveKey{tcell.KeyRune, '$', tcell.ModCtrl | tcell.ModAlt}, wantKey{Rune, '$', nil}}, // CS: Ctrl+Alt+ů = "$"
- {giveKey{tcell.KeyRune, '~', tcell.ModCtrl | tcell.ModAlt}, wantKey{Rune, '~', nil}}, // CS: Ctrl+Alt++ = "~"
- {giveKey{tcell.KeyRune, '`', tcell.ModCtrl | tcell.ModAlt}, wantKey{Rune, '`', nil}}, // CS: Ctrl+Alt+ý,Space = "`" // this is dead key, space is required to emit the char
-
- {giveKey{tcell.KeyRune, '{', tcell.ModCtrl | tcell.ModAlt}, wantKey{Rune, '{', nil}}, // DE: Ctrl+Alt+7 = "{"
- {giveKey{tcell.KeyRune, '@', tcell.ModCtrl | tcell.ModAlt}, wantKey{Rune, '@', nil}}, // DE: Ctrl+Alt+q = "@"
- {giveKey{tcell.KeyRune, 'µ', tcell.ModCtrl | tcell.ModAlt}, wantKey{Rune, 'µ', nil}}, // DE: Ctrl+Alt+m = "µ"
-
- // section 7: Esc
- // KeyEsc and KeyEscape are aliases for KeyESC
- {giveKey{tcell.KeyEsc, rune(tcell.KeyEsc), tcell.ModNone}, wantKey{ESC, 0, nil}}, // fabricated
- {giveKey{tcell.KeyESC, rune(tcell.KeyESC), tcell.ModNone}, wantKey{ESC, 0, nil}}, // unhandled
- {giveKey{tcell.KeyEscape, rune(tcell.KeyEscape), tcell.ModNone}, wantKey{ESC, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyESC, rune(tcell.KeyESC), tcell.ModCtrl}, wantKey{ESC, 0, nil}}, // actual Ctrl+[ keystroke
- {giveKey{tcell.KeyCtrlLeftSq, rune(tcell.KeyCtrlLeftSq), tcell.ModCtrl}, wantKey{ESC, 0, nil}}, // fabricated, unhandled
-
- // section 8: Invalid
- {giveKey{tcell.KeyRune, 'a', tcell.ModMeta}, wantKey{Rune, 'a', nil}}, // fabricated
- {giveKey{tcell.KeyF24, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}},
- {giveKey{tcell.KeyHelp, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyExit, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyClear, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // unhandled, actual keystroke Numpad_5 with Numlock OFF
- {giveKey{tcell.KeyCancel, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyPrint, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
- {giveKey{tcell.KeyPause, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // unhandled
-
- }
- r := NewFullscreenRenderer(&ColorTheme{}, false, false)
- r.Init()
-
- // run and evaluate the tests
- for _, test := range tests {
- // generate key event
- giveEvent := tcell.NewEventKey(test.giveKey.Type, test.giveKey.Char, test.giveKey.Mods)
- _screen.PostEventWait(giveEvent)
- t.Logf("giveEvent = %T{key: %v, ch: %q (%[3]v), mod: %#04b}\n", giveEvent, giveEvent.Key(), giveEvent.Rune(), giveEvent.Modifiers())
-
- // process the event in fzf and evaluate the test
- gotEvent := r.GetChar()
- // skip Resize events, those are sometimes put in the buffer outside of this test
- for gotEvent.Type == Resize {
- t.Logf("Resize swallowed")
- gotEvent = r.GetChar()
- }
- t.Logf("wantEvent = %T{Type: %v, Char: %q (%[3]v)}\n", test.wantKey, test.wantKey.Type, test.wantKey.Char)
- t.Logf("gotEvent = %T{Type: %v, Char: %q (%[3]v)}\n", gotEvent, gotEvent.Type, gotEvent.Char)
-
- assert(t, "r.GetChar().Type", gotEvent.Type, test.wantKey.Type)
- assert(t, "r.GetChar().Char", gotEvent.Char, test.wantKey.Char)
- }
-
- r.Close()
-}
-
-/*
-Quick reference
----------------
-
-(tabstop=18)
-(this is not mapping table, it merely puts multiple constants ranges in one table)
-
-¹) the two columns are each other implicit alias
-²) explicit aliases here
-
-%v section # tcell ctrl key¹ tcell ctrl char¹ tcell alias² tui constants tcell named keys tcell mods
--- --------- -------------- --------------- ----------- ------------- ---------------- ----------
-0 2 KeyCtrlSpace KeyNUL = ^@ Rune ModNone
-1 1 KeyCtrlA KeySOH = ^A CtrlA ModShift
-2 1 KeyCtrlB KeySTX = ^B CtrlB ModCtrl
-3 1 KeyCtrlC KeyETX = ^C CtrlC
-4 1 KeyCtrlD KeyEOT = ^D CtrlD ModAlt
-5 1 KeyCtrlE KeyENQ = ^E CtrlE
-6 1 KeyCtrlF KeyACK = ^F CtrlF
-7 1 KeyCtrlG KeyBEL = ^G CtrlG
-8 1 KeyCtrlH KeyBS = ^H KeyBackspace CtrlH ModMeta
-9 1 KeyCtrlI KeyTAB = ^I KeyTab Tab
-10 1 KeyCtrlJ KeyLF = ^J CtrlJ
-11 1 KeyCtrlK KeyVT = ^K CtrlK
-12 1 KeyCtrlL KeyFF = ^L CtrlL
-13 1 KeyCtrlM KeyCR = ^M KeyEnter CtrlM
-14 1 KeyCtrlN KeySO = ^N CtrlN
-15 1 KeyCtrlO KeySI = ^O CtrlO
-16 1 KeyCtrlP KeyDLE = ^P CtrlP
-17 1 KeyCtrlQ KeyDC1 = ^Q CtrlQ
-18 1 KeyCtrlR KeyDC2 = ^R CtrlR
-19 1 KeyCtrlS KeyDC3 = ^S CtrlS
-20 1 KeyCtrlT KeyDC4 = ^T CtrlT
-21 1 KeyCtrlU KeyNAK = ^U CtrlU
-22 1 KeyCtrlV KeySYN = ^V CtrlV
-23 1 KeyCtrlW KeyETB = ^W CtrlW
-24 1 KeyCtrlX KeyCAN = ^X CtrlX
-25 1 KeyCtrlY KeyEM = ^Y CtrlY
-26 1 KeyCtrlZ KeySUB = ^Z CtrlZ
-27 7 KeyCtrlLeftSq KeyESC = ^[ KeyEsc, KeyEscape ESC
-28 2 KeyCtrlBackslash KeyFS = ^\ CtrlSpace
-29 2 KeyCtrlRightSq KeyGS = ^] CtrlBackSlash
-30 2 KeyCtrlCarat KeyRS = ^^ CtrlRightBracket
-31 2 KeyCtrlUnderscore KeyUS = ^_ CtrlCaret
-32 CtrlSlash
-33 Invalid
-34 Resize
-35 Mouse
-36 DoubleClick
-37 LeftClick
-38 RightClick
-39 BTab
-40 BSpace
-41 Del
-42 PgUp
-43 PgDn
-44 Up
-45 Down
-46 Left
-47 Right
-48 Home
-49 End
-50 Insert
-51 SUp
-52 SDown
-53 SLeft
-54 SRight
-55 F1
-56 F2
-57 F3
-58 F4
-59 F5
-60 F6
-61 F7
-62 F8
-63 F9
-64 F10
-65 F11
-66 F12
-67 Change
-68 BackwardEOF
-69 AltBS
-70 AltUp
-71 AltDown
-72 AltLeft
-73 AltRight
-74 AltSUp
-75 AltSDown
-76 AltSLeft
-77 AltSRight
-78 Alt
-79 CtrlAlt
-..
-127 3 KeyDEL KeyBackspace2
-..
-256 6 KeyRune
-257 4 KeyUp
-258 4 KeyDown
-259 4 KeyRight
-260 4 KeyLeft
-261 8 KeyUpLeft
-262 8 KeyUpRight
-263 8 KeyDownLeft
-264 8 KeyDownRight
-265 8 KeyCenter
-266 5 KeyPgUp
-267 5 KeyPgDn
-268 5 KeyHome
-269 5 KeyEnd
-270 5 KeyInsert
-271 5 KeyDelete
-272 8 KeyHelp
-273 8 KeyExit
-274 8 KeyClear
-275 8 KeyCancel
-276 8 KeyPrint
-277 8 KeyPause
-278 5 KeyBacktab
-279 5 KeyF1
-280 5 KeyF2
-281 5 KeyF3
-282 5 KeyF4
-283 5 KeyF5
-284 5 KeyF6
-285 5 KeyF7
-286 5 KeyF8
-287 5 KeyF9
-288 5 KeyF10
-289 5 KeyF11
-290 5 KeyF12
-291 8 KeyF13
-292 8 KeyF14
-293 8 KeyF15
-294 8 KeyF16
-295 8 KeyF17
-296 8 KeyF18
-297 8 KeyF19
-298 8 KeyF20
-299 8 KeyF21
-300 8 KeyF22
-301 8 KeyF23
-302 8 KeyF24
-303 8 KeyF25
-304 8 KeyF26
-305 8 KeyF27
-306 8 KeyF28
-307 8 KeyF29
-308 8 KeyF30
-309 8 KeyF31
-310 8 KeyF32
-311 8 KeyF33
-312 8 KeyF34
-313 8 KeyF35
-314 8 KeyF36
-315 8 KeyF37
-316 8 KeyF38
-317 8 KeyF39
-318 8 KeyF40
-319 8 KeyF41
-320 8 KeyF42
-321 8 KeyF43
-322 8 KeyF44
-323 8 KeyF45
-324 8 KeyF46
-325 8 KeyF47
-326 8 KeyF48
-327 8 KeyF49
-328 8 KeyF50
-329 8 KeyF51
-330 8 KeyF52
-331 8 KeyF53
-332 8 KeyF54
-333 8 KeyF55
-334 8 KeyF56
-335 8 KeyF57
-336 8 KeyF58
-337 8 KeyF59
-338 8 KeyF60
-339 8 KeyF61
-340 8 KeyF62
-341 8 KeyF63
-342 8 KeyF64
--- --------- -------------- --------------- ----------- ------------- ---------------- ----------
-%v section # tcell ctrl key tcell ctrl char tcell alias tui constants tcell named keys tcell mods
-*/
diff --git a/.fzf/src/tui/ttyname_unix.go b/.fzf/src/tui/ttyname_unix.go
deleted file mode 100644
index 68298cd..0000000
--- a/.fzf/src/tui/ttyname_unix.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// +build !windows
-
-package tui
-
-import (
- "io/ioutil"
- "os"
- "syscall"
-)
-
-var devPrefixes = [...]string{"/dev/pts/", "/dev/"}
-
-func ttyname() string {
- var stderr syscall.Stat_t
- if syscall.Fstat(2, &stderr) != nil {
- return ""
- }
-
- for _, prefix := range devPrefixes {
- files, err := ioutil.ReadDir(prefix)
- if err != nil {
- continue
- }
-
- for _, file := range files {
- if stat, ok := file.Sys().(*syscall.Stat_t); ok && stat.Rdev == stderr.Rdev {
- return prefix + file.Name()
- }
- }
- }
- return ""
-}
-
-// TtyIn returns terminal device to be used as STDIN, falls back to os.Stdin
-func TtyIn() *os.File {
- in, err := os.OpenFile(consoleDevice, syscall.O_RDONLY, 0)
- if err != nil {
- tty := ttyname()
- if len(tty) > 0 {
- if in, err := os.OpenFile(tty, syscall.O_RDONLY, 0); err == nil {
- return in
- }
- }
- return os.Stdin
- }
- return in
-}
diff --git a/.fzf/src/tui/ttyname_windows.go b/.fzf/src/tui/ttyname_windows.go
deleted file mode 100644
index 8db490a..0000000
--- a/.fzf/src/tui/ttyname_windows.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// +build windows
-
-package tui
-
-import "os"
-
-func ttyname() string {
- return ""
-}
-
-// TtyIn on Windows returns os.Stdin
-func TtyIn() *os.File {
- return os.Stdin
-}
diff --git a/.fzf/src/tui/tui.go b/.fzf/src/tui/tui.go
deleted file mode 100644
index eb09da4..0000000
--- a/.fzf/src/tui/tui.go
+++ /dev/null
@@ -1,625 +0,0 @@
-package tui
-
-import (
- "fmt"
- "os"
- "strconv"
- "time"
-)
-
-// Types of user action
-type EventType int
-
-const (
- Rune EventType = iota
-
- CtrlA
- CtrlB
- CtrlC
- CtrlD
- CtrlE
- CtrlF
- CtrlG
- CtrlH
- Tab
- CtrlJ
- CtrlK
- CtrlL
- CtrlM
- CtrlN
- CtrlO
- CtrlP
- CtrlQ
- CtrlR
- CtrlS
- CtrlT
- CtrlU
- CtrlV
- CtrlW
- CtrlX
- CtrlY
- CtrlZ
- ESC
- CtrlSpace
-
- // https://apple.stackexchange.com/questions/24261/how-do-i-send-c-that-is-control-slash-to-the-terminal
- CtrlBackSlash
- CtrlRightBracket
- CtrlCaret
- CtrlSlash
-
- Invalid
- Resize
- Mouse
- DoubleClick
- LeftClick
- RightClick
-
- BTab
- BSpace
-
- Del
- PgUp
- PgDn
-
- Up
- Down
- Left
- Right
- Home
- End
- Insert
-
- SUp
- SDown
- SLeft
- SRight
-
- F1
- F2
- F3
- F4
- F5
- F6
- F7
- F8
- F9
- F10
- F11
- F12
-
- Change
- BackwardEOF
-
- AltBS
-
- AltUp
- AltDown
- AltLeft
- AltRight
-
- AltSUp
- AltSDown
- AltSLeft
- AltSRight
-
- Alt
- CtrlAlt
-)
-
-func (t EventType) AsEvent() Event {
- return Event{t, 0, nil}
-}
-
-func (t EventType) Int() int {
- return int(t)
-}
-
-func (t EventType) Byte() byte {
- return byte(t)
-}
-
-func (e Event) Comparable() Event {
- // Ignore MouseEvent pointer
- return Event{e.Type, e.Char, nil}
-}
-
-func Key(r rune) Event {
- return Event{Rune, r, nil}
-}
-
-func AltKey(r rune) Event {
- return Event{Alt, r, nil}
-}
-
-func CtrlAltKey(r rune) Event {
- return Event{CtrlAlt, r, nil}
-}
-
-const (
- doubleClickDuration = 500 * time.Millisecond
-)
-
-type Color int32
-
-func (c Color) IsDefault() bool {
- return c == colDefault
-}
-
-func (c Color) is24() bool {
- return c > 0 && (c&(1<<24)) > 0
-}
-
-type ColorAttr struct {
- Color Color
- Attr Attr
-}
-
-func NewColorAttr() ColorAttr {
- return ColorAttr{Color: colUndefined, Attr: AttrUndefined}
-}
-
-const (
- colUndefined Color = -2
- colDefault Color = -1
-)
-
-const (
- colBlack Color = iota
- colRed
- colGreen
- colYellow
- colBlue
- colMagenta
- colCyan
- colWhite
-)
-
-type FillReturn int
-
-const (
- FillContinue FillReturn = iota
- FillNextLine
- FillSuspend
-)
-
-type ColorPair struct {
- fg Color
- bg Color
- attr Attr
-}
-
-func HexToColor(rrggbb string) Color {
- r, _ := strconv.ParseInt(rrggbb[1:3], 16, 0)
- g, _ := strconv.ParseInt(rrggbb[3:5], 16, 0)
- b, _ := strconv.ParseInt(rrggbb[5:7], 16, 0)
- return Color((1 << 24) + (r << 16) + (g << 8) + b)
-}
-
-func NewColorPair(fg Color, bg Color, attr Attr) ColorPair {
- return ColorPair{fg, bg, attr}
-}
-
-func (p ColorPair) Fg() Color {
- return p.fg
-}
-
-func (p ColorPair) Bg() Color {
- return p.bg
-}
-
-func (p ColorPair) Attr() Attr {
- return p.attr
-}
-
-func (p ColorPair) HasBg() bool {
- return p.attr&Reverse == 0 && p.bg != colDefault ||
- p.attr&Reverse > 0 && p.fg != colDefault
-}
-
-func (p ColorPair) merge(other ColorPair, except Color) ColorPair {
- dup := p
- dup.attr = dup.attr.Merge(other.attr)
- if other.fg != except {
- dup.fg = other.fg
- }
- if other.bg != except {
- dup.bg = other.bg
- }
- return dup
-}
-
-func (p ColorPair) WithAttr(attr Attr) ColorPair {
- dup := p
- dup.attr = dup.attr.Merge(attr)
- return dup
-}
-
-func (p ColorPair) MergeAttr(other ColorPair) ColorPair {
- return p.WithAttr(other.attr)
-}
-
-func (p ColorPair) Merge(other ColorPair) ColorPair {
- return p.merge(other, colUndefined)
-}
-
-func (p ColorPair) MergeNonDefault(other ColorPair) ColorPair {
- return p.merge(other, colDefault)
-}
-
-type ColorTheme struct {
- Colored bool
- Input ColorAttr
- Disabled ColorAttr
- Fg ColorAttr
- Bg ColorAttr
- PreviewFg ColorAttr
- PreviewBg ColorAttr
- DarkBg ColorAttr
- Gutter ColorAttr
- Prompt ColorAttr
- Match ColorAttr
- Current ColorAttr
- CurrentMatch ColorAttr
- Spinner ColorAttr
- Info ColorAttr
- Cursor ColorAttr
- Selected ColorAttr
- Header ColorAttr
- Border ColorAttr
-}
-
-type Event struct {
- Type EventType
- Char rune
- MouseEvent *MouseEvent
-}
-
-type MouseEvent struct {
- Y int
- X int
- S int
- Left bool
- Down bool
- Double bool
- Mod bool
-}
-
-type BorderShape int
-
-const (
- BorderNone BorderShape = iota
- BorderRounded
- BorderSharp
- BorderHorizontal
- BorderVertical
- BorderTop
- BorderBottom
- BorderLeft
- BorderRight
-)
-
-type BorderStyle struct {
- shape BorderShape
- horizontal rune
- vertical rune
- topLeft rune
- topRight rune
- bottomLeft rune
- bottomRight rune
-}
-
-type BorderCharacter int
-
-func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
- if unicode {
- if shape == BorderRounded {
- return BorderStyle{
- shape: shape,
- horizontal: '─',
- vertical: '│',
- topLeft: '╭',
- topRight: '╮',
- bottomLeft: '╰',
- bottomRight: '╯',
- }
- }
- return BorderStyle{
- shape: shape,
- horizontal: '─',
- vertical: '│',
- topLeft: '┌',
- topRight: '┐',
- bottomLeft: '└',
- bottomRight: '┘',
- }
- }
- return BorderStyle{
- shape: shape,
- horizontal: '-',
- vertical: '|',
- topLeft: '+',
- topRight: '+',
- bottomLeft: '+',
- bottomRight: '+',
- }
-}
-
-func MakeTransparentBorder() BorderStyle {
- return BorderStyle{
- shape: BorderRounded,
- horizontal: ' ',
- vertical: ' ',
- topLeft: ' ',
- topRight: ' ',
- bottomLeft: ' ',
- bottomRight: ' '}
-}
-
-type Renderer interface {
- Init()
- Pause(clear bool)
- Resume(clear bool, sigcont bool)
- Clear()
- RefreshWindows(windows []Window)
- Refresh()
- Close()
-
- GetChar() Event
-
- MaxX() int
- MaxY() int
-
- NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window
-}
-
-type Window interface {
- Top() int
- Left() int
- Width() int
- Height() int
-
- Refresh()
- FinishFill()
- Close()
-
- X() int
- Y() int
- Enclose(y int, x int) bool
-
- Move(y int, x int)
- MoveAndClear(y int, x int)
- Print(text string)
- CPrint(color ColorPair, text string)
- Fill(text string) FillReturn
- CFill(fg Color, bg Color, attr Attr, text string) FillReturn
- Erase()
-}
-
-type FullscreenRenderer struct {
- theme *ColorTheme
- mouse bool
- forceBlack bool
- prevDownTime time.Time
- clickY []int
-}
-
-func NewFullscreenRenderer(theme *ColorTheme, forceBlack bool, mouse bool) Renderer {
- r := &FullscreenRenderer{
- theme: theme,
- mouse: mouse,
- forceBlack: forceBlack,
- prevDownTime: time.Unix(0, 0),
- clickY: []int{}}
- return r
-}
-
-var (
- Default16 *ColorTheme
- Dark256 *ColorTheme
- Light256 *ColorTheme
-
- ColPrompt ColorPair
- ColNormal ColorPair
- ColInput ColorPair
- ColDisabled ColorPair
- ColMatch ColorPair
- ColCursor ColorPair
- ColCursorEmpty ColorPair
- ColSelected ColorPair
- ColCurrent ColorPair
- ColCurrentMatch ColorPair
- ColCurrentCursor ColorPair
- ColCurrentCursorEmpty ColorPair
- ColCurrentSelected ColorPair
- ColCurrentSelectedEmpty ColorPair
- ColSpinner ColorPair
- ColInfo ColorPair
- ColHeader ColorPair
- ColBorder ColorPair
- ColPreview ColorPair
- ColPreviewBorder ColorPair
-)
-
-func EmptyTheme() *ColorTheme {
- return &ColorTheme{
- Colored: true,
- Input: ColorAttr{colUndefined, AttrUndefined},
- Disabled: ColorAttr{colUndefined, AttrUndefined},
- Fg: ColorAttr{colUndefined, AttrUndefined},
- Bg: ColorAttr{colUndefined, AttrUndefined},
- PreviewFg: ColorAttr{colUndefined, AttrUndefined},
- PreviewBg: ColorAttr{colUndefined, AttrUndefined},
- DarkBg: ColorAttr{colUndefined, AttrUndefined},
- Gutter: ColorAttr{colUndefined, AttrUndefined},
- Prompt: ColorAttr{colUndefined, AttrUndefined},
- Match: ColorAttr{colUndefined, AttrUndefined},
- Current: ColorAttr{colUndefined, AttrUndefined},
- CurrentMatch: ColorAttr{colUndefined, AttrUndefined},
- Spinner: ColorAttr{colUndefined, AttrUndefined},
- Info: ColorAttr{colUndefined, AttrUndefined},
- Cursor: ColorAttr{colUndefined, AttrUndefined},
- Selected: ColorAttr{colUndefined, AttrUndefined},
- Header: ColorAttr{colUndefined, AttrUndefined},
- Border: ColorAttr{colUndefined, AttrUndefined}}
-}
-
-func NoColorTheme() *ColorTheme {
- return &ColorTheme{
- Colored: false,
- Input: ColorAttr{colDefault, AttrRegular},
- Disabled: ColorAttr{colDefault, AttrRegular},
- Fg: ColorAttr{colDefault, AttrRegular},
- Bg: ColorAttr{colDefault, AttrRegular},
- PreviewFg: ColorAttr{colDefault, AttrRegular},
- PreviewBg: ColorAttr{colDefault, AttrRegular},
- DarkBg: ColorAttr{colDefault, AttrRegular},
- Gutter: ColorAttr{colDefault, AttrRegular},
- Prompt: ColorAttr{colDefault, AttrRegular},
- Match: ColorAttr{colDefault, Underline},
- Current: ColorAttr{colDefault, Reverse},
- CurrentMatch: ColorAttr{colDefault, Reverse | Underline},
- Spinner: ColorAttr{colDefault, AttrRegular},
- Info: ColorAttr{colDefault, AttrRegular},
- Cursor: ColorAttr{colDefault, AttrRegular},
- Selected: ColorAttr{colDefault, AttrRegular},
- Header: ColorAttr{colDefault, AttrRegular},
- Border: ColorAttr{colDefault, AttrRegular}}
-}
-
-func errorExit(message string) {
- fmt.Fprintln(os.Stderr, message)
- os.Exit(2)
-}
-
-func init() {
- Default16 = &ColorTheme{
- Colored: true,
- Input: ColorAttr{colDefault, AttrUndefined},
- Disabled: ColorAttr{colUndefined, AttrUndefined},
- Fg: ColorAttr{colDefault, AttrUndefined},
- Bg: ColorAttr{colDefault, AttrUndefined},
- PreviewFg: ColorAttr{colUndefined, AttrUndefined},
- PreviewBg: ColorAttr{colUndefined, AttrUndefined},
- DarkBg: ColorAttr{colBlack, AttrUndefined},
- Gutter: ColorAttr{colUndefined, AttrUndefined},
- Prompt: ColorAttr{colBlue, AttrUndefined},
- Match: ColorAttr{colGreen, AttrUndefined},
- Current: ColorAttr{colYellow, AttrUndefined},
- CurrentMatch: ColorAttr{colGreen, AttrUndefined},
- Spinner: ColorAttr{colGreen, AttrUndefined},
- Info: ColorAttr{colWhite, AttrUndefined},
- Cursor: ColorAttr{colRed, AttrUndefined},
- Selected: ColorAttr{colMagenta, AttrUndefined},
- Header: ColorAttr{colCyan, AttrUndefined},
- Border: ColorAttr{colBlack, AttrUndefined}}
- Dark256 = &ColorTheme{
- Colored: true,
- Input: ColorAttr{colDefault, AttrUndefined},
- Disabled: ColorAttr{colUndefined, AttrUndefined},
- Fg: ColorAttr{colDefault, AttrUndefined},
- Bg: ColorAttr{colDefault, AttrUndefined},
- PreviewFg: ColorAttr{colUndefined, AttrUndefined},
- PreviewBg: ColorAttr{colUndefined, AttrUndefined},
- DarkBg: ColorAttr{236, AttrUndefined},
- Gutter: ColorAttr{colUndefined, AttrUndefined},
- Prompt: ColorAttr{110, AttrUndefined},
- Match: ColorAttr{108, AttrUndefined},
- Current: ColorAttr{254, AttrUndefined},
- CurrentMatch: ColorAttr{151, AttrUndefined},
- Spinner: ColorAttr{148, AttrUndefined},
- Info: ColorAttr{144, AttrUndefined},
- Cursor: ColorAttr{161, AttrUndefined},
- Selected: ColorAttr{168, AttrUndefined},
- Header: ColorAttr{109, AttrUndefined},
- Border: ColorAttr{59, AttrUndefined}}
- Light256 = &ColorTheme{
- Colored: true,
- Input: ColorAttr{colDefault, AttrUndefined},
- Disabled: ColorAttr{colUndefined, AttrUndefined},
- Fg: ColorAttr{colDefault, AttrUndefined},
- Bg: ColorAttr{colDefault, AttrUndefined},
- PreviewFg: ColorAttr{colUndefined, AttrUndefined},
- PreviewBg: ColorAttr{colUndefined, AttrUndefined},
- DarkBg: ColorAttr{251, AttrUndefined},
- Gutter: ColorAttr{colUndefined, AttrUndefined},
- Prompt: ColorAttr{25, AttrUndefined},
- Match: ColorAttr{66, AttrUndefined},
- Current: ColorAttr{237, AttrUndefined},
- CurrentMatch: ColorAttr{23, AttrUndefined},
- Spinner: ColorAttr{65, AttrUndefined},
- Info: ColorAttr{101, AttrUndefined},
- Cursor: ColorAttr{161, AttrUndefined},
- Selected: ColorAttr{168, AttrUndefined},
- Header: ColorAttr{31, AttrUndefined},
- Border: ColorAttr{145, AttrUndefined}}
-}
-
-func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
- if forceBlack {
- theme.Bg = ColorAttr{colBlack, AttrUndefined}
- }
-
- o := func(a ColorAttr, b ColorAttr) ColorAttr {
- c := a
- if b.Color != colUndefined {
- c.Color = b.Color
- }
- if b.Attr != AttrUndefined {
- c.Attr = b.Attr
- }
- return c
- }
- theme.Input = o(baseTheme.Input, theme.Input)
- theme.Disabled = o(theme.Input, o(baseTheme.Disabled, theme.Disabled))
- theme.Fg = o(baseTheme.Fg, theme.Fg)
- theme.Bg = o(baseTheme.Bg, theme.Bg)
- theme.PreviewFg = o(theme.Fg, o(baseTheme.PreviewFg, theme.PreviewFg))
- theme.PreviewBg = o(theme.Bg, o(baseTheme.PreviewBg, theme.PreviewBg))
- theme.DarkBg = o(baseTheme.DarkBg, theme.DarkBg)
- theme.Gutter = o(theme.DarkBg, o(baseTheme.Gutter, theme.Gutter))
- theme.Prompt = o(baseTheme.Prompt, theme.Prompt)
- theme.Match = o(baseTheme.Match, theme.Match)
- theme.Current = o(baseTheme.Current, theme.Current)
- theme.CurrentMatch = o(baseTheme.CurrentMatch, theme.CurrentMatch)
- theme.Spinner = o(baseTheme.Spinner, theme.Spinner)
- theme.Info = o(baseTheme.Info, theme.Info)
- theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
- theme.Selected = o(baseTheme.Selected, theme.Selected)
- theme.Header = o(baseTheme.Header, theme.Header)
- theme.Border = o(baseTheme.Border, theme.Border)
-
- initPalette(theme)
-}
-
-func initPalette(theme *ColorTheme) {
- pair := func(fg, bg ColorAttr) ColorPair {
- if fg.Color == colDefault && (fg.Attr&Reverse) > 0 {
- bg.Color = colDefault
- }
- return ColorPair{fg.Color, bg.Color, fg.Attr}
- }
- blank := theme.Fg
- blank.Attr = AttrRegular
-
- ColPrompt = pair(theme.Prompt, theme.Bg)
- ColNormal = pair(theme.Fg, theme.Bg)
- ColInput = pair(theme.Input, theme.Bg)
- ColDisabled = pair(theme.Disabled, theme.Bg)
- ColMatch = pair(theme.Match, theme.Bg)
- ColCursor = pair(theme.Cursor, theme.Gutter)
- ColCursorEmpty = pair(blank, theme.Gutter)
- ColSelected = pair(theme.Selected, theme.Gutter)
- ColCurrent = pair(theme.Current, theme.DarkBg)
- ColCurrentMatch = pair(theme.CurrentMatch, theme.DarkBg)
- ColCurrentCursor = pair(theme.Cursor, theme.DarkBg)
- ColCurrentCursorEmpty = pair(blank, theme.DarkBg)
- ColCurrentSelected = pair(theme.Selected, theme.DarkBg)
- ColCurrentSelectedEmpty = pair(blank, theme.DarkBg)
- ColSpinner = pair(theme.Spinner, theme.Bg)
- ColInfo = pair(theme.Info, theme.Bg)
- ColHeader = pair(theme.Header, theme.Bg)
- ColBorder = pair(theme.Border, theme.Bg)
- ColPreview = pair(theme.PreviewFg, theme.PreviewBg)
- ColPreviewBorder = pair(theme.Border, theme.PreviewBg)
-}
diff --git a/.fzf/src/tui/tui_test.go b/.fzf/src/tui/tui_test.go
deleted file mode 100644
index 3ba9bf3..0000000
--- a/.fzf/src/tui/tui_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package tui
-
-import "testing"
-
-func TestHexToColor(t *testing.T) {
- assert := func(expr string, r, g, b int) {
- color := HexToColor(expr)
- if !color.is24() ||
- int((color>>16)&0xff) != r ||
- int((color>>8)&0xff) != g ||
- int((color)&0xff) != b {
- t.Fail()
- }
- }
-
- assert("#ff0000", 255, 0, 0)
- assert("#010203", 1, 2, 3)
- assert("#102030", 16, 32, 48)
- assert("#ffffff", 255, 255, 255)
-}
diff --git a/.fzf/src/util/atomicbool.go b/.fzf/src/util/atomicbool.go
deleted file mode 100644
index c5c7e69..0000000
--- a/.fzf/src/util/atomicbool.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package util
-
-import (
- "sync/atomic"
-)
-
-func convertBoolToInt32(b bool) int32 {
- if b {
- return 1
- }
- return 0
-}
-
-// AtomicBool is a boxed-class that provides synchronized access to the
-// underlying boolean value
-type AtomicBool struct {
- state int32 // "1" is true, "0" is false
-}
-
-// NewAtomicBool returns a new AtomicBool
-func NewAtomicBool(initialState bool) *AtomicBool {
- return &AtomicBool{state: convertBoolToInt32(initialState)}
-}
-
-// Get returns the current boolean value synchronously
-func (a *AtomicBool) Get() bool {
- return atomic.LoadInt32(&a.state) == 1
-}
-
-// Set updates the boolean value synchronously
-func (a *AtomicBool) Set(newState bool) bool {
- atomic.StoreInt32(&a.state, convertBoolToInt32(newState))
- return newState
-}
diff --git a/.fzf/src/util/atomicbool_test.go b/.fzf/src/util/atomicbool_test.go
deleted file mode 100644
index 1feff79..0000000
--- a/.fzf/src/util/atomicbool_test.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package util
-
-import "testing"
-
-func TestAtomicBool(t *testing.T) {
- if !NewAtomicBool(true).Get() || NewAtomicBool(false).Get() {
- t.Error("Invalid initial value")
- }
-
- ab := NewAtomicBool(true)
- if ab.Set(false) {
- t.Error("Invalid return value")
- }
- if ab.Get() {
- t.Error("Invalid state")
- }
-}
diff --git a/.fzf/src/util/chars.go b/.fzf/src/util/chars.go
deleted file mode 100644
index 41de924..0000000
--- a/.fzf/src/util/chars.go
+++ /dev/null
@@ -1,198 +0,0 @@
-package util
-
-import (
- "fmt"
- "unicode"
- "unicode/utf8"
- "unsafe"
-)
-
-const (
- overflow64 uint64 = 0x8080808080808080
- overflow32 uint32 = 0x80808080
-)
-
-type Chars struct {
- slice []byte // or []rune
- inBytes bool
- trimLengthKnown bool
- trimLength uint16
-
- // XXX Piggybacking item index here is a horrible idea. But I'm trying to
- // minimize the memory footprint by not wasting padded spaces.
- Index int32
-}
-
-func checkAscii(bytes []byte) (bool, int) {
- i := 0
- for ; i <= len(bytes)-8; i += 8 {
- if (overflow64 & *(*uint64)(unsafe.Pointer(&bytes[i]))) > 0 {
- return false, i
- }
- }
- for ; i <= len(bytes)-4; i += 4 {
- if (overflow32 & *(*uint32)(unsafe.Pointer(&bytes[i]))) > 0 {
- return false, i
- }
- }
- for ; i < len(bytes); i++ {
- if bytes[i] >= utf8.RuneSelf {
- return false, i
- }
- }
- return true, 0
-}
-
-// ToChars converts byte array into rune array
-func ToChars(bytes []byte) Chars {
- inBytes, bytesUntil := checkAscii(bytes)
- if inBytes {
- return Chars{slice: bytes, inBytes: inBytes}
- }
-
- runes := make([]rune, bytesUntil, len(bytes))
- for i := 0; i < bytesUntil; i++ {
- runes[i] = rune(bytes[i])
- }
- for i := bytesUntil; i < len(bytes); {
- r, sz := utf8.DecodeRune(bytes[i:])
- i += sz
- runes = append(runes, r)
- }
- return RunesToChars(runes)
-}
-
-func RunesToChars(runes []rune) Chars {
- return Chars{slice: *(*[]byte)(unsafe.Pointer(&runes)), inBytes: false}
-}
-
-func (chars *Chars) IsBytes() bool {
- return chars.inBytes
-}
-
-func (chars *Chars) Bytes() []byte {
- return chars.slice
-}
-
-func (chars *Chars) optionalRunes() []rune {
- if chars.inBytes {
- return nil
- }
- return *(*[]rune)(unsafe.Pointer(&chars.slice))
-}
-
-func (chars *Chars) Get(i int) rune {
- if runes := chars.optionalRunes(); runes != nil {
- return runes[i]
- }
- return rune(chars.slice[i])
-}
-
-func (chars *Chars) Length() int {
- if runes := chars.optionalRunes(); runes != nil {
- return len(runes)
- }
- return len(chars.slice)
-}
-
-// String returns the string representation of a Chars object.
-func (chars *Chars) String() string {
- return fmt.Sprintf("Chars{slice: []byte(%q), inBytes: %v, trimLengthKnown: %v, trimLength: %d, Index: %d}", chars.slice, chars.inBytes, chars.trimLengthKnown, chars.trimLength, chars.Index)
-}
-
-// TrimLength returns the length after trimming leading and trailing whitespaces
-func (chars *Chars) TrimLength() uint16 {
- if chars.trimLengthKnown {
- return chars.trimLength
- }
- chars.trimLengthKnown = true
- var i int
- len := chars.Length()
- for i = len - 1; i >= 0; i-- {
- char := chars.Get(i)
- if !unicode.IsSpace(char) {
- break
- }
- }
- // Completely empty
- if i < 0 {
- return 0
- }
-
- var j int
- for j = 0; j < len; j++ {
- char := chars.Get(j)
- if !unicode.IsSpace(char) {
- break
- }
- }
- chars.trimLength = AsUint16(i - j + 1)
- return chars.trimLength
-}
-
-func (chars *Chars) LeadingWhitespaces() int {
- whitespaces := 0
- for i := 0; i < chars.Length(); i++ {
- char := chars.Get(i)
- if !unicode.IsSpace(char) {
- break
- }
- whitespaces++
- }
- return whitespaces
-}
-
-func (chars *Chars) TrailingWhitespaces() int {
- whitespaces := 0
- for i := chars.Length() - 1; i >= 0; i-- {
- char := chars.Get(i)
- if !unicode.IsSpace(char) {
- break
- }
- whitespaces++
- }
- return whitespaces
-}
-
-func (chars *Chars) TrimTrailingWhitespaces() {
- whitespaces := chars.TrailingWhitespaces()
- chars.slice = chars.slice[0 : len(chars.slice)-whitespaces]
-}
-
-func (chars *Chars) ToString() string {
- if runes := chars.optionalRunes(); runes != nil {
- return string(runes)
- }
- return string(chars.slice)
-}
-
-func (chars *Chars) ToRunes() []rune {
- if runes := chars.optionalRunes(); runes != nil {
- return runes
- }
- bytes := chars.slice
- runes := make([]rune, len(bytes))
- for idx, b := range bytes {
- runes[idx] = rune(b)
- }
- return runes
-}
-
-func (chars *Chars) CopyRunes(dest []rune) {
- if runes := chars.optionalRunes(); runes != nil {
- copy(dest, runes)
- return
- }
- for idx, b := range chars.slice[:len(dest)] {
- dest[idx] = rune(b)
- }
-}
-
-func (chars *Chars) Prepend(prefix string) {
- if runes := chars.optionalRunes(); runes != nil {
- runes = append([]rune(prefix), runes...)
- chars.slice = *(*[]byte)(unsafe.Pointer(&runes))
- } else {
- chars.slice = append([]byte(prefix), chars.slice...)
- }
-}
diff --git a/.fzf/src/util/chars_test.go b/.fzf/src/util/chars_test.go
deleted file mode 100644
index b7983f3..0000000
--- a/.fzf/src/util/chars_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package util
-
-import "testing"
-
-func TestToCharsAscii(t *testing.T) {
- chars := ToChars([]byte("foobar"))
- if !chars.inBytes || chars.ToString() != "foobar" || !chars.inBytes {
- t.Error()
- }
-}
-
-func TestCharsLength(t *testing.T) {
- chars := ToChars([]byte("\tabc한글 "))
- if chars.inBytes || chars.Length() != 8 || chars.TrimLength() != 5 {
- t.Error()
- }
-}
-
-func TestCharsToString(t *testing.T) {
- text := "\tabc한글 "
- chars := ToChars([]byte(text))
- if chars.ToString() != text {
- t.Error()
- }
-}
-
-func TestTrimLength(t *testing.T) {
- check := func(str string, exp uint16) {
- chars := ToChars([]byte(str))
- trimmed := chars.TrimLength()
- if trimmed != exp {
- t.Errorf("Invalid TrimLength result for '%s': %d (expected %d)",
- str, trimmed, exp)
- }
- }
- check("hello", 5)
- check("hello ", 5)
- check("hello ", 5)
- check(" hello", 5)
- check(" hello", 5)
- check(" hello ", 5)
- check(" hello ", 5)
- check("h o", 5)
- check(" h o ", 5)
- check(" ", 0)
-}
diff --git a/.fzf/src/util/eventbox.go b/.fzf/src/util/eventbox.go
deleted file mode 100644
index b710cf1..0000000
--- a/.fzf/src/util/eventbox.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package util
-
-import "sync"
-
-// EventType is the type for fzf events
-type EventType int
-
-// Events is a type that associates EventType to any data
-type Events map[EventType]interface{}
-
-// EventBox is used for coordinating events
-type EventBox struct {
- events Events
- cond *sync.Cond
- ignore map[EventType]bool
-}
-
-// NewEventBox returns a new EventBox
-func NewEventBox() *EventBox {
- return &EventBox{
- events: make(Events),
- cond: sync.NewCond(&sync.Mutex{}),
- ignore: make(map[EventType]bool)}
-}
-
-// Wait blocks the goroutine until signaled
-func (b *EventBox) Wait(callback func(*Events)) {
- b.cond.L.Lock()
-
- if len(b.events) == 0 {
- b.cond.Wait()
- }
-
- callback(&b.events)
- b.cond.L.Unlock()
-}
-
-// Set turns on the event type on the box
-func (b *EventBox) Set(event EventType, value interface{}) {
- b.cond.L.Lock()
- b.events[event] = value
- if _, found := b.ignore[event]; !found {
- b.cond.Broadcast()
- }
- b.cond.L.Unlock()
-}
-
-// Clear clears the events
-// Unsynchronized; should be called within Wait routine
-func (events *Events) Clear() {
- for event := range *events {
- delete(*events, event)
- }
-}
-
-// Peek peeks at the event box if the given event is set
-func (b *EventBox) Peek(event EventType) bool {
- b.cond.L.Lock()
- _, ok := b.events[event]
- b.cond.L.Unlock()
- return ok
-}
-
-// Watch deletes the events from the ignore list
-func (b *EventBox) Watch(events ...EventType) {
- b.cond.L.Lock()
- for _, event := range events {
- delete(b.ignore, event)
- }
- b.cond.L.Unlock()
-}
-
-// Unwatch adds the events to the ignore list
-func (b *EventBox) Unwatch(events ...EventType) {
- b.cond.L.Lock()
- for _, event := range events {
- b.ignore[event] = true
- }
- b.cond.L.Unlock()
-}
-
-// WaitFor blocks the execution until the event is received
-func (b *EventBox) WaitFor(event EventType) {
- looping := true
- for looping {
- b.Wait(func(events *Events) {
- for evt := range *events {
- switch evt {
- case event:
- looping = false
- return
- }
- }
- })
- }
-}
diff --git a/.fzf/src/util/eventbox_test.go b/.fzf/src/util/eventbox_test.go
deleted file mode 100644
index 5a9dc30..0000000
--- a/.fzf/src/util/eventbox_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package util
-
-import "testing"
-
-// fzf events
-const (
- EvtReadNew EventType = iota
- EvtReadFin
- EvtSearchNew
- EvtSearchProgress
- EvtSearchFin
- EvtClose
-)
-
-func TestEventBox(t *testing.T) {
- eb := NewEventBox()
-
- // Wait should return immediately
- ch := make(chan bool)
-
- go func() {
- eb.Set(EvtReadNew, 10)
- ch <- true
- <-ch
- eb.Set(EvtSearchNew, 10)
- eb.Set(EvtSearchNew, 15)
- eb.Set(EvtSearchNew, 20)
- eb.Set(EvtSearchProgress, 30)
- ch <- true
- <-ch
- eb.Set(EvtSearchFin, 40)
- ch <- true
- <-ch
- }()
-
- count := 0
- sum := 0
- looping := true
- for looping {
- <-ch
- eb.Wait(func(events *Events) {
- for _, value := range *events {
- switch val := value.(type) {
- case int:
- sum += val
- looping = sum < 100
- }
- }
- events.Clear()
- })
- ch <- true
- count++
- }
-
- if count != 3 {
- t.Error("Invalid number of events", count)
- }
- if sum != 100 {
- t.Error("Invalid sum", sum)
- }
-}
diff --git a/.fzf/src/util/slab.go b/.fzf/src/util/slab.go
deleted file mode 100644
index 0c49d2d..0000000
--- a/.fzf/src/util/slab.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package util
-
-type Slab struct {
- I16 []int16
- I32 []int32
-}
-
-func MakeSlab(size16 int, size32 int) *Slab {
- return &Slab{
- I16: make([]int16, size16),
- I32: make([]int32, size32)}
-}
diff --git a/.fzf/src/util/util.go b/.fzf/src/util/util.go
deleted file mode 100644
index c16f1af..0000000
--- a/.fzf/src/util/util.go
+++ /dev/null
@@ -1,138 +0,0 @@
-package util
-
-import (
- "math"
- "os"
- "strings"
- "time"
-
- "github.com/mattn/go-isatty"
- "github.com/mattn/go-runewidth"
- "github.com/rivo/uniseg"
-)
-
-// RunesWidth returns runes width
-func RunesWidth(runes []rune, prefixWidth int, tabstop int, limit int) (int, int) {
- width := 0
- gr := uniseg.NewGraphemes(string(runes))
- idx := 0
- for gr.Next() {
- rs := gr.Runes()
- var w int
- if len(rs) == 1 && rs[0] == '\t' {
- w = tabstop - (prefixWidth+width)%tabstop
- } else {
- s := string(rs)
- w = runewidth.StringWidth(s) + strings.Count(s, "\n")
- }
- width += w
- if limit > 0 && width > limit {
- return width, idx
- }
- idx += len(rs)
- }
- return width, -1
-}
-
-// Max returns the largest integer
-func Max(first int, second int) int {
- if first >= second {
- return first
- }
- return second
-}
-
-// Max16 returns the largest integer
-func Max16(first int16, second int16) int16 {
- if first >= second {
- return first
- }
- return second
-}
-
-// Max32 returns the largest 32-bit integer
-func Max32(first int32, second int32) int32 {
- if first > second {
- return first
- }
- return second
-}
-
-// Min returns the smallest integer
-func Min(first int, second int) int {
- if first <= second {
- return first
- }
- return second
-}
-
-// Min32 returns the smallest 32-bit integer
-func Min32(first int32, second int32) int32 {
- if first <= second {
- return first
- }
- return second
-}
-
-// Constrain32 limits the given 32-bit integer with the upper and lower bounds
-func Constrain32(val int32, min int32, max int32) int32 {
- if val < min {
- return min
- }
- if val > max {
- return max
- }
- return val
-}
-
-// Constrain limits the given integer with the upper and lower bounds
-func Constrain(val int, min int, max int) int {
- if val < min {
- return min
- }
- if val > max {
- return max
- }
- return val
-}
-
-func AsUint16(val int) uint16 {
- if val > math.MaxUint16 {
- return math.MaxUint16
- } else if val < 0 {
- return 0
- }
- return uint16(val)
-}
-
-// DurWithin limits the given time.Duration with the upper and lower bounds
-func DurWithin(
- val time.Duration, min time.Duration, max time.Duration) time.Duration {
- if val < min {
- return min
- }
- if val > max {
- return max
- }
- return val
-}
-
-// IsTty returns true if stdin is a terminal
-func IsTty() bool {
- return isatty.IsTerminal(os.Stdin.Fd())
-}
-
-// ToTty returns true if stdout is a terminal
-func ToTty() bool {
- return isatty.IsTerminal(os.Stdout.Fd())
-}
-
-// Once returns a function that returns the specified boolean value only once
-func Once(nextResponse bool) func() bool {
- state := nextResponse
- return func() bool {
- prevState := state
- state = false
- return prevState
- }
-}
diff --git a/.fzf/src/util/util_test.go b/.fzf/src/util/util_test.go
deleted file mode 100644
index 4baa56f..0000000
--- a/.fzf/src/util/util_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package util
-
-import "testing"
-
-func TestMax(t *testing.T) {
- if Max(-2, 5) != 5 {
- t.Error("Invalid result")
- }
-}
-
-func TestContrain(t *testing.T) {
- if Constrain(-3, -1, 3) != -1 {
- t.Error("Expected", -1)
- }
- if Constrain(2, -1, 3) != 2 {
- t.Error("Expected", 2)
- }
-
- if Constrain(5, -1, 3) != 3 {
- t.Error("Expected", 3)
- }
-}
-
-func TestOnce(t *testing.T) {
- o := Once(false)
- if o() {
- t.Error("Expected: false")
- }
- if o() {
- t.Error("Expected: false")
- }
-
- o = Once(true)
- if !o() {
- t.Error("Expected: true")
- }
- if o() {
- t.Error("Expected: false")
- }
-}
diff --git a/.fzf/src/util/util_unix.go b/.fzf/src/util/util_unix.go
deleted file mode 100644
index 6331275..0000000
--- a/.fzf/src/util/util_unix.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// +build !windows
-
-package util
-
-import (
- "os"
- "os/exec"
- "syscall"
-)
-
-// ExecCommand executes the given command with $SHELL
-func ExecCommand(command string, setpgid bool) *exec.Cmd {
- shell := os.Getenv("SHELL")
- if len(shell) == 0 {
- shell = "sh"
- }
- return ExecCommandWith(shell, command, setpgid)
-}
-
-// ExecCommandWith executes the given command with the specified shell
-func ExecCommandWith(shell string, command string, setpgid bool) *exec.Cmd {
- cmd := exec.Command(shell, "-c", command)
- if setpgid {
- cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
- }
- return cmd
-}
-
-// KillCommand kills the process for the given command
-func KillCommand(cmd *exec.Cmd) error {
- return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
-}
-
-// IsWindows returns true on Windows
-func IsWindows() bool {
- return false
-}
-
-// SetNonblock executes syscall.SetNonblock on file descriptor
-func SetNonblock(file *os.File, nonblock bool) {
- syscall.SetNonblock(int(file.Fd()), nonblock)
-}
-
-// Read executes syscall.Read on file descriptor
-func Read(fd int, b []byte) (int, error) {
- return syscall.Read(int(fd), b)
-}
diff --git a/.fzf/src/util/util_windows.go b/.fzf/src/util/util_windows.go
deleted file mode 100644
index e4e0437..0000000
--- a/.fzf/src/util/util_windows.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// +build windows
-
-package util
-
-import (
- "fmt"
- "os"
- "os/exec"
- "strings"
- "sync/atomic"
- "syscall"
-)
-
-var shellPath atomic.Value
-
-// ExecCommand executes the given command with $SHELL
-func ExecCommand(command string, setpgid bool) *exec.Cmd {
- var shell string
- if cached := shellPath.Load(); cached != nil {
- shell = cached.(string)
- } else {
- shell = os.Getenv("SHELL")
- if len(shell) == 0 {
- shell = "cmd"
- } else if strings.Contains(shell, "/") {
- out, err := exec.Command("cygpath", "-w", shell).Output()
- if err == nil {
- shell = strings.Trim(string(out), "\n")
- }
- }
- shellPath.Store(shell)
- }
- return ExecCommandWith(shell, command, setpgid)
-}
-
-// ExecCommandWith executes the given command with the specified shell
-// FIXME: setpgid is unused. We set it in the Unix implementation so that we
-// can kill preview process with its child processes at once.
-// NOTE: For "powershell", we should ideally set output encoding to UTF8,
-// but it is left as is now because no adverse effect has been observed.
-func ExecCommandWith(shell string, command string, setpgid bool) *exec.Cmd {
- var cmd *exec.Cmd
- if strings.Contains(shell, "cmd") {
- cmd = exec.Command(shell)
- cmd.SysProcAttr = &syscall.SysProcAttr{
- HideWindow: false,
- CmdLine: fmt.Sprintf(` /v:on/s/c "%s"`, command),
- CreationFlags: 0,
- }
- return cmd
- }
-
- if strings.Contains(shell, "pwsh") || strings.Contains(shell, "powershell") {
- cmd = exec.Command(shell, "-NoProfile", "-Command", command)
- } else {
- cmd = exec.Command(shell, "-c", command)
- }
- cmd.SysProcAttr = &syscall.SysProcAttr{
- HideWindow: false,
- CreationFlags: 0,
- }
- return cmd
-}
-
-// KillCommand kills the process for the given command
-func KillCommand(cmd *exec.Cmd) error {
- return cmd.Process.Kill()
-}
-
-// IsWindows returns true on Windows
-func IsWindows() bool {
- return true
-}
-
-// SetNonblock executes syscall.SetNonblock on file descriptor
-func SetNonblock(file *os.File, nonblock bool) {
- syscall.SetNonblock(syscall.Handle(file.Fd()), nonblock)
-}
-
-// Read executes syscall.Read on file descriptor
-func Read(fd int, b []byte) (int, error) {
- return syscall.Read(syscall.Handle(fd), b)
-}
diff --git a/.fzf/test/fzf.vader b/.fzf/test/fzf.vader
deleted file mode 100644
index 07f0c8d..0000000
--- a/.fzf/test/fzf.vader
+++ /dev/null
@@ -1,175 +0,0 @@
-Execute (Setup):
- let g:dir = fnamemodify(g:vader_file, ':p:h')
- unlet! g:fzf_layout g:fzf_action g:fzf_history_dir
- Log 'Test directory: ' . g:dir
- Save &acd
-
-Execute (fzf#run with dir option):
- let cwd = getcwd()
- let result = fzf#run({ 'source': 'git ls-files', 'options': '--filter=vdr', 'dir': g:dir })
- AssertEqual ['fzf.vader'], result
- AssertEqual 0, haslocaldir()
- AssertEqual getcwd(), cwd
-
- execute 'lcd' fnameescape(cwd)
- let result = sort(fzf#run({ 'source': 'git ls-files', 'options': '--filter e', 'dir': g:dir }))
- AssertEqual ['fzf.vader', 'test_go.rb'], result
- AssertEqual 1, haslocaldir()
- AssertEqual getcwd(), cwd
-
-Execute (fzf#run with Funcref command):
- let g:ret = []
- function! g:FzfTest(e)
- call add(g:ret, a:e)
- endfunction
- let result = sort(fzf#run({ 'source': 'git ls-files', 'sink': function('g:FzfTest'), 'options': '--filter e', 'dir': g:dir }))
- AssertEqual ['fzf.vader', 'test_go.rb'], result
- AssertEqual ['fzf.vader', 'test_go.rb'], sort(g:ret)
-
-Execute (fzf#run with string source):
- let result = sort(fzf#run({ 'source': 'echo hi', 'options': '-f i' }))
- AssertEqual ['hi'], result
-
-Execute (fzf#run with list source):
- let result = sort(fzf#run({ 'source': ['hello', 'world'], 'options': '-f e' }))
- AssertEqual ['hello'], result
- let result = sort(fzf#run({ 'source': ['hello', 'world'], 'options': '-f o' }))
- AssertEqual ['hello', 'world'], result
-
-Execute (fzf#run with string source):
- let result = sort(fzf#run({ 'source': 'echo hi', 'options': '-f i' }))
- AssertEqual ['hi'], result
-
-Execute (fzf#run with dir option and noautochdir):
- set noacd
- let cwd = getcwd()
- call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/tmp', 'options': '-1'})
- " No change in working directory
- AssertEqual cwd, getcwd()
-
- call fzf#run({'source': ['/foobar'], 'sink': 'tabe', 'dir': '/tmp', 'options': '-1'})
- AssertEqual cwd, getcwd()
- tabclose
- AssertEqual cwd, getcwd()
-
-Execute (Incomplete fzf#run with dir option and autochdir):
- set acd
- let cwd = getcwd()
- call fzf#run({'source': [], 'sink': 'e', 'dir': '/tmp', 'options': '-0'})
- " No change in working directory even if &acd is set
- AssertEqual cwd, getcwd()
-
-Execute (FIXME: fzf#run with dir option and autochdir):
- set acd
- call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/tmp', 'options': '-1'})
- " Working directory changed due to &acd
- AssertEqual '/foobar', expand('%')
- AssertEqual '/', getcwd()
-
-Execute (fzf#run with dir option and autochdir when final cwd is same as dir):
- set acd
- cd /tmp
- call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/', 'options': '-1'})
- " Working directory changed due to &acd
- AssertEqual '/', getcwd()
-
-Execute (fzf#wrap):
- AssertThrows fzf#wrap({'foo': 'bar'})
-
- let opts = fzf#wrap('foobar')
- Log opts
- AssertEqual '~40%', opts.down
- Assert opts.options =~ '--expect='
- Assert !has_key(opts, 'sink')
- Assert has_key(opts, 'sink*')
-
- let opts = fzf#wrap('foobar', {}, 0)
- Log opts
- AssertEqual '~40%', opts.down
-
- let opts = fzf#wrap('foobar', {}, 1)
- Log opts
- Assert !has_key(opts, 'down')
-
- let opts = fzf#wrap('foobar', {'down': '50%'})
- Log opts
- AssertEqual '50%', opts.down
-
- let opts = fzf#wrap('foobar', {'down': '50%'}, 1)
- Log opts
- Assert !has_key(opts, 'down')
-
- let opts = fzf#wrap('foobar', {'sink': 'e'})
- Log opts
- AssertEqual 'e', opts.sink
- Assert !has_key(opts, 'sink*')
-
- let opts = fzf#wrap('foobar', {'options': '--reverse'})
- Log opts
- Assert opts.options =~ '--expect='
- Assert opts.options =~ '--reverse'
-
- let g:fzf_layout = {'window': 'enew'}
- let opts = fzf#wrap('foobar')
- Log opts
- AssertEqual 'enew', opts.window
-
- let opts = fzf#wrap('foobar', {}, 1)
- Log opts
- Assert !has_key(opts, 'window')
-
- let opts = fzf#wrap('foobar', {'right': '50%'})
- Log opts
- Assert !has_key(opts, 'window')
- AssertEqual '50%', opts.right
-
- let opts = fzf#wrap('foobar', {'right': '50%'}, 1)
- Log opts
- Assert !has_key(opts, 'window')
- Assert !has_key(opts, 'right')
-
- let g:fzf_action = {'a': 'tabe'}
- let opts = fzf#wrap('foobar')
- Log opts
- Assert opts.options =~ '--expect=a'
- Assert !has_key(opts, 'sink')
- Assert has_key(opts, 'sink*')
-
- let opts = fzf#wrap('foobar', {'sink': 'e'})
- Log opts
- AssertEqual 'e', opts.sink
- Assert !has_key(opts, 'sink*')
-
- let g:fzf_history_dir = '/tmp'
- let opts = fzf#wrap('foobar', {'options': '--color light'})
- Log opts
- Assert opts.options =~ "--history '/tmp/foobar'"
- Assert opts.options =~ '--color light'
-
- let g:fzf_colors = { 'fg': ['fg', 'Error'] }
- let opts = fzf#wrap({})
- Assert opts.options =~ '^--color=fg:'
-
-Execute (fzf#shellescape with sh):
- AssertEqual '''''', fzf#shellescape('', 'sh')
- AssertEqual '''\''', fzf#shellescape('\', 'sh')
- AssertEqual '''""''', fzf#shellescape('""', 'sh')
- AssertEqual '''foobar>''', fzf#shellescape('foobar>', 'sh')
- AssertEqual '''\\\"\\\''', fzf#shellescape('\\\"\\\', 'sh')
- AssertEqual '''echo ''\''''a''\'''' && echo ''\''''b''\''''''', fzf#shellescape('echo ''a'' && echo ''b''', 'sh')
-
-Execute (fzf#shellescape with cmd.exe):
- AssertEqual '^"^"', fzf#shellescape('', 'cmd.exe')
- AssertEqual '^"\\^"', fzf#shellescape('\', 'cmd.exe')
- AssertEqual '^"\^"\^"^"', fzf#shellescape('""', 'cmd.exe')
- AssertEqual '^"foobar^>^"', fzf#shellescape('foobar>', 'cmd.exe')
- AssertEqual '^"\\\\\\\^"\\\\\\^"', fzf#shellescape('\\\"\\\', 'cmd.exe')
- AssertEqual '^"echo ''a'' ^&^& echo ''b''^"', fzf#shellescape('echo ''a'' && echo ''b''', 'cmd.exe')
-
- AssertEqual '^"C:\Program Files ^(x86^)\\^"', fzf#shellescape('C:\Program Files (x86)\', 'cmd.exe')
- AssertEqual '^"C:/Program Files ^(x86^)/^"', fzf#shellescape('C:/Program Files (x86)/', 'cmd.exe')
- AssertEqual '^"%%USERPROFILE%%^"', fzf#shellescape('%USERPROFILE%', 'cmd.exe')
-
-Execute (Cleanup):
- unlet g:dir
- Restore
diff --git a/.fzf/test/test_go.rb b/.fzf/test/test_go.rb
deleted file mode 100755
index 20a4c92..0000000
--- a/.fzf/test/test_go.rb
+++ /dev/null
@@ -1,2626 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'minitest/autorun'
-require 'fileutils'
-require 'English'
-require 'shellwords'
-require 'erb'
-require 'tempfile'
-
-TEMPLATE = DATA.read
-UNSETS = %w[
- FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS
- FZF_TMUX FZF_TMUX_OPTS
- FZF_CTRL_T_COMMAND FZF_CTRL_T_OPTS
- FZF_ALT_C_COMMAND
- FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
- fish_history
-].freeze
-DEFAULT_TIMEOUT = 10
-
-FILE = File.expand_path(__FILE__)
-BASE = File.expand_path('..', __dir__)
-Dir.chdir(BASE)
-FZF = "FZF_DEFAULT_OPTS= FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf"
-
-def wait
- since = Time.now
- begin
- yield or raise Minitest::Assertion, 'Assertion failure'
- rescue Minitest::Assertion
- raise if Time.now - since > DEFAULT_TIMEOUT
-
- sleep(0.05)
- retry
- end
-end
-
-class Shell
- class << self
- def bash
- @bash ||=
- begin
- bashrc = '/tmp/fzf.bash'
- File.open(bashrc, 'w') do |f|
- f.puts ERB.new(TEMPLATE).result(binding)
- end
-
- "bash --rcfile #{bashrc}"
- end
- end
-
- def zsh
- @zsh ||=
- begin
- zdotdir = '/tmp/fzf-zsh'
- FileUtils.rm_rf(zdotdir)
- FileUtils.mkdir_p(zdotdir)
- File.open("#{zdotdir}/.zshrc", 'w') do |f|
- f.puts ERB.new(TEMPLATE).result(binding)
- end
- "ZDOTDIR=#{zdotdir} zsh"
- end
- end
-
- def fish
- UNSETS.map { |v| v + '= ' }.join + 'fish'
- end
- end
-end
-
-class Tmux
- attr_reader :win
-
- def initialize(shell = :bash)
- @win = go(%W[new-window -d -P -F #I #{Shell.send(shell)}]).first
- go(%W[set-window-option -t #{@win} pane-base-index 0])
- return unless shell == :fish
-
- send_keys 'function fish_prompt; end; clear', :Enter
- self.until(&:empty?)
- end
-
- def kill
- go(%W[kill-window -t #{win}])
- end
-
- def focus
- go(%W[select-window -t #{win}])
- end
-
- def send_keys(*args)
- go(%W[send-keys -t #{win}] + args.map(&:to_s))
- end
-
- def paste(str)
- system('tmux', 'setb', str, ';', 'pasteb', '-t', win, ';', 'send-keys', '-t', win, 'Enter')
- end
-
- def capture
- go(%W[capture-pane -p -J -t #{win}]).map(&:rstrip).reverse.drop_while(&:empty?).reverse
- end
-
- def until(refresh = false)
- lines = nil
- begin
- wait do
- lines = capture
- class << lines
- def counts
- lazy
- .map { |l| l.scan(%r{^. ([0-9]+)/([0-9]+)( \(([0-9]+)\))?}) }
- .reject(&:empty?)
- .first&.first&.map(&:to_i)&.values_at(0, 1, 3) || [0, 0, 0]
- end
-
- def match_count
- counts[0]
- end
-
- def item_count
- counts[1]
- end
-
- def select_count
- counts[2]
- end
-
- def any_include?(val)
- method = val.is_a?(Regexp) ? :match : :include?
- find { |line| line.send(method, val) }
- end
- end
- yield(lines).tap do |ok|
- send_keys 'C-l' if refresh && !ok
- end
- end
- rescue Minitest::Assertion
- puts $ERROR_INFO.backtrace
- puts '>' * 80
- puts lines
- puts '<' * 80
- raise
- end
- lines
- end
-
- def prepare
- tries = 0
- begin
- self.until(true) do |lines|
- message = "Prepare[#{tries}]"
- send_keys ' ', 'C-u', :Enter, message, :Left, :Right
- lines[-1] == message
- end
- rescue Minitest::Assertion
- (tries += 1) < 5 ? retry : raise
- end
- send_keys 'C-u', 'C-l'
- end
-
- private
-
- def go(args)
- IO.popen(%w[tmux] + args) { |io| io.readlines(chomp: true) }
- end
-end
-
-class TestBase < Minitest::Test
- TEMPNAME = '/tmp/output'
-
- attr_reader :tmux
-
- def tempname
- @temp_suffix ||= 0
- [TEMPNAME,
- caller_locations.map(&:label).find { |l| l.start_with?('test_') },
- @temp_suffix].join('-')
- end
-
- def writelines(path, lines)
- File.unlink(path) while File.exist?(path)
- File.open(path, 'w') { |f| f.puts lines }
- end
-
- def readonce
- wait { assert_path_exists tempname }
- File.read(tempname)
- ensure
- File.unlink(tempname) while File.exist?(tempname)
- @temp_suffix += 1
- tmux.prepare
- end
-
- def fzf(*opts)
- fzf!(*opts) + " > #{tempname}.tmp; mv #{tempname}.tmp #{tempname}"
- end
-
- def fzf!(*opts)
- opts = opts.map do |o|
- case o
- when Symbol
- o = o.to_s
- o.length > 1 ? "--#{o.tr('_', '-')}" : "-#{o}"
- when String, Numeric
- o.to_s
- end
- end.compact
- "#{FZF} #{opts.join(' ')}"
- end
-end
-
-class TestGoFZF < TestBase
- def setup
- super
- @tmux = Tmux.new
- end
-
- def teardown
- @tmux.kill
- end
-
- def test_vanilla
- tmux.send_keys "seq 1 100000 | #{fzf}", :Enter
- tmux.until do |lines|
- assert_equal '>', lines.last
- assert_equal ' 100000/100000', lines[-2]
- end
- lines = tmux.capture
- assert_equal ' 2', lines[-4]
- assert_equal '> 1', lines[-3]
- assert_equal ' 100000/100000', lines[-2]
- assert_equal '>', lines[-1]
-
- # Testing basic key bindings
- tmux.send_keys '99', 'C-a', '1', 'C-f', '3', 'C-b', 'C-h', 'C-u', 'C-e', 'C-y', 'C-k', 'Tab', 'BTab'
- tmux.until do |lines|
- assert_equal '> 3910', lines[-4]
- assert_equal ' 391', lines[-3]
- assert_equal ' 856/100000', lines[-2]
- assert_equal '> 391', lines[-1]
- end
-
- tmux.send_keys :Enter
- assert_equal '3910', readonce.chomp
- end
-
- def test_fzf_default_command
- tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', "FZF_DEFAULT_COMMAND='echo hello'"), :Enter
- tmux.until { |lines| assert_equal '> hello', lines[-3] }
-
- tmux.send_keys :Enter
- assert_equal 'hello', readonce.chomp
- end
-
- def test_fzf_default_command_failure
- tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', 'FZF_DEFAULT_COMMAND=false'), :Enter
- tmux.until { |lines| assert_equal ' [Command failed: false]', lines[-2] }
- tmux.send_keys :Enter
- end
-
- def test_key_bindings
- tmux.send_keys "#{FZF} -q 'foo bar foo-bar'", :Enter
- tmux.until { |lines| assert_equal '> foo bar foo-bar', lines.last }
-
- # CTRL-A
- tmux.send_keys 'C-A', '('
- tmux.until { |lines| assert_equal '> (foo bar foo-bar', lines.last }
-
- # META-F
- tmux.send_keys :Escape, :f, ')'
- tmux.until { |lines| assert_equal '> (foo) bar foo-bar', lines.last }
-
- # CTRL-B
- tmux.send_keys 'C-B', 'var'
- tmux.until { |lines| assert_equal '> (foovar) bar foo-bar', lines.last }
-
- # Left, CTRL-D
- tmux.send_keys :Left, :Left, 'C-D'
- tmux.until { |lines| assert_equal '> (foovr) bar foo-bar', lines.last }
-
- # META-BS
- tmux.send_keys :Escape, :BSpace
- tmux.until { |lines| assert_equal '> (r) bar foo-bar', lines.last }
-
- # CTRL-Y
- tmux.send_keys 'C-Y', 'C-Y'
- tmux.until { |lines| assert_equal '> (foovfoovr) bar foo-bar', lines.last }
-
- # META-B
- tmux.send_keys :Escape, :b, :Space, :Space
- tmux.until { |lines| assert_equal '> ( foovfoovr) bar foo-bar', lines.last }
-
- # CTRL-F / Right
- tmux.send_keys 'C-F', :Right, '/'
- tmux.until { |lines| assert_equal '> ( fo/ovfoovr) bar foo-bar', lines.last }
-
- # CTRL-H / BS
- tmux.send_keys 'C-H', :BSpace
- tmux.until { |lines| assert_equal '> ( fovfoovr) bar foo-bar', lines.last }
-
- # CTRL-E
- tmux.send_keys 'C-E', 'baz'
- tmux.until { |lines| assert_equal '> ( fovfoovr) bar foo-barbaz', lines.last }
-
- # CTRL-U
- tmux.send_keys 'C-U'
- tmux.until { |lines| assert_equal '>', lines.last }
-
- # CTRL-Y
- tmux.send_keys 'C-Y'
- tmux.until { |lines| assert_equal '> ( fovfoovr) bar foo-barbaz', lines.last }
-
- # CTRL-W
- tmux.send_keys 'C-W', 'bar-foo'
- tmux.until { |lines| assert_equal '> ( fovfoovr) bar bar-foo', lines.last }
-
- # META-D
- tmux.send_keys :Escape, :b, :Escape, :b, :Escape, :d, 'C-A', 'C-Y'
- tmux.until { |lines| assert_equal '> bar( fovfoovr) bar -foo', lines.last }
-
- # CTRL-M
- tmux.send_keys 'C-M'
- tmux.until { |lines| refute_equal '>', lines.last }
- end
-
- def test_file_word
- tmux.send_keys "#{FZF} -q '--/foo bar/foo-bar/baz' --filepath-word", :Enter
- tmux.until { |lines| assert_equal '> --/foo bar/foo-bar/baz', lines.last }
-
- tmux.send_keys :Escape, :b
- tmux.send_keys :Escape, :b
- tmux.send_keys :Escape, :b
- tmux.send_keys :Escape, :d
- tmux.send_keys :Escape, :f
- tmux.send_keys :Escape, :BSpace
- tmux.until { |lines| assert_equal '> --///baz', lines.last }
- end
-
- def test_multi_order
- tmux.send_keys "seq 1 10 | #{fzf(:multi)}", :Enter
- tmux.until { |lines| assert_equal '>', lines.last }
-
- tmux.send_keys :Tab, :Up, :Up, :Tab, :Tab, :Tab, # 3, 2
- 'C-K', 'C-K', 'C-K', 'C-K', :BTab, :BTab, # 5, 6
- :PgUp, 'C-J', :Down, :Tab, :Tab # 8, 7
- tmux.until { |lines| assert_equal ' 10/10 (6)', lines[-2] }
- tmux.send_keys 'C-M'
- assert_equal %w[3 2 5 6 8 7], readonce.lines(chomp: true)
- end
-
- def test_multi_max
- tmux.send_keys "seq 1 10 | #{FZF} -m 3 --bind A:select-all,T:toggle-all --preview 'echo [{+}]/{}'", :Enter
-
- tmux.until { |lines| assert_equal 10, lines.item_count }
-
- tmux.send_keys '1'
- tmux.until do |lines|
- assert_includes lines[1], ' [1]/1 '
- assert lines[-2]&.start_with?(' 2/10 ')
- end
-
- tmux.send_keys 'A'
- tmux.until do |lines|
- assert_includes lines[1], ' [1 10]/1 '
- assert lines[-2]&.start_with?(' 2/10 (2/3)')
- end
-
- tmux.send_keys :BSpace
- tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 (2/3)') }
-
- tmux.send_keys 'T'
- tmux.until do |lines|
- assert_includes lines[1], ' [2 3 4]/1 '
- assert lines[-2]&.start_with?(' 10/10 (3/3)')
- end
-
- %w[T A].each do |key|
- tmux.send_keys key
- tmux.until do |lines|
- assert_includes lines[1], ' [1 5 6]/1 '
- assert lines[-2]&.start_with?(' 10/10 (3/3)')
- end
- end
-
- tmux.send_keys :BTab
- tmux.until do |lines|
- assert_includes lines[1], ' [5 6]/2 '
- assert lines[-2]&.start_with?(' 10/10 (2/3)')
- end
-
- [:BTab, :BTab, 'A'].each do |key|
- tmux.send_keys key
- tmux.until do |lines|
- assert_includes lines[1], ' [5 6 2]/3 '
- assert lines[-2]&.start_with?(' 10/10 (3/3)')
- end
- end
-
- tmux.send_keys '2'
- tmux.until { |lines| assert lines[-2]&.start_with?(' 1/10 (3/3)') }
-
- tmux.send_keys 'T'
- tmux.until do |lines|
- assert_includes lines[1], ' [5 6]/2 '
- assert lines[-2]&.start_with?(' 1/10 (2/3)')
- end
-
- tmux.send_keys :BSpace
- tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 (2/3)') }
-
- tmux.send_keys 'A'
- tmux.until do |lines|
- assert_includes lines[1], ' [5 6 1]/1 '
- assert lines[-2]&.start_with?(' 10/10 (3/3)')
- end
- end
-
- def test_with_nth
- [true, false].each do |multi|
- tmux.send_keys "(echo ' 1st 2nd 3rd/';
- echo ' first second third/') |
- #{fzf(multi && :multi, :x, :nth, 2, :with_nth, '2,-1,1')}",
- :Enter
- tmux.until { |lines| assert_equal multi ? ' 2/2 (0)' : ' 2/2', lines[-2] }
-
- # Transformed list
- lines = tmux.capture
- assert_equal ' second third/first', lines[-4]
- assert_equal '> 2nd 3rd/1st', lines[-3]
-
- # However, the output must not be transformed
- if multi
- tmux.send_keys :BTab, :BTab
- tmux.until { |lines| assert_equal ' 2/2 (2)', lines[-2] }
- tmux.send_keys :Enter
- assert_equal [' 1st 2nd 3rd/', ' first second third/'], readonce.lines(chomp: true)
- else
- tmux.send_keys '^', '3'
- tmux.until { |lines| assert_equal ' 1/2', lines[-2] }
- tmux.send_keys :Enter
- assert_equal [' 1st 2nd 3rd/'], readonce.lines(chomp: true)
- end
- end
- end
-
- def test_scroll
- [true, false].each do |rev|
- tmux.send_keys "seq 1 100 | #{fzf(rev && :reverse)}", :Enter
- tmux.until { |lines| assert_includes lines, ' 100/100' }
- tmux.send_keys(*Array.new(110) { rev ? :Down : :Up })
- tmux.until { |lines| assert_includes lines, '> 100' }
- tmux.send_keys :Enter
- assert_equal '100', readonce.chomp
- end
- end
-
- def test_select_1
- tmux.send_keys "seq 1 100 | #{fzf(:with_nth, '..,..', :print_query, :q, 5555, :'1')}", :Enter
- assert_equal %w[5555 55], readonce.lines(chomp: true)
- end
-
- def test_exit_0
- tmux.send_keys "seq 1 100 | #{fzf(:with_nth, '..,..', :print_query, :q, 555_555, :'0')}", :Enter
- assert_equal %w[555555], readonce.lines(chomp: true)
- end
-
- def test_select_1_exit_0_fail
- [:'0', :'1', %i[1 0]].each do |opt|
- tmux.send_keys "seq 1 100 | #{fzf(:print_query, :multi, :q, 5, *opt)}", :Enter
- tmux.until { |lines| assert_equal '> 5', lines.last }
- tmux.send_keys :BTab, :BTab, :BTab
- tmux.until { |lines| assert_equal ' 19/100 (3)', lines[-2] }
- tmux.send_keys :Enter
- assert_equal %w[5 5 50 51], readonce.lines(chomp: true)
- end
- end
-
- def test_query_unicode
- tmux.paste "(echo abc; echo $'\\352\\260\\200\\353\\202\\230\\353\\213\\244') | #{fzf(:query, "$'\\352\\260\\200\\353\\213\\244'")}"
- tmux.until { |lines| assert_equal ' 1/2', lines[-2] }
- tmux.send_keys :Enter
- assert_equal %w[가나다], readonce.lines(chomp: true)
- end
-
- def test_sync
- tmux.send_keys "seq 1 100 | #{fzf!(:multi)} | awk '{print $1 $1}' | #{fzf(:sync)}", :Enter
- tmux.until { |lines| assert_equal '>', lines[-1] }
- tmux.send_keys 9
- tmux.until { |lines| assert_equal ' 19/100 (0)', lines[-2] }
- tmux.send_keys :BTab, :BTab, :BTab
- tmux.until { |lines| assert_equal ' 19/100 (3)', lines[-2] }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal '>', lines[-1] }
- tmux.send_keys 'C-K', :Enter
- assert_equal %w[9090], readonce.lines(chomp: true)
- end
-
- def test_tac
- tmux.send_keys "seq 1 1000 | #{fzf(:tac, :multi)}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
- tmux.send_keys :BTab, :BTab, :BTab
- tmux.until { |lines| assert_equal ' 1000/1000 (3)', lines[-2] }
- tmux.send_keys :Enter
- assert_equal %w[1000 999 998], readonce.lines(chomp: true)
- end
-
- def test_tac_sort
- tmux.send_keys "seq 1 1000 | #{fzf(:tac, :multi)}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
- tmux.send_keys '99'
- tmux.until { |lines| assert_equal ' 28/1000 (0)', lines[-2] }
- tmux.send_keys :BTab, :BTab, :BTab
- tmux.until { |lines| assert_equal ' 28/1000 (3)', lines[-2] }
- tmux.send_keys :Enter
- assert_equal %w[99 999 998], readonce.lines(chomp: true)
- end
-
- def test_tac_nosort
- tmux.send_keys "seq 1 1000 | #{fzf(:tac, :no_sort, :multi)}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
- tmux.send_keys '00'
- tmux.until { |lines| assert_equal ' 10/1000 (0)', lines[-2] }
- tmux.send_keys :BTab, :BTab, :BTab
- tmux.until { |lines| assert_equal ' 10/1000 (3)', lines[-2] }
- tmux.send_keys :Enter
- assert_equal %w[1000 900 800], readonce.lines(chomp: true)
- end
-
- def test_expect
- test = lambda do |key, feed, expected = key|
- tmux.send_keys "seq 1 100 | #{fzf(:expect, key)}", :Enter
- tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
- tmux.send_keys '55'
- tmux.until { |lines| assert_equal ' 1/100', lines[-2] }
- tmux.send_keys(*feed)
- tmux.prepare
- assert_equal [expected, '55'], readonce.lines(chomp: true)
- end
- test.call('ctrl-t', 'C-T')
- test.call('ctrl-t', 'Enter', '')
- test.call('alt-c', %i[Escape c])
- test.call('f1', 'f1')
- test.call('f2', 'f2')
- test.call('f3', 'f3')
- test.call('f2,f4', 'f2', 'f2')
- test.call('f2,f4', 'f4', 'f4')
- test.call('alt-/', %i[Escape /])
- %w[f5 f6 f7 f8 f9 f10].each do |key|
- test.call('f5,f6,f7,f8,f9,f10', key, key)
- end
- test.call('@', '@')
- end
-
- def test_expect_print_query
- tmux.send_keys "seq 1 100 | #{fzf('--expect=alt-z', :print_query)}", :Enter
- tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
- tmux.send_keys '55'
- tmux.until { |lines| assert_equal ' 1/100', lines[-2] }
- tmux.send_keys :Escape, :z
- assert_equal %w[55 alt-z 55], readonce.lines(chomp: true)
- end
-
- def test_expect_printable_character_print_query
- tmux.send_keys "seq 1 100 | #{fzf('--expect=z --print-query')}", :Enter
- tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
- tmux.send_keys '55'
- tmux.until { |lines| assert_equal ' 1/100', lines[-2] }
- tmux.send_keys 'z'
- assert_equal %w[55 z 55], readonce.lines(chomp: true)
- end
-
- def test_expect_print_query_select_1
- tmux.send_keys "seq 1 100 | #{fzf('-q55 -1 --expect=alt-z --print-query')}", :Enter
- assert_equal ['55', '', '55'], readonce.lines(chomp: true)
- end
-
- def test_toggle_sort
- ['--toggle-sort=ctrl-r', '--bind=ctrl-r:toggle-sort'].each do |opt|
- tmux.send_keys "seq 1 111 | #{fzf("-m +s --tac #{opt} -q11")}", :Enter
- tmux.until { |lines| assert_equal '> 111', lines[-3] }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal ' 4/111 -S (1)', lines[-2] }
- tmux.send_keys 'C-R'
- tmux.until { |lines| assert_equal '> 11', lines[-3] }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal ' 4/111 +S (2)', lines[-2] }
- tmux.send_keys :Enter
- assert_equal %w[111 11], readonce.lines(chomp: true)
- end
- end
-
- def test_unicode_case
- writelines(tempname, %w[строКА1 СТРОКА2 строка3 Строка4])
- assert_equal %w[СТРОКА2 Строка4], `#{FZF} -fС < #{tempname}`.lines(chomp: true)
- assert_equal %w[строКА1 СТРОКА2 строка3 Строка4], `#{FZF} -fс < #{tempname}`.lines(chomp: true)
- end
-
- def test_tiebreak
- input = %w[
- --foobar--------
- -----foobar---
- ----foobar--
- -------foobar-
- ]
- writelines(tempname, input)
-
- assert_equal input, `#{FZF} -ffoobar --tiebreak=index < #{tempname}`.lines(chomp: true)
-
- by_length = %w[
- ----foobar--
- -----foobar---
- -------foobar-
- --foobar--------
- ]
- assert_equal by_length, `#{FZF} -ffoobar < #{tempname}`.lines(chomp: true)
- assert_equal by_length, `#{FZF} -ffoobar --tiebreak=length < #{tempname}`.lines(chomp: true)
-
- by_begin = %w[
- --foobar--------
- ----foobar--
- -----foobar---
- -------foobar-
- ]
- assert_equal by_begin, `#{FZF} -ffoobar --tiebreak=begin < #{tempname}`.lines(chomp: true)
- assert_equal by_begin, `#{FZF} -f"!z foobar" -x --tiebreak begin < #{tempname}`.lines(chomp: true)
-
- assert_equal %w[
- -------foobar-
- ----foobar--
- -----foobar---
- --foobar--------
- ], `#{FZF} -ffoobar --tiebreak end < #{tempname}`.lines(chomp: true)
-
- assert_equal input, `#{FZF} -f"!z" -x --tiebreak end < #{tempname}`.lines(chomp: true)
- end
-
- def test_tiebreak_index_begin
- writelines(tempname, [
- 'xoxxxxxoxx',
- 'xoxxxxxox',
- 'xxoxxxoxx',
- 'xxxoxoxxx',
- 'xxxxoxox',
- ' xxoxoxxx'
- ])
-
- assert_equal [
- 'xxxxoxox',
- ' xxoxoxxx',
- 'xxxoxoxxx',
- 'xxoxxxoxx',
- 'xoxxxxxox',
- 'xoxxxxxoxx'
- ], `#{FZF} -foo < #{tempname}`.lines(chomp: true)
-
- assert_equal [
- 'xxxoxoxxx',
- 'xxxxoxox',
- ' xxoxoxxx',
- 'xxoxxxoxx',
- 'xoxxxxxoxx',
- 'xoxxxxxox'
- ], `#{FZF} -foo --tiebreak=index < #{tempname}`.lines(chomp: true)
-
- # Note that --tiebreak=begin is now based on the first occurrence of the
- # first character on the pattern
- assert_equal [
- ' xxoxoxxx',
- 'xxxoxoxxx',
- 'xxxxoxox',
- 'xxoxxxoxx',
- 'xoxxxxxoxx',
- 'xoxxxxxox'
- ], `#{FZF} -foo --tiebreak=begin < #{tempname}`.lines(chomp: true)
-
- assert_equal [
- ' xxoxoxxx',
- 'xxxoxoxxx',
- 'xxxxoxox',
- 'xxoxxxoxx',
- 'xoxxxxxox',
- 'xoxxxxxoxx'
- ], `#{FZF} -foo --tiebreak=begin,length < #{tempname}`.lines(chomp: true)
- end
-
- def test_tiebreak_begin_algo_v2
- writelines(tempname, [
- 'baz foo bar',
- 'foo bar baz'
- ])
- assert_equal [
- 'foo bar baz',
- 'baz foo bar'
- ], `#{FZF} -fbar --tiebreak=begin --algo=v2 < #{tempname}`.lines(chomp: true)
- end
-
- def test_tiebreak_end
- writelines(tempname, [
- 'xoxxxxxxxx',
- 'xxoxxxxxxx',
- 'xxxoxxxxxx',
- 'xxxxoxxxx',
- 'xxxxxoxxx',
- ' xxxxoxxx'
- ])
-
- assert_equal [
- ' xxxxoxxx',
- 'xxxxoxxxx',
- 'xxxxxoxxx',
- 'xoxxxxxxxx',
- 'xxoxxxxxxx',
- 'xxxoxxxxxx'
- ], `#{FZF} -fo < #{tempname}`.lines(chomp: true)
-
- assert_equal [
- 'xxxxxoxxx',
- ' xxxxoxxx',
- 'xxxxoxxxx',
- 'xxxoxxxxxx',
- 'xxoxxxxxxx',
- 'xoxxxxxxxx'
- ], `#{FZF} -fo --tiebreak=end < #{tempname}`.lines(chomp: true)
-
- assert_equal [
- 'xxxxxoxxx',
- ' xxxxoxxx',
- 'xxxxoxxxx',
- 'xxxoxxxxxx',
- 'xxoxxxxxxx',
- 'xoxxxxxxxx'
- ], `#{FZF} -fo --tiebreak=end,length,begin < #{tempname}`.lines(chomp: true)
- end
-
- def test_tiebreak_length_with_nth
- input = %w[
- 1:hell
- 123:hello
- 12345:he
- 1234567:h
- ]
- writelines(tempname, input)
-
- output = %w[
- 1:hell
- 12345:he
- 123:hello
- 1234567:h
- ]
- assert_equal output, `#{FZF} -fh < #{tempname}`.lines(chomp: true)
-
- # Since 0.16.8, --nth doesn't affect --tiebreak
- assert_equal output, `#{FZF} -fh -n2 -d: < #{tempname}`.lines(chomp: true)
- end
-
- def test_invalid_cache
- tmux.send_keys "(echo d; echo D; echo x) | #{fzf('-q d')}", :Enter
- tmux.until { |lines| assert_equal ' 2/3', lines[-2] }
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal ' 3/3', lines[-2] }
- tmux.send_keys :D
- tmux.until { |lines| assert_equal ' 1/3', lines[-2] }
- tmux.send_keys :Enter
- end
-
- def test_invalid_cache_query_type
- command = %[(echo 'foo$bar'; echo 'barfoo'; echo 'foo^bar'; echo "foo'1-2"; seq 100) | #{fzf}]
-
- # Suffix match
- tmux.send_keys command, :Enter
- tmux.until { |lines| assert_equal 104, lines.match_count }
- tmux.send_keys 'foo$'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys 'bar'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Enter
-
- # Prefix match
- tmux.prepare
- tmux.send_keys command, :Enter
- tmux.until { |lines| assert_equal 104, lines.match_count }
- tmux.send_keys '^bar'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys 'C-a', 'foo'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Enter
-
- # Exact match
- tmux.prepare
- tmux.send_keys command, :Enter
- tmux.until { |lines| assert_equal 104, lines.match_count }
- tmux.send_keys "'12"
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys 'C-a', 'foo'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- end
-
- def test_smart_case_for_each_term
- assert_equal 1, `echo Foo bar | #{FZF} -x -f "foo Fbar" | wc -l`.to_i
- end
-
- def test_bind
- tmux.send_keys "seq 1 1000 | #{fzf('-m --bind=ctrl-j:accept,u:up,T:toggle-up,t:toggle')}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
- tmux.send_keys 'uuu', 'TTT', 'tt', 'uu', 'ttt', 'C-j'
- assert_equal %w[4 5 6 9], readonce.lines(chomp: true)
- end
-
- def test_bind_print_query
- tmux.send_keys "seq 1 1000 | #{fzf('-m --bind=ctrl-j:print-query')}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
- tmux.send_keys 'print-my-query', 'C-j'
- assert_equal %w[print-my-query], readonce.lines(chomp: true)
- end
-
- def test_bind_replace_query
- tmux.send_keys "seq 1 1000 | #{fzf('--print-query --bind=ctrl-j:replace-query')}", :Enter
- tmux.send_keys '1'
- tmux.until { |lines| assert_equal ' 272/1000', lines[-2] }
- tmux.send_keys 'C-k', 'C-j'
- tmux.until { |lines| assert_equal ' 29/1000', lines[-2] }
- tmux.until { |lines| assert_equal '> 10', lines[-1] }
- end
-
- def test_long_line
- data = '.' * 256 * 1024
- File.open(tempname, 'w') do |f|
- f << data
- end
- assert_equal data, `#{FZF} -f . < #{tempname}`.chomp
- end
-
- def test_read0
- lines = `find .`.lines(chomp: true)
- assert_equal lines.last, `find . | #{FZF} -e -f "^#{lines.last}$"`.chomp
- assert_equal \
- lines.last,
- `find . -print0 | #{FZF} --read0 -e -f "^#{lines.last}$"`.chomp
- end
-
- def test_select_all_deselect_all_toggle_all
- tmux.send_keys "seq 100 | #{fzf('--bind ctrl-a:select-all,ctrl-d:deselect-all,ctrl-t:toggle-all --multi')}", :Enter
- tmux.until { |lines| assert_equal ' 100/100 (0)', lines[-2] }
- tmux.send_keys :BTab, :BTab, :BTab
- tmux.until { |lines| assert_equal ' 100/100 (3)', lines[-2] }
- tmux.send_keys 'C-t'
- tmux.until { |lines| assert_equal ' 100/100 (97)', lines[-2] }
- tmux.send_keys 'C-a'
- tmux.until { |lines| assert_equal ' 100/100 (100)', lines[-2] }
- tmux.send_keys :Tab, :Tab
- tmux.until { |lines| assert_equal ' 100/100 (98)', lines[-2] }
- tmux.send_keys '100'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys 'C-d'
- tmux.until { |lines| assert_equal ' 1/100 (97)', lines[-2] }
- tmux.send_keys 'C-u'
- tmux.until { |lines| assert_equal 100, lines.match_count }
- tmux.send_keys 'C-d'
- tmux.until { |lines| assert_equal ' 100/100 (0)', lines[-2] }
- tmux.send_keys :BTab, :BTab
- tmux.until { |lines| assert_equal ' 100/100 (2)', lines[-2] }
- tmux.send_keys 0
- tmux.until { |lines| assert_equal ' 10/100 (2)', lines[-2] }
- tmux.send_keys 'C-a'
- tmux.until { |lines| assert_equal ' 10/100 (12)', lines[-2] }
- tmux.send_keys :Enter
- assert_equal %w[1 2 10 20 30 40 50 60 70 80 90 100],
- readonce.lines(chomp: true)
- end
-
- def test_history
- history_file = '/tmp/fzf-test-history'
-
- # History with limited number of entries
- begin
- File.unlink(history_file)
- rescue StandardError
- nil
- end
- opts = "--history=#{history_file} --history-size=4"
- input = %w[00 11 22 33 44]
- input.each do |keys|
- tmux.prepare
- tmux.send_keys "seq 100 | #{fzf(opts)}", :Enter
- tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
- tmux.send_keys keys
- tmux.until { |lines| assert_equal ' 1/100', lines[-2] }
- tmux.send_keys :Enter
- end
- wait do
- assert_path_exists history_file
- assert_equal input[1..-1], File.readlines(history_file, chomp: true)
- end
-
- # Update history entries (not changed on disk)
- tmux.send_keys "seq 100 | #{fzf(opts)}", :Enter
- tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
- tmux.send_keys 'C-p'
- tmux.until { |lines| assert_equal '> 44', lines[-1] }
- tmux.send_keys 'C-p'
- tmux.until { |lines| assert_equal '> 33', lines[-1] }
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal '> 3', lines[-1] }
- tmux.send_keys 1
- tmux.until { |lines| assert_equal '> 31', lines[-1] }
- tmux.send_keys 'C-p'
- tmux.until { |lines| assert_equal '> 22', lines[-1] }
- tmux.send_keys 'C-n'
- tmux.until { |lines| assert_equal '> 31', lines[-1] }
- tmux.send_keys 0
- tmux.until { |lines| assert_equal '> 310', lines[-1] }
- tmux.send_keys :Enter
- wait do
- assert_path_exists history_file
- assert_equal %w[22 33 44 310], File.readlines(history_file, chomp: true)
- end
-
- # Respect --bind option
- tmux.send_keys "seq 100 | #{fzf(opts + ' --bind ctrl-p:next-history,ctrl-n:previous-history')}", :Enter
- tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
- tmux.send_keys 'C-n', 'C-n', 'C-n', 'C-n', 'C-p'
- tmux.until { |lines| assert_equal '> 33', lines[-1] }
- tmux.send_keys :Enter
- ensure
- File.unlink(history_file)
- end
-
- def test_execute
- output = '/tmp/fzf-test-execute'
- opts = %[--bind "alt-a:execute(echo /{}/ >> #{output}),alt-b:execute[echo /{}{}/ >> #{output}],C:execute:echo /{}{}{}/ >> #{output}"]
- writelines(tempname, %w[foo'bar foo"bar foo$bar])
- tmux.send_keys "cat #{tempname} | #{fzf(opts)}", :Enter
- tmux.until { |lines| assert_equal ' 3/3', lines[-2] }
- tmux.send_keys :Escape, :a
- tmux.send_keys :Escape, :a
- tmux.send_keys :Up
- tmux.send_keys :Escape, :b
- tmux.send_keys :Escape, :b
- tmux.send_keys :Up
- tmux.send_keys :C
- tmux.send_keys 'barfoo'
- tmux.until { |lines| assert_equal ' 0/3', lines[-2] }
- tmux.send_keys :Escape, :a
- tmux.send_keys :Escape, :b
- wait do
- assert_path_exists output
- assert_equal %w[
- /foo'bar/ /foo'bar/
- /foo"barfoo"bar/ /foo"barfoo"bar/
- /foo$barfoo$barfoo$bar/
- ], File.readlines(output, chomp: true)
- end
- ensure
- begin
- File.unlink(output)
- rescue StandardError
- nil
- end
- end
-
- def test_execute_multi
- output = '/tmp/fzf-test-execute-multi'
- opts = %[--multi --bind "alt-a:execute-multi(echo {}/{+} >> #{output})"]
- writelines(tempname, %w[foo'bar foo"bar foo$bar foobar])
- tmux.send_keys "cat #{tempname} | #{fzf(opts)}", :Enter
- tmux.until { |lines| assert_equal ' 4/4 (0)', lines[-2] }
- tmux.send_keys :Escape, :a
- tmux.send_keys :BTab, :BTab, :BTab
- tmux.until { |lines| assert_equal ' 4/4 (3)', lines[-2] }
- tmux.send_keys :Escape, :a
- tmux.send_keys :Tab, :Tab
- tmux.until { |lines| assert_equal ' 4/4 (3)', lines[-2] }
- tmux.send_keys :Escape, :a
- wait do
- assert_path_exists output
- assert_equal [
- %(foo'bar/foo'bar),
- %(foo'bar foo"bar foo$bar/foo'bar foo"bar foo$bar),
- %(foo'bar foo"bar foobar/foo'bar foo"bar foobar)
- ], File.readlines(output, chomp: true)
- end
- ensure
- begin
- File.unlink(output)
- rescue StandardError
- nil
- end
- end
-
- def test_execute_plus_flag
- output = tempname + '.tmp'
- begin
- File.unlink(output)
- rescue StandardError
- nil
- end
- writelines(tempname, ['foo bar', '123 456'])
-
- tmux.send_keys "cat #{tempname} | #{FZF} --multi --bind 'x:execute-silent(echo {+}/{}/{+2}/{2} >> #{output})'", :Enter
-
- tmux.until { |lines| assert_equal ' 2/2 (0)', lines[-2] }
- tmux.send_keys 'xy'
- tmux.until { |lines| assert_equal ' 0/2 (0)', lines[-2] }
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal ' 2/2 (0)', lines[-2] }
-
- tmux.send_keys :Up
- tmux.send_keys :Tab
- tmux.send_keys 'xy'
- tmux.until { |lines| assert_equal ' 0/2 (1)', lines[-2] }
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal ' 2/2 (1)', lines[-2] }
-
- tmux.send_keys :Tab
- tmux.send_keys 'xy'
- tmux.until { |lines| assert_equal ' 0/2 (2)', lines[-2] }
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal ' 2/2 (2)', lines[-2] }
-
- wait do
- assert_path_exists output
- assert_equal [
- %(foo bar/foo bar/bar/bar),
- %(123 456/foo bar/456/bar),
- %(123 456 foo bar/foo bar/456 bar/bar)
- ], File.readlines(output, chomp: true)
- end
- rescue StandardError
- begin
- File.unlink(output)
- rescue StandardError
- nil
- end
- end
-
- def test_execute_shell
- # Custom script to use as $SHELL
- output = tempname + '.out'
- begin
- File.unlink(output)
- rescue StandardError
- nil
- end
- writelines(tempname,
- ['#!/usr/bin/env bash', "echo $1 / $2 > #{output}"])
- system("chmod +x #{tempname}")
-
- tmux.send_keys "echo foo | SHELL=#{tempname} fzf --bind 'enter:execute:{}bar'", :Enter
- tmux.until { |lines| assert_equal ' 1/1', lines[-2] }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal ' 1/1', lines[-2] }
- wait do
- assert_path_exists output
- assert_equal ["-c / 'foo'bar"], File.readlines(output, chomp: true)
- end
- ensure
- begin
- File.unlink(output)
- rescue StandardError
- nil
- end
- end
-
- def test_cycle
- tmux.send_keys "seq 8 | #{fzf(:cycle)}", :Enter
- tmux.until { |lines| assert_equal ' 8/8', lines[-2] }
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal '> 8', lines[-10] }
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal '> 7', lines[-9] }
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal '> 8', lines[-10] }
- tmux.send_keys :PgUp
- tmux.until { |lines| assert_equal '> 8', lines[-10] }
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal '> 1', lines[-3] }
- tmux.send_keys :PgDn
- tmux.until { |lines| assert_equal '> 1', lines[-3] }
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal '> 8', lines[-10] }
- end
-
- def test_header_lines
- tmux.send_keys "seq 100 | #{fzf('--header-lines=10 -q 5')}", :Enter
- 2.times do
- tmux.until do |lines|
- assert_equal ' 18/90', lines[-2]
- assert_equal ' 1', lines[-3]
- assert_equal ' 2', lines[-4]
- assert_equal '> 50', lines[-13]
- end
- tmux.send_keys :Down
- end
- tmux.send_keys :Enter
- assert_equal '50', readonce.chomp
- end
-
- def test_header_lines_reverse
- tmux.send_keys "seq 100 | #{fzf('--header-lines=10 -q 5 --reverse')}", :Enter
- 2.times do
- tmux.until do |lines|
- assert_equal ' 18/90', lines[1]
- assert_equal ' 1', lines[2]
- assert_equal ' 2', lines[3]
- assert_equal '> 50', lines[12]
- end
- tmux.send_keys :Up
- end
- tmux.send_keys :Enter
- assert_equal '50', readonce.chomp
- end
-
- def test_header_lines_reverse_list
- tmux.send_keys "seq 100 | #{fzf('--header-lines=10 -q 5 --layout=reverse-list')}", :Enter
- 2.times do
- tmux.until do |lines|
- assert_equal '> 50', lines[0]
- assert_equal ' 2', lines[-4]
- assert_equal ' 1', lines[-3]
- assert_equal ' 18/90', lines[-2]
- end
- tmux.send_keys :Up
- end
- tmux.send_keys :Enter
- assert_equal '50', readonce.chomp
- end
-
- def test_header_lines_overflow
- tmux.send_keys "seq 100 | #{fzf('--header-lines=200')}", :Enter
- tmux.until do |lines|
- assert_equal ' 0/0', lines[-2]
- assert_equal ' 1', lines[-3]
- end
- tmux.send_keys :Enter
- assert_equal '', readonce.chomp
- end
-
- def test_header_lines_with_nth
- tmux.send_keys "seq 100 | #{fzf('--header-lines 5 --with-nth 1,1,1,1,1')}", :Enter
- tmux.until do |lines|
- assert_equal ' 95/95', lines[-2]
- assert_equal ' 11111', lines[-3]
- assert_equal ' 55555', lines[-7]
- assert_equal '> 66666', lines[-8]
- end
- tmux.send_keys :Enter
- assert_equal '6', readonce.chomp
- end
-
- def test_header
- tmux.send_keys "seq 100 | #{fzf("--header \"$(head -5 #{FILE})\"")}", :Enter
- header = File.readlines(FILE, chomp: true).take(5)
- tmux.until do |lines|
- assert_equal ' 100/100', lines[-2]
- assert_equal header.map { |line| " #{line}".rstrip }, lines[-7..-3]
- assert_equal '> 1', lines[-8]
- end
- end
-
- def test_header_reverse
- tmux.send_keys "seq 100 | #{fzf("--header \"$(head -5 #{FILE})\" --reverse")}", :Enter
- header = File.readlines(FILE, chomp: true).take(5)
- tmux.until do |lines|
- assert_equal ' 100/100', lines[1]
- assert_equal header.map { |line| " #{line}".rstrip }, lines[2..6]
- assert_equal '> 1', lines[7]
- end
- end
-
- def test_header_reverse_list
- tmux.send_keys "seq 100 | #{fzf("--header \"$(head -5 #{FILE})\" --layout=reverse-list")}", :Enter
- header = File.readlines(FILE, chomp: true).take(5)
- tmux.until do |lines|
- assert_equal ' 100/100', lines[-2]
- assert_equal header.map { |line| " #{line}".rstrip }, lines[-7..-3]
- assert_equal '> 1', lines[0]
- end
- end
-
- def test_header_and_header_lines
- tmux.send_keys "seq 100 | #{fzf("--header-lines 10 --header \"$(head -5 #{FILE})\"")}", :Enter
- header = File.readlines(FILE, chomp: true).take(5)
- tmux.until do |lines|
- assert_equal ' 90/90', lines[-2]
- assert_equal header.map { |line| " #{line}".rstrip }, lines[-7...-2]
- assert_equal (' 1'..' 10').to_a.reverse, lines[-17...-7]
- end
- end
-
- def test_header_and_header_lines_reverse
- tmux.send_keys "seq 100 | #{fzf("--reverse --header-lines 10 --header \"$(head -5 #{FILE})\"")}", :Enter
- header = File.readlines(FILE, chomp: true).take(5)
- tmux.until do |lines|
- assert_equal ' 90/90', lines[1]
- assert_equal header.map { |line| " #{line}".rstrip }, lines[2...7]
- assert_equal (' 1'..' 10').to_a, lines[7...17]
- end
- end
-
- def test_header_and_header_lines_reverse_list
- tmux.send_keys "seq 100 | #{fzf("--layout=reverse-list --header-lines 10 --header \"$(head -5 #{FILE})\"")}", :Enter
- header = File.readlines(FILE, chomp: true).take(5)
- tmux.until do |lines|
- assert_equal ' 90/90', lines[-2]
- assert_equal header.map { |line| " #{line}".rstrip }, lines[-7...-2]
- assert_equal (' 1'..' 10').to_a.reverse, lines[-17...-7]
- end
- end
-
- def test_cancel
- tmux.send_keys "seq 10 | #{fzf('--bind 2:cancel')}", :Enter
- tmux.until { |lines| assert_equal ' 10/10', lines[-2] }
- tmux.send_keys '123'
- tmux.until do |lines|
- assert_equal '> 3', lines[-1]
- assert_equal ' 1/10', lines[-2]
- end
- tmux.send_keys 'C-y', 'C-y'
- tmux.until { |lines| assert_equal '> 311', lines[-1] }
- tmux.send_keys 2
- tmux.until { |lines| assert_equal '>', lines[-1] }
- tmux.send_keys 2
- tmux.prepare
- end
-
- def test_margin
- tmux.send_keys "yes | head -1000 | #{fzf('--margin 5,3')}", :Enter
- tmux.until do |lines|
- assert_equal '', lines[4]
- assert_equal ' y', lines[5]
- end
- tmux.send_keys :Enter
- end
-
- def test_margin_reverse
- tmux.send_keys "seq 1000 | #{fzf('--margin 7,5 --reverse')}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000', lines[1 + 7] }
- tmux.send_keys :Enter
- end
-
- def test_margin_reverse_list
- tmux.send_keys "yes | head -1000 | #{fzf('--margin 5,3 --layout=reverse-list')}", :Enter
- tmux.until do |lines|
- assert_equal '', lines[4]
- assert_equal ' > y', lines[5]
- end
- tmux.send_keys :Enter
- end
-
- def test_tabstop
- writelines(tempname, %W[f\too\tba\tr\tbaz\tbarfooq\tux])
- {
- 1 => '> f oo ba r baz barfooq ux',
- 2 => '> f oo ba r baz barfooq ux',
- 3 => '> f oo ba r baz barfooq ux',
- 4 => '> f oo ba r baz barfooq ux',
- 5 => '> f oo ba r baz barfooq ux',
- 6 => '> f oo ba r baz barfooq ux',
- 7 => '> f oo ba r baz barfooq ux',
- 8 => '> f oo ba r baz barfooq ux',
- 9 => '> f oo ba r baz barfooq ux'
- }.each do |ts, exp|
- tmux.prepare
- tmux.send_keys %(cat #{tempname} | fzf --tabstop=#{ts}), :Enter
- tmux.until(true) do |lines|
- assert_equal exp, lines[-3]
- end
- tmux.send_keys :Enter
- end
- end
-
- def test_with_nth_basic
- writelines(tempname, ['hello world ', 'byebye'])
- assert_equal \
- 'hello world ',
- `#{FZF} -f"^he hehe" -x -n 2.. --with-nth 2,1,1 < #{tempname}`.chomp
- end
-
- def test_with_nth_ansi
- writelines(tempname, ["\x1b[33mhello \x1b[34;1mworld\x1b[m ", 'byebye'])
- assert_equal \
- 'hello world ',
- `#{FZF} -f"^he hehe" -x -n 2.. --with-nth 2,1,1 --ansi < #{tempname}`.chomp
- end
-
- def test_with_nth_no_ansi
- src = "\x1b[33mhello \x1b[34;1mworld\x1b[m "
- writelines(tempname, [src, 'byebye'])
- assert_equal \
- src,
- `#{FZF} -fhehe -x -n 2.. --with-nth 2,1,1 --no-ansi < #{tempname}`.chomp
- end
-
- def test_exit_0_exit_code
- `echo foo | #{FZF} -q bar -0`
- assert_equal 1, $CHILD_STATUS.exitstatus
- end
-
- def test_invalid_option
- lines = `#{FZF} --foobar 2>&1`
- assert_equal 2, $CHILD_STATUS.exitstatus
- assert_includes lines, 'unknown option: --foobar'
- end
-
- def test_filter_exitstatus
- # filter / streaming filter
- ['', '--no-sort'].each do |opts|
- assert_includes `echo foo | #{FZF} -f foo #{opts}`, 'foo'
- assert_equal 0, $CHILD_STATUS.exitstatus
-
- assert_empty `echo foo | #{FZF} -f bar #{opts}`
- assert_equal 1, $CHILD_STATUS.exitstatus
- end
- end
-
- def test_exitstatus_empty
- { '99' => '0', '999' => '1' }.each do |query, status|
- tmux.send_keys "seq 100 | #{FZF} -q #{query}; echo --$?--", :Enter
- tmux.until { |lines| assert_match %r{ [10]/100}, lines[-2] }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal "--#{status}--", lines.last }
- end
- end
-
- def test_default_extended
- assert_equal '100', `seq 100 | #{FZF} -f "1 00$"`.chomp
- assert_equal '', `seq 100 | #{FZF} -f "1 00$" +x`.chomp
- end
-
- def test_exact
- assert_equal 4, `seq 123 | #{FZF} -f 13`.lines.length
- assert_equal 2, `seq 123 | #{FZF} -f 13 -e`.lines.length
- assert_equal 4, `seq 123 | #{FZF} -f 13 +e`.lines.length
- end
-
- def test_or_operator
- assert_equal %w[1 5 10], `seq 10 | #{FZF} -f "1 | 5"`.lines(chomp: true)
- assert_equal %w[1 10 2 3 4 5 6 7 8 9],
- `seq 10 | #{FZF} -f '1 | !1'`.lines(chomp: true)
- end
-
- def test_hscroll_off
- writelines(tempname, ['=' * 10_000 + '0123456789'])
- [0, 3, 6].each do |off|
- tmux.prepare
- tmux.send_keys "#{FZF} --hscroll-off=#{off} -q 0 < #{tempname}", :Enter
- tmux.until { |lines| assert lines[-3]&.end_with?((0..off).to_a.join + '..') }
- tmux.send_keys '9'
- tmux.until { |lines| assert lines[-3]&.end_with?('789') }
- tmux.send_keys :Enter
- end
- end
-
- def test_partial_caching
- tmux.send_keys 'seq 1000 | fzf -e', :Enter
- tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
- tmux.send_keys 11
- tmux.until { |lines| assert_equal ' 19/1000', lines[-2] }
- tmux.send_keys 'C-a', "'"
- tmux.until { |lines| assert_equal ' 28/1000', lines[-2] }
- tmux.send_keys :Enter
- end
-
- def test_jump
- tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump'")}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
- tmux.send_keys 'C-j'
- tmux.until { |lines| assert_equal '5 5', lines[-7] }
- tmux.until { |lines| assert_equal ' 6', lines[-8] }
- tmux.send_keys '5'
- tmux.until { |lines| assert_equal '> 5', lines[-7] }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal ' >5', lines[-7] }
- tmux.send_keys 'C-j'
- tmux.until { |lines| assert_equal '5>5', lines[-7] }
- tmux.send_keys '2'
- tmux.until { |lines| assert_equal '> 2', lines[-4] }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal ' >2', lines[-4] }
- tmux.send_keys 'C-j'
- tmux.until { |lines| assert_equal '5>5', lines[-7] }
-
- # Press any key other than jump labels to cancel jump
- tmux.send_keys '6'
- tmux.until { |lines| assert_equal '> 1', lines[-3] }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal '>>1', lines[-3] }
- tmux.send_keys :Enter
- assert_equal %w[5 2 1], readonce.lines(chomp: true)
- end
-
- def test_jump_accept
- tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump-accept'")}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
- tmux.send_keys 'C-j'
- tmux.until { |lines| assert_equal '5 5', lines[-7] }
- tmux.send_keys '3'
- assert_equal '3', readonce.chomp
- end
-
- def test_pointer
- tmux.send_keys "seq 10 | #{fzf("--pointer '>>'")}", :Enter
- # Assert that specified pointer is displayed
- tmux.until { |lines| assert_equal '>> 1', lines[-3] }
- end
-
- def test_pointer_with_jump
- tmux.send_keys "seq 10 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump' --pointer '>>'")}", :Enter
- tmux.until { |lines| assert_equal ' 10/10 (0)', lines[-2] }
- tmux.send_keys 'C-j'
- # Correctly padded jump label should appear
- tmux.until { |lines| assert_equal '5 5', lines[-7] }
- tmux.until { |lines| assert_equal ' 6', lines[-8] }
- tmux.send_keys '5'
- # Assert that specified pointer is displayed
- tmux.until { |lines| assert_equal '>> 5', lines[-7] }
- end
-
- def test_marker
- tmux.send_keys "seq 10 | #{fzf("--multi --marker '>>'")}", :Enter
- tmux.until { |lines| assert_equal ' 10/10 (0)', lines[-2] }
- tmux.send_keys :BTab
- # Assert that specified marker is displayed
- tmux.until { |lines| assert_equal ' >>1', lines[-3] }
- end
-
- def test_preview
- tmux.send_keys %(seq 1000 | sed s/^2$// | #{FZF} -m --preview 'sleep 0.2; echo {{}-{+}}' --bind ?:toggle-preview), :Enter
- tmux.until { |lines| assert_includes lines[1], ' {1-1} ' }
- tmux.send_keys :Up
- tmux.until { |lines| assert_includes lines[1], ' {-} ' }
- tmux.send_keys '555'
- tmux.until { |lines| assert_includes lines[1], ' {555-555} ' }
- tmux.send_keys '?'
- tmux.until { |lines| refute_includes lines[1], ' {555-555} ' }
- tmux.send_keys '?'
- tmux.until { |lines| assert_includes lines[1], ' {555-555} ' }
- tmux.send_keys :BSpace
- tmux.until { |lines| assert lines[-2]&.start_with?(' 28/1000 ') }
- tmux.send_keys 'foobar'
- tmux.until { |lines| refute_includes lines[1], ' {55-55} ' }
- tmux.send_keys 'C-u'
- tmux.until { |lines| assert_equal 1000, lines.match_count }
- tmux.until { |lines| assert_includes lines[1], ' {1-1} ' }
- tmux.send_keys :BTab
- tmux.until { |lines| assert_includes lines[1], ' {-1} ' }
- tmux.send_keys :BTab
- tmux.until { |lines| assert_includes lines[1], ' {3-1 } ' }
- tmux.send_keys :BTab
- tmux.until { |lines| assert_includes lines[1], ' {4-1 3} ' }
- tmux.send_keys :BTab
- tmux.until { |lines| assert_includes lines[1], ' {5-1 3 4} ' }
- end
-
- def test_preview_hidden
- tmux.send_keys %(seq 1000 | #{FZF} --preview 'echo {{}-{}-$FZF_PREVIEW_LINES-$FZF_PREVIEW_COLUMNS}' --preview-window down:1:hidden --bind ?:toggle-preview), :Enter
- tmux.until { |lines| assert_equal '>', lines[-1] }
- tmux.send_keys '?'
- tmux.until { |lines| assert_match(/ {1-1-1-[0-9]+}/, lines[-2]) }
- tmux.send_keys '555'
- tmux.until { |lines| assert_match(/ {555-555-1-[0-9]+}/, lines[-2]) }
- tmux.send_keys '?'
- tmux.until { |lines| assert_equal '> 555', lines[-1] }
- end
-
- def test_preview_size_0
- begin
- File.unlink(tempname)
- rescue StandardError
- nil
- end
- tmux.send_keys %(seq 100 | #{FZF} --reverse --preview 'echo {} >> #{tempname}; echo ' --preview-window 0), :Enter
- tmux.until do |lines|
- assert_equal 100, lines.item_count
- assert_equal ' 100/100', lines[1]
- assert_equal '> 1', lines[2]
- end
- wait do
- assert_path_exists tempname
- assert_equal %w[1], File.readlines(tempname, chomp: true)
- end
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal '> 2', lines[3] }
- wait do
- assert_path_exists tempname
- assert_equal %w[1 2], File.readlines(tempname, chomp: true)
- end
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal '> 3', lines[4] }
- wait do
- assert_path_exists tempname
- assert_equal %w[1 2 3], File.readlines(tempname, chomp: true)
- end
- end
-
- def test_preview_flags
- tmux.send_keys %(seq 10 | sed 's/^/:: /; s/$/ /' |
- #{FZF} --multi --preview 'echo {{2}/{s2}/{+2}/{+s2}/{q}/{n}/{+n}}'), :Enter
- tmux.until { |lines| assert_includes lines[1], ' {1/1 /1/1 //0/0} ' }
- tmux.send_keys '123'
- tmux.until { |lines| assert_includes lines[1], ' {////123//} ' }
- tmux.send_keys 'C-u', '1'
- tmux.until { |lines| assert_equal 2, lines.match_count }
- tmux.until { |lines| assert_includes lines[1], ' {1/1 /1/1 /1/0/0} ' }
- tmux.send_keys :BTab
- tmux.until { |lines| assert_includes lines[1], ' {10/10 /1/1 /1/9/0} ' }
- tmux.send_keys :BTab
- tmux.until { |lines| assert_includes lines[1], ' {10/10 /1 10/1 10 /1/9/0 9} ' }
- tmux.send_keys '2'
- tmux.until { |lines| assert_includes lines[1], ' {//1 10/1 10 /12//0 9} ' }
- tmux.send_keys '3'
- tmux.until { |lines| assert_includes lines[1], ' {//1 10/1 10 /123//0 9} ' }
- end
-
- def test_preview_file
- tmux.send_keys %[(echo foo bar; echo bar foo) | #{FZF} --multi --preview 'cat {+f} {+f2} {+nf} {+fn}' --print0], :Enter
- tmux.until { |lines| assert_includes lines[1], ' foo barbar00 ' }
- tmux.send_keys :BTab
- tmux.until { |lines| assert_includes lines[1], ' foo barbar00 ' }
- tmux.send_keys :BTab
- tmux.until { |lines| assert_includes lines[1], ' foo barbar foobarfoo0101 ' }
- end
-
- def test_preview_q_no_match
- tmux.send_keys %(: | #{FZF} --preview 'echo foo {q}'), :Enter
- tmux.until { |lines| assert_equal 0, lines.match_count }
- tmux.until { |lines| refute_includes lines[1], ' foo ' }
- tmux.send_keys 'bar'
- tmux.until { |lines| assert_includes lines[1], ' foo bar ' }
- tmux.send_keys 'C-u'
- tmux.until { |lines| refute_includes lines[1], ' foo ' }
- end
-
- def test_preview_q_no_match_with_initial_query
- tmux.send_keys %(: | #{FZF} --preview 'echo foo {q}{q}' --query foo), :Enter
- tmux.until { |lines| assert_equal 0, lines.match_count }
- tmux.until { |lines| assert_includes lines[1], ' foofoo ' }
- end
-
- def test_no_clear
- tmux.send_keys "seq 10 | fzf --no-clear --inline-info --height 5 > #{tempname}", :Enter
- prompt = '> < 10/10'
- tmux.until { |lines| assert_equal prompt, lines[-1] }
- tmux.send_keys :Enter
- wait do
- assert_path_exists tempname
- assert_equal %w[1], File.readlines(tempname, chomp: true)
- end
- tmux.until { |lines| assert_equal prompt, lines[-1] }
- end
-
- def test_info_hidden
- tmux.send_keys 'seq 10 | fzf --info=hidden', :Enter
- tmux.until { |lines| assert_equal '> 1', lines[-2] }
- end
-
- def test_change_first_last
- tmux.send_keys %(seq 1000 | #{FZF} --bind change:first,alt-Z:last), :Enter
- tmux.until { |lines| assert_equal 1000, lines.match_count }
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal '> 2', lines[-4] }
- tmux.send_keys 1
- tmux.until { |lines| assert_equal '> 1', lines[-3] }
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal '> 10', lines[-4] }
- tmux.send_keys 1
- tmux.until { |lines| assert_equal '> 11', lines[-3] }
- tmux.send_keys 'C-u'
- tmux.until { |lines| assert_equal '> 1', lines[-3] }
- tmux.send_keys :Escape, 'Z'
- tmux.until { |lines| assert_equal '> 1000', lines[0] }
- tmux.send_keys :Enter
- end
-
- def test_accept_non_empty
- tmux.send_keys %(seq 1000 | #{fzf('--print-query --bind enter:accept-non-empty')}), :Enter
- tmux.until { |lines| assert_equal 1000, lines.match_count }
- tmux.send_keys 'foo'
- tmux.until { |lines| assert_equal ' 0/1000', lines[-2] }
- # fzf doesn't exit since there's no selection
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal ' 0/1000', lines[-2] }
- tmux.send_keys 'C-u'
- tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
- tmux.send_keys '999'
- tmux.until { |lines| assert_equal ' 1/1000', lines[-2] }
- tmux.send_keys :Enter
- assert_equal %w[999 999], readonce.lines(chomp: true)
- end
-
- def test_accept_non_empty_with_multi_selection
- tmux.send_keys %(seq 1000 | #{fzf('-m --print-query --bind enter:accept-non-empty')}), :Enter
- tmux.until { |lines| assert_equal 1000, lines.match_count }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal ' 1000/1000 (1)', lines[-2] }
- tmux.send_keys 'foo'
- tmux.until { |lines| assert_equal ' 0/1000 (1)', lines[-2] }
- # fzf will exit in this case even though there's no match for the current query
- tmux.send_keys :Enter
- assert_equal %w[foo 1], readonce.lines(chomp: true)
- end
-
- def test_accept_non_empty_with_empty_list
- tmux.send_keys %(: | #{fzf('-q foo --print-query --bind enter:accept-non-empty')}), :Enter
- tmux.until { |lines| assert_equal ' 0/0', lines[-2] }
- tmux.send_keys :Enter
- # fzf will exit anyway since input list is empty
- assert_equal %w[foo], readonce.lines(chomp: true)
- end
-
- def test_preview_update_on_select
- tmux.send_keys %(seq 10 | fzf -m --preview 'echo {+}' --bind a:toggle-all),
- :Enter
- tmux.until { |lines| assert_equal 10, lines.item_count }
- tmux.send_keys 'a'
- tmux.until { |lines| assert(lines.any? { |line| line.include?(' 1 2 3 4 5 ') }) }
- tmux.send_keys 'a'
- tmux.until { |lines| lines.each { |line| refute_includes line, ' 1 2 3 4 5 ' } }
- end
-
- def test_escaped_meta_characters
- input = [
- 'foo^bar',
- 'foo$bar',
- 'foo!bar',
- "foo'bar",
- 'foo bar',
- 'bar foo'
- ]
- writelines(tempname, input)
-
- assert_equal input.length, `#{FZF} -f'foo bar' < #{tempname}`.lines.length
- assert_equal input.length - 1, `#{FZF} -f'^foo bar$' < #{tempname}`.lines.length
- assert_equal ['foo bar'], `#{FZF} -f'foo\\ bar' < #{tempname}`.lines(chomp: true)
- assert_equal ['foo bar'], `#{FZF} -f'^foo\\ bar$' < #{tempname}`.lines(chomp: true)
- assert_equal input.length - 1, `#{FZF} -f'!^foo\\ bar$' < #{tempname}`.lines.length
- end
-
- def test_inverse_only_search_should_not_sort_the_result
- # Filter
- assert_equal %w[aaaaa b ccc],
- `printf '%s\n' aaaaa b ccc BAD | #{FZF} -f '!bad'`.lines(chomp: true)
-
- # Interactive
- tmux.send_keys %(printf '%s\n' aaaaa b ccc BAD | #{FZF} -q '!bad'), :Enter
- tmux.until do |lines|
- assert_equal 4, lines.item_count
- assert_equal 3, lines.match_count
- end
- tmux.until { |lines| assert_equal '> aaaaa', lines[-3] }
- tmux.until { |lines| assert_equal ' b', lines[-4] }
- tmux.until { |lines| assert_equal ' ccc', lines[-5] }
- end
-
- def test_preview_correct_tab_width_after_ansi_reset_code
- writelines(tempname, ["\x1b[31m+\x1b[m\t\x1b[32mgreen"])
- tmux.send_keys "#{FZF} --preview 'cat #{tempname}'", :Enter
- tmux.until { |lines| assert_includes lines[1], ' + green ' }
- end
-
- def test_disabled
- tmux.send_keys %(seq 1000 | #{FZF} --query 333 --disabled --bind a:enable-search,b:disable-search,c:toggle-search --preview 'echo {} {q}'), :Enter
- tmux.until { |lines| assert_equal 1000, lines.match_count }
- tmux.until { |lines| assert_includes lines[1], ' 1 333 ' }
- tmux.send_keys 'foo'
- tmux.until { |lines| assert_equal 1000, lines.match_count }
- tmux.until { |lines| assert_includes lines[1], ' 1 333foo ' }
-
- # Already disabled, no change
- tmux.send_keys 'b'
- tmux.until { |lines| assert_equal 1000, lines.match_count }
-
- # Enable search
- tmux.send_keys 'a'
- tmux.until { |lines| assert_equal 0, lines.match_count }
- tmux.send_keys :BSpace, :BSpace, :BSpace
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.until { |lines| assert_includes lines[1], ' 333 333 ' }
-
- # Toggle search -> disabled again, but retains the previous result
- tmux.send_keys 'c'
- tmux.send_keys 'foo'
- tmux.until { |lines| assert_includes lines[1], ' 333 333foo ' }
- tmux.until { |lines| assert_equal 1, lines.match_count }
-
- # Enabled, no match
- tmux.send_keys 'c'
- tmux.until { |lines| assert_equal 0, lines.match_count }
- tmux.until { |lines| assert_includes lines[1], ' 333foo ' }
- end
-
- def test_reload
- tmux.send_keys %(seq 1000 | #{FZF} --bind 'change:reload(seq {q}),a:reload(seq 100),b:reload:seq 200' --header-lines 2 --multi 2), :Enter
- tmux.until { |lines| assert_equal 998, lines.match_count }
- tmux.send_keys 'a'
- tmux.until do |lines|
- assert_equal 98, lines.item_count
- assert_equal 98, lines.match_count
- end
- tmux.send_keys 'b'
- tmux.until do |lines|
- assert_equal 198, lines.item_count
- assert_equal 198, lines.match_count
- end
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal ' 198/198 (1/2)', lines[-2] }
- tmux.send_keys '555'
- tmux.until { |lines| assert_equal ' 1/553 (0/2)', lines[-2] }
- end
-
- def test_reload_even_when_theres_no_match
- tmux.send_keys %(: | #{FZF} --bind 'space:reload(seq 10)'), :Enter
- tmux.until { |lines| assert_equal 0, lines.item_count }
- tmux.send_keys :Space
- tmux.until { |lines| assert_equal 10, lines.item_count }
- end
-
- def test_clear_list_when_header_lines_changed_due_to_reload
- tmux.send_keys %(seq 10 | #{FZF} --header 0 --header-lines 3 --bind 'space:reload(seq 1)'), :Enter
- tmux.until { |lines| assert_includes lines, ' 9' }
- tmux.send_keys :Space
- tmux.until { |lines| refute_includes lines, ' 9' }
- end
-
- def test_clear_query
- tmux.send_keys %(: | #{FZF} --query foo --bind space:clear-query), :Enter
- tmux.until { |lines| assert_equal 0, lines.item_count }
- tmux.until { |lines| assert_equal '> foo', lines.last }
- tmux.send_keys 'C-a', 'bar'
- tmux.until { |lines| assert_equal '> barfoo', lines.last }
- tmux.send_keys :Space
- tmux.until { |lines| assert_equal '>', lines.last }
- end
-
- def test_clear_selection
- tmux.send_keys %(seq 100 | #{FZF} --multi --bind space:clear-selection), :Enter
- tmux.until { |lines| assert_equal 100, lines.match_count }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal ' 100/100 (1)', lines[-2] }
- tmux.send_keys 'foo'
- tmux.until { |lines| assert_equal ' 0/100 (1)', lines[-2] }
- tmux.send_keys :Space
- tmux.until { |lines| assert_equal ' 0/100 (0)', lines[-2] }
- end
-
- def test_backward_delete_char_eof
- tmux.send_keys "seq 1000 | #{fzf("--bind 'bs:backward-delete-char/eof'")}", :Enter
- tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
- tmux.send_keys '11'
- tmux.until { |lines| assert_equal '> 11', lines[-1] }
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal '> 1', lines[-1] }
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal '>', lines[-1] }
- tmux.send_keys :BSpace
- tmux.prepare
- end
-
- def test_strip_xterm_osc_sequence
- %W[\x07 \x1b\\].each do |esc|
- writelines(tempname, [%(printf $1"\e]4;3;rgb:aa/bb/cc#{esc} "$2)])
- File.chmod(0o755, tempname)
- tmux.prepare
- tmux.send_keys \
- %(echo foo bar | #{FZF} --preview '#{tempname} {2} {1}'), :Enter
-
- tmux.until { |lines| assert lines.any_include?('bar foo') }
- tmux.send_keys :Enter
- end
- end
-
- def test_keep_right
- tmux.send_keys "seq 10000 | #{FZF} --read0 --keep-right", :Enter
- tmux.until { |lines| assert lines.any_include?('9999 10000') }
- end
-
- def test_backward_eof
- tmux.send_keys "echo foo | #{FZF} --bind 'backward-eof:reload(seq 100)'", :Enter
- tmux.until { |lines| lines.item_count == 1 && lines.match_count == 1 }
- tmux.send_keys 'x'
- tmux.until { |lines| lines.item_count == 1 && lines.match_count == 0 }
- tmux.send_keys :BSpace
- tmux.until { |lines| lines.item_count == 1 && lines.match_count == 1 }
- tmux.send_keys :BSpace
- tmux.until { |lines| lines.item_count == 100 && lines.match_count == 100 }
- end
-
- def test_preview_bindings_with_default_preview
- tmux.send_keys "seq 10 | #{FZF} --preview 'echo [{}]' --bind 'a:preview(echo [{}{}]),b:preview(echo [{}{}{}]),c:refresh-preview'", :Enter
- tmux.until { |lines| lines.item_count == 10 }
- tmux.until { |lines| assert_includes lines[1], '[1]' }
- tmux.send_keys 'a'
- tmux.until { |lines| assert_includes lines[1], '[11]' }
- tmux.send_keys 'c'
- tmux.until { |lines| assert_includes lines[1], '[1]' }
- tmux.send_keys 'b'
- tmux.until { |lines| assert_includes lines[1], '[111]' }
- tmux.send_keys :Up
- tmux.until { |lines| assert_includes lines[1], '[2]' }
- end
-
- def test_preview_bindings_without_default_preview
- tmux.send_keys "seq 10 | #{FZF} --bind 'a:preview(echo [{}{}]),b:preview(echo [{}{}{}]),c:refresh-preview'", :Enter
- tmux.until { |lines| lines.item_count == 10 }
- tmux.until { |lines| refute_includes lines[1], '1' }
- tmux.send_keys 'a'
- tmux.until { |lines| assert_includes lines[1], '[11]' }
- tmux.send_keys 'c' # does nothing
- tmux.until { |lines| assert_includes lines[1], '[11]' }
- tmux.send_keys 'b'
- tmux.until { |lines| assert_includes lines[1], '[111]' }
- tmux.send_keys 9
- tmux.until { |lines| lines.match_count == 1 }
- tmux.until { |lines| refute_includes lines[1], '2' }
- tmux.until { |lines| assert_includes lines[1], '[111]' }
- end
-
- def test_preview_scroll_begin_constant
- tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+123", :Enter
- tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
- tmux.until { |lines| assert_match %r{123.*123/1000}, lines[1] }
- end
-
- def test_preview_scroll_begin_expr
- tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+{3}", :Enter
- tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
- tmux.until { |lines| assert_match %r{321.*321/1000}, lines[1] }
- end
-
- def test_preview_scroll_begin_and_offset
- ['echo foo 123 321', 'echo foo :123: 321'].each do |input|
- tmux.send_keys "#{input} | #{FZF} --preview 'seq 1000' --preview-window left:+{2}-2", :Enter
- tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
- tmux.until { |lines| assert_match %r{121.*121/1000}, lines[1] }
- tmux.send_keys 'C-c'
- end
- end
-
- def test_normalized_match
- echoes = '(echo a; echo á; echo A; echo Á;)'
- assert_equal %w[a á A Á], `#{echoes} | #{FZF} -f a`.lines.map(&:chomp)
- assert_equal %w[á Á], `#{echoes} | #{FZF} -f á`.lines.map(&:chomp)
- assert_equal %w[A Á], `#{echoes} | #{FZF} -f A`.lines.map(&:chomp)
- assert_equal %w[Á], `#{echoes} | #{FZF} -f Á`.lines.map(&:chomp)
- end
-
- def test_preview_clear_screen
- tmux.send_keys %{seq 100 | #{FZF} --preview 'for i in $(seq 300); do (( i % 200 == 0 )) && printf "\\033[2J"; echo "[$i]"; sleep 0.001; done'}, :Enter
- tmux.until { |lines| lines.item_count == 100 }
- tmux.until { |lines| lines[1]&.include?('[200]') }
- end
-
- def test_change_prompt
- tmux.send_keys "#{FZF} --bind 'a:change-prompt(a> ),b:change-prompt:b> ' --query foo", :Enter
- tmux.until { |lines| assert_equal '> foo', lines[-1] }
- tmux.send_keys 'a'
- tmux.until { |lines| assert_equal 'a> foo', lines[-1] }
- tmux.send_keys 'b'
- tmux.until { |lines| assert_equal 'b> foo', lines[-1] }
- end
-
- def test_preview_window_follow
- tmux.send_keys "#{FZF} --preview 'seq 1000 | nl' --preview-window down:noborder:follow", :Enter
- tmux.until { |lines| assert_equal '1000 1000', lines[-1].strip }
- end
-
- def test_toggle_preview_wrap
- tmux.send_keys "#{FZF} --preview 'for i in $(seq $FZF_PREVIEW_COLUMNS); do echo -n .; done; echo wrapped; echo 2nd line' --bind ctrl-w:toggle-preview-wrap", :Enter
- 2.times do
- tmux.until { |lines| assert_includes lines[2], '2nd line' }
- tmux.send_keys 'C-w'
- tmux.until do |lines|
- assert_includes lines[2], 'wrapped'
- assert_includes lines[3], '2nd line'
- end
- tmux.send_keys 'C-w'
- end
- end
-
- def test_close
- tmux.send_keys "seq 100 | #{FZF} --preview 'echo foo' --bind ctrl-c:close", :Enter
- tmux.until { |lines| assert_equal 100, lines.match_count }
- tmux.until { |lines| assert_includes lines[1], 'foo' }
- tmux.send_keys 'C-c'
- tmux.until { |lines| refute_includes lines[1], 'foo' }
- tmux.send_keys '10'
- tmux.until { |lines| assert_equal 2, lines.match_count }
- tmux.send_keys 'C-c'
- tmux.send_keys 'C-l', 'closed'
- tmux.until { |lines| assert_includes lines[0], 'closed' }
- end
-
- def test_select_deselect
- tmux.send_keys "seq 3 | #{FZF} --multi --bind up:deselect+up,down:select+down", :Enter
- tmux.until { |lines| assert_equal 3, lines.match_count }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal 1, lines.select_count }
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal 0, lines.select_count }
- tmux.send_keys :Down, :Down
- tmux.until { |lines| assert_equal 2, lines.select_count }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal 1, lines.select_count }
- tmux.send_keys :Down, :Down
- tmux.until { |lines| assert_equal 2, lines.select_count }
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal 1, lines.select_count }
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal 1, lines.select_count }
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal 2, lines.select_count }
- end
-
- def test_interrupt_execute
- tmux.send_keys "seq 100 | #{FZF} --bind 'ctrl-l:execute:echo executing {}; sleep 100'", :Enter
- tmux.until { |lines| assert_equal 100, lines.item_count }
- tmux.send_keys 'C-l'
- tmux.until { |lines| assert lines.any_include?('executing 1') }
- tmux.send_keys 'C-c'
- tmux.until { |lines| assert_equal 100, lines.item_count }
- tmux.send_keys 99
- tmux.until { |lines| assert_equal 1, lines.match_count }
- end
-
- def test_kill_default_command_on_abort
- script = tempname + '.sh'
- writelines(script,
- ['#!/usr/bin/env bash',
- "echo 'Started'",
- 'while :; do sleep 1; done'])
- system("chmod +x #{script}")
-
- tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', "FZF_DEFAULT_COMMAND=#{script}"), :Enter
- tmux.until { |lines| assert_equal 1, lines.item_count }
- tmux.send_keys 'C-c'
- tmux.send_keys 'C-l', 'closed'
- tmux.until { |lines| assert_includes lines[0], 'closed' }
- wait { refute system("pgrep -f #{script}") }
- ensure
- system("pkill -9 -f #{script}")
- begin
- File.unlink(script)
- rescue StandardError
- nil
- end
- end
-
- def test_kill_default_command_on_accept
- script = tempname + '.sh'
- writelines(script,
- ['#!/usr/bin/env bash',
- "echo 'Started'",
- 'while :; do sleep 1; done'])
- system("chmod +x #{script}")
-
- tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', "FZF_DEFAULT_COMMAND=#{script}"), :Enter
- tmux.until { |lines| assert_equal 1, lines.item_count }
- tmux.send_keys :Enter
- assert_equal 'Started', readonce.chomp
- wait { refute system("pgrep -f #{script}") }
- ensure
- system("pkill -9 -f #{script}")
- begin
- File.unlink(script)
- rescue StandardError
- nil
- end
- end
-
- def test_kill_reload_command_on_abort
- script = tempname + '.sh'
- writelines(script,
- ['#!/usr/bin/env bash',
- "echo 'Started'",
- 'while :; do sleep 1; done'])
- system("chmod +x #{script}")
-
- tmux.send_keys "seq 1 3 | #{fzf("--bind 'ctrl-r:reload(#{script})'")}", :Enter
- tmux.until { |lines| assert_equal 3, lines.item_count }
- tmux.send_keys 'C-r'
- tmux.until { |lines| assert_equal 1, lines.item_count }
- tmux.send_keys 'C-c'
- tmux.send_keys 'C-l', 'closed'
- tmux.until { |lines| assert_includes lines[0], 'closed' }
- wait { refute system("pgrep -f #{script}") }
- ensure
- system("pkill -9 -f #{script}")
- begin
- File.unlink(script)
- rescue StandardError
- nil
- end
- end
-
- def test_kill_reload_command_on_accept
- script = tempname + '.sh'
- writelines(script,
- ['#!/usr/bin/env bash',
- "echo 'Started'",
- 'while :; do sleep 1; done'])
- system("chmod +x #{script}")
-
- tmux.send_keys "seq 1 3 | #{fzf("--bind 'ctrl-r:reload(#{script})'")}", :Enter
- tmux.until { |lines| assert_equal 3, lines.item_count }
- tmux.send_keys 'C-r'
- tmux.until { |lines| assert_equal 1, lines.item_count }
- tmux.send_keys :Enter
- assert_equal 'Started', readonce.chomp
- wait { refute system("pgrep -f #{script}") }
- ensure
- system("pkill -9 -f #{script}")
- begin
- File.unlink(script)
- rescue StandardError
- nil
- end
- end
-
- def test_preview_header
- tmux.send_keys "seq 100 | #{FZF} --bind ctrl-k:preview-up+preview-up,ctrl-j:preview-down+preview-down+preview-down --preview 'seq 1000' --preview-window 'top:+{1}:~3'", :Enter
- tmux.until { |lines| assert_equal 100, lines.item_count }
- top5 = ->(lines) { lines.drop(1).take(5).map { |s| s[/[0-9]+/] } }
- tmux.until do |lines|
- assert_includes lines[1], '4/1000'
- assert_equal(%w[1 2 3 4 5], top5[lines])
- end
- tmux.send_keys '55'
- tmux.until do |lines|
- assert_equal 1, lines.match_count
- assert_equal(%w[1 2 3 55 56], top5[lines])
- end
- tmux.send_keys 'C-J'
- tmux.until do |lines|
- assert_equal(%w[1 2 3 58 59], top5[lines])
- end
- tmux.send_keys :BSpace
- tmux.until do |lines|
- assert_equal 19, lines.match_count
- assert_equal(%w[1 2 3 5 6], top5[lines])
- end
- tmux.send_keys 'C-K'
- tmux.until { |lines| assert_equal(%w[1 2 3 4 5], top5[lines]) }
- end
-
- def test_unbind
- tmux.send_keys "seq 100 | #{FZF} --bind 'c:clear-query,d:unbind(c,d)'", :Enter
- tmux.until { |lines| assert_equal 100, lines.item_count }
- tmux.send_keys 'ab'
- tmux.until { |lines| assert_equal '> ab', lines[-1] }
- tmux.send_keys 'c'
- tmux.until { |lines| assert_equal '>', lines[-1] }
- tmux.send_keys 'dabcd'
- tmux.until { |lines| assert_equal '> abcd', lines[-1] }
- end
-
- def test_item_index_reset_on_reload
- tmux.send_keys "seq 10 | #{FZF} --preview 'echo [[{n}]]' --bind 'up:last,down:first,space:reload:seq 100'", :Enter
- tmux.until { |lines| assert_includes lines[1], '[[0]]' }
- tmux.send_keys :Up
- tmux.until { |lines| assert_includes lines[1], '[[9]]' }
- tmux.send_keys :Down
- tmux.until { |lines| assert_includes lines[1], '[[0]]' }
- tmux.send_keys :Space
- tmux.until do |lines|
- assert_equal 100, lines.item_count
- assert_includes lines[1], '[[0]]'
- end
- tmux.send_keys :Up
- tmux.until { |lines| assert_includes lines[1], '[[99]]' }
- end
-
- def test_reload_should_update_preview
- tmux.send_keys "seq 3 | #{FZF} --bind 'ctrl-t:reload:echo 4' --preview 'echo {}' --preview-window 'nohidden'", :Enter
- tmux.until { |lines| assert_includes lines[1], '1' }
- tmux.send_keys 'C-t'
- tmux.until { |lines| assert_includes lines[1], '4' }
- end
-
- def test_scroll_off
- tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
- tmux.until { |lines| assert_equal 1000, lines.item_count }
- height = tmux.until { |lines| lines }.first.to_i
- tmux.send_keys :PgUp
- tmux.until do |lines|
- assert_equal height + 3, lines.first.to_i
- assert_equal "> #{height}", lines[3].strip
- end
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal "> #{height + 1}", lines[3].strip }
- tmux.send_keys 'l'
- tmux.until { |lines| assert_equal '> 1000', lines.first.strip }
- tmux.send_keys :PgDn
- tmux.until { |lines| assert_equal "> #{1000 - height + 1}", lines.reverse[5].strip }
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal "> #{1000 - height}", lines.reverse[5].strip }
- end
-
- def test_scroll_off_large
- tmux.send_keys "seq 1000 | #{FZF} --scroll-off=9999", :Enter
- tmux.until { |lines| assert_equal 1000, lines.item_count }
- height = tmux.until { |lines| lines }.first.to_i
- tmux.send_keys :PgUp
- tmux.until { |lines| assert_equal "> #{height}", lines[height / 2].strip }
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal "> #{height + 1}", lines[height / 2].strip }
- tmux.send_keys :Up
- tmux.until { |lines| assert_equal "> #{height + 2}", lines[height / 2].strip }
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal "> #{height + 1}", lines[height / 2].strip }
- end
-
- def test_header_first
- tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first", :Enter
- tmux.until do |lines|
- expected = <<~OUTPUT
- > 4
- 997/997
- >
- 3
- 2
- 1
- foobar
- OUTPUT
-
- assert_equal expected.chomp, lines.reverse.take(7).reverse.join("\n")
- end
- end
-
- def test_header_first_reverse
- tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first --reverse --inline-info", :Enter
- tmux.until do |lines|
- expected = <<~OUTPUT
- foobar
- 1
- 2
- 3
- > < 997/997
- > 4
- OUTPUT
-
- assert_equal expected.chomp, lines.take(6).join("\n")
- end
- end
-end
-
-module TestShell
- def setup
- @tmux = Tmux.new(shell)
- tmux.prepare
- end
-
- def teardown
- @tmux.kill
- end
-
- def set_var(name, val)
- tmux.prepare
- tmux.send_keys "export #{name}='#{val}'", :Enter
- tmux.prepare
- end
-
- def unset_var(name)
- tmux.prepare
- tmux.send_keys "unset #{name}", :Enter
- tmux.prepare
- end
-
- def test_ctrl_t
- set_var('FZF_CTRL_T_COMMAND', 'seq 100')
-
- tmux.prepare
- tmux.send_keys 'C-t'
- tmux.until { |lines| assert_equal 100, lines.item_count }
- tmux.send_keys :Tab, :Tab, :Tab
- tmux.until { |lines| assert lines.any_include?(' (3)') }
- tmux.send_keys :Enter
- tmux.until { |lines| assert lines.any_include?('1 2 3') }
- tmux.send_keys 'C-c'
- end
-
- def test_ctrl_t_unicode
- writelines(tempname, ['fzf-unicode 테스트1', 'fzf-unicode 테스트2'])
- set_var('FZF_CTRL_T_COMMAND', "cat #{tempname}")
-
- tmux.prepare
- tmux.send_keys 'echo ', 'C-t'
- tmux.until { |lines| assert_equal 2, lines.item_count }
- tmux.send_keys 'fzf-unicode'
- tmux.until { |lines| assert_equal 2, lines.match_count }
-
- tmux.send_keys '1'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal 1, lines.select_count }
-
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal 2, lines.match_count }
-
- tmux.send_keys '2'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal 2, lines.select_count }
-
- tmux.send_keys :Enter
- tmux.until { |lines| assert_match(/echo .*fzf-unicode.*1.* .*fzf-unicode.*2/, lines.join) }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal 'fzf-unicode 테스트1 fzf-unicode 테스트2', lines[-1] }
- end
-
- def test_alt_c
- tmux.prepare
- tmux.send_keys :Escape, :c
- lines = tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- expected = lines.reverse.find { |l| l.start_with?('> ') }[2..-1]
- tmux.send_keys :Enter
- tmux.prepare
- tmux.send_keys :pwd, :Enter
- tmux.until { |lines| assert lines[-1]&.end_with?(expected) }
- end
-
- def test_alt_c_command
- set_var('FZF_ALT_C_COMMAND', 'echo /tmp')
-
- tmux.prepare
- tmux.send_keys 'cd /', :Enter
-
- tmux.prepare
- tmux.send_keys :Escape, :c
- tmux.until { |lines| assert_equal 1, lines.item_count }
- tmux.send_keys :Enter
-
- tmux.prepare
- tmux.send_keys :pwd, :Enter
- tmux.until { |lines| assert_equal '/tmp', lines[-1] }
- end
-
- def test_ctrl_r
- tmux.prepare
- tmux.send_keys 'echo 1st', :Enter
- tmux.prepare
- tmux.send_keys 'echo 2nd', :Enter
- tmux.prepare
- tmux.send_keys 'echo 3d', :Enter
- tmux.prepare
- 3.times do
- tmux.send_keys 'echo 3rd', :Enter
- tmux.prepare
- end
- tmux.send_keys 'echo 4th', :Enter
- tmux.prepare
- tmux.send_keys 'C-r'
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- tmux.send_keys 'e3d'
- # Duplicates removed: 3d (1) + 3rd (1) => 2 matches
- tmux.until { |lines| assert_equal 2, lines.match_count }
- tmux.until { |lines| assert lines[-3]&.end_with?(' echo 3d') }
- tmux.send_keys 'C-r'
- tmux.until { |lines| assert lines[-3]&.end_with?(' echo 3rd') }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal 'echo 3rd', lines[-1] }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal '3rd', lines[-1] }
- end
-
- def test_ctrl_r_multiline
- tmux.send_keys 'echo "foo', :Enter, 'bar"', :Enter
- tmux.until { |lines| assert_equal %w[foo bar], lines[-2..-1] }
- tmux.prepare
- tmux.send_keys 'C-r'
- tmux.until { |lines| assert_equal '>', lines[-1] }
- tmux.send_keys 'foo bar'
- tmux.until { |lines| assert lines[-3]&.end_with?('bar"') }
- tmux.send_keys :Enter
- tmux.until { |lines| assert lines[-1]&.end_with?('bar"') }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal %w[foo bar], lines[-2..-1] }
- end
-
- def test_ctrl_r_abort
- skip("doesn't restore the original line when search is aborted pre Bash 4") if shell == :bash && `#{Shell.bash} --version`[/(?<= version )\d+/].to_i < 4
- %w[foo ' "].each do |query|
- tmux.prepare
- tmux.send_keys :Enter, query
- tmux.until { |lines| assert lines[-1]&.start_with?(query) }
- tmux.send_keys 'C-r'
- tmux.until { |lines| assert_equal "> #{query}", lines[-1] }
- tmux.send_keys 'C-g'
- tmux.until { |lines| assert lines[-1]&.start_with?(query) }
- end
- end
-end
-
-module CompletionTest
- def test_file_completion
- FileUtils.mkdir_p('/tmp/fzf-test')
- FileUtils.mkdir_p('/tmp/fzf test')
- (1..100).each { |i| FileUtils.touch("/tmp/fzf-test/#{i}") }
- ['no~such~user', '/tmp/fzf test/foobar'].each do |f|
- FileUtils.touch(File.expand_path(f))
- end
- tmux.prepare
- tmux.send_keys 'cat /tmp/fzf-test/10**', :Tab
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- tmux.send_keys ' !d'
- tmux.until { |lines| assert_equal 2, lines.match_count }
- tmux.send_keys :Tab, :Tab
- tmux.until { |lines| assert_equal 2, lines.select_count }
- tmux.send_keys :Enter
- tmux.until(true) do |lines|
- assert_equal 'cat /tmp/fzf-test/10 /tmp/fzf-test/100', lines[-1]
- end
-
- # ~USERNAME**
- user = `whoami`.chomp
- tmux.send_keys 'C-u'
- tmux.send_keys "cat ~#{user}**", :Tab
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- tmux.send_keys "/#{user}"
- tmux.until { |lines| assert(lines.any? { |l| l.end_with?("/#{user}") }) }
- tmux.send_keys :Enter
- tmux.until(true) do |lines|
- assert_match %r{cat .*/#{user}}, lines[-1]
- end
-
- # ~INVALID_USERNAME**
- tmux.send_keys 'C-u'
- tmux.send_keys 'cat ~such**', :Tab
- tmux.until(true) { |lines| assert lines.any_include?('no~such~user') }
- tmux.send_keys :Enter
- tmux.until(true) { |lines| assert_equal 'cat no~such~user', lines[-1] }
-
- # /tmp/fzf\ test**
- tmux.send_keys 'C-u'
- tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- tmux.send_keys 'foobar$'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Enter
- tmux.until(true) { |lines| assert_equal 'cat /tmp/fzf\ test/foobar', lines[-1] }
-
- # Should include hidden files
- (1..100).each { |i| FileUtils.touch("/tmp/fzf-test/.hidden-#{i}") }
- tmux.send_keys 'C-u'
- tmux.send_keys 'cat /tmp/fzf-test/hidden**', :Tab
- tmux.until(true) do |lines|
- assert_equal 100, lines.match_count
- assert lines.any_include?('/tmp/fzf-test/.hidden-')
- end
- tmux.send_keys :Enter
- ensure
- ['/tmp/fzf-test', '/tmp/fzf test', '~/.fzf-home', 'no~such~user'].each do |f|
- FileUtils.rm_rf(File.expand_path(f))
- end
- end
-
- def test_file_completion_root
- tmux.send_keys 'ls /**', :Tab
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- tmux.send_keys :Enter
- end
-
- def test_dir_completion
- (1..100).each do |idx|
- FileUtils.mkdir_p("/tmp/fzf-test/d#{idx}")
- end
- FileUtils.touch('/tmp/fzf-test/d55/xxx')
- tmux.prepare
- tmux.send_keys 'cd /tmp/fzf-test/**', :Tab
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- tmux.send_keys :Tab, :Tab # Tab does not work here
- tmux.send_keys 55
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Enter
- tmux.until(true) { |lines| assert_equal 'cd /tmp/fzf-test/d55/', lines[-1] }
- tmux.send_keys :xx
- tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
-
- # Should not match regular files (bash-only)
- if instance_of?(TestBash)
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
- end
-
- # Fail back to plusdirs
- tmux.send_keys :BSpace, :BSpace, :BSpace
- tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55', lines[-1] }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/', lines[-1] }
- end
-
- def test_process_completion
- tmux.send_keys 'sleep 12345 &', :Enter
- lines = tmux.until { |lines| assert lines[-1]&.start_with?('[1] ') }
- pid = lines[-1]&.split&.last
- tmux.prepare
- tmux.send_keys 'C-L'
- tmux.send_keys 'kill ', :Tab
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- tmux.send_keys 'sleep12345'
- tmux.until { |lines| assert lines.any_include?('sleep 12345') }
- tmux.send_keys :Enter
- tmux.until(true) { |lines| assert_equal "kill #{pid}", lines[-1] }
- ensure
- if pid
- begin
- Process.kill('KILL', pid.to_i)
- rescue StandardError
- nil
- end
- end
- end
-
- def test_custom_completion
- tmux.send_keys '_fzf_compgen_path() { echo "$1"; seq 10; }', :Enter
- tmux.prepare
- tmux.send_keys 'ls /tmp/**', :Tab
- tmux.until { |lines| assert_equal 11, lines.match_count }
- tmux.send_keys :Tab, :Tab, :Tab
- tmux.until { |lines| assert_equal 3, lines.select_count }
- tmux.send_keys :Enter
- tmux.until(true) { |lines| assert_equal 'ls /tmp 1 2', lines[-1] }
- end
-
- def test_unset_completion
- tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
- tmux.prepare
-
- # Using tmux
- tmux.send_keys 'unset FZFFOOBR**', :Tab
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
- tmux.send_keys 'C-c'
-
- # FZF_TMUX=1
- new_shell
- tmux.focus
- tmux.send_keys 'unset FZFFOOBR**', :Tab
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
- end
-
- def test_file_completion_unicode
- FileUtils.mkdir_p('/tmp/fzf-test')
- tmux.paste "cd /tmp/fzf-test; echo test3 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2701'; echo test4 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2702'"
- tmux.prepare
- tmux.send_keys 'cat fzf-unicode**', :Tab
- tmux.until { |lines| assert_equal 2, lines.match_count }
-
- tmux.send_keys '1'
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal 1, lines.select_count }
-
- tmux.send_keys :BSpace
- tmux.until { |lines| assert_equal 2, lines.match_count }
-
- tmux.send_keys '2'
- tmux.until { |lines| assert_equal 1, lines.select_count }
- tmux.send_keys :Tab
- tmux.until { |lines| assert_equal 2, lines.select_count }
-
- tmux.send_keys :Enter
- tmux.until(true) { |lines| assert_match(/cat .*fzf-unicode.*1.* .*fzf-unicode.*2/, lines[-1]) }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal %w[test3 test4], lines[-2..-1] }
- end
-
- def test_custom_completion_api
- tmux.send_keys 'eval "_fzf$(declare -f _comprun)"', :Enter
- %w[f g].each do |command|
- tmux.prepare
- tmux.send_keys "#{command} b**", :Tab
- tmux.until do |lines|
- assert_equal 2, lines.item_count
- assert_equal 1, lines.match_count
- assert lines.any_include?("prompt-#{command}")
- assert lines.any_include?("preview-#{command}-bar")
- end
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal "#{command} #{command}barbar", lines[-1] }
- tmux.send_keys 'C-u'
- end
- ensure
- tmux.prepare
- tmux.send_keys 'unset -f _fzf_comprun', :Enter
- end
-end
-
-class TestBash < TestBase
- include TestShell
- include CompletionTest
-
- def shell
- :bash
- end
-
- def new_shell
- tmux.prepare
- tmux.send_keys "FZF_TMUX=1 #{Shell.bash}", :Enter
- tmux.prepare
- end
-
- def test_dynamic_completion_loader
- tmux.paste 'touch /tmp/foo; _fzf_completion_loader=1'
- tmux.paste '_completion_loader() { complete -o default fake; }'
- tmux.paste 'complete -F _fzf_path_completion -o default -o bashdefault fake'
- tmux.send_keys 'fake /tmp/foo**', :Tab
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- tmux.send_keys 'C-c'
-
- tmux.prepare
- tmux.send_keys 'fake /tmp/foo'
- tmux.send_keys :Tab, 'C-u'
-
- tmux.prepare
- tmux.send_keys 'fake /tmp/foo**', :Tab
- tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
- end
-end
-
-class TestZsh < TestBase
- include TestShell
- include CompletionTest
-
- def shell
- :zsh
- end
-
- def new_shell
- tmux.send_keys "FZF_TMUX=1 #{Shell.zsh}", :Enter
- tmux.prepare
- end
-
- def test_complete_quoted_command
- tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
- ['unset', '\unset', "'unset'"].each do |command|
- tmux.prepare
- tmux.send_keys "#{command} FZFFOOBR**", :Tab
- tmux.until { |lines| assert_equal 1, lines.match_count }
- tmux.send_keys :Enter
- tmux.until { |lines| assert_equal "#{command} FZFFOOBAR", lines[-1] }
- tmux.send_keys 'C-c'
- end
- end
-end
-
-class TestFish < TestBase
- include TestShell
-
- def shell
- :fish
- end
-
- def new_shell
- tmux.send_keys 'env FZF_TMUX=1 fish', :Enter
- tmux.send_keys 'function fish_prompt; end; clear', :Enter
- tmux.until { |lines| assert_empty lines }
- end
-
- def set_var(name, val)
- tmux.prepare
- tmux.send_keys "set -g #{name} '#{val}'", :Enter
- tmux.prepare
- end
-end
-
-__END__
-PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
-unset <%= UNSETS.join(' ') %>
-unset $(env | sed -n /^_fzf_orig/s/=.*//p)
-unset $(declare -F | sed -n "/_fzf/s/.*-f //p")
-
-# Setup fzf
-# ---------
-if [[ ! "$PATH" == *<%= BASE %>/bin* ]]; then
- export PATH="${PATH:+${PATH}:}<%= BASE %>/bin"
-fi
-
-# Auto-completion
-# ---------------
-[[ $- == *i* ]] && source "<%= BASE %>/shell/completion.<%= __method__ %>" 2> /dev/null
-
-# Key bindings
-# ------------
-source "<%= BASE %>/shell/key-bindings.<%= __method__ %>"
-
-# Old API
-_fzf_complete_f() {
- _fzf_complete "+m --multi --prompt \"prompt-f> \"" "$@" < <(
- echo foo
- echo bar
- )
-}
-
-# New API
-_fzf_complete_g() {
- _fzf_complete +m --multi --prompt "prompt-g> " -- "$@" < <(
- echo foo
- echo bar
- )
-}
-
-_fzf_complete_f_post() {
- awk '{print "f" $0 $0}'
-}
-
-_fzf_complete_g_post() {
- awk '{print "g" $0 $0}'
-}
-
-[ -n "$BASH" ] && complete -F _fzf_complete_f -o default -o bashdefault f
-[ -n "$BASH" ] && complete -F _fzf_complete_g -o default -o bashdefault g
-
-_comprun() {
- local command=$1
- shift
-
- case "$command" in
- f) fzf "$@" --preview 'echo preview-f-{}' ;;
- g) fzf "$@" --preview 'echo preview-g-{}' ;;
- *) fzf "$@" ;;
- esac
-}
diff --git a/.fzf/uninstall b/.fzf/uninstall
deleted file mode 100755
index 094d689..0000000
--- a/.fzf/uninstall
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/env bash
-
-xdg=0
-prefix='~/.fzf'
-prefix_expand=~/.fzf
-fish_dir=${XDG_CONFIG_HOME:-$HOME/.config}/fish
-
-help() {
- cat << EOF
-usage: $0 [OPTIONS]
-
- --help Show this message
- --xdg Remove files generated under \$XDG_CONFIG_HOME/fzf
-EOF
-}
-
-for opt in "$@"; do
- case $opt in
- --help)
- help
- exit 0
- ;;
- --xdg)
- xdg=1
- prefix='"${XDG_CONFIG_HOME:-$HOME/.config}"/fzf/fzf'
- prefix_expand=${XDG_CONFIG_HOME:-$HOME/.config}/fzf/fzf
- ;;
- *)
- echo "unknown option: $opt"
- help
- exit 1
- ;;
- esac
-done
-
-ask() {
- while true; do
- read -p "$1 ([y]/n) " -r
- REPLY=${REPLY:-"y"}
- if [[ $REPLY =~ ^[Yy]$ ]]; then
- return 0
- elif [[ $REPLY =~ ^[Nn]$ ]]; then
- return 1
- fi
- done
-}
-
-remove() {
- echo "Remove $1"
- rm -f "$1"
-}
-
-remove_line() {
- src=$(readlink "$1")
- if [ $? -eq 0 ]; then
- echo "Remove from $1 ($src):"
- else
- src=$1
- echo "Remove from $1:"
- fi
-
- shift
- line_no=1
- match=0
- while [ -n "$1" ]; do
- line=$(sed -n "$line_no,\$p" "$src" | \grep -m1 -nF "$1")
- if [ $? -ne 0 ]; then
- shift
- line_no=1
- continue
- fi
- line_no=$(( $(sed 's/:.*//' <<< "$line") + line_no - 1 ))
- content=$(sed 's/^[0-9]*://' <<< "$line")
- match=1
- echo " - Line #$line_no: $content"
- [ "$content" = "$1" ] || ask " - Remove?"
- if [ $? -eq 0 ]; then
- awk -v n=$line_no 'NR == n {next} {print}' "$src" > "$src.bak" &&
- mv "$src.bak" "$src" || break
- echo " - Removed"
- else
- echo " - Skipped"
- line_no=$(( line_no + 1 ))
- fi
- done
- [ $match -eq 0 ] && echo " - Nothing found"
- echo
-}
-
-for shell in bash zsh; do
- shell_config=${prefix_expand}.${shell}
- remove "${shell_config}"
- remove_line ~/.${shell}rc \
- "[ -f ${prefix}.${shell} ] && source ${prefix}.${shell}" \
- "source ${prefix}.${shell}"
-done
-
-bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
-if [ -f "$bind_file" ]; then
- remove_line "$bind_file" "fzf_key_bindings"
-fi
-
-if [ -d "${fish_dir}/functions" ]; then
- remove "${fish_dir}/functions/fzf.fish"
- remove "${fish_dir}/functions/fzf_key_bindings.fish"
-
- if [ -z "$(ls -A "${fish_dir}/functions")" ]; then
- rmdir "${fish_dir}/functions"
- else
- echo "Can't delete non-empty directory: \"${fish_dir}/functions\""
- fi
-fi
-
-config_dir=$(dirname "$prefix_expand")
-if [[ "$xdg" = 1 ]] && [[ "$config_dir" = */fzf ]] && [[ -d "$config_dir" ]]; then
- rmdir "$config_dir"
-fi