rschmitt / heatseeker

A high-performance Selecta clone, written in Rust.
MIT License
213 stars 10 forks source link

Feature request: non-interactive when 1 search matches #38

Open focusaurus opened 5 years ago

focusaurus commented 5 years ago

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 commented 5 years ago

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

rschmitt commented 5 years ago

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

focusaurus commented 5 years ago

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

jonahx commented 4 years ago

@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
}