heatseeker icon indicating copy to clipboard operation
heatseeker copied to clipboard

Feature request: non-interactive when 1 search matches

Open focusaurus opened this issue 5 years ago • 4 comments

I'd like a way to express "Given this stdin and this --search argument, if after filtering there's only 1 remaining candidate, select it and write to stdout non-interactively, but if there's >1, go into interactive mode".

fzf has an argument -f, --filter=STR Filter mode. Do not start interactive finder., which I use to simulate this by filtering the input then checking the number of matches with wc and if there's 1 just proceeding to use the result, but piping to fzf if necessary to filter further. It's close, but what I really want is as described above.

For example, given this use case, I'd rather just get cat on stdout immediately and have hs exit 0.

printf 'cat\nrat\nbat' | hs -s cat
> cat (1/3 choices)

focusaurus avatar Apr 02 '19 20:04 focusaurus

Actually I now see what I'm asking for is basically fzf --select-1.

focusaurus avatar Apr 02 '19 21:04 focusaurus

This is a good idea. Should -s behave this way by default, or might that be confusing?

rschmitt avatar Apr 03 '19 05:04 rschmitt

I think it would be OK for -s to do this by default, and maybe a flag to force interactive mode unconditionally like -i.

focusaurus avatar Apr 03 '19 13:04 focusaurus

@rschmitt I heartily +1 this request :)

@focusaurus Until it is added I'm using this bash hack, which gets the desired behavior but only for file selection. Eg, it lets you do:

fz nvim d7

And if exactly one file in the directory matches *d*7* it will executenvim <matched file>. If more than one matches, hs will open to let you narrow it down further, and the it will execute nvim <hs match or matches>.

I had to use the glob hack because afaict hs does not expose the underlying fuzzy search algorithm directly. You can only access it via the interactive UI. That direct access would be yet another useful feature @rschmitt, fwiw.

In any case here's the bash script:

# fz = fuzzy:
# fz <cmd> <file-filter>
# Performs fuzzy search on dir files.  If exactly one match executes:
#     cmd <matched file> 
# Otherwise uses heatseeker to narrow the results further and executes: 
#     cmd <results of `hs <matched files>`> 
fz() {
  local cmd=$1
  local filter=$2
  local fuzzy='*'
  local results

  for i in $(seq 1 ${#filter}); do
    fuzzy="${fuzzy}${filter:i-1:1}*"
  done

  # Note: we want globbing
  # shellcheck disable=SC2086
  mapfile -t results < <(\ls $fuzzy)
  case ${#results[@]} in
    0 )
      echo "No matches"
      return
      ;;
    1 )
      fname=${results[0]}
      ;;
    * )
      fname=$(printf '%s\n' "${results[@]}" | hs)
      ;;
  esac

  # hs selection might have been cancelled
  if [ -n "$fname" ]; then
    $cmd "$fname"
  fi
}

jonahx avatar Dec 07 '19 18:12 jonahx