Readlineのキリングを確認するには、非常にハッキングされたBashスクリプトの助けが必要です。

Readlineのキリングを確認するには、非常にハッキングされたBashスクリプトの助けが必要です。

私はBashでキリングサークルを可視化するデバイスを設計しました。

仕組みは次のとおりです。

$ killring
Killring:
1 Line one in killring
2 Line two in killring
3 Line three in killring

しかし、いくつかの問題もあります。

  1. killringが空の場合、ユーザーはEnterを押す必要があります。これは内部関数名に再キャストされ、エラーメッセージとしても使用されます。

    $ killring
    __Killring_is_empty__Press_enter_to_continue <<'EOF'
    > EOF
    
  2. 関数が成功すると、プロンプトの後に無関係の文字が表示されます。

    $ killring
    Killring:
    1 Line one in killring
    2 Line two in killring
    3 Line three in killring
    ^[[0n$
    
  3. 100行の回転するキルループを印刷する中間機能を隠すために画面を消去する必要がありました。しかし、それは私が余裕がある問題だ。

  4. /dev/nullリダイレクトを削除すると、無関係なメッセージが表示されます。

    $ killring
    Killring:
    1 Line one in killring
    2 Line two in killring
    3 Line three in killring
    gstty: 'standard input': Inappropriate ioctl for device
    gstty: 'standard input': Inappropriate ioctl for device
    ^[[0ngstty: invalid argument ‘’
    Try 'gstty --help' for more information.
    

ソースコードは次のとおりです。

# from https://unix.stackexchange.com/a/217390/569343
# write provided text to the terminal input
# (does not work in subshells)
# - There are some weird errors when I remove the 2>/dev/nulls, but I don't
#   even know how to begin to fix them.
function write_to_input () {
  bind '"\e[0n": "'"$*"'"'

  saved_settings=$(stty -g 2>/dev/null)
  stty -echo -icanon min 1 time 0 2>/dev/null
  printf '\e[5n'
  until read -r -t0; do
    sleep 0.02
  done
  stty "$saved_settings" 2>/dev/null
}

# Killring internal function
# - this function performs the actual job of calculating and printing the
#   killring
# - the strange name is because, due to the ungodly hack required for this
#   thing to work, there is an edge case when the kill ring is empty, in which
#   this function will be typed into the Readline input line but the user still
#   needs to press enter. Therefore the function itself acts as the
#   user-friendly message.
# - It works like this:
#   - It guesses the content of the keyring after 10 rotations, and prints it.
#   - It calculates how many times it must rotate to return to the original
#     position, and performs the rotation by editing the Readline input line.
#   - Then it preses delete 500 times, in the hope of returning the input line
#     in pristine condition.
function __Killring_is_empty__Press_enter_to_continue () {
  local array;
  readarray -t array

  # if array is empty, killring is empty
  if [[ ${#array[@]} == 0 ]]; then
    return
  fi

  # the last yanked thing
  local current_position=$((${#array[@]} - 1))

  # find index of element that matches the first, or -1 if none
  # Note: this script assumns that there are no repeated elements in the
  #       killring. If that's not the case, the ring will rotate from the
  #       original position.
  #       A better algorithm to detect repeated elements could help, but I
  #       can't be arsed.
  local index_of_repeat=-1
  for (( i = 1; i < ${#array[@]}; i++ )); do
    if [[ "${array[$i]}" == "${array[0]}" ]]; then
      index_of_repeat=$i
      break
    fi
  done

  # reduce array to the actual killring
  if [[ $index_of_repeat != -1 ]]; then
    array=("${array[@]:0:$index_of_repeat}")
  fi

  local killring_size="${#array[@]}"
  
  # print killring
  local green='\x1b[32m'
  local reset='\x1b[0m'
  clear
  echo "${green}"Killring:"${reset}"
  for (( i = 0; i < ${#array[@]}; i++ )); do
    echo "${green}$i${reset} ${array[$i]}"
  done

  local needed_pops=0
  local killring_position=$((current_position % killring_size))
  if [[ $killring_position != 0 ]]; then
    needed_pops=$((killring_size - killring_position))
  fi

  # doing the pops
  local command='\C-y'
  for (( i = 0; i < needed_pops; i++ )); do
    command="$command\ey"
  done

  # append 500 backspaces to clear the input (\C-?)
  # Forgive me, Lord.
  for (( i = 0; i < 500; i++ )); do
    command="$command\C-?"
  done
  write_to_input "$command"
  set -o history
}

# show the current killring
function killring () {
  set +o history

  # a killring has a maximum size of 10 in bash, so this should be enough to
  # figure out the contents
  local size=10
  
  # compose command to show killring.
  # - the command consists of pressing \C-y to yank 100 times, hoping to cover
  #   the entire killring.
  # - for some reason, \C-y stops the command generation if there's nothing in
  #   the killring, so we leave the command in a functional state in that
  #   situation, so that the user can press enter to proceed.
  local command="__Killring_is_empty__Press_enter_to_continue <<'EOF'\nEOF"
  command="$command\C-y\C-a\C-d\C-d\C-d\n"
  for (( i = 1; i < size; i++ )); do
    command="$command\C-y\ey\n"
  done
  command="${command}EOF\n"
  write_to_input "$command"
}

もちろん、もしキリングサークルを見る他の方法を知っていれば教えてください。

関連情報