Repository: junegunn/fzf
Pull request #2817 (2022-05-14 17:57:06)

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

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



Patch:
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 21ef360c..f2848951 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,1 +1,1 @@
-custom: ["https://paypal.me/junegunn", "https://www.buymeacoffee.com/junegunn"]
+github: junegunn
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 291aaf7c..2c3e5d98 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -27,18 +27,18 @@ jobs:
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
+      uses: actions/checkout@v3
       with:
         fetch-depth: 0
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@1ed1437484560351c5be56cf73a48a279d116b78
+      uses: github/codeql-action/init@v2
       with:
         languages: ${{ matrix.language }}
 
     - name: Autobuild
-      uses: github/codeql-action/autobuild@1ed1437484560351c5be56cf73a48a279d116b78
+      uses: github/codeql-action/autobuild@v2
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@1ed1437484560351c5be56cf73a48a279d116b78
+      uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/depsreview.yaml b/.github/workflows/depsreview.yaml
new file mode 100644
index 00000000..a25de591
--- /dev/null
+++ b/.github/workflows/depsreview.yaml
@@ -0,0 +1,14 @@
+name: 'Dependency Review'
+on: [pull_request]
+
+permissions:
+  contents: read
+
+jobs:
+  dependency-review:
+    runs-on: ubuntu-latest
+    steps:
+      - name: 'Checkout Repository'
+        uses: actions/checkout@v3
+      - name: 'Dependency Review'
+        uses: actions/dependency-review-action@v2
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index db1acf1e..2ab076ca 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -15,17 +15,17 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
+    - uses: actions/checkout@v3
       with:
         fetch-depth: 0
 
     - name: Set up Go
-      uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab # v2
+      uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2
       with:
-        go-version: 1.18
+        go-version: 1.19
 
     - name: Setup Ruby
-      uses: ruby/setup-ruby@ebaea52cb20fea395b0904125276395e37183dac
+      uses: ruby/setup-ruby@v1
       with:
         ruby-version: 3.0.0
 
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 06b63d6b..ae14bb4c 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -15,17 +15,17 @@ jobs:
   build:
     runs-on: macos-latest
     steps:
-    - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
+    - uses: actions/checkout@v3
       with:
         fetch-depth: 0
 
     - name: Set up Go
-      uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab # v2
+      uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2
       with:
         go-version: 1.18
 
     - name: Setup Ruby
-      uses: ruby/setup-ruby@ebaea52cb20fea395b0904125276395e37183dac
+      uses: ruby/setup-ruby@v1
       with:
         ruby-version: 3.0.0
 
diff --git a/.goreleaser.yml b/.goreleaser.yml
index bab275d6..a90b4ddb 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -18,7 +18,7 @@ builds:
       post: |
         sh -c '
         cat > /tmp/fzf-gon-amd64.hcl << EOF
-        source = ["./dist/fzf-macos_darwin_amd64/fzf"]
+        source = ["./dist/fzf-macos_darwin_amd64_v1/fzf"]
         bundle_id = "kr.junegunn.fzf"
         apple_id {
           username = "junegunn.c@gmail.com"
@@ -99,6 +99,10 @@ archives:
     files:
       - non-existent*
 
+checksum:
+  extra_files:
+    - glob: ./dist/fzf-*darwin*.zip
+
 release:
   github:
     owner: junegunn
diff --git a/.tool-versions b/.tool-versions
index 4933a41e..1799f52f 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1,1 +1,1 @@
-golang 1.18
+golang 1.19
diff --git a/ADVANCED.md b/ADVANCED.md
index ed18c61c..dcff947c 100644
--- a/ADVANCED.md
+++ b/ADVANCED.md
@@ -1,7 +1,7 @@
 Advanced fzf examples
 ======================
 
-*(Last update: 2021/05/22)*
+*(Last update: 2022/08/25)*
 
 <!-- vim-markdown-toc GFM -->
 
@@ -496,9 +496,17 @@ pods() {
 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.
+Oftentimes, you want to put the identifiers of various Git object to the
+command-line. For example, it is common to write commands like these:
+
+```sh
+git checkout [SOME_COMMIT_HASH or BRANCH or TAG]
+git diff [SOME_COMMIT_HASH or BRANCH or TAG] [SOME_COMMIT_HASH or BRANCH or TAG]
+```
+
+[fzf-git.sh](https://github.com/junegunn/fzf-git.sh) project defines a set of
+fzf-based key bindings for Git objects. I strongly recommend that you check
+them out because they are seriously useful.
 
 ### Files listed in `git status`
 
@@ -518,9 +526,6 @@ seriously useful.
 
 ![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
 ------------
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4e74934..5036d2ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,130 @@
 CHANGELOG
 =========
 
+0.34.0
+------
+- Added support for adaptive `--height`. If the `--height` value is prefixed
+  with `~`, fzf will automatically determine the height in the range according
+  to the input size.
+  ```sh
+  seq 1 | fzf --height ~70% --border --padding 1 --margin 1
+  seq 10 | fzf --height ~70% --border --padding 1 --margin 1
+  seq 100 | fzf --height ~70% --border --padding 1 --margin 1
+  ```
+    - There are a few limitations
+        - Not compatible with percent top/bottom margin/padding
+          ```sh
+          # This is not allowed (top/bottom margin in percent value)
+          fzf --height ~50% --border --margin 5%,10%
+
+          # This is allowed (top/bottom margin in fixed value)
+          fzf --height ~50% --border --margin 2,10%
+          ```
+        - fzf will not start until it can determine the right height for the input
+          ```sh
+          # fzf will open immediately
+          (sleep 2; seq 10) | fzf --height 50%
+
+          # fzf will open after 2 seconds
+          (sleep 2; seq 10) | fzf --height ~50%
+          (sleep 2; seq 1000) | fzf --height ~50%
+          ```
+- Fixed tcell renderer used to render full-screen fzf on Windows
+- `--no-clear` is deprecated. Use `reload` action instead.
+
+0.33.0
+------
+- Added `--scheme=[default|path|history]` option to choose scoring scheme
+    - (Experimental)
+    - We updated the scoring algorithm in 0.32.0, however we have learned that
+      this new scheme (`default`) is not always giving the optimal result
+    - `path`: Additional bonus point is only given to the characters after
+      path separator. You might want to choose this scheme if you have many
+      files with spaces in their paths.
+    - `history`: No additional bonus points are given so that we give more
+      weight to the chronological ordering. This is equivalent to the scoring
+      scheme before 0.32.0. This also sets `--tiebreak=index`.
+- ANSI color sequences with colon delimiters are now supported.
+  ```sh
+  printf "\e[38;5;208mOption 1\e[m\nOption 2" | fzf --ansi
+  printf "\e[38:5:208mOption 1\e[m\nOption 2" | fzf --ansi
+  ```
+- Support `border-{up,down}` as the synonyms for `border-{top,bottom}` in
+  `--preview-window`
+- Added support for ANSI `strikethrough`
+  ```sh
+  printf "\e[9mdeleted" | fzf --ansi
+  fzf --color fg+:strikethrough
+  ```
+
+0.32.1
+------
+- Fixed incorrect ordering of `--tiebreak=chunk`
+- fzf-tmux will show fzf border instead of tmux popup border (requires tmux 3.3)
+  ```sh
+  fzf-tmux -p70%
+  fzf-tmux -p70% --color=border:bright-red
+  fzf-tmux -p100%,60% --color=border:bright-yellow --border=horizontal --padding 1,5 --margin 1,0
+  fzf-tmux -p70%,100% --color=border:bright-green --border=vertical
+
+  # Key bindings (CTRL-T, CTRL-R, ALT-C) will use these options
+  export FZF_TMUX_OPTS='-p100%,60% --color=border:green --border=horizontal --padding 1,5 --margin 1,0'
+  ```
+
+0.32.0
+------
+- Updated the scoring algorithm
+    - Different bonus points to different categories of word boundaries
+      (listed higher to lower bonus point)
+        - Word after whitespace characters or beginning of the string
+        - Word after common delimiter characters (`/,:;|`)
+        - Word after other non-word characters
+      ```sh
+      # foo/bar.sh` is preferred over `foo-bar.sh` on `bar`
+      fzf --query=bar --height=4 << EOF
+      foo-bar.sh
+      foo/bar.sh
+      EOF
+      ```
+- Added a new tiebreak `chunk`
+    - Favors the line with shorter matched chunk. A chunk is a set of
+      consecutive non-whitespace characters.
+    - Unlike the default `length`, this scheme works well with tabular input
+      ```sh
+      # length prefers item #1, because the whole line is shorter,
+      # chunk prefers item #2, because the matched chunk ("foo") is shorter
+      fzf --height=6 --header-lines=2 --tiebreak=chunk --reverse --query=fo << "EOF"
+      N | Field1 | Field2 | Field3
+      - | ------ | ------ | ------
+      1 | hello  | foobar | baz
+      2 | world  | foo    | bazbaz
+      EOF
+      ```
+    - If the input does not contain any spaces, `chunk` is equivalent to
+      `length`. But we're not going to set it as the default because it is
+      computationally more expensive.
+- Bug fixes and improvements
+
+0.31.0
+------
+- Added support for an alternative preview window layout that is activated
+  when the size of the preview window is smaller than a certain threshold.
+  ```sh
+  # If the width of the preview window is smaller than 50 columns,
+  # it will be displayed above the search window.
+  fzf --preview 'cat {}' --preview-window 'right,50%,border-left,<50(up,30%,border-bottom)'
+
+  # Or you can just hide it like so
+  fzf --preview 'cat {}' --preview-window '<50(hidden)'
+  ```
+- fzf now uses SGR mouse mode to properly support mouse on larger terminals
+- You can now use characters that do not satisfy `unicode.IsGraphic` constraint
+  for `--marker`, `--pointer`, and `--ellipsis`. Allows Nerd Fonts and stuff.
+  Use at your own risk.
+- Bug fixes and improvements
+- Shell extension
+    - `kill` completion now requires trigger sequence (`**`) for consistency
+
 0.30.0
 ------
 - Fixed cursor flickering over the screen by hiding it during rendering
diff --git a/LICENSE b/LICENSE
index 50aa5d92..533a79de 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2013-2021 Junegunn Choi
+Copyright (c) 2013-2022 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
diff --git a/Makefile b/Makefile
index 91c5711e..15808154 100644
--- a/Makefile
+++ b/Makefile
@@ -35,6 +35,7 @@ BINARYARM7     := fzf-$(GOOS)_arm7
 BINARYARM8     := fzf-$(GOOS)_arm8
 BINARYPPC64LE  := fzf-$(GOOS)_ppc64le
 BINARYRISCV64  := fzf-$(GOOS)_riscv64
+BINARYLOONG64  := fzf-$(GOOS)_loong64
 
 # https://en.wikipedia.org/wiki/Uname
 UNAME_M := $(shell uname -m)
@@ -62,6 +63,8 @@ else ifeq ($(UNAME_M),ppc64le)
 	BINARY := $(BINARYPPC64LE)
 else ifeq ($(UNAME_M),riscv64)
 	BINARY := $(BINARYRISCV64)
+else ifeq ($(UNAME_M),loongarch64)
+	BINARY := $(BINARYLOONG64)
 else
 $(error Build on $(UNAME_M) is not supported, yet.)
 endif
@@ -148,7 +151,11 @@ target/$(BINARYPPC64LE): $(SOURCES)
 target/$(BINARYRISCV64): $(SOURCES)
 	GOARCH=riscv64 $(GO) build $(BUILD_FLAGS) -o $@
 
+target/$(BINARYLOONG64): $(SOURCES)
+	GOARCH=loong64 $(GO) build $(BUILD_FLAGS) -o $@
+
 bin/fzf: target/$(BINARY) | bin
+	-rm -f bin/fzf
 	cp -f target/$(BINARY) bin/fzf
 
 docker:
diff --git a/README.md b/README.md
index 92eab8b7..ebd83e29 100644
--- a/README.md
+++ b/README.md
@@ -382,12 +382,11 @@ cd ~/github/fzf**<TAB>
 
 #### 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.
+Fuzzy completion for PIDs is provided for kill command.
 
 ```sh
 # Can select multiple processes with <TAB> or <Shift-TAB> keys
-kill -9 <TAB>
+kill -9 **<TAB>
 ```
 
 #### Host names
@@ -558,7 +557,7 @@ more details.
 
 ```sh
 FZF_DEFAULT_COMMAND='ps -ef' \
-  fzf --bind 'ctrl-r:reload($FZF_DEFAULT_COMMAND)' \
+  fzf --bind 'ctrl-r:reload(eval "$FZF_DEFAULT_COMMAND")' \
       --header 'Press CTRL-R to reload' --header-lines=1 \
       --height=50% --layout=reverse
 ```
@@ -567,7 +566,7 @@ FZF_DEFAULT_COMMAND='ps -ef' \
 
 ```sh
 FZF_DEFAULT_COMMAND='find . -type f' \
-  fzf --bind 'ctrl-d:reload(find . -type d),ctrl-f:reload($FZF_DEFAULT_COMMAND)' \
+  fzf --bind 'ctrl-d:reload(find . -type d),ctrl-f:reload(eval "$FZF_DEFAULT_COMMAND")' \
       --height=50% --layout=reverse
 ```
 
@@ -628,10 +627,7 @@ fzf --height 40% --layout reverse --info inline --border \
 
 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/
+More advanced examples can be found [here](https://github.com/junegunn/fzf/blob/master/ADVANCED.md).
 
 ----
 
@@ -663,10 +659,10 @@ default find command to traverse the file system while respecting
 
 ```sh
 # Feed the output of fd into fzf
-fd --type f | fzf
+fd --type f --strip-cwd-prefix | fzf
 
 # Setting fd as the default source for fzf
-export FZF_DEFAULT_COMMAND='fd --type f'
+export FZF_DEFAULT_COMMAND='fd --type f --strip-cwd-prefix'
 
 # Now fzf (w/o pipe) will use fd instead of find
 fzf
@@ -679,7 +675,7 @@ 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'
+export FZF_DEFAULT_COMMAND='fd --type f --strip-cwd-prefix --hidden --follow --exclude .git'
 ```
 
 #### Fish shell
diff --git a/bin/fzf-tmux b/bin/fzf-tmux
index 6a18cf8a..e461fd77 100755
--- a/bin/fzf-tmux
+++ b/bin/fzf-tmux
@@ -10,7 +10,6 @@ fail() {
 fzf="$(command -v fzf 2> /dev/null)" || fzf="$(dirname "$0")/fzf"
 [[ -x "$fzf" ]] || fail 'fzf executable not found'
 
-tmux_args=()
 args=()
 opt=""
 skip=""
@@ -58,7 +57,7 @@ while [[ $# -gt 0 ]]; do
       ;;
     -p*|-w*|-h*|-x*|-y*|-d*|-u*|-r*|-l*)
       if [[ "$arg" =~ ^-[pwhxy] ]]; then
-        [[ "$opt" =~ "-K -E" ]] || opt="-K -E"
+        [[ "$opt" =~ "-E" ]] || opt="-E"
       elif [[ "$arg" =~ ^.[lr] ]]; then
         opt="-h"
         if [[ "$arg" =~ ^.l ]]; then
@@ -119,8 +118,6 @@ while [[ $# -gt 0 ]]; do
       # "--" can be used to separate fzf-tmux options from fzf options to
       # avoid conflicts
       skip=1
-      tmux_args=("${args[@]}")
-      args=()
       continue
       ;;
     *)
@@ -139,7 +136,7 @@ fi
 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
+if [[ ! "$opt" =~ "-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'")
@@ -181,7 +178,14 @@ trap 'cleanup 1' SIGUSR1
 trap 'cleanup' EXIT
 
 envs="export TERM=$TERM "
-[[ "$opt" =~ "-K -E" ]] && FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS"
+if [[ "$opt" =~ "-E" ]]; then
+  FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS"
+  tmux_verson=$(tmux -V)
+  if [[ ! $tmux_verson =~ 3\.2 ]]; then
+    FZF_DEFAULT_OPTS="--border $FZF_DEFAULT_OPTS"
+    opt="-B $opt"
+  fi
+fi
 [[ -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"
@@ -195,7 +199,7 @@ close="; trap - EXIT SIGINT SIGTERM $close"
 
 export TMUX=$(cut -d , -f 1,2 <<< "$TMUX")
 mkfifo -m o+w $fifo2
-if [[ "$opt" =~ "-K -E" ]]; then
+if [[ "$opt" =~ "-E" ]]; then
   cat $fifo2 &
   if [[ -n "$term" ]] || [[ -t 0 ]]; then
     cat <<< "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
@@ -205,15 +209,7 @@ if [[ "$opt" =~ "-K -E" ]]; then
     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
+  tmux popup -d "$PWD" $opt "bash $argsf" > /dev/null 2>&1
   exit $?
 fi
 
@@ -227,7 +223,7 @@ else
 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 \
+  split-window -c "$PWD" $opt "bash -c 'exec -a fzf bash $argsf'" $swap \
   > /dev/null 2>&1 || { "$fzf" "${args[@]}"; exit $?; }
 cat $fifo2
 exit "$(cat $fifo3)"
diff --git a/go.mod b/go.mod
index 3eb15167..5a5bdcb2 100644
--- a/go.mod
+++ b/go.mod
@@ -1,20 +1,20 @@
 module github.com/junegunn/fzf
 
 require (
-	github.com/gdamore/tcell v1.4.0
-	github.com/mattn/go-isatty v0.0.14
-	github.com/mattn/go-runewidth v0.0.13
+	github.com/gdamore/tcell/v2 v2.5.3
+	github.com/mattn/go-isatty v0.0.16
+	github.com/mattn/go-runewidth v0.0.14
 	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/sys v0.0.0-20220406163625-3f8b81556e12
+	github.com/rivo/uniseg v0.4.2
+	github.com/saracen/walker v0.1.3
+	golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
 )
 
 require (
 	github.com/gdamore/encoding v1.0.0 // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
-	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
+	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
 	golang.org/x/text v0.3.7 // indirect
 )
 
diff --git a/go.sum b/go.sum
index f81baf26..5e774c68 100644
--- a/go.sum
+++ b/go.sum
@@ -1,29 +1,29 @@
 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/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0=
+github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
 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-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
+github.com/mattn/go-runewidth v0.0.14/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=
+github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
+github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/saracen/walker v0.1.3 h1:YtcKKmpRPy6XJTHJ75J2QYXXZYWnZNQxPCVqZSHVV/g=
+github.com/saracen/walker v0.1.3/go.mod h1:FU+7qU8DeQQgSZDmmThMJi93kPkLFgy0oVAcLxurjIk=
+golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
+golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12 h1:QyVthZKMsyaQwBTJE04jdNN0Pp5Fn9Qga0mrgxyERQM=
-golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/install b/install
index 4efc3705..ae21ee89 100755
--- a/install
+++ b/install
@@ -2,7 +2,7 @@
 
 set -u
 
-version=0.30.0
+version=0.34.0
 auto_completion=
 key_bindings=
 update_config=2
@@ -256,7 +256,7 @@ for shell in $shells; do
 # Setup fzf
 # ---------
 if [[ ! "\$PATH" == *$fzf_base_esc/bin* ]]; then
-  export PATH="\${PATH:+\${PATH}:}$fzf_base/bin"
+  PATH="\${PATH:+\${PATH}:}$fzf_base/bin"
 fi
 
 # Auto-completion
diff --git a/install.ps1 b/install.ps1
index e154b108..8b4070b9 100644
--- a/install.ps1
+++ b/install.ps1
@@ -1,4 +1,4 @@
-$version="0.30.0"
+$version="0.34.0"
 
 $fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
 
diff --git a/main.go b/main.go
index 24a8b3d2..fe17a562 100644
--- a/main.go
+++ b/main.go
@@ -5,7 +5,7 @@ import (
 	"github.com/junegunn/fzf/src/protector"
 )
 
-var version string = "0.30"
+var version string = "0.34"
 var revision string = "devel"
 
 func main() {
diff --git a/man/man1/fzf-tmux.1 b/man/man1/fzf-tmux.1
index 5667c7f1..f3b35403 100644
--- a/man/man1/fzf-tmux.1
+++ b/man/man1/fzf-tmux.1
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ..
-.TH fzf-tmux 1 "Apr 2022" "fzf 0.30.0" "fzf-tmux - open fzf in tmux split pane"
+.TH fzf-tmux 1 "Sep 2022" "fzf 0.34.0" "fzf-tmux - open fzf in tmux split pane"
 
 .SH NAME
 fzf-tmux - open fzf in tmux split pane
diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index 906ab199..bf71e7cd 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ..
-.TH fzf 1 "Apr 2022" "fzf 0.30.0" "fzf - a command-line fuzzy finder"
+.TH fzf 1 "Sep 2022" "fzf 0.34.0" "fzf - a command-line fuzzy finder"
 
 .SH NAME
 fzf - a command-line fuzzy finder
@@ -51,6 +51,18 @@ Case-sensitive match
 .B "--literal"
 Do not normalize latin script letters for matching.
 .TP
+.BI "--scheme=" SCHEME
+Choose scoring scheme tailored for different types of input.
+
+.br
+.BR default "  Generic scoring scheme designed to work well with any type of input"
+.br
+.BR path "     Scoring scheme for paths (additional bonus point only after path separator)
+.br
+.BR history "  Scoring scheme for command history (no additional bonus points).
+         Sets \fB--tiebreak=index\fR as well.
+.br
+.TP
 .BI "--algo=" TYPE
 Fuzzy matching algorithm (default: v2)
 
@@ -95,6 +107,8 @@ Comma-separated list of sort criteria to apply when the scores are tied.
 .br
 .BR length "  Prefers line with shorter length"
 .br
+.BR chunk "   Prefers line with shorter matched chunk (delimited by whitespaces)"
+.br
 .BR begin "   Prefers line with matched substring closer to the beginning"
 .br
 .BR end "     Prefers line with matched substring closer to the end"
@@ -163,9 +177,11 @@ actions are affected:
 Label characters for \fBjump\fR and \fBjump-accept\fR
 .SS Layout
 .TP
-.BI "--height=" "HEIGHT[%]"
+.BI "--height=" "[~]HEIGHT[%]"
 Display fzf window below the cursor with the given height instead of using
-the full screen.
+the full screen. When prefixed with \fB~\fR, fzf will automatically determine
+the height in the range according to the input size. Note that adaptive height
+is not compatible with top/bottom margin and padding given in percent size.
 .TP
 .BI "--min-height=" "HEIGHT"
 Minimum height when \fB--height\fR is given in percent (default: 10).
@@ -187,21 +203,21 @@ Choose the layout (default: default)
 A synonym for \fB--layout=reverse\fB
 
 .TP
-.BI "--border" [=STYLE]
+.BI "--border" [=BORDER_OPT]
 Draw border around the finder
 
 .br
-.BR rounded "     Border with rounded corners (default)"
+.BR rounded "       Border with rounded corners (default)"
 .br
-.BR sharp "       Border with sharp corners"
+.BR sharp "         Border with sharp corners"
 .br
-.BR horizontal "  Horizontal lines above and below the finder"
+.BR horizontal "    Horizontal lines above and below the finder"
 .br
-.BR vertical "    Vertical lines on each side of the finder"
+.BR vertical "      Vertical lines on each side of the finder"
 .br
-.BR top
+.BR top " (up)"
 .br
-.BR bottom
+.BR bottom " (down)"
 .br
 .BR left
 .br
@@ -265,7 +281,7 @@ e.g.
 
 .TP
 .BI "--info=" "STYLE"
-Determines the display style of finder info.
+Determines the display style of finder info (match counters).
 
 .br
 .BR default "       Display on the next line to the prompt"
@@ -376,6 +392,7 @@ color mappings.
     \fBreverse\fR
     \fBdim\fR
     \fBitalic\fR
+    \fBstrikethrough\fR
 
 .B EXAMPLES:
 
@@ -450,7 +467,7 @@ 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
+                      --preview "awk '{sum+=\\$1} END {print sum}' {+f}"\fR
 
 Note that you can escape a placeholder pattern by prepending a backslash.
 
@@ -470,7 +487,7 @@ e.g.
       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]"
+.BI "--preview-window=" "[POSITION][,SIZE[%]][,border-BORDER_OPT][,[no]wrap][,[no]follow][,[no]cycle][,[no]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]"
 
 .RS
 .B POSITION: (default: right)
@@ -488,9 +505,9 @@ default until \fBtoggle-preview\fR action is triggered.
 execute the command in the background.
 
 * Long lines are truncated by default. Line wrap can be enabled with
-\fB:wrap\fR flag.
+\fBwrap\fR flag.
 
-* Preview window will automatically scroll to the bottom when \fB:follow\fR
+* Preview window will automatically scroll to the bottom when \fBfollow\fR
 flag is set, similarly to how \fBtail -f\fR works.
 
 .RS
@@ -502,7 +519,7 @@ e.g.
       done'\fR
 .RE
 
-* Cyclic scrolling is enabled with \fB:cycle\fR flag.
+* Cyclic scrolling is enabled with \fBcycle\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.
@@ -552,6 +569,15 @@ e.g.
      fzf --preview 'bat --style=full --color=always {}' --preview-window '~3'\fR
 .RE
 
+* You can specify an alternative set of options that are used only when the size
+  of the preview window is below a certain threshold. Note that only one
+  alternative layout is allowed.
+
+.RS
+e.g.
+      \fBfzf --preview 'cat {}' --preview-window 'right,border-left,<30(up,30%,border-bottom)'\fR
+.RE
+
 .SS Scripting
 .TP
 .BI "-q, --query=" "STR"
@@ -592,12 +618,6 @@ Read input delimited by ASCII NUL characters instead of newline characters
 .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.
diff --git a/plugin/fzf.vim b/plugin/fzf.vim
index 3c705e91..40f01a0e 100644
--- a/plugin/fzf.vim
+++ b/plugin/fzf.vim
@@ -96,7 +96,12 @@ function! fzf#shellescape(arg, ...)
   if shell =~# 'cmd.exe$'
     return s:shellesc_cmd(a:arg)
   endif
-  return s:fzf_call('shellescape', a:arg)
+  try
+    let [shell, &shell] = [&shell, shell]
+    return s:fzf_call('shellescape', a:arg)
+  finally
+    let [shell, &shell] = [&shell, shell]
+  endtry
 endfunction
 
 function! s:fzf_getcwd()
@@ -138,7 +143,7 @@ function! fzf#install()
     if !filereadable(script)
       throw script.' not found'
     endif
-    let script = 'powershell -ExecutionPolicy Bypass -file ' . script
+    let script = 'powershell -ExecutionPolicy Bypass -file ' . shellescape(script)
   else
     let script = s:base_dir.'/install'
     if !executable(script)
@@ -159,7 +164,7 @@ 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 command = (&shell =~ 'powershell' ? '&' : '') . shellescape(a:bin) . ' --version --no-height'
   let output = systemlist(command)
   if v:shell_error || empty(output)
     return ''
@@ -337,7 +342,8 @@ function! s:common_sink(action, lines) abort
 endfunction
 
 function! s:get_color(attr, ...)
-  let gui = !s:is_win && !has('win32unix') && has('termguicolors') && &termguicolors
+  " Force 24 bit colors: g:fzf_force_termguicolors (temporary workaround for https://github.com/junegunn/fzf.vim/issues/1152)
+  let gui = get(g:, 'fzf_force_termguicolors', 0) || (!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
@@ -458,7 +464,7 @@ try
   let temps  = { 'result': s:fzf_tempname() }
   let optstr = s:evaluate_opts(get(dict, 'options', ''))
   try
-    let fzf_exec = fzf#shellescape(fzf#exec())
+    let fzf_exec = shellescape(fzf#exec())
   catch
     throw v:exception
   endtry
diff --git a/shell/completion.bash b/shell/completion.bash
index 21aa4505..51687a08 100644
--- a/shell/completion.bash
+++ b/shell/completion.bash
@@ -37,7 +37,7 @@ 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
+  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
@@ -55,8 +55,8 @@ __fzf_orig_completion() {
       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 "
+      if [[ "$l" = *" -o nospace "* ]] && [[ ! "${__fzf_nospace_commands-}" = *" $cmd "* ]]; then
+        __fzf_nospace_commands="${__fzf_nospace_commands-} $cmd "
       fi
     fi
   done
@@ -139,17 +139,18 @@ _fzf_handle_dynamic_completion() {
   shift
   orig_cmd="$1"
   orig_var="_fzf_orig_completion_$cmd"
-  orig="${!orig_var##*#}"
+  orig="${!orig_var-}"
+  orig="${orig##*#}"
   if [[ -n "$orig" ]] && type "$orig" > /dev/null 2>&1; then
     $orig "$@"
-  elif [[ -n "$_fzf_completion_loader" ]]; then
+  elif [[ -n "${_fzf_completion_loader-}" ]]; then
     orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
     _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
+      if [[ "${__fzf_nospace_commands-}" = *" $orig_cmd "* ]]; then
         eval "${orig_complete/ -F / -o nospace -F }"
       else
         eval "$orig_complete"
@@ -161,7 +162,11 @@ _fzf_handle_dynamic_completion() {
 
 __fzf_generic_path_completion() {
   local cur base dir leftover matches trigger cmd
-  cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
+  cmd="${COMP_WORDS[0]}"
+  if [[ $cmd == \\* ]]; then
+    cmd="${cmd:1}"
+  fi
+  cmd="${cmd//[^A-Za-z0-9_=]/_}"
   COMPREPLY=()
   trigger=${FZF_COMPLETION_TRIGGER-'**'}
   cur="${COMP_WORDS[COMP_CWORD]}"
@@ -169,6 +174,7 @@ __fzf_generic_path_completion() {
     base=${cur:0:${#cur}-${#trigger}}
     eval "base=$base"
 
+    dir=
     [[ $base = *"/"* ]] && dir="$base"
     while true; do
       if [[ -z "$dir" ]] || [[ -d "$dir" ]]; then
@@ -176,11 +182,11 @@ __fzf_generic_path_completion() {
         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"
+        matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do
+          printf "%q " "${item%$3}$3"
         done)
         matches=${matches% }
-        [[ -z "$3" ]] && [[ "$__fzf_nospace_commands" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
+        [[ -z "$3" ]] && [[ "${__fzf_nospace_commands-}" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
         if [[ -n "$matches" ]]; then
           COMPREPLY=( "$matches" )
         else
@@ -232,7 +238,7 @@ _fzf_complete() {
   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=$(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")
@@ -260,14 +266,6 @@ _fzf_dir_completion() {
 }
 
 _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 "$@"
 }
 
@@ -283,7 +281,7 @@ _fzf_proc_completion_post() {
 
 _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 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
@@ -292,18 +290,22 @@ _fzf_host_completion() {
 
 _fzf_var_completion() {
   _fzf_complete -m -- "$@" < <(
-    declare -xp | sed 's/=.*//' | sed 's/.* //'
+    declare -xp | sed -En 's|^declare [^ ]+ ([^=]+).*|\1|p'
   )
 }
 
 _fzf_alias_completion() {
   _fzf_complete -m -- "$@" < <(
-    alias | sed 's/=.*//' | sed 's/.* //'
+    alias | sed -En 's|^alias ([^=]+).*|\1|p'
   )
 }
 
 # fzf options
 complete -o default -F _fzf_opts_completion fzf
+# fzf-tmux is a thin fzf wrapper that has only a few more options than fzf
+# itself. As a quick improvement we take fzf's completion. Adding the few extra
+# fzf-tmux specific options (like `-w WIDTH`) are left as a future patch.
+complete -o default -F _fzf_opts_completion fzf-tmux
 
 d_cmds="${FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}"
 a_cmds="
@@ -329,7 +331,7 @@ __fzf_defc() {
   func="$2"
   opts="$3"
   orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
-  orig="${!orig_var}"
+  orig="${!orig_var-}"
   if [[ -n "$orig" ]]; then
     printf -v def "$orig" "$func"
     eval "$def"
@@ -348,9 +350,6 @@ 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() {
@@ -373,9 +372,10 @@ _fzf_setup_completion() {
   done
 }
 
-# Environment variables / Aliases / Hosts
+# Environment variables / Aliases / Hosts / Process
 _fzf_setup_completion 'var'   export unset
 _fzf_setup_completion 'alias' unalias
 _fzf_setup_completion 'host'  ssh telnet
+_fzf_setup_completion 'proc'  kill
 
 fi
diff --git a/shell/completion.zsh b/shell/completion.zsh
index f12afca1..807d7b3a 100644
--- a/shell/completion.zsh
+++ b/shell/completion.zsh
@@ -99,9 +99,9 @@ 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
+  elif [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; }; then
     shift
-    if [ -n "$FZF_TMUX_OPTS" ]; then
+    if [ -n "${FZF_TMUX_OPTS-}" ]; then
       fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@"
     else
       fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@"
@@ -145,8 +145,9 @@ __fzf_generic_path_completion() {
       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 "
+      matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-}" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
+        item="${item%$suffix}$suffix"
+        echo -n "${(q)item} "
       done)
       matches=${matches% }
       if [ -n "$matches" ]; then
@@ -183,7 +184,7 @@ _fzf_complete() {
   args=("$@")
   sep=
   for i in {0..${#args[@]}}; do
-    if [[ "${args[$i]}" = -- ]]; then
+    if [[ "${args[$i]-}" = -- ]]; then
       sep=$i
       break
     fi
@@ -207,7 +208,7 @@ _fzf_complete() {
   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' ' ')
+  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
@@ -224,7 +225,7 @@ _fzf_complete_telnet() {
 _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 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
@@ -278,19 +279,13 @@ fzf-completion() {
   [ -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]}"
+  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
diff --git a/shell/key-bindings.bash b/shell/key-bindings.bash
index 701cf5b0..d83f9d3e 100644
--- a/shell/key-bindings.bash
+++ b/shell/key-bindings.bash
@@ -19,7 +19,7 @@ __fzf_select__() {
     -o -type f -print \
     -o -type d -print \
     -o -type l -print 2> /dev/null | cut -b3-"}"
-  opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS -m"
+  opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m"
   eval "$cmd" |
     FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" |
     while read -r item; do
@@ -30,7 +30,7 @@ __fzf_select__() {
 if [[ $- =~ i ]]; then
 
 __fzfcmd() {
-  [[ -n "$TMUX_PANE" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "$FZF_TMUX_OPTS" ]]; } &&
+  [[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; } &&
     echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
 }
 
@@ -44,13 +44,13 @@ __fzf_cd__() {
   local cmd opts dir
   cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
     -o -type d -print 2> /dev/null | cut -b3-"}"
-  opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS +m"
+  opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-} +m"
   dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)) && printf 'builtin cd -- %q' "$dir"
 }
 
 __fzf_history__() {
   local output opts script
-  opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m --read0"
+  opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort ${FZF_CTRL_R_OPTS-} +m --read0"
   script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++'
   output=$(
     builtin fc -lnr -2147483648 |
diff --git a/shell/key-bindings.fish b/shell/key-bindings.fish
index 36b3aa34..743c7c18 100644
--- a/shell/key-bindings.fish
+++ b/shell/key-bindings.fish
@@ -53,7 +53,7 @@ function fzf_key_bindings
   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 -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT $FZF_DEFAULT_OPTS --scheme=history --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.)
@@ -87,7 +87,7 @@ function fzf_key_bindings
       eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)' +m --query "'$fzf_query'"' | read -l result
 
       if [ -n "$result" ]
-        builtin cd -- $result
+        cd -- $result
 
         # Remove last token from commandline.
         commandline -t ""
diff --git a/shell/key-bindings.zsh b/shell/key-bindings.zsh
index fe219bf4..dfb473cc 100644
--- a/shell/key-bindings.zsh
+++ b/shell/key-bindings.zsh
@@ -46,7 +46,7 @@ __fsel() {
     -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
+  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=$?
@@ -55,7 +55,7 @@ __fsel() {
 }
 
 __fzfcmd() {
-  [ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; } &&
+  [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; } &&
     echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
 }
 
@@ -75,7 +75,7 @@ fzf-cd-widget() {
   local cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
     -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)"
+  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
@@ -97,8 +97,8 @@ bindkey -M viins '\ec' fzf-cd-widget
 fzf-history-widget() {
   local selected num
   setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
-  selected=( $(fc -rl 1 | perl -ne 'print if !$seen{(/^\s*[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)) )
+  selected=( $(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
+    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
   local ret=$?
   if [ -n "$selected" ]; then
     num=$selected[1]
diff --git a/src/algo/algo.go b/src/algo/algo.go
index 40fb2af6..50dd466c 100644
--- a/src/algo/algo.go
+++ b/src/algo/algo.go
@@ -80,6 +80,7 @@ Scoring criteria
 import (
 	"bytes"
 	"fmt"
+	"os"
 	"strings"
 	"unicode"
 	"unicode/utf8"
@@ -89,6 +90,10 @@ import (
 
 var DEBUG bool
 
+var delimiterChars = "/,:;|"
+
+const whiteChars = " \t\n\v\f\r\x85\xA0"
+
 func indexAt(index int, max int, forward bool) int {
 	if forward {
 		return index
@@ -140,16 +145,51 @@ const (
 	bonusFirstCharMultiplier = 2
 )
 
+var (
+	// Extra bonus for word boundary after whitespace character or beginning of the string
+	bonusBoundaryWhite int16 = bonusBoundary + 2
+
+	// Extra bonus for word boundary after slash, colon, semi-colon, and comma
+	bonusBoundaryDelimiter int16 = bonusBoundary + 1
+
+	initialCharClass charClass = charWhite
+)
+
 type charClass int
 
 const (
-	charNonWord charClass = iota
+	charWhite charClass = iota
+	charNonWord
+	charDelimiter
 	charLower
 	charUpper
 	charLetter
 	charNumber
 )
 
+func Init(scheme string) bool {
+	switch scheme {
+	case "default":
+		bonusBoundaryWhite = bonusBoundary + 2
+		bonusBoundaryDelimiter = bonusBoundary + 1
+	case "path":
+		bonusBoundaryWhite = bonusBoundary
+		bonusBoundaryDelimiter = bonusBoundary + 1
+		if os.PathSeparator == '/' {
+			delimiterChars = "/"
+		} else {
+			delimiterChars = string([]rune{os.PathSeparator, '/'})
+		}
+		initialCharClass = charDelimiter
+	case "history":
+		bonusBoundaryWhite = bonusBoundary
+		bonusBoundaryDelimiter = bonusBoundary
+	default:
+		return false
+	}
+	return true
+}
+
 func posArray(withPos bool, len int) *[]int {
 	if withPos {
 		pos := make([]int, 0, len)
@@ -181,6 +221,10 @@ func charClassOfAscii(char rune) charClass {
 		return charUpper
 	} else if char >= '0' && char <= '9' {
 		return charNumber
+	} else if strings.IndexRune(whiteChars, char) >= 0 {
+		return charWhite
+	} else if strings.IndexRune(delimiterChars, char) >= 0 {
+		return charDelimiter
 	}
 	return charNonWord
 }
@@ -194,6 +238,10 @@ func charClassOfNonAscii(char rune) charClass {
 		return charNumber
 	} else if unicode.IsLetter(char) {
 		return charLetter
+	} else if unicode.IsSpace(char) {
+		return charWhite
+	} else if strings.IndexRune(delimiterChars, char) >= 0 {
+		return charDelimiter
 	}
 	return charNonWord
 }
@@ -206,22 +254,33 @@ func charClassOf(char rune) charClass {
 }
 
 func bonusFor(prevClass charClass, class charClass) int16 {
-	if prevClass == charNonWord && class != charNonWord {
-		// Word boundary
-		return bonusBoundary
-	} else if prevClass == charLower && class == charUpper ||
+	if class > charNonWord {
+		if prevClass == charWhite {
+			// Word boundary after whitespace
+			return bonusBoundaryWhite
+		} else if prevClass == charDelimiter {
+			// Word boundary after a delimiter character
+			return bonusBoundaryDelimiter
+		} else if prevClass == charNonWord {
+			// Word boundary
+			return bonusBoundary
+		}
+	}
+	if prevClass == charLower && class == charUpper ||
 		prevClass != charNumber && class == charNumber {
 		// camelCase letter123
 		return bonusCamel123
 	} else if class == charNonWord {
 		return bonusNonWord
+	} else if class == charWhite {
+		return bonusBoundaryWhite
 	}
 	return 0
 }
 
 func bonusAt(input *util.Chars, idx int) int16 {
 	if idx == 0 {
-		return bonusBoundary
+		return bonusBoundaryWhite
 	}
 	return bonusFor(charClassOf(input.Get(idx-1)), charClassOf(input.Get(idx)))
 }
@@ -377,7 +436,7 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
 	// 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
+	pchar0, pchar, prevH0, prevClass, inGap := pattern[0], pattern[0], int16(0), initialCharClass, false
 	Tsub := T[idx:]
 	H0sub, C0sub, Bsub := H0[idx:][:len(Tsub)], C0[idx:][:len(Tsub)], B[idx:][:len(Tsub)]
 	for off, char := range Tsub {
@@ -417,7 +476,7 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
 			C0sub[off] = 1
 			if M == 1 && (forward && score > maxScore || !forward && score >= maxScore) {
 				maxScore, maxScorePos = score, idx+off
-				if forward && bonus == bonusBoundary {
+				if forward && bonus >= bonusBoundary {
 					break
 				}
 			}
@@ -486,11 +545,14 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
 				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 consecutive > 1 {
+					fb := B[col-int(consecutive)+1]
+					// Break consecutive chunk
+					if b >= bonusBoundary && b > fb {
+						consecutive = 1
+					} else {
+						b = util.Max16(b, util.Max16(bonusConsecutive, fb))
+					}
 				}
 				if s1+b < s2 {
 					s1 += Bsub[off]
@@ -555,7 +617,7 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
 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
+	prevClass := charWhite
 	if sidx > 0 {
 		prevClass = charClassOf(text.Get(sidx - 1))
 	}
@@ -583,7 +645,7 @@ func calculateScore(caseSensitive bool, normalize bool, text *util.Chars, patter
 				firstBonus = bonus
 			} else {
 				// Break consecutive chunk
-				if bonus == bonusBoundary {
+				if bonus >= bonusBoundary && bonus > firstBonus {
 					firstBonus = bonus
 				}
 				bonus = util.Max16(util.Max16(bonus, firstBonus), bonusConsecutive)
@@ -741,7 +803,7 @@ func ExactMatchNaive(caseSensitive bool, normalize bool, forward bool, text *uti
 				if bonus > bestBonus {
 					bestPos, bestBonus = index, bonus
 				}
-				if bonus == bonusBoundary {
+				if bonus >= bonusBoundary {
 					break
 				}
 				index -= pidx - 1
@@ -877,8 +939,8 @@ func EqualMatch(caseSensitive bool, normalize bool, forward bool, text *util.Cha
 		match = runesStr == string(pattern)
 	}
 	if match {
-		return Result{trimmedLen, trimmedLen + lenPattern, (scoreMatch+bonusBoundary)*lenPattern +
-			(bonusFirstCharMultiplier-1)*bonusBoundary}, nil
+		return Result{trimmedLen, trimmedLen + lenPattern, (scoreMatch+int(bonusBoundaryWhite))*lenPattern +
+			(bonusFirstCharMultiplier-1)*int(bonusBoundaryWhite)}, nil
 	}
 	return Result{-1, -1, 0}, nil
 }
diff --git a/src/ansi.go b/src/ansi.go
index 698bf89e..24acc396 100644
--- a/src/ansi.go
+++ b/src/ansi.go
@@ -55,6 +55,9 @@ func (s *ansiState) ToString() string {
 	if s.attr&tui.Reverse > 0 {
 		ret += "7;"
 	}
+	if s.attr&tui.StrikeThrough > 0 {
+		ret += "9;"
+	}
 	ret += toAnsiString(s.fg, 30) + toAnsiString(s.bg, 40)
 
 	return "\x1b[" + strings.TrimSuffix(ret, ";") + "m"
@@ -85,7 +88,7 @@ func isPrint(c uint8) bool {
 }
 
 func matchOperatingSystemCommand(s string) int {
-	// `\x1b][0-9];[[:print:]]+(?:\x1b\\\\|\x07)`
+	// `\x1b][0-9][;:][[:print:]]+(?:\x1b\\\\|\x07)`
 	//                        ^ match starting here
 	//
 	i := 5 // prefix matched in nextAnsiEscapeSequence()
@@ -103,30 +106,37 @@ func matchOperatingSystemCommand(s string) int {
 }
 
 func matchControlSequence(s string) int {
-	// `\x1b[\\[()][0-9;?]*[a-zA-Z@]`
-	//                    ^ match starting here
+	// `\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) {
+	for ; i < len(s); i++ {
 		c := s[i]
-		if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '@' {
-			return i + 1
+		switch c {
+		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';', ':', '?':
+			// ok
+		default:
+			if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '@' {
+				return i + 1
+			}
+			return -1
 		}
 	}
 	return -1
 }
 
 func isCtrlSeqStart(c uint8) bool {
-	return c == '\\' || c == '[' || c == '(' || c == ')'
+	switch c {
+	case '\\', '[', '(', ')':
+		return true
+	}
+	return false
 }
 
 // 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)"
-//
+// "(?:\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
@@ -154,16 +164,16 @@ Loop:
 				return i - n, i + 1
 			}
 		case '\x1b':
-			// match: `\x1b[\\[()][0-9;?]*[a-zA-Z@]`
+			// 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)`
+			// 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]) {
+				(s[i+3] == ';' || s[i+3] == ':') && isPrint(s[i+4]) {
 
 				if j := matchOperatingSystemCommand(s[i:]); j != -1 {
 					return i, i + j
@@ -280,9 +290,20 @@ func extractColor(str string, state *ansiState, proc func(string, *ansiState) bo
 	return trimmed, nil, state
 }
 
-func parseAnsiCode(s string) (int, string) {
+func parseAnsiCode(s string, delimiter byte) (int, byte, string) {
 	var remaining string
-	if i := strings.IndexByte(s, ';'); i >= 0 {
+	i := -1
+	if delimiter == 0 {
+		// Faster than strings.IndexAny(";:")
+		i = strings.IndexByte(s, ';')
+		if i < 0 {
+			i = strings.IndexByte(s, ':')
+		}
+	} else {
+		i = strings.IndexByte(s, delimiter)
+	}
+	if i >= 0 {
+		delimiter = s[i]
 		remaining = s[i+1:]
 		s = s[:i]
 	}
@@ -294,14 +315,14 @@ func parseAnsiCode(s string) (int, string) {
 		for _, ch := range []byte(s) {
 			ch -= '0'
 			if ch > 9 {
-				return -1, remaining
+				return -1, delimiter, remaining
 			}
 			code = code*10 + int(ch)
 		}
-		return code, remaining
+		return code, delimiter, remaining
 	}
 
-	return -1, remaining
+	return -1, delimiter, remaining
 }
 
 func interpretCode(ansiCode string, prevState *ansiState) ansiState {
@@ -329,9 +350,10 @@ func interpretCode(ansiCode string, prevState *ansiState) ansiState {
 	state256 := 0
 	ptr := &state.fg
 
+	var delimiter byte = 0
 	for len(ansiCode) != 0 {
 		var num int
-		if num, ansiCode = parseAnsiCode(ansiCode); num != -1 {
+		if num, delimiter, ansiCode = parseAnsiCode(ansiCode, delimiter); num != -1 {
 			switch state256 {
 			case 0:
 				switch num {
@@ -357,6 +379,8 @@ func interpretCode(ansiCode string, prevState *ansiState) ansiState {
 					state.attr = state.attr | tui.Blink
 				case 7:
 					state.attr = state.attr | tui.Reverse
+				case 9:
+					state.attr = state.attr | tui.StrikeThrough
 				case 23: // tput rmso
 					state.attr = state.attr &^ tui.Italic
 				case 24: // tput rmul
diff --git a/src/core.go b/src/core.go
index 17198f5b..2ddddc35 100644
--- a/src/core.go
+++ b/src/core.go
@@ -1,28 +1,4 @@
-/*
-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 implements fzf, a command-line fuzzy finder.
 package fzf
 
 import (
@@ -146,18 +122,20 @@ func Run(opts *Options, version string, revision string) {
 
 	// Matcher
 	forward := true
-	for _, cri := range opts.Criteria[1:] {
-		if cri == byEnd {
+	withPos := false
+	for idx := len(opts.Criteria) - 1; idx > 0; idx-- {
+		switch opts.Criteria[idx] {
+		case byChunk:
+			withPos = true
+		case byEnd:
 			forward = false
-			break
-		}
-		if cri == byBegin {
-			break
+		case byBegin:
+			forward = true
 		}
 	}
 	patternBuilder := func(runes []rune) *Pattern {
 		return BuildPattern(
-			opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward,
+			opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos,
 			opts.Filter == nil, opts.Nth, opts.Delimiter, runes)
 	}
 	matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox)
@@ -216,10 +194,17 @@ func Run(opts *Options, version string, revision string) {
 
 	// Terminal I/O
 	terminal := NewTerminal(opts, eventBox)
+	maxFit := 0 // Maximum number of items that can fit on screen
+	padHeight := 0
+	heightUnknown := opts.Height.auto
+	if heightUnknown {
+		maxFit, padHeight = terminal.MaxFitAndPad(opts)
+	}
 	deferred := opts.Select1 || opts.Exit0
 	go terminal.Loop()
-	if !deferred {
-		terminal.startChan <- true
+	if !deferred && !heightUnknown {
+		// Start right away
+		terminal.startChan <- fitpad{-1, -1}
 	}
 
 	// Event coordination
@@ -238,7 +223,19 @@ func Run(opts *Options, version string, revision string) {
 		go reader.restart(command)
 	}
 	eventBox.Watch(EvtReadNew)
+	total := 0
 	query := []rune{}
+	determine := func(final bool) {
+		if heightUnknown {
+			if total >= maxFit || final {
+				heightUnknown = false
+				terminal.startChan <- fitpad{util.Min(total, maxFit), padHeight}
+			}
+		} else if deferred {
+			deferred = false
+			terminal.startChan <- fitpad{-1, -1}
+		}
+	}
 	for {
 		delay := true
 		ticks++
@@ -271,11 +268,15 @@ func Run(opts *Options, version string, revision string) {
 						reading = reading && evt == EvtReadNew
 					}
 					snapshot, count := chunkList.Snapshot()
-					terminal.UpdateCount(count, !reading, value.(*string))
+					total = count
+					terminal.UpdateCount(total, !reading, value.(*string))
 					if opts.Sync {
 						opts.Sync = false
 						terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false)
 					}
+					if heightUnknown && !deferred {
+						determine(!reading)
+					}
 					reset := clearCache()
 					matcher.Reset(snapshot, input(reset), false, !reading, sort, reset)
 
@@ -317,8 +318,7 @@ func Run(opts *Options, version string, revision string) {
 						if deferred {
 							count := val.Length()
 							if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 {
-								deferred = false
-								terminal.startChan <- true
+								determine(val.final)
 							} else if val.final {
 								if opts.Exit0 && count == 0 || opts.Select1 && count == 1 {
 									if opts.PrintQuery {
@@ -335,8 +335,7 @@ func Run(opts *Options, version string, revision string) {
 									}
 									os.Exit(exitNoMatch)
 								}
-								deferred = false
-								terminal.startChan <- true
+								determine(val.final)
 							}
 						}
 						terminal.UpdateList(val, clearSelection())
diff --git a/src/options.go b/src/options.go
index b0afacda..e17efc96 100644
--- a/src/options.go
+++ b/src/options.go
@@ -21,9 +21,9 @@ const usage = `usage: fzf [options]
     -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
+    --scheme=SCHEME       Scoring scheme [default|path|history]
     --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
@@ -35,7 +35,7 @@ const usage = `usage: fzf [options]
     --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]
+                          when the scores are tied [length|chunk|begin|end|index]
                           (default: length)
 
   Interface
@@ -53,8 +53,10 @@ const usage = `usage: fzf [options]
     --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
+    --height=[~]HEIGHT[%] Display fzf window below the cursor with the given
+                          height instead of using fullscreen.
+                          If prefixed with '~', fzf will determine the height
+                          according to the input size.
     --min-height=HEIGHT   Minimum height when --height is given in percent
                           (default: 10)
     --layout=LAYOUT       Choose layout: [default|reverse|reverse-list]
@@ -89,7 +91,7 @@ const usage = `usage: fzf [options]
                           [,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden]
                           [,border-BORDER_OPT]
                           [,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
-                          [,default]
+                          [,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]
 
   Scripting
     -q, --query=STR       Start the finder with the given query
@@ -125,11 +127,18 @@ type criterion int
 
 const (
 	byScore criterion = iota
+	byChunk
 	byLength
 	byBegin
 	byEnd
 )
 
+type heightSpec struct {
+	size    float64
+	percent bool
+	auto    bool
+}
+
 type sizeSpec struct {
 	size    float64
 	percent bool
@@ -175,10 +184,18 @@ type previewOpts struct {
 	follow      bool
 	border      tui.BorderShape
 	headerLines int
+	threshold   int
+	alternative *previewOpts
+}
+
+func (a previewOpts) aboveOrBelow() bool {
+	return a.size.size > 0 && (a.position == posUp || a.position == posDown)
 }
 
 func (a previewOpts) sameLayout(b previewOpts) bool {
-	return a.size == b.size && a.position == b.position && a.border == b.border && a.hidden == b.hidden
+	return a.size == b.size && a.position == b.position && a.border == b.border && a.hidden == b.hidden && a.threshold == b.threshold &&
+		(a.alternative != nil && b.alternative != nil && a.alternative.sameLayout(*b.alternative) ||
+			a.alternative == nil && b.alternative == nil)
 }
 
 func (a previewOpts) sameContentLayout(b previewOpts) bool {
@@ -189,6 +206,7 @@ func (a previewOpts) sameContentLayout(b previewOpts) bool {
 type Options struct {
 	Fuzzy       bool
 	FuzzyAlgo   algo.Algo
+	Scheme      string
 	Extended    bool
 	Phony       bool
 	Case        Case
@@ -205,7 +223,7 @@ type Options struct {
 	Theme       *tui.ColorTheme
 	Black       bool
 	Bold        bool
-	Height      sizeSpec
+	Height      heightSpec
 	MinHeight   int
 	Layout      layoutType
 	Cycle       bool
@@ -247,13 +265,14 @@ type Options struct {
 }
 
 func defaultPreviewOpts(command string) previewOpts {
-	return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.BorderRounded, 0}
+	return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.BorderRounded, 0, 0, nil}
 }
 
 func defaultOptions() *Options {
 	return &Options{
 		Fuzzy:       true,
 		FuzzyAlgo:   algo.FuzzyMatchV2,
+		Scheme:      "default",
 		Extended:    true,
 		Phony:       false,
 		Case:        CaseSmart,
@@ -436,6 +455,15 @@ func parseAlgo(str string) algo.Algo {
 	return algo.FuzzyMatchV2
 }
 
+func processScheme(opts *Options) {
+	if !algo.Init(opts.Scheme) {
+		errorExit("invalid scoring scheme (expected: default|path|history)")
+	}
+	if opts.Scheme == "history" {
+		opts.Criteria = []criterion{byScore}
+	}
+}
+
 func parseBorder(str string, optional bool) tui.BorderShape {
 	switch str {
 	case "rounded":
@@ -607,6 +635,7 @@ func parseKeyChords(str string, message string) map[tui.Event]string {
 func parseTiebreak(str string) []criterion {
 	criteria := []criterion{byScore}
 	hasIndex := false
+	hasChunk := false
 	hasLength := false
 	hasBegin := false
 	hasEnd := false
@@ -623,6 +652,9 @@ func parseTiebreak(str string) []criterion {
 		switch str {
 		case "index":
 			check(&hasIndex, "index")
+		case "chunk":
+			check(&hasChunk, "chunk")
+			criteria = append(criteria, byChunk)
 		case "length":
 			check(&hasLength, "length")
 			criteria = append(criteria, byLength)
@@ -636,6 +668,9 @@ func parseTiebreak(str string) []criterion {
 			errorExit("invalid sort criterion: " + str)
 		}
 	}
+	if len(criteria) > 4 {
+		errorExit("at most 3 tiebreaks are allowed: " + str)
+	}
 	return criteria
 }
 
@@ -688,6 +723,8 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
 						cattr.Attr |= tui.Blink
 					case "reverse":
 						cattr.Attr |= tui.Reverse
+					case "strikethrough":
+						cattr.Attr |= tui.StrikeThrough
 					case "black":
 						cattr.Color = tui.Color(0)
 					case "red":
@@ -1051,11 +1088,6 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) {
 					}
 					if t == actUnbind || t == actRebind {
 						parseKeyChords(actionArg, spec[0:offset]+" target required")
-					} else if t == actChangePreviewWindow {
-						opts := previewOpts{}
-						for _, arg := range strings.Split(actionArg, "|") {
-							parsePreviewWindow(&opts, arg)
-						}
 					}
 				}
 			}
@@ -1135,9 +1167,17 @@ func parseSize(str string, maxPercent float64, label string) sizeSpec {
 	return sizeSpec{val, percent}
 }
 
-func parseHeight(str string) sizeSpec {
+func parseHeight(str string) heightSpec {
+	heightSpec := heightSpec{}
+	if strings.HasPrefix(str, "~") {
+		heightSpec.auto = true
+		str = str[1:]
+	}
+
 	size := parseSize(str, 100, "height")
-	return size
+	heightSpec.size = size.size
+	heightSpec.percent = size.percent
+	return heightSpec
 }
 
 func parseLayout(str string) layoutType {
@@ -1169,12 +1209,19 @@ func parseInfoStyle(str string) infoStyle {
 }
 
 func parsePreviewWindow(opts *previewOpts, input string) {
-	delimRegex := regexp.MustCompile("[:,]") // : for backward compatibility
+	tokenRegex := regexp.MustCompile(`[:,]*(<([1-9][0-9]*)\(([^)<]+)\)|[^,:]+)`)
 	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 {
+	tokens := tokenRegex.FindAllStringSubmatch(input, -1)
+	var alternative string
+	for _, match := range tokens {
+		if len(match[2]) > 0 {
+			opts.threshold = atoi(match[2])
+			alternative = match[3]
+			continue
+		}
+		token := match[1]
 		switch token {
 		case "":
 		case "default":
@@ -1209,9 +1256,9 @@ func parsePreviewWindow(opts *previewOpts, input string) {
 			opts.border = tui.BorderHorizontal
 		case "border-vertical":
 			opts.border = tui.BorderVertical
-		case "border-top":
+		case "border-up", "border-top":
 			opts.border = tui.BorderTop
-		case "border-bottom":
+		case "border-down", "border-bottom":
 			opts.border = tui.BorderBottom
 		case "border-left":
 			opts.border = tui.BorderLeft
@@ -1233,6 +1280,13 @@ func parsePreviewWindow(opts *previewOpts, input string) {
 			}
 		}
 	}
+	if len(alternative) > 0 {
+		alternativeOpts := *opts
+		opts.alternative = &alternativeOpts
+		opts.alternative.hidden = false
+		opts.alternative.alternative = nil
+		parsePreviewWindow(opts.alternative, alternative)
+	}
 }
 
 func parseMargin(opt string, margin string) [4]sizeSpec {
@@ -1289,7 +1343,6 @@ func parseOptions(opts *Options, allArgs []string) {
 	validateJumpLabels := false
 	validatePointer := false
 	validateMarker := false
-	validateEllipsis := false
 	for i := 0; i < len(allArgs); i++ {
 		arg := allArgs[i]
 		switch arg {
@@ -1318,6 +1371,8 @@ func parseOptions(opts *Options, allArgs []string) {
 			opts.Normalize = true
 		case "--algo":
 			opts.FuzzyAlgo = parseAlgo(nextString(allArgs, &i, "algorithm required (v1|v2)"))
+		case "--scheme":
+			opts.Scheme = strings.ToLower(nextString(allArgs, &i, "scoring scheme required (default|path|history)"))
 		case "--expect":
 			for k, v := range parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required") {
 				opts.Expect[k] = v
@@ -1477,7 +1532,6 @@ func parseOptions(opts *Options, allArgs []string) {
 			opts.HeaderFirst = false
 		case "--ellipsis":
 			opts.Ellipsis = nextString(allArgs, &i, "ellipsis string required")
-			validateEllipsis = true
 		case "--preview":
 			opts.Preview.command = nextString(allArgs, &i, "preview command required")
 		case "--no-preview":
@@ -1486,11 +1540,11 @@ func parseOptions(opts *Options, allArgs []string) {
 			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[%]"))
+			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{}
+			opts.Height = heightSpec{}
 		case "--no-margin":
 			opts.Margin = defaultMargin()
 		case "--no-padding":
@@ -1520,9 +1574,13 @@ func parseOptions(opts *Options, allArgs []string) {
 			opts.ClearOnExit = false
 		case "--version":
 			opts.Version = true
+		case "--":
+			// Ignored
 		default:
 			if match, value := optString(arg, "--algo="); match {
 				opts.FuzzyAlgo = parseAlgo(value)
+			} else if match, value := optString(arg, "--scheme="); match {
+				opts.Scheme = strings.ToLower(value)
 			} else if match, value := optString(arg, "-q", "--query="); match {
 				opts.Query = value
 			} else if match, value := optString(arg, "-f", "--filter="); match {
@@ -1577,7 +1635,6 @@ func parseOptions(opts *Options, allArgs []string) {
 				opts.HeaderLines = atoi(value)
 			} else if match, value := optString(arg, "--ellipsis="); match {
 				opts.Ellipsis = value
-				validateEllipsis = true
 			} else if match, value := optString(arg, "--preview="); match {
 				opts.Preview.command = value
 			} else if match, value := optString(arg, "--preview-window="); match {
@@ -1640,25 +1697,12 @@ func parseOptions(opts *Options, allArgs []string) {
 			errorExit(err.Error())
 		}
 	}
-
-	if validateEllipsis {
-		for _, r := range opts.Ellipsis {
-			if !unicode.IsGraphic(r) {
-				errorExit("invalid character in ellipsis")
-			}
-		}
-	}
 }
 
 func validateSign(sign string, signOptName string) error {
 	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)
 	}
@@ -1680,6 +1724,7 @@ func postProcessOptions(opts *Options) {
 	}
 
 	// Extend the default key map
+	previewEnabled := len(opts.Preview.command) > 0 || hasPreviewAction(opts)
 	keymap := defaultKeymap()
 	for key, actions := range opts.Keymap {
 		var lastChangePreviewWindow *action
@@ -1690,8 +1735,18 @@ func postProcessOptions(opts *Options) {
 				opts.ToggleSort = true
 			case actChangePreviewWindow:
 				lastChangePreviewWindow = act
+				if !previewEnabled {
+					// Doesn't matter
+					continue
+				}
+				opts := previewOpts{}
+				for _, arg := range strings.Split(act.a, "|") {
+					// Make sure that each expression is valid
+					parsePreviewWindow(&opts, arg)
+				}
 			}
 		}
+
 		// Re-organize actions so that we only keep the last change-preview-window
 		// and it comes first in the list.
 		//  *  change-preview-window(up,+10)+preview(sleep 3; cat {})+change-preview-window(up,+20)
@@ -1709,6 +1764,19 @@ func postProcessOptions(opts *Options) {
 	}
 	opts.Keymap = keymap
 
+	if opts.Height.auto {
+		for _, s := range []sizeSpec{opts.Margin[0], opts.Margin[2]} {
+			if s.percent {
+				errorExit("adaptive height is not compatible with top/bottom percent margin")
+			}
+		}
+		for _, s := range []sizeSpec{opts.Padding[0], opts.Padding[2]} {
+			if s.percent {
+				errorExit("adaptive height is not compatible with top/bottom percent padding")
+			}
+		}
+	}
+
 	// 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 {
@@ -1738,12 +1806,31 @@ func postProcessOptions(opts *Options) {
 		theme.Cursor = boldify(theme.Cursor)
 		theme.Spinner = boldify(theme.Spinner)
 	}
+
+	if opts.Scheme != "default" {
+		processScheme(opts)
+	}
+}
+
+func expectsArbitraryString(opt string) bool {
+	switch opt {
+	case "-q", "--query", "-f", "--filter", "--header", "--prompt":
+		return true
+	}
+	return false
 }
 
 // ParseOptions parses command-line options
 func ParseOptions() *Options {
 	opts := defaultOptions()
 
+	for idx, arg := range os.Args[1:] {
+		if arg == "--version" && (idx == 0 || idx > 0 && !expectsArbitraryString(os.Args[idx])) {
+			opts.Version = true
+			return opts
+		}
+	}
+
 	// Options from Env var
 	words, _ := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS"))
 	if len(words) > 0 {
diff --git a/src/pattern.go b/src/pattern.go
index 4a7a87a6..95f23556 100644
--- a/src/pattern.go
+++ b/src/pattern.go
@@ -51,6 +51,7 @@ type Pattern struct {
 	caseSensitive bool
 	normalize     bool
 	forward       bool
+	withPos       bool
 	text          []rune
 	termSets      []termSet
 	sortable      bool
@@ -85,7 +86,7 @@ func clearChunkCache() {
 
 // 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 {
+	withPos bool, cacheable bool, nth []Range, delimiter Delimiter, runes []rune) *Pattern {
 
 	var asString string
 	if extended {
@@ -145,6 +146,7 @@ func BuildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case,
 		caseSensitive: caseSensitive,
 		normalize:     normalize,
 		forward:       forward,
+		withPos:       withPos,
 		text:          []rune(asString),
 		termSets:      termSets,
 		sortable:      sortable,
@@ -302,13 +304,13 @@ func (p *Pattern) matchChunk(chunk *Chunk, space []Result, slab *util.Slab) []Re
 
 	if space == nil {
 		for idx := 0; idx < chunk.count; idx++ {
-			if match, _, _ := p.MatchItem(&chunk.items[idx], false, slab); match != nil {
+			if match, _, _ := p.MatchItem(&chunk.items[idx], p.withPos, slab); match != nil {
 				matches = append(matches, *match)
 			}
 		}
 	} else {
 		for _, result := range space {
-			if match, _, _ := p.MatchItem(result.item, false, slab); match != nil {
+			if match, _, _ := p.MatchItem(result.item, p.withPos, slab); match != nil {
 				matches = append(matches, *match)
 			}
 		}
diff --git a/src/result.go b/src/result.go
index 8abe0d3d..c4acb5fa 100644
--- a/src/result.go
+++ b/src/result.go
@@ -49,6 +49,21 @@ func buildResult(item *Item, offsets []Offset, score int) Result {
 		case byScore:
 			// Higher is better
 			val = math.MaxUint16 - util.AsUint16(score)
+		case byChunk:
+			b := minBegin
+			e := maxEnd
+			l := item.text.Length()
+			for ; b >= 1; b-- {
+				if unicode.IsSpace(item.text.Get(b - 1)) {
+					break
+				}
+			}
+			for ; e < l; e++ {
+				if unicode.IsSpace(item.text.Get(e)) {
+					break
+				}
+			}
+			val = util.AsUint16(e - b)
 		case byLength:
 			val = item.TrimLength()
 		case byBegin, byEnd:
diff --git a/src/terminal.go b/src/terminal.go
index 3c13b373..493c8b9f 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -25,14 +25,14 @@ import (
 // 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.
+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.
+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
@@ -100,6 +100,11 @@ type itemLine struct {
 	result   Result
 }
 
+type fitpad struct {
+	fit int
+	pad int
+}
+
 var emptyLine = itemLine{}
 
 // Terminal represents terminal input/output
@@ -183,7 +188,7 @@ type Terminal struct {
 	prevLines          []itemLine
 	suppress           bool
 	sigstop            bool
-	startChan          chan bool
+	startChan          chan fitpad
 	killChan           chan int
 	slab               *util.Slab
 	theme              *tui.ColorTheme
@@ -439,6 +444,13 @@ func makeSpinner(unicode bool) []string {
 	return []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
 }
 
+func evaluateHeight(opts *Options, termHeight int) int {
+	if opts.Height.percent {
+		return util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight)
+	}
+	return int(opts.Height.size)
+}
+
 // NewTerminal returns new Terminal object
 func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
 	input := trimQuery(opts.Query)
@@ -465,7 +477,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
 		strongAttr = tui.AttrRegular
 	}
 	var renderer tui.Renderer
-	fullscreen := opts.Height.size == 0 || opts.Height.percent && opts.Height.size == 100
+	fullscreen := !opts.Height.auto && (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)
@@ -475,24 +487,16 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
 		}
 	} 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)
-			}
-
+			// Minimum height required to render fzf excluding margin and padding
 			effectiveMinHeight := minHeight
-			if previewBox != nil && (opts.Preview.position == posUp || opts.Preview.position == posDown) {
-				effectiveMinHeight *= 2
+			if previewBox != nil && opts.Preview.aboveOrBelow() {
+				effectiveMinHeight += 1 + borderLines(opts.Preview.border)
 			}
 			if opts.InfoStyle != infoDefault {
 				effectiveMinHeight--
 			}
-			if opts.BorderShape != tui.BorderNone {
-				effectiveMinHeight += 2
-			}
-			return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight))
+			effectiveMinHeight += borderLines(opts.BorderShape)
+			return util.Min(termHeight, util.Max(evaluateHeight(opts, termHeight), effectiveMinHeight))
 		}
 		renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit, false, maxHeightFunc)
 	}
@@ -572,7 +576,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
 		sigstop:            false,
 		slab:               util.MakeSlab(slab16Size, slab32Size),
 		theme:              opts.Theme,
-		startChan:          make(chan bool, 1),
+		startChan:          make(chan fitpad, 1),
 		killChan:           make(chan int),
 		tui:                renderer,
 		initFunc:           func() { renderer.Init() },
@@ -587,6 +591,32 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
 	return &t
 }
 
+func borderLines(shape tui.BorderShape) int {
+	switch shape {
+	case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp:
+		return 2
+	case tui.BorderTop, tui.BorderBottom:
+		return 1
+	}
+	return 0
+}
+
+// Extra number of lines needed to display fzf
+func (t *Terminal) extraLines() int {
+	extra := len(t.header0) + t.headerLines + 1
+	if !t.noInfoLine() {
+		extra++
+	}
+	return extra
+}
+
+func (t *Terminal) MaxFitAndPad(opts *Options) (int, int) {
+	_, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding()
+	padHeight := marginInt[0] + marginInt[2] + paddingInt[0] + paddingInt[2]
+	fit := screenHeight - padHeight - t.extraLines()
+	return fit, padHeight
+}
+
 func (t *Terminal) parsePrompt(prompt string) (func(), int) {
 	var state *ansiState
 	trimmed, colors, _ := extractColor(prompt, state, nil)
@@ -725,22 +755,23 @@ func (t *Terminal) displayWidth(runes []rune) int {
 
 const (
 	minWidth  = 4
-	minHeight = 4
+	minHeight = 3
 )
 
 func calculateSize(base int, size sizeSpec, occupied int, minSize int, pad int) int {
 	max := base - occupied
+	if max < minSize {
+		max = minSize
+	}
 	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() {
+func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
 	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 {
@@ -789,48 +820,66 @@ func (t *Terminal) resizeWindows() {
 	}
 
 	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)
-			}
+		if min > max {
+			min = max
+		}
+		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 {
+	if t.noInfoLine() {
+		minAreaHeight -= 1
+	}
+	if t.isPreviewVisible() {
+		minPreviewHeight := 1 + borderLines(t.previewOpts.border)
+		minPreviewWidth := 5
 		switch t.previewOpts.position {
 		case posUp, posDown:
-			minAreaHeight *= 2
+			minAreaHeight += minPreviewHeight
+			minAreaWidth = util.Max(minPreviewWidth, minAreaWidth)
 		case posLeft, posRight:
-			minAreaWidth *= 2
+			minAreaWidth += minPreviewWidth
+			minAreaHeight = util.Max(minPreviewHeight, minAreaHeight)
 		}
 	}
 	adjust(1, 3, screenWidth, minAreaWidth)
 	adjust(0, 2, screenHeight, minAreaHeight)
+
+	return screenWidth, screenHeight, marginInt, paddingInt
+}
+
+func (t *Terminal) resizeWindows() {
+	screenWidth, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding()
+	width := screenWidth - marginInt[1] - marginInt[3]
+	height := screenHeight - marginInt[0] - marginInt[2]
+
+	t.prevLines = make([]itemLine, screenHeight)
 	if t.border != nil {
 		t.border.Close()
 	}
 	if t.window != nil {
 		t.window.Close()
+		t.window = nil
 	}
 	if t.pborder != nil {
 		t.pborder.Close()
+		t.pborder = nil
 	}
 	if t.pwindow != nil {
 		t.pwindow.Close()
+		t.pwindow = nil
 	}
 	// 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(
@@ -862,83 +911,104 @@ func (t *Terminal) resizeWindows() {
 			false, tui.MakeBorderStyle(t.borderShape, t.unicode))
 	}
 
-	// Add padding
+	// Add padding to margin
 	for idx, val := range paddingInt {
 		marginInt[idx] += val
 	}
-	width = screenWidth - marginInt[1] - marginInt[3]
-	height = screenHeight - marginInt[0] - marginInt[2]
+	width -= paddingInt[1] + paddingInt[3]
+	height -= paddingInt[0] + paddingInt[2]
 
+	// Set up preview window
 	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)
+	if t.isPreviewVisible() {
+		var resizePreviewWindows func(previewOpts previewOpts)
+		resizePreviewWindows = func(previewOpts previewOpts) {
+			hasThreshold := previewOpts.threshold > 0 && previewOpts.alternative != nil
+			createPreviewWindow := func(y int, x int, w int, h int) {
+				pwidth := w
+				pheight := h
+				var previewBorder tui.BorderStyle
+				if previewOpts.border == tui.BorderNone {
+					previewBorder = tui.MakeTransparentBorder()
+				} else {
+					previewBorder = tui.MakeBorderStyle(previewOpts.border, t.unicode)
+				}
+				t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
+				switch 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)
 			}
-			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
+			verticalPad := 2
+			minPreviewHeight := 3
+			switch 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 previewOpts.position {
+			case posUp, posDown:
+				pheight := calculateSize(height, previewOpts.size, minHeight, minPreviewHeight, verticalPad)
+				if hasThreshold && pheight < previewOpts.threshold {
+					if !previewOpts.alternative.hidden {
+						resizePreviewWindows(*previewOpts.alternative)
+					}
+					return
+				}
+				if previewOpts.position == posUp {
+					t.window = t.tui.NewWindow(
+						marginInt[0]+pheight, marginInt[3], width, height-pheight, false, noBorder)
+					createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
+				} else {
+					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, posRight:
+				pwidth := calculateSize(width, previewOpts.size, minWidth, 5, 4)
+				if hasThreshold && pwidth < previewOpts.threshold {
+					if !previewOpts.alternative.hidden {
+						resizePreviewWindows(*previewOpts.alternative)
+					}
+					return
+				}
+				if previewOpts.position == posLeft {
+					t.window = t.tui.NewWindow(
+						marginInt[0], marginInt[3]+pwidth, width-pwidth, height, false, noBorder)
+					createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
+				} else {
+					t.window = t.tui.NewWindow(
+						marginInt[0], marginInt[3], width-pwidth, height, false, noBorder)
+					createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height)
+				}
 			}
-			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 {
+		resizePreviewWindows(t.previewOpts)
+	}
+	if t.window == nil {
 		t.window = t.tui.NewWindow(
 			marginInt[0],
 			marginInt[3],
@@ -1839,6 +1909,10 @@ func (t *Terminal) isPreviewEnabled() bool {
 	return t.hasPreviewer() && t.previewer.enabled
 }
 
+func (t *Terminal) isPreviewVisible() bool {
+	return t.isPreviewEnabled() && t.previewOpts.size.size > 0
+}
+
 func (t *Terminal) hasPreviewWindow() bool {
 	return t.pwindow != nil && t.isPreviewEnabled()
 }
@@ -1938,7 +2012,28 @@ func (t *Terminal) cancelPreview() {
 // Loop is called to start Terminal I/O
 func (t *Terminal) Loop() {
 	// prof := profile.Start(profile.ProfilePath("/tmp/"))
-	<-t.startChan
+	fitpad := <-t.startChan
+	fit := fitpad.fit
+	if fit >= 0 {
+		pad := fitpad.pad
+		t.tui.Resize(func(termHeight int) int {
+			contentHeight := fit + t.extraLines()
+			if t.hasPreviewer() {
+				if t.previewOpts.aboveOrBelow() {
+					if t.previewOpts.size.percent {
+						newContentHeight := int(float64(contentHeight) * 100. / (100. - t.previewOpts.size.size))
+						contentHeight = util.Max(contentHeight+1+borderLines(t.previewOpts.border), newContentHeight)
+					} else {
+						contentHeight += int(t.previewOpts.size.size) + borderLines(t.previewOpts.border)
+					}
+				} else {
+					// Minimum height if preview window can appear
+					contentHeight = util.Max(contentHeight, 1+borderLines(t.previewOpts.border))
+				}
+			}
+			return util.Min(termHeight, contentHeight+pad)
+		})
+	}
 	{ // Late initialization
 		intChan := make(chan os.Signal, 1)
 		signal.Notify(intChan, os.Interrupt, syscall.SIGTERM)
diff --git a/src/tokenizer.go b/src/tokenizer.go
index 26f42d25..9f9e2c19 100644
--- a/src/tokenizer.go
+++ b/src/tokenizer.go
@@ -156,14 +156,14 @@ func Tokenize(text string, delimiter Delimiter) []Token {
 	// 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:]
+		locs := delimiter.regex.FindAllStringIndex(text, -1)
+		begin := 0
+		for _, loc := range locs {
+			tokens = append(tokens, text[begin:loc[1]])
+			begin = loc[1]
+		}
+		if begin < len(text) {
+			tokens = append(tokens, text[begin:])
 		}
 	}
 	return withPrefixLengths(tokens, 0)
diff --git a/src/tui/dummy.go b/src/tui/dummy.go
index 686a1009..adecd6fc 100644
--- a/src/tui/dummy.go
+++ b/src/tui/dummy.go
@@ -14,24 +14,26 @@ func (a Attr) Merge(b Attr) Attr {
 
 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)
+	AttrRegular   = Attr(1 << 8)
+	AttrClear     = Attr(1 << 9)
+
+	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)
+	StrikeThrough = Attr(1 << 7)
 )
 
-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) Init()                              {}
+func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
+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 }
diff --git a/src/tui/light.go b/src/tui/light.go
index 8f2952b4..20b7b9d5 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -176,6 +176,7 @@ func (r *LightRenderer) Init() {
 
 	if r.mouse {
 		r.csi("?1000h")
+		r.csi("?1006h")
 	}
 	r.csi(fmt.Sprintf("%dA", r.MaxY()-1))
 	r.csi("G")
@@ -188,6 +189,10 @@ func (r *LightRenderer) Init() {
 	}
 }
 
+func (r *LightRenderer) Resize(maxHeightFunc func(int) int) {
+	r.maxHeightFunc = maxHeightFunc
+}
+
 func (r *LightRenderer) makeSpace() {
 	r.stderr("\n")
 	r.csi("G")
@@ -378,7 +383,7 @@ func (r *LightRenderer) escSequence(sz *int) Event {
 			return Event{Home, 0, nil}
 		case 'F':
 			return Event{End, 0, nil}
-		case 'M':
+		case '<':
 			return r.mouseSequence(sz)
 		case 'P':
 			return Event{F1, 0, nil}
@@ -519,47 +524,73 @@ func (r *LightRenderer) escSequence(sz *int) Event {
 	return Event{Invalid, 0, nil}
 }
 
+// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
 func (r *LightRenderer) mouseSequence(sz *int) Event {
-	if len(r.buffer) < 6 || !r.mouse {
+	// "\e[<0;0;0M"
+	if len(r.buffer) < 9 || !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
+
+	rest := r.buffer[*sz:]
+	end := bytes.IndexAny(rest, "mM")
+	if end == -1 {
+		return Event{Invalid, 0, nil}
+	}
+
+	elems := strings.SplitN(string(rest[:end]), ";", 3)
+	if len(elems) != 3 {
+		return Event{Invalid, 0, nil}
+	}
+
+	t := atoi(elems[0], -1)
+	x := atoi(elems[1], -1) - 1
+	y := atoi(elems[2], -1) - 1 - r.yoffset
+	if t < 0 || x < 0 || y < 0 {
+		return Event{Invalid, 0, nil}
+	}
+	*sz += end + 1
+
+	down := rest[end] == 'M'
+
+	scroll := 0
+	if t >= 64 {
+		t -= 64
+		if t&0b1 == 1 {
+			scroll = -1
 		} else {
-			if len(r.clickY) > 1 && r.clickY[0] == r.clickY[1] &&
-				time.Since(r.prevDownTime) < doubleClickDuration {
-				double = true
-			}
+			scroll = 1
 		}
+	}
+
+	// middle := t & 0b1
+	left := t&0b11 == 0
 
-		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}}
+	// shift := t & 0b100
+	// ctrl := t & 0b1000
+	mod := t&0b1100 > 0
+
+	if scroll != 0 {
+		return Event{Mouse, 0, &MouseEvent{y, x, scroll, false, false, false, mod}}
 	}
-	return Event{Invalid, 0, nil}
+
+	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}}
 }
 
 func (r *LightRenderer) smcup() {
@@ -597,6 +628,7 @@ func (r *LightRenderer) Resume(clear bool, sigcont bool) {
 		// 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.csi("?1006l")
 		r.mouse = false
 	}
 }
@@ -636,6 +668,7 @@ func (r *LightRenderer) Close() {
 	}
 	if r.mouse {
 		r.csi("?1000l")
+		r.csi("?1006l")
 	}
 	r.flush()
 	r.closePlatform()
@@ -647,6 +680,9 @@ func (r *LightRenderer) MaxX() int {
 }
 
 func (r *LightRenderer) MaxY() int {
+	if r.height == 0 {
+		r.updateTerminalSize()
+	}
 	return r.height
 }
 
@@ -827,6 +863,9 @@ func attrCodes(attr Attr) []string {
 	if (attr & Reverse) > 0 {
 		codes = append(codes, "7")
 	}
+	if (attr & StrikeThrough) > 0 {
+		codes = append(codes, "9")
+	}
 	return codes
 }
 
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index 6f9b53a9..1f9a832b 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -8,8 +8,8 @@ import (
 
 	"runtime"
 
-	"github.com/gdamore/tcell"
-	"github.com/gdamore/tcell/encoding"
+	"github.com/gdamore/tcell/v2"
+	"github.com/gdamore/tcell/v2/encoding"
 
 	"github.com/mattn/go-runewidth"
 	"github.com/rivo/uniseg"
@@ -19,12 +19,24 @@ func HasFullscreenRenderer() bool {
 	return true
 }
 
+func asTcellColor(color Color) tcell.Color {
+	if color == colDefault {
+		return tcell.ColorDefault
+	}
+
+	value := uint64(tcell.ColorValid) + uint64(color)
+	if color.is24() {
+		value = value | uint64(tcell.ColorIsRGB)
+	}
+	return tcell.Color(value)
+}
+
 func (p ColorPair) style() tcell.Style {
 	style := tcell.StyleDefault
-	return style.Foreground(tcell.Color(p.Fg())).Background(tcell.Color(p.Bg()))
+	return style.Foreground(asTcellColor(p.Fg())).Background(asTcellColor(p.Bg()))
 }
 
-type Attr tcell.Style
+type Attr int32
 
 type TcellWindow struct {
 	color       bool
@@ -72,12 +84,13 @@ func (w *TcellWindow) FinishFill() {
 }
 
 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)
+	Bold          Attr = Attr(tcell.AttrBold)
+	Dim                = Attr(tcell.AttrDim)
+	Blink              = Attr(tcell.AttrBlink)
+	Reverse            = Attr(tcell.AttrReverse)
+	Underline          = Attr(tcell.AttrUnderline)
+	StrikeThrough      = Attr(tcell.AttrStrikeThrough)
+	Italic             = Attr(tcell.AttrItalic)
 )
 
 const (
@@ -86,6 +99,8 @@ const (
 	AttrClear     = Attr(1 << 8)
 )
 
+func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
+
 func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
 	if _screen.Colors() >= 256 {
 		return Dark256
@@ -561,6 +576,7 @@ func (w *TcellWindow) printString(text string, pair ColorPair) {
 		style = style.
 			Reverse(a&Attr(tcell.AttrReverse) != 0).
 			Underline(a&Attr(tcell.AttrUnderline) != 0).
+			StrikeThrough(a&Attr(tcell.AttrStrikeThrough) != 0).
 			Italic(a&Attr(tcell.AttrItalic) != 0).
 			Blink(a&Attr(tcell.AttrBlink) != 0).
 			Dim(a&Attr(tcell.AttrDim) != 0)
@@ -612,6 +628,7 @@ func (w *TcellWindow) fillString(text string, pair ColorPair) FillReturn {
 		Dim(a&Attr(tcell.AttrDim) != 0).
 		Reverse(a&Attr(tcell.AttrReverse) != 0).
 		Underline(a&Attr(tcell.AttrUnderline) != 0).
+		StrikeThrough(a&Attr(tcell.AttrStrikeThrough) != 0).
 		Italic(a&Attr(tcell.AttrItalic) != 0)
 
 	gr := uniseg.NewGraphemes(text)
diff --git a/src/tui/tui.go b/src/tui/tui.go
index eb09da40..c6d71c12 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -358,6 +358,7 @@ func MakeTransparentBorder() BorderStyle {
 
 type Renderer interface {
 	Init()
+	Resize(maxHeightFunc func(int) int)
 	Pause(clear bool)
 	Resume(clear bool, sigcont bool)
 	Clear()
diff --git a/uninstall b/uninstall
index 094d6891..4e587211 100755
--- a/uninstall
+++ b/uninstall
@@ -51,13 +51,8 @@ remove() {
 }
 
 remove_line() {
-  src=$(readlink "$1")
-  if [ $? -eq 0 ]; then
-    echo "Remove from $1 ($src):"
-  else
-    src=$1
-    echo "Remove from $1:"
-  fi
+  src=$1
+  echo "Remove from $1:"
 
   shift
   line_no=1
@@ -75,8 +70,9 @@ remove_line() {
     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
+      temp=$(mktemp)
+      awk -v n=$line_no 'NR == n {next} {print}' "$src" > "$temp" &&
+        cat "$temp" > "$src" && rm -f "$temp" || break
       echo  "      - Removed"
     else
       echo  "      - Skipped"


Test patch:
diff --git a/src/algo/algo_test.go b/src/algo/algo_test.go
index 218ca1f1..a7c4e1d3 100644
--- a/src/algo/algo_test.go
+++ b/src/algo/algo_test.go
@@ -45,29 +45,29 @@ func TestFuzzyMatch(t *testing.T) {
 			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)
+				scoreMatch*3+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+
+					int(bonusBoundaryWhite)*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)
+				scoreMatch*4+int(bonusBoundaryDelimiter)*bonusFirstCharMultiplier+int(bonusBoundaryDelimiter)*3)
 			assertMatch(t, fn, false, forward, "/.oh-my-zsh/cache", "zshc", 8, 13,
-				scoreMatch*4+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary*3+scoreGapStart)
+				scoreMatch*4+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary*2+scoreGapStart+int(bonusBoundaryDelimiter))
 			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)
+				scoreMatch*3+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+
+					int(bonusBoundaryDelimiter)*2+2*scoreGapStart+4*scoreGapExtension)
 			assertMatch(t, fn, false, forward, "fooBarBaz", "fbb", 0, 7,
-				scoreMatch*3+bonusBoundary*bonusFirstCharMultiplier+
+				scoreMatch*3+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+
 					bonusCamel123*2+2*scoreGapStart+2*scoreGapExtension)
 			assertMatch(t, fn, false, forward, "foo barbaz", "fbb", 0, 8,
-				scoreMatch*3+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary+
+				scoreMatch*3+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+int(bonusBoundaryWhite)+
 					scoreGapStart*2+scoreGapExtension*3)
 			assertMatch(t, fn, false, forward, "fooBar Baz", "foob", 0, 4,
-				scoreMatch*4+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary*3)
+				scoreMatch*4+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+int(bonusBoundaryWhite)*3)
 			assertMatch(t, fn, false, forward, "xFoo-Bar Baz", "foo-b", 1, 6,
 				scoreMatch*5+bonusCamel123*bonusFirstCharMultiplier+bonusCamel123*2+
 					bonusNonWord+bonusBoundary)
@@ -75,14 +75,14 @@ func TestFuzzyMatch(t *testing.T) {
 			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)+
+				scoreMatch*3+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+int(bonusBoundaryDelimiter)*2+
 					scoreGapStart*2+scoreGapExtension*4)
 			assertMatch(t, fn, true, forward, "FooBarBaz", "FBB", 0, 7,
-				scoreMatch*3+bonusBoundary*bonusFirstCharMultiplier+bonusCamel123*2+
+				scoreMatch*3+int(bonusBoundaryWhite)*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))
+				scoreMatch*4+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+int(bonusBoundaryWhite)*2+
+					util.Max(bonusCamel123, int(bonusBoundaryWhite)))
 
 			// Consecutive bonus updated
 			assertMatch(t, fn, true, forward, "foo-bar", "o-ba", 2, 6,
@@ -98,10 +98,10 @@ func TestFuzzyMatch(t *testing.T) {
 
 func TestFuzzyMatchBackward(t *testing.T) {
 	assertMatch(t, FuzzyMatchV1, false, true, "foobar fb", "fb", 0, 4,
-		scoreMatch*2+bonusBoundary*bonusFirstCharMultiplier+
+		scoreMatch*2+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+
 			scoreGapStart+scoreGapExtension)
 	assertMatch(t, FuzzyMatchV1, false, false, "foobar fb", "fb", 7, 9,
-		scoreMatch*2+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary)
+		scoreMatch*2+int(bonusBoundaryWhite)*bonusFirstCharMultiplier+int(bonusBoundaryWhite))
 }
 
 func TestExactMatchNaive(t *testing.T) {
@@ -114,9 +114,9 @@ func TestExactMatchNaive(t *testing.T) {
 		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))
+			scoreMatch*4+int(bonusBoundaryDelimiter)*(bonusFirstCharMultiplier+3))
 		assertMatch(t, ExactMatchNaive, false, dir, "/.oh-my-zsh/cache", "zsh/c", 8, 13,
-			scoreMatch*5+bonusBoundary*(bonusFirstCharMultiplier+4))
+			scoreMatch*5+bonusBoundary*(bonusFirstCharMultiplier+3)+int(bonusBoundaryDelimiter))
 	}
 }
 
@@ -128,7 +128,7 @@ func TestExactMatchNaiveBackward(t *testing.T) {
 }
 
 func TestPrefixMatch(t *testing.T) {
-	score := (scoreMatch+bonusBoundary)*3 + bonusBoundary*(bonusFirstCharMultiplier-1)
+	score := scoreMatch*3 + int(bonusBoundaryWhite)*bonusFirstCharMultiplier + int(bonusBoundaryWhite)*2
 
 	for _, dir := range []bool{true, false} {
 		assertMatch(t, PrefixMatch, true, dir, "fooBarbaz", "Foo", -1, -1, 0)
@@ -156,9 +156,10 @@ func TestSuffixMatch(t *testing.T) {
 		// 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)
+			scoreMatch*4+bonusConsecutive*2+int(bonusBoundaryWhite))
 	}
 }
 
@@ -182,9 +183,9 @@ func TestNormalize(t *testing.T) {
 				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)
+	test("Só Danço Samba", "So", 0, 2, 62, FuzzyMatchV1, FuzzyMatchV2, PrefixMatch, ExactMatchNaive)
+	test("Só Danço Samba", "sodc", 0, 7, 97, FuzzyMatchV1, FuzzyMatchV2)
+	test("Danço", "danco", 0, 5, 140, FuzzyMatchV1, FuzzyMatchV2, PrefixMatch, SuffixMatch, ExactMatchNaive, EqualMatch)
 }
 
 func TestLongString(t *testing.T) {
diff --git a/src/ansi_test.go b/src/ansi_test.go
index cdccc10c..f352c99e 100644
--- a/src/ansi_test.go
+++ b/src/ansi_test.go
@@ -15,14 +15,14 @@ import (
 // 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)")
+//   - 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()
@@ -358,6 +358,7 @@ func TestAnsiCodeStringConversion(t *testing.T) {
 	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[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",
@@ -377,7 +378,7 @@ func TestParseAnsiCode(t *testing.T) {
 		{"-2", "", -1},
 	}
 	for _, x := range tests {
-		n, s := parseAnsiCode(x.In)
+		n, _, s := parseAnsiCode(x.In, 0)
 		if n != x.N || s != x.Exp {
 			t.Fatalf("%q: got: (%d %q) want: (%d %q)", x.In, n, s, x.N, x.Exp)
 		}
@@ -385,9 +386,9 @@ func TestParseAnsiCode(t *testing.T) {
 }
 
 // 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"
+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)))
diff --git a/src/options_test.go b/src/options_test.go
index bb946235..e9cb5922 100644
--- a/src/options_test.go
+++ b/src/options_test.go
@@ -65,6 +65,19 @@ func TestDelimiterRegexRegex(t *testing.T) {
 	}
 }
 
+func TestDelimiterRegexRegexCaret(t *testing.T) {
+	delim := delimiterRegexp(`(^\s*|\s+)`)
+	tokens := Tokenize("foo  bar baz", delim)
+	if delim.str != nil ||
+		len(tokens) != 4 ||
+		tokens[0].text.ToString() != "" ||
+		tokens[1].text.ToString() != "foo  " ||
+		tokens[2].text.ToString() != "bar " ||
+		tokens[3].text.ToString() != "baz" {
+		t.Errorf("%s %d", tokens, len(tokens))
+	}
+}
+
 func TestSplitNth(t *testing.T) {
 	{
 		ranges := splitNth("..")
@@ -440,8 +453,6 @@ func TestValidateSign(t *testing.T) {
 		{"😀", true},
 		{"", false},
 		{">>>", false},
-		{"\n", false},
-		{"\t", false},
 	}
 
 	for _, testCase := range testCases {
diff --git a/src/pattern_test.go b/src/pattern_test.go
index b95d1513..5eb5f6d7 100644
--- a/src/pattern_test.go
+++ b/src/pattern_test.go
@@ -67,7 +67,7 @@ func TestParseTermsEmpty(t *testing.T) {
 func TestExact(t *testing.T) {
 	defer clearPatternCache()
 	clearPatternCache()
-	pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true,
+	pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, false, true,
 		[]Range{}, Delimiter{}, []rune("'abc"))
 	chars := util.ToChars([]byte("aabbcc abc"))
 	res, pos := algo.ExactMatchNaive(
@@ -83,7 +83,7 @@ func TestExact(t *testing.T) {
 func TestEqual(t *testing.T) {
 	defer clearPatternCache()
 	clearPatternCache()
-	pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("^AbC$"))
+	pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, false, true, []Range{}, Delimiter{}, []rune("^AbC$"))
 
 	match := func(str string, sidxExpected int, eidxExpected int) {
 		chars := util.ToChars([]byte(str))
@@ -106,17 +106,17 @@ func TestEqual(t *testing.T) {
 func TestCaseSensitivity(t *testing.T) {
 	defer clearPatternCache()
 	clearPatternCache()
-	pat1 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
+	pat1 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseSmart, false, true, false, true, []Range{}, Delimiter{}, []rune("abc"))
 	clearPatternCache()
-	pat2 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
+	pat2 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseSmart, false, true, false, true, []Range{}, Delimiter{}, []rune("Abc"))
 	clearPatternCache()
-	pat3 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseIgnore, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
+	pat3 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseIgnore, false, true, false, true, []Range{}, Delimiter{}, []rune("abc"))
 	clearPatternCache()
-	pat4 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseIgnore, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
+	pat4 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseIgnore, false, true, false, true, []Range{}, Delimiter{}, []rune("Abc"))
 	clearPatternCache()
-	pat5 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseRespect, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
+	pat5 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseRespect, false, true, false, true, []Range{}, Delimiter{}, []rune("abc"))
 	clearPatternCache()
-	pat6 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseRespect, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
+	pat6 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseRespect, false, true, false, true, []Range{}, Delimiter{}, []rune("Abc"))
 
 	if string(pat1.text) != "abc" || pat1.caseSensitive != false ||
 		string(pat2.text) != "Abc" || pat2.caseSensitive != true ||
@@ -129,7 +129,7 @@ func TestCaseSensitivity(t *testing.T) {
 }
 
 func TestOrigTextAndTransformed(t *testing.T) {
-	pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("jg"))
+	pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, false, true, []Range{}, Delimiter{}, []rune("jg"))
 	tokens := Tokenize("junegunn", Delimiter{})
 	trans := Transform(tokens, []Range{{1, 1}})
 
@@ -164,7 +164,7 @@ func TestOrigTextAndTransformed(t *testing.T) {
 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))
+		pat := BuildPattern(true, algo.FuzzyMatchV2, extended, CaseSmart, false, true, false, true, []Range{}, Delimiter{}, []rune(patStr))
 		if pat.CacheKey() != expected {
 			t.Errorf("Expected: %s, actual: %s", expected, pat.CacheKey())
 		}
@@ -188,7 +188,7 @@ func TestCacheKey(t *testing.T) {
 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))
+		pat := BuildPattern(fuzzy, algo.FuzzyMatchV2, true, CaseSmart, true, true, false, true, []Range{}, Delimiter{}, []rune(str))
 		if pat.CacheKey() != expected {
 			t.Errorf("Expected: %s, actual: %s", expected, pat.CacheKey())
 		}
diff --git a/src/result_test.go b/src/result_test.go
index 2fd3127f..a930447a 100644
--- a/src/result_test.go
+++ b/src/result_test.go
@@ -54,9 +54,9 @@ func TestResultRank(t *testing.T) {
 	// FIXME global
 	sortCriteria = []criterion{byScore, byLength}
 
-	strs := [][]rune{[]rune("foo"), []rune("foobar"), []rune("bar"), []rune("baz")}
+	str := []rune("foo")
 	item1 := buildResult(
-		withIndex(&Item{text: util.RunesToChars(strs[0])}, 1), []Offset{}, 2)
+		withIndex(&Item{text: util.RunesToChars(str)}, 1), []Offset{}, 2)
 	if item1.points[3] != math.MaxUint16-2 || // Bonus
 		item1.points[2] != 3 || // Length
 		item1.points[1] != 0 || // Unused
@@ -65,7 +65,7 @@ func TestResultRank(t *testing.T) {
 		t.Error(item1)
 	}
 	// Only differ in index
-	item2 := buildResult(&Item{text: util.RunesToChars(strs[0])}, []Offset{}, 2)
+	item2 := buildResult(&Item{text: util.RunesToChars(str)}, []Offset{}, 2)
 
 	items := []Result{item1, item2}
 	sort.Sort(ByRelevance(items))
@@ -98,6 +98,23 @@ func TestResultRank(t *testing.T) {
 	}
 }
 
+func TestChunkTiebreak(t *testing.T) {
+	// FIXME global
+	sortCriteria = []criterion{byScore, byChunk}
+
+	score := 100
+	test := func(input string, offset Offset, chunk string) {
+		item := buildResult(withIndex(&Item{text: util.RunesToChars([]rune(input))}, 1), []Offset{offset}, score)
+		if !(item.points[3] == math.MaxUint16-uint16(score) && item.points[2] == uint16(len(chunk))) {
+			t.Error(item.points)
+		}
+	}
+	test("hello foobar goodbye", Offset{8, 9}, "foobar")
+	test("hello foobar goodbye", Offset{7, 18}, "foobar goodbye")
+	test("hello foobar goodbye", Offset{0, 1}, "hello")
+	test("hello foobar goodbye", Offset{5, 7}, "hello foobar") // TBD
+}
+
 func TestColorOffset(t *testing.T) {
 	// ------------ 20 ----  --  ----
 	//   ++++++++        ++++++++++
diff --git a/src/terminal_test.go b/src/terminal_test.go
index ee19b674..ebb6bda5 100644
--- a/src/terminal_test.go
+++ b/src/terminal_test.go
@@ -421,10 +421,10 @@ func TestPowershellCommands(t *testing.T) {
 }
 
 /*
-	Test typical valid placeholders and parsing of them.
+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.
+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
diff --git a/src/tui/tcell_test.go b/src/tui/tcell_test.go
index 96ea7ed3..0d50bb76 100644
--- a/src/tui/tcell_test.go
+++ b/src/tui/tcell_test.go
@@ -5,7 +5,7 @@ package tui
 import (
 	"testing"
 
-	"github.com/gdamore/tcell"
+	"github.com/gdamore/tcell/v2"
 	"github.com/junegunn/fzf/src/util"
 )
 
diff --git a/test/test_go.rb b/test/test_go.rb
index 3050fa0a..ab5a4ae6 100755
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -754,6 +754,26 @@ class TestGoFZF < TestBase
     assert_equal output, `#{FZF} -fh -n2 -d: < #{tempname}`.lines(chomp: true)
   end
 
+  def test_tiebreak_chunk
+    writelines(tempname, [
+                 '1 foobarbaz ba',
+                 '2 foobar baz',
+                 '3 foo barbaz'
+               ])
+
+    assert_equal [
+      '3 foo barbaz',
+      '2 foobar baz',
+      '1 foobarbaz ba'
+    ], `#{FZF} -fo --tiebreak=chunk < #{tempname}`.lines(chomp: true)
+
+    assert_equal [
+      '1 foobarbaz ba',
+      '2 foobar baz',
+      '3 foo barbaz'
+    ], `#{FZF} -fba --tiebreak=chunk < #{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] }
@@ -2225,6 +2245,93 @@ class TestGoFZF < TestBase
     tmux.until { |lines| assert_equal 1, lines.match_count }
     tmux.until { |lines| assert_match(/^> SNIPSNIP.*SNIPSNIP$/, lines[-3]) }
   end
+
+  def assert_block(expected, lines)
+    cols = expected.lines.map(&:chomp).map(&:length).max
+    actual = lines.reverse.take(expected.lines.length).reverse.map { _1[0, cols].rstrip + "\n" }.join
+    assert_equal expected, actual
+  end
+
+  def test_height_range_fit
+    tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border', :Enter
+    expected = <<~OUTPUT
+      ╭──────────
+      │   3
+      │   2
+      │ > 1
+      │ >   < 3/3
+      ╰──────────
+    OUTPUT
+    tmux.until { assert_block(expected, _1) }
+  end
+
+  def test_height_range_fit_preview_above
+    tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border --preview "seq {}" --preview-window up,60%', :Enter
+    expected = <<~OUTPUT
+      ╭──────────
+      │ ╭────────
+      │ │ 1
+      │ │
+      │ │
+      │ │
+      │ ╰────────
+      │   3
+      │   2
+      │ > 1
+      │ >   < 3/3
+      ╰──────────
+    OUTPUT
+    tmux.until { assert_block(expected, _1) }
+  end
+
+  def test_height_range_fit_preview_above_alternative
+    tmux.send_keys 'seq 3 | fzf --height ~100% --border=sharp --preview "seq {}" --preview-window up,40%,border-bottom --padding 1 --exit-0 --header hello --header-lines=2', :Enter
+    expected = <<~OUTPUT
+      ┌─────────
+      │
+      │  1
+      │  2
+      │  3
+      │  ───────
+      │  > 3
+      │    2
+      │    1
+      │    hello
+      │    1/1
+      │  >
+      │
+      └─────────
+    OUTPUT
+    tmux.until { assert_block(expected, _1) }
+  end
+
+  def test_height_range_fit_preview_left
+    tmux.send_keys "seq 3 | fzf --height ~100% --border=vertical --preview 'seq {}' --preview-window left,5,border-right --padding 1 --exit-0 --header $'hello\\nworld' --header-lines=2", :Enter
+    expected = <<~OUTPUT
+      │
+      │  1       │> 3
+      │  2       │  2
+      │  3       │  1
+      │          │  hello
+      │          │  world
+      │          │  1/1
+      │          │>
+      │
+    OUTPUT
+    tmux.until { assert_block(expected, _1) }
+  end
+
+  def test_height_range_overflow
+    tmux.send_keys 'seq 100 | fzf --height ~5 --info=inline --border', :Enter
+    expected = <<~OUTPUT
+      ╭──────────────
+      │   2
+      │ > 1
+      │ >   < 100/100
+      ╰──────────────
+    OUTPUT
+    tmux.until { assert_block(expected, _1) }
+  end
 end
 
 module TestShell
@@ -2478,7 +2585,7 @@ module CompletionTest
     pid = lines[-1]&.split&.last
     tmux.prepare
     tmux.send_keys 'C-L'
-    tmux.send_keys 'kill ', :Tab
+    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') }
@@ -2651,6 +2758,7 @@ class TestFish < TestBase
 end
 
 __END__
+set -u
 PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
 unset <%= UNSETS.join(' ') %>
 unset $(env | sed -n /^_fzf_orig/s/=.*//p)
@@ -2694,8 +2802,8 @@ _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
+[ -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