def bash_engine

cli@0.2.0.0 · cli.bend:6603

import cli@0.2.0.0/cli.bend as Cli
import Base

6603# The rest of every bash script, for function prefix id and the quoted
6604# command name: the parser of COMP_WORDS, then the binding.
6605def bash_engine(+id: String, name: String) -> List<&2, String>:
6606  [
6607    "",
6608    "# Offers each value of kind that starts with $1, written after $2 and cut to",
6609    "# the part readline completes: the text after the last \"=\" or \":\". Readline",
6610    "# quotes files and directories; words are quoted here.",
6611    String.concat([id, "_offer() {"]),
6612    "  local word full i",
6613    "  case $kind in",
6614    "    f) compopt -o filenames; mapfile -t values < <(compgen -f -- \"$1\") ;;",
6615    "    d) compopt -o filenames; mapfile -t values < <(compgen -d -- \"$1\") ;;",
6616    "    w)",
6617    "      for ((i = 0; i < ${#values[@]}; i++)); do",
6618    "        printf -v 'values[i]' %q \"${values[i]}\"",
6619    "      done ;;",
6620    "    *) return ;;",
6621    "  esac",
6622    "  for word in \"${values[@]}\"; do",
6623    "    full=$2$word",
6624    "    [[ $word == \"$1\"* ]] && COMPREPLY+=(\"${full:${#2}+${#1}-${#tail}}\")",
6625    "  done",
6626    "}",
6627    "",
6628    String.concat([id, "() {"]),
6629    "  local -a words=() opts=() subs=() dests=() values=()",
6630    "  local cur tail word line gap path= want= kind opt many rest i j n=0 end=0 help=0",
6631    "  COMPREPLY=()",
6632    "  # Rejoin the \"=\" and \":\" that bash splits words at. COMP_LINE has no blank",
6633    "  # at such a split, so a \"=\" typed as its own word stays one.",
6634    "  line=${COMP_LINE#*\"${COMP_WORDS[0]}\"}",
6635    "  for ((i = 1; i <= COMP_CWORD; i++)); do",
6636    "    word=${COMP_WORDS[i]}",
6637    "    tail=${COMP_WORDS[i-1]}",
6638    "    gap=${line%%[![:blank:]]*}",
6639    "    line=${line:${#gap}+${#word}}",
6640    "    if ((i > 1)) && [[ -z $gap ]] &&",
6641    "      [[ -n $word && -z ${word//[=:]} || -n $tail && -z ${tail//[=:]} ]]; then",
6642    "      words[${#words[@]}-1]+=$word",
6643    "    else",
6644    "      words+=(\"$word\")",
6645    "    fi",
6646    "  done",
6647    "  tail=${COMP_WORDS[COMP_CWORD]}",
6648    "  [[ -z ${tail//[=:]} ]] && tail=",
6649    "  cur=${words[${#words[@]}-1]}",
6650    "  unset 'words[${#words[@]}-1]'",
6651    String.concat(["  ", id, "_scope"]),
6652    "  for word in \"${words[@]}\"; do",
6653    "    if [[ -n $want ]]; then",
6654    "      want=",
6655    "    elif ((!end)) && [[ $word == -- ]]; then",
6656    "      end=1",
6657    "    elif ((!end)) && [[ $word == --* ]]; then",
6658    String.concat(["      ", id, "_arg \"${word%%=*}\""]),
6659    "      [[ -n $kind && $opt == 0 && $word != *=* ]] && want=$word",
6660    "    elif ((!end)) && [[ $word == -?* ]]; then",
6661    "      for ((j = 1; j < ${#word}; j++)); do",
6662    String.concat(["        ", id, "_arg \"-${word:j:1}\""]),
6663    "        [[ -n $kind ]] || continue",
6664    "        ((j + 1 == ${#word})) && want=-${word:j:1}",
6665    "        break",
6666    "      done",
6667    "    elif ((${#subs[@]})); then",
6668    "      ((end)) && return",
6669    "      if [[ $word == help ]] && ((!help)); then",
6670    "        help=1",
6671    "        continue",
6672    "      fi",
6673    "      for ((j = 0; j < ${#subs[@]}; j++)); do",
6674    "        [[ $word == \"${subs[j]}\" ]] && break",
6675    "      done",
6676    "      ((j < ${#subs[@]})) || return",
6677    "      path=${dests[j]}",
6678    String.concat(["      ", id, "_scope"]),
6679    "    else",
6680    "      # n counts the slots filled. The variadic slot stays open, but a rest()",
6681    "      # value takes every later word, so nothing more completes.",
6682    "      n=$((n + 1))",
6683    "      if ((n == many && rest)); then",
6684    "        end=1",
6685    "      elif ((n == many)); then",
6686    "        n=$((n - 1))",
6687    "      fi",
6688    "    fi",
6689    "  done",
6690    "  if [[ -n $want ]]; then",
6691    String.concat(["    ", id, "_arg \"$want\""]),
6692    String.concat(["    ", id, "_offer \"$cur\" ''"]),
6693    "  elif ((!end)) && [[ $cur == --*=* ]]; then",
6694    String.concat(["    ", id, "_arg \"${cur%%=*}\""]),
6695    String.concat(["    ", id, "_offer \"${cur#*=}\" \"${cur%%=*}=\""]),
6696    "  elif ((!end)) && [[ $cur == -* ]]; then",
6697    "    values=(\"${opts[@]}\") kind=w",
6698    "    ((help)) && values=('-h' '--help')",
6699    String.concat(["    ", id, "_offer \"$cur\" ''"]),
6700    "  elif ((!end && ${#subs[@]})); then",
6701    "    values=(\"${subs[@]}\") kind=w",
6702    "    ((help)) || values+=('help')",
6703    String.concat(["    ", id, "_offer \"$cur\" ''"]),
6704    "  elif ((!help)); then",
6705    String.concat(["    ", id, "_arg $((n + 1))"]),
6706    String.concat(["    ", id, "_offer \"$cur\" ''"]),
6707    "  fi",
6708    "}",
6709    "",
6710    String.concat(["complete -F ", id, " ", name]),
6711  ]

Uses (3)

Used by (1)