Repository: junegunn/fzf (language: go)

Issue:
bash programmable_completion: possible retry loop
<!-- ISSUES NOT FOLLOWING THIS TEMPLATE WILL BE CLOSED AND DELETED -->

<!-- Check all that apply [x] -->

- [x] I have read through the manual page (`man fzf`)
- [x] I have the latest version of fzf
- [x] I have searched through the existing issues

## Info

- OS
    - [ ] Linux
    - [x] Mac OS X
    - [ ] Windows
    - [ ] Etc.
- Shell
    - [x] bash
    - [ ] zsh
    - [ ] fish
    
## Problem / Steps to reproduce

I have the following alias in my .bash_profile because I like the verbose output _most_ of the time: `alias rm='rm -v'`. However, sometimes I want to skip all that output - deleting a `node_modules` directory, for example. So, I'll start my command with a backslash to skip the alias, like: `\rm -drf node_modules`.

I recently upgraded to fzf 0.27 (I'm not sure what version I was on previously - 0.24 maybe?) and started getting `-bash: warning: programmable_completion: \rm: possible retry loop` if I try to tab-complete something like `\rm -drf nod<tab>`. If I disable fzf's bash completion, I don't get this error. I think I tracked it down to [this line](https://github.com/junegunn/fzf/blob/0.27.0/shell/completion.bash#L164): if I change it to the following, it works, but I'm not sure if that's the best solution:

```bash
  cmd="${COMP_WORDS[0]}"
  if [[ $cmd == \\* ]]; then
    cmd="${cmd:1}"
  fi
  cmd="${cmd//[^A-Za-z0-9_=]/_}"
```

Happy to open a PR if it looks good, though!

In addition to fzf's completion, I also have https://github.com/scop/bash-completion installed. If I `set -x` before attempting the completion, I can see that it seems to get stuck in a loop calling into fzf's completion, then scop's, then fzf's, etc, so, maybe it's some interaction between the two? Scop's completions don't have anything defined explicitly for `rm` as far as I can tell.


Discussion / hints:
Can you provide a minimal bash configuration file that I can use to reproduce the case?
A docker image would also be very helpful.
I came up with a simple repro using docker, but in the process, I _think_ I discovered that it's more related to the bash-completion package than fzf. But, for completeness, I figured I'd let you take a look. It seems to happen with any version of fzf (at least, I checked back as far as 0.20.0), but only v2.11 of bash-completion.

Dockerfile:
```Dockerfile
FROM debian:buster-slim
RUN apt-get update && apt-get install -y autoconf curl git make

RUN git clone https://github.com/scop/bash-completion.git ~/bash-completion
RUN cd ~/bash-completion && git checkout 2.11 && autoreconf -i && ./configure && make && make install

RUN git clone https://github.com/junegunn/fzf.git ~/.fzf
RUN cd ~/.fzf && git checkout 0.27.0 && ./install

COPY .bashrc /root
```

.bashrc:
```bash
[ -f /usr/local/etc/profile.d/bash_completion.sh ] && source /usr/local/etc/profile.d/bash_completion.sh
[ -f ~/.fzf.bash ] && source ~/.fzf.bash
```

I'm going to submit an issue with bash-completion - feel free to close this ticket if you think it's not relevant to fzf. Thanks!
Place the Dockerfile and .bashrc in the same directory. Then, build, run the docker container, and test such as:
```bash
docker build -t test .
docker run -it test /bin/bash
cd ~
mkdir test
\rm -drf te<tab>
```
Thanks for the repro! I'll take a look when I get some time.
I am seeing this same issue. Whenever I prepend a command with `\` then bash completion fails.

I feel like this used to work before.

As a note if I remove the file `/etc/bash_completions.d/fzf` then it works correctly.
Here is the end of my output when trying.

To get this output I did:
```
$ export PS4='+ ${BASH_SOURCE:-}:${FUNCNAME[0]:-}:L${LINENO:-}:   '
$ set -x
$ \cd fo <tab>
```

```
<snip>
+ /usr/share/bash-completion/bash_completion:__load_completion:L3:   local 'ifs=
' IFS=: dir 'cmd=\cd' compfile
+ /usr/share/bash-completion/bash_completion:__load_completion:L4:   [[ -n \cd ]]
+ /usr/share/bash-completion/bash_completion:__load_completion:L2180:   for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ /usr/share/bash-completion/bash_completion:__load_completion:L6:   dirs+=($dir/bash-completion/completions)
+ /usr/share/bash-completion/bash_completion:__load_completion:L2180:   for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ /usr/share/bash-completion/bash_completion:__load_completion:L6:   dirs+=($dir/bash-completion/completions)
+ /usr/share/bash-completion/bash_completion:__load_completion:L2180:   for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ /usr/share/bash-completion/bash_completion:__load_completion:L6:   dirs+=($dir/bash-completion/completions)
+ /usr/share/bash-completion/bash_completion:__load_completion:L2180:   for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ /usr/share/bash-completion/bash_completion:__load_completion:L6:   dirs+=($dir/bash-completion/completions)
+ /usr/share/bash-completion/bash_completion:__load_completion:L8:   IFS='
'
+ /usr/share/bash-completion/bash_completion:__load_completion:L10:   [[ /usr/share/bash-completion/bash_completion == */* ]]
+ /usr/share/bash-completion/bash_completion:__load_completion:L11:   dirs+=("${BASH_SOURCE%/*}/completions")
+ /usr/share/bash-completion/bash_completion:__load_completion:L16:   local backslash=
+ /usr/share/bash-completion/bash_completion:__load_completion:L17:   [[ \cd == \\* ]]
+ /usr/share/bash-completion/bash_completion:__load_completion:L18:   cmd=cd
++ /usr/share/bash-completion/bash_completion:__load_completion:L1:   complete -p cd
+ /usr/share/bash-completion/bash_completion:__load_completion:L20:   complete -o nospace -F _fzf_dir_completion cd '\cd'
+ /usr/share/bash-completion/bash_completion:__load_completion:L20:   return 0
+ /usr/share/bash-completion/bash_completion:_completion_loader:L5:   return 124
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L13:   ret=124
++ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L1:   complete -p '\cd'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L15:   '[' 'complete -o nospace -F _fzf_dir_completion \cd' '!=' 'complete -o nospace -F _fzf_dir_completion \cd' ']'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L23:   return 124
+ /etc/bash_completion.d/fzf:_fzf_dir_completion:L2:   __fzf_generic_path_completion _fzf_compgen_dir '' / '\cd' un '\cd'
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L2:   local cur base dir leftover matches trigger cmd
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L3:   cmd=_cd
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L4:   COMPREPLY=()
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L5:   trigger='**'
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L6:   cur=un
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L7:   [[ un == *\*\* ]]
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L35:   shift
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L36:   shift
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L37:   shift
+ /etc/bash_completion.d/fzf:__fzf_generic_path_completion:L38:   _fzf_handle_dynamic_completion _cd '\cd' un '\cd'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L2:   local cmd orig_var orig ret orig_cmd orig_complete
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L3:   cmd=_cd
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L4:   shift
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L5:   orig_cmd='\cd'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L6:   orig_var=_fzf_orig_completion__cd
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L7:   orig=
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L8:   '[' -n '' ']'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L10:   '[' -n 1 ']'
++ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L1:   complete -p '\cd'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L11:   orig_complete='complete -o nospace -F _fzf_dir_completion \cd'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L12:   _completion_loader '\cd' un '\cd'
+ /usr/share/bash-completion/bash_completion:_completion_loader:L3:   local 'cmd=\cd'
+ /usr/share/bash-completion/bash_completion:_completion_loader:L5:   __load_completion '\cd'
+ /usr/share/bash-completion/bash_completion:__load_completion:L2:   dirs=('/home/jlvillal/.local/share/bash-completion/completions')
+ /usr/share/bash-completion/bash_completion:__load_completion:L2:   local -a dirs
+ /usr/share/bash-completion/bash_completion:__load_completion:L3:   local 'ifs=
' IFS=: dir 'cmd=\cd' compfile
+ /usr/share/bash-completion/bash_completion:__load_completion:L4:   [[ -n \cd ]]
+ /usr/share/bash-completion/bash_completion:__load_completion:L2180:   for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ /usr/share/bash-completion/bash_completion:__load_completion:L6:   dirs+=($dir/bash-completion/completions)
+ /usr/share/bash-completion/bash_completion:__load_completion:L2180:   for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ /usr/share/bash-completion/bash_completion:__load_completion:L6:   dirs+=($dir/bash-completion/completions)
+ /usr/share/bash-completion/bash_completion:__load_completion:L2180:   for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ /usr/share/bash-completion/bash_completion:__load_completion:L6:   dirs+=($dir/bash-completion/completions)
+ /usr/share/bash-completion/bash_completion:__load_completion:L2180:   for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ /usr/share/bash-completion/bash_completion:__load_completion:L6:   dirs+=($dir/bash-completion/completions)
+ /usr/share/bash-completion/bash_completion:__load_completion:L8:   IFS='
'
+ /usr/share/bash-completion/bash_completion:__load_completion:L10:   [[ /usr/share/bash-completion/bash_completion == */* ]]
+ /usr/share/bash-completion/bash_completion:__load_completion:L11:   dirs+=("${BASH_SOURCE%/*}/completions")
+ /usr/share/bash-completion/bash_completion:__load_completion:L16:   local backslash=
+ /usr/share/bash-completion/bash_completion:__load_completion:L17:   [[ \cd == \\* ]]
+ /usr/share/bash-completion/bash_completion:__load_completion:L18:   cmd=cd
++ /usr/share/bash-completion/bash_completion:__load_completion:L1:   complete -p cd
+ /usr/share/bash-completion/bash_completion:__load_completion:L20:   complete -o nospace -F _fzf_dir_completion cd '\cd'
+ /usr/share/bash-completion/bash_completion:__load_completion:L20:   return 0
+ /usr/share/bash-completion/bash_completion:_completion_loader:L5:   return 124
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L13:   ret=124
++ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L1:   complete -p '\cd'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L15:   '[' 'complete -o nospace -F _fzf_dir_completion \cd' '!=' 'complete -o nospace -F _fzf_dir_completion \cd' ']'
+ /etc/bash_completion.d/fzf:_fzf_handle_dynamic_completion:L23:   return 124
-bash: warning: programmable_completion: \cd: possible retry loop
```
> I think I tracked it down to [this line](https://github.com/junegunn/fzf/blob/0.27.0/shell/completion.bash#L164): if I change it to the following, it works, but I'm not sure if that's the best solution:
> 
> ```shell
>   cmd="${COMP_WORDS[0]}"
>   if [[ $cmd == \\* ]]; then
>     cmd="${cmd:1}"
>   fi
>   cmd="${cmd//[^A-Za-z0-9_=]/_}"
> ```
> 
> Happy to open a PR if it looks good, though!

@bmatcuk  I personally would love it if you did a PR as this has been annoying...
That fix looks good to me.  I have tested it locally and it fixes the problem for me.

Created PR #2536 
It does fix the problem, but the reason I didn't open a PR is because the issue seems to be more with [bash-completion](https://github.com/scop/bash-completion) than fzf, ie, the issue only happens with v2.11 of bash-completion from my testing. So it feels like something should be fixed there.

Fix patch:
diff --git a/shell/completion.bash b/shell/completion.bash
index b1e79353c2e..77aa580705d 100644
--- a/shell/completion.bash
+++ b/shell/completion.bash
@@ -161,7 +161,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]}"


Test patch: