Skip to content

Instantly share code, notes, and snippets.

@Kyure-A
Created November 23, 2023 14:06
Show Gist options
  • Save Kyure-A/7f8fdfb25d43a36ff3b008f0382da2ba to your computer and use it in GitHub Desktop.
Save Kyure-A/7f8fdfb25d43a36ff3b008f0382da2ba to your computer and use it in GitHub Desktop.
zsh predict-on を読む

https://github.com/zsh-users/zsh/blob/master/Functions/Zle/predict-on を GPT-3.5 に投げて説明をつけてみた

# このセットの関数は、ある種の魔法の履歴検索を実装しています。
# predict-on 後、文字を入力すると、エディタは履歴を逆に検索し、入力された文字列で始まる最初の行を見つけます。
# predict-off 後、編集は通常のモードに戻り、見つかった行を元に戻します。
# 実際、predict-off を使わなくても十分です。なぜなら、行が履歴に一致しない場合、キーを追加すると通常の補完が実行されるからです。
# ただし、行の中で編集すると、残りの部分が削除される可能性があるため注意が必要です。

# 関数ベースの補完システム(これが必要です)を使用すると、ほとんどどこでもTABを入力して
# カーソルを次の「興味深い」文字位置(通常は現在の単語の終わりですが、時には単語の途中になります)に進めるはずです。
# もちろん、行全体が欲しいものになったら、カーソルを最後に移動する必要なくRETURNで受け入れることができます。

# これを使うには:
# autoload -Uz predict-on
# zle -N predict-on
# zle -N predict-off
# bindkey '...' predict-on
# bindkey '...' predict-off
# 注意: 最初に predict-on キーを入力すると、すべての関数が定義されるため、それより前に predict-off キーを入力すると無害なエラーメッセージが表示されます。

# キーが押されたときに呼び出される関数を定義
predict-on() {
  # self-insert, magic-space, backward-delete-char のキーが押されたときに
  # 対応する関数(insert-and-predict, delete-no-predict, delete-backward-and-predict)を呼び出す
  zle -N self-insert insert-and-predict
  zle -N magic-space insert-and-predict
  zle -N backward-delete-char delete-backward-and-predict
  zle -N delete-char-or-list delete-no-predict
  # predict モードのときに冗長なメッセージを表示するかどうか
  zstyle -t :predict verbose && zle -M predict-on
  return 0
}

# predict モードを無効にする
predict-off() {
  # zle に登録した関数を元に戻す
  zle -A .self-insert self-insert
  zle -A .magic-space magic-space
  zle -A .backward-delete-char backward-delete-char
  # predict モードのときに冗長なメッセージを表示するかどうか
  zstyle -t :predict verbose && zle -M predict-off
  return 0
}

# 文字を挿入して予測する関数
insert-and-predict() {
  # オプションの設定(関数内でのみ有効)
  setopt localoptions noshwordsplit noksharrays

  # マルチラインバッファを編集中か、テキストのチャンクを貼り付けている場合は予測が望ましくない可能性がある
  if [[ $LBUFFER == *$'\012'* ]] || (( PENDING ))
  then
    # マルチラインバッファを編集中またはテキストを貼り付けている場合は予測が不要なので、predict-off を呼び出す
    zstyle -t ":predict" toggle && predict-off
    zle .$WIDGET "$@"
    return
  elif [[ ${RBUFFER[1]} == ${KEYS[-1]} ]]
  then
    # 入力と同じなので次に進むだけ
    ((++CURSOR))
  else
    # 入力を LBUFFER に追加
    LBUFFER="$LBUFFER$KEYS"
    if [[ $LASTWIDGET == (self-insert|magic-space|backward-delete-char) ||
          $LASTWIDGET == (complete-word|accept-*|predict-*|zle-line-init) ]]
    then
      # 最後のウィジェットが self-insert、magic-space、backward-delete-char、complete-word、accept-*
      # または predict-* または zle-line-init のいずれかである場合は予測を実行
      if ! zle .history-beginning-search-backward
      then
        RBUFFER=""
        if [[ ${KEYS[-1]} != ' ' ]]
        then
          # 自動メニューと再帰的な完全一致の無効化
          unsetopt automenu recexact
          # カーソル、位置、キャラクタ数の設定
          integer curs=$CURSOR pos nchar=${#LBUFFER//[^${KEYS[-1]}]}
          local -a +h comppostfuncs
          local crs curcontext="predict:${${curcontext:-:::}#*:}"

          # 補完後の処理関数の指定
          comppostfuncs=( predict-limit-list )
          zle complete-word

          # カーソルをどこに置くかを決定する
          repeat 1
          do
            zstyle -s ":predict" cursor crs
            case $crs in
            (complete)
              # 補完がカーソルを残した場所(通常は入力された文字の後)にカーソルを置く
              [[ ${LBUFFER[-1]} = ${KEYS[-1]} ]] && break
              ;;
            (key)
              # または入力された文字の n 番目の出現位置にカーソルを置く
              pos=${BUFFER[(in:nchar:)${KEYS[-1]}]}
              if [[ pos -gt curs ]]
              then
                CURSOR=$pos
                break
              fi
              ;;
            (*)
              # または前回の位置にカーソルを置く
              CURSOR=$curs
            esac
          done
        fi
      fi
    else
      # 最後のウィジェットがそれ以外の場合は予測を無効にする
      zstyle -t ":predict" toggle && predict-off
    fi
  fi
  return 0
}

# 後方に文字を削除して予測する関数
delete-backward-and-predict() {
  if (( $#LBUFFER > 1 ))
  then
    setopt localoptions noshwordsplit noksharrays
    # マルチラインバッファを編集中、または最後のウィジェットが self-insert、magic-space、backward-delete-char 以外の場合
    # は予測が望ましくない可能性がある
    if [[ $LBUFFER = *$'\012'* ||
          $LASTWIDGET != (self-insert|magic-space|backward-delete-char) ]]
    then
      # マルチラインバッファを編集中または最後のウィジェットが self-insert、magic-space、backward-delete-char 以外の場合は予測を無効にする
      zstyle -t ":predict" toggle && predict-off
      LBUFFER="$LBUFFER[1,-2]"
    else
      # カーソルを前に移動して履歴を逆検索し、見つかったらそれを元に戻す
      ((--CURSOR))
      zle .history-beginning-search-forward || RBUFFER=""
      return 0
    fi
  else
    # 1 文字より少ない場合は行全体を削除
    zle .kill-whole-line
  fi
}

# 削除して予測しない関数
delete-no-predict() {
  # delete-char-or-list かつ RBUFFER が空でない場合、または予測が無効にされている場合は予測を無効にする
  [[ $WIDGET != delete-char-or-list || -n $RBUFFER ]] && predict-off
  zle .$WIDGET "$@"
}

# 補完リストを制限するための補助関数
predict-limit-list() {
  # リストの行数が画面の行数を超えるか、
  # compstate[list_max] が設定されておりかつ compstate[nmatches] が compstate[list_max] を超える場合
  if (( compstate[list_lines]+BUFFERLINES > LINES ||
        ( compstate[list_max] != 0 &&
          compstate[nmatches] > compstate[list_max] ) ))
  then
    # リストを空にする
    compstate[list]=''
  # 補完リストを常に表示する設定がされている場合はリストを強制的に表示
  elif zstyle -t ":predict" list always
  then
    compstate[list]='force list'
  fi
}

# zsh の autoloading 慣例を処理する
[[ -o kshautoload ]] || predict-on "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment