Zsh zle shift selection

25
如何使用Shift键选择命令行中的一部分(就像在许多文本编辑器中一样)?

哪个操作系统?哪个终端?此外,这可能更适合在超级用户上提问,因为它与编程无关(但我猜程序员更有可能使用CLI;-) - DarkDust
希望我寻找的是答案而非平台或终端依赖。如果是这样,你就对了superUser,但答案可能是一个zle脚本… - log0
不知道什么是zle,但在Unix上,“选择”是每个终端实现的功能。这不是基本功能,例如在“真正”的终端上,如文本模式控制台或硬件终端上,没有选择支持(据我所知)。 - DarkDust
在zsh中,“选择”或标记由zle(zsh行编辑器)处理,至少我现在是这样认为的。 - log0
@Ugo 我知道第二个事实,但我不知道 set-mark-command,所以我提供的数据可能没有太大帮助。你使用 shift 是指 <S-Right><S-Left> 组合键吗?你可以使用 <C-v> 来获取终端实际发出的内容,当你按下 <S-{Arrow}> 时将其绑定到某个键位,但这将是特定于终端的。如果你想要高亮选择并且它不存在(我实际上不知道),你应该查看 $zle_highlightingxclip。在 zsh 中模拟选择并不是不可能的,但我从未费心去做过,也不认识任何人这样做过。 - ZyX
显示剩余3条评论
6个回答

42
在Stéphane出色的回答基础上进行扩展,我添加了一些绑定,使得行为(几乎)与Windows的标准键盘行为完全一致:
- 使用导航键(箭头、Home、End)时,如果没有按下Shift键,则清除选择。 - 使用Backspace和Del键时,删除活动选择。 - 使用Ctrl+Shift+Left/Ctrl+Shift+Right时,将选择扩展到下一个/上一个单词。 - 使用Shift+Home和Shift+End时,将选择扩展到行的开头和结尾。Ctrl+Shift+Home和Ctrl+Shift+End也是如此。
有两个不完全相同的地方:
- 将选择扩展到下一个单词时,会包括尾随的空格,这与Windows不同。这个问题可以修复,但我并不介意。 - 当存在活动选择时输入字符不会删除选择并替换为您输入的字符。要实现这一点似乎需要更多的工作来重新映射整个键盘。对我来说不值得麻烦。
请注意,默认的mintty行为是将Shift+EndShift+Home绑定到访问滚动缓冲区。这将覆盖zsh配置;这些键永远不会被传递。为了使其正常工作,您需要在/etc/minttyrc~/.minttyrc中配置不同的键(或禁用滚动缓冲区)。请参阅“滚动修饰符”这里 - 最简单的解决方案是将ScrollMod=2设置为将其绑定到Alt而不是Shift
所以一切都明了:

###~/.minttyrc

ScrollMod=2

###~/.zshrc

r-delregion() {
  if ((REGION_ACTIVE)) then
     zle kill-region
  else 
    local widget_name=$1
    shift
    zle $widget_name -- $@
  fi
}

r-deselect() {
  ((REGION_ACTIVE = 0))
  local widget_name=$1
  shift
  zle $widget_name -- $@
}

r-select() {
  ((REGION_ACTIVE)) || zle set-mark-command
  local widget_name=$1
  shift
  zle $widget_name -- $@
}

for key     kcap   seq        mode   widget (
    sleft   kLFT   $'\e[1;2D' select   backward-char
    sright  kRIT   $'\e[1;2C' select   forward-char
    sup     kri    $'\e[1;2A' select   up-line-or-history
    sdown   kind   $'\e[1;2B' select   down-line-or-history

    send    kEND   $'\E[1;2F' select   end-of-line
    send2   x      $'\E[4;2~' select   end-of-line
    
    shome   kHOM   $'\E[1;2H' select   beginning-of-line
    shome2  x      $'\E[1;2~' select   beginning-of-line

    left    kcub1  $'\EOD'    deselect backward-char
    right   kcuf1  $'\EOC'    deselect forward-char
    
    end     kend   $'\EOF'    deselect end-of-line
    end2    x      $'\E4~'    deselect end-of-line
    
    home    khome  $'\EOH'    deselect beginning-of-line
    home2   x      $'\E1~'    deselect beginning-of-line
    
    csleft  x      $'\E[1;6D' select   backward-word
    csright x      $'\E[1;6C' select   forward-word
    csend   x      $'\E[1;6F' select   end-of-line
    cshome  x      $'\E[1;6H' select   beginning-of-line
    
    cleft   x      $'\E[1;5D' deselect backward-word
    cright  x      $'\E[1;5C' deselect forward-word

    del     kdch1   $'\E[3~'  delregion delete-char
    bs      x       $'^?'     delregion backward-delete-char

  ) {
  eval "key-$key() {
    r-$mode $widget \$@
  }"
  zle -N key-$key
  bindkey ${terminfo[$kcap]-$seq} key-$key
}

# restore backward-delete-char for Backspace in the incremental
# search keymap so it keeps working there:
bindkey -M isearch '^?' backward-delete-char

这涵盖了我使用过的几种不同键盘配置的关键码。
注意:在“键”列中的值没有任何意义,它们只是用于为zle构建一个命名引用。它们可以是任何值。重要的是“seq”,“mode”和“widget”列。
注意2:您可以绑定几乎任何您想要的键,只需使用您的控制台模拟器中使用的键码。打开一个常规控制台(不运行zsh),然后键入Ctrl+V,然后是您想要的键。它应该输出代码。“^ [”表示“\E”。

我在哪里可以找到有关“shome”、“csleft”等的更多文件?Google这里不太友好。 - Myer
2
我该如何在这里将Ctrl换成Alt?另外,我该如何使它们与Ubuntu和xterm兼容? - Myer
不幸的是,以这种方式重新绑定退格键会在“bck-i-search”期间打退格键时导致打字中断。 - Vladimir Panteleev
@VladimirPanteleev 确实 - 我无法想出一个好的解决方法 - 它似乎有特殊的行为(例如 backward-delete-char,它是 BS 的默认绑定,在 bck-i-search 上下文中从函数调用时不会产生相同的效果)。您仍然可以使用 CTRL+H 作为退格键;我想人们需要决定 BS 键是否应该在此上下文中工作还是在正常的 CLI 上工作。 - Jamie Treworgy
这些\E[1;6D代码中的第二个数字确定了修饰符:基础为1,Shift加1,Alt/Option/Meta加2,Ctrl加4。因此,6表示Shift+Ctrl(1+1+4)。Alt为3,Shift+Alt为4。 - ak2
显示剩余2条评论

22
shift-arrow() {
  ((REGION_ACTIVE)) || zle set-mark-command
  zle $1
}
shift-left()  shift-arrow backward-char
shift-right() shift-arrow forward-char
shift-up()    shift-arrow up-line-or-history
shift-down()  shift-arrow down-line-or-history
zle -N shift-left
zle -N shift-right
zle -N shift-up
zle -N shift-down

bindkey $terminfo[kLFT] shift-left
bindkey $terminfo[kRIT] shift-right
bindkey $terminfo[kri]  shift-up
bindkey $terminfo[kind] shift-down

这假设你的终端在按下Shift-Arrows时发送的转义序列与按下Arrow时发送的不同,并且你的terminfo数据库正确地填充了相应的kLFT和kRIT功能,并且你正在使用emacs样式的键位绑定。

或者,为了将代码因素化一些:

shift-arrow() {
  ((REGION_ACTIVE)) || zle set-mark-command
  zle $1
}
for key  kcap seq        widget (
    left  LFT $'\e[1;2D' backward-char
    right RIT $'\e[1;2C' forward-char
    up    ri  $'\e[1;2A' up-line-or-history
    down  ind $'\e[1;2B' down-line-or-history
  ) {
  functions[shift-$key]="shift-arrow $widget"
  zle -N shift-$key
  bindkey ${terminfo[k$kcap]-$seq} shift-$key
}

在上面,硬编码序列用于在terminfo数据库没有信息的情况下使用xterm序列。


11

在Jamie Treworgy的回答基础上进行扩展。

包含以下功能:

  • cmd+a:选择整个命令行提示文本
  • cmd+x:将当前命令行选择剪切(复制并删除)到剪贴板
  • cmd+c:将当前命令行选择复制到剪贴板
  • cmd+v:粘贴剪贴板中的选择
  • ctrl+u:向后删除至行首
  • cmd+z:撤销
  • cmd+shift+z:重做
  • Shift选择
    • shift-left:向左选择字符
    • shift-right:向右选择字符
    • shift-up:向上选择行
    • shift-down:向下选择行
    • cmd-shift-left:选择至行首
    • cmd-shift-right:选择至行尾
    • alt-shift-left:选择左边的单词
    • alt-shift-right:选择右边的单词
    • ctrl-shift-left:选择至行首
    • ctrl-shift-right:选择至行尾
    • ctrl-shift-a:选择至行首
    • ctrl-shift-e:选择至行尾
  • 取消选择:在left/rightalt-left/rightcmd/ctrl-left/rightesc+esc上按预期工作。
  • 删除选择:在Deletectrl+dbackspace上按预期工作
  • 删除选择并插入字符:对于所有可见ASCII字符和空格,都按预期工作
  • 删除选择并插入剪贴板:按预期工作

.zshrc
# for my own convenience I explicitly set the signals
#   that my terminal sends to the shell as variables.
#   you might have different signals. you can see what
#   signal each of your keys sends by running `$> cat`
#   and pressing keys (you'll be able to see most keys)
#   also some of the signals sent might be set in your 
#   terminal emulator application/program
#   configurations/preferences. finally some terminals
#   have a feature that shows you what signals are sent
#   per key press.
#
# for context, at the time of writing these variables are
#   set for the kitty terminal program, i.e these signals  
#   are mostly ones sent by default by this terminal.
export KEY_ALT_F='ƒ'
export KEY_ALT_B='∫'
export KEY_ALT_D='∂'
export KEY_CTRL_U=$'\x15' # ^U
export KEY_CMD_BACKSPACE=$'^[b'   # arbitrary; added via kitty config (send_text)
export KEY_CMD_Z=^[[122;9u
export KEY_SHIFT_CMD_Z=^[[122;10u
export KEY_CTRL_R=$'\x12' # ^R
export KEY_CMD_C=^[[99;9u
export KEY_CMD_X=^[[120;9u
export KEY_CMD_V=^[[118;9u
export KEY_CMD_A=^[[97;9u
export KEY_CTRL_L=$'\x0c' # ^L
export KEY_LEFT=${terminfo[kcub1]:-$'^[[D'}
export KEY_RIGHT=${terminfo[kcuf1]:-$'^[[C'}
export KEY_SHIFT_UP=${terminfo[kri]:-$'^[[1;2A'}
export KEY_SHIFT_DOWN=${terminfo[kind]:-$'^[[1;2B'}
export KEY_SHIFT_RIGHT=${terminfo[kRIT]:-$'^[[1;2C'}
export KEY_SHIFT_LEFT=${terminfo[kLFT]:-$'^[[1;2D'}
export KEY_ALT_LEFT=$'^[[1;3D'
export KEY_ALT_RIGHT=$'^[[1;3C'
export KEY_SHIFT_ALT_LEFT=$'^[[1;4D'
export KEY_SHIFT_ALT_RIGHT=$'^[[1;4C'
export KEY_CMD_LEFT=$'^[[1;9D'
export KEY_CMD_RIGHT=$'^[[1;9C'
export KEY_SHIFT_CMD_LEFT=$'^[[1;10D'
export KEY_SHIFT_CMD_RIGHT=$'^[[1;10C'
export KEY_CTRL_A=$'\x01' # ^A
export KEY_CTRL_E=$'\x05' # ^E
export KEY_SHIFT_CTRL_A=$'^[[97;6u'
export KEY_SHIFT_CTRL_E=$'^[[101;6u'
export KEY_SHIFT_CTRL_LEFT=$'^[[1;6D'
export KEY_SHIFT_CTRL_RIGHT=$'^[[1;6C'
export KEY_CTRL_D=$'\x04' # ^D

# -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

# copy selected terminal text to clipboard
zle -N widget::copy-selection
function widget::copy-selection {
    if ((REGION_ACTIVE)); then
        zle copy-region-as-kill
        printf "%s" $CUTBUFFER | pbcopy
    fi
}

# cut selected terminal text to clipboard
zle -N widget::cut-selection
function widget::cut-selection() {
    if ((REGION_ACTIVE)) then
        zle kill-region
        printf "%s" $CUTBUFFER | pbcopy
    fi
}

# paste clipboard contents
zle -N widget::paste
function widget::paste() {
    ((REGION_ACTIVE)) && zle kill-region
    RBUFFER="$(pbpaste)${RBUFFER}"
    CURSOR=$(( CURSOR + $(echo -n "$(pbpaste)" | wc -m | bc) ))
}

# select entire prompt
zle -N widget::select-all
function widget::select-all() {
    local buflen=$(echo -n "$BUFFER" | wc -m | bc)
    CURSOR=$buflen   # if this is messing up try: CURSOR=9999999
    zle set-mark-command
    while [[ $CURSOR > 0 ]]; do
        zle beginning-of-line
    done
}

# scrolls the screen up, in effect clearing it
zle -N widget::scroll-and-clear-screen
function widget::scroll-and-clear-screen() {
    printf "\n%.0s" {1..$LINES}
    zle clear-screen
}

function widget::util-select() {
    ((REGION_ACTIVE)) || zle set-mark-command
    local widget_name=$1
    shift
    zle $widget_name -- $@
}

function widget::util-unselect() {
    REGION_ACTIVE=0
    local widget_name=$1
    shift
    zle $widget_name -- $@
}

function widget::util-delselect() {
    if ((REGION_ACTIVE)) then
        zle kill-region
    else
        local widget_name=$1
        shift
        zle $widget_name -- $@
    fi
}

function widget::util-insertchar() {
    ((REGION_ACTIVE)) && zle kill-region
    RBUFFER="${1}${RBUFFER}"
    zle forward-char
}

#                       |  key sequence                   | command
# --------------------- | ------------------------------- | -------------

bindkey                   $KEY_ALT_F                        forward-word
bindkey                   $KEY_ALT_B                        backward-word
bindkey                   $KEY_ALT_D                        kill-word
bindkey                   $KEY_CTRL_U                       backward-kill-line
bindkey                   $KEY_CMD_BACKSPACE                backward-kill-line
bindkey                   $KEY_CMD_Z                        undo
bindkey                   $KEY_SHIFT_CMD_Z                  redo
bindkey                   $KEY_CTRL_R                       history-incremental-search-backward
bindkey                   $KEY_CMD_C                        widget::copy-selection
bindkey                   $KEY_CMD_X                        widget::cut-selection
bindkey                   $KEY_CMD_V                        widget::paste
bindkey                   $KEY_CMD_A                        widget::select-all
bindkey                   $KEY_CTRL_L                       widget::scroll-and-clear-screen

for keyname        kcap   seq                   mode        widget (

    left           kcub1  $KEY_LEFT             unselect    backward-char
    right          kcuf1  $KEY_RIGHT            unselect    forward-char

    shift-up       kri    $KEY_SHIFT_UP         select      up-line-or-history
    shift-down     kind   $KEY_SHIFT_DOWN       select      down-line-or-history
    shift-right    kRIT   $KEY_SHIFT_RIGHT      select      forward-char
    shift-left     kLFT   $KEY_SHIFT_LEFT       select      backward-char

    alt-right         x   $KEY_ALT_RIGHT        unselect    forward-word
    alt-left          x   $KEY_ALT_LEFT         unselect    backward-word
    shift-alt-right   x   $KEY_SHIFT_ALT_RIGHT  select      forward-word
    shift-alt-left    x   $KEY_SHIFT_ALT_LEFT   select      backward-word

    cmd-right         x   $KEY_CMD_RIGHT        unselect    end-of-line
    cmd-left          x   $KEY_CMD_LEFT         unselect    beginning-of-line
    shift-cmd-right   x   $KEY_SHIFT_CMD_RIGHT  select      end-of-line
    shift-cmd-left    x   $KEY_SHIFT_CMD_LEFT   select      beginning-of-line

    ctrl-e            x   $KEY_CTRL_E           unselect    end-of-line
    ctrl-a            x   $KEY_CTRL_A           unselect    beginning-of-line
    shift-ctrl-e      x   $KEY_SHIFT_CTRL_E     select      end-of-line
    shift-ctrl-a      x   $KEY_SHIFT_CTRL_A     select      beginning-of-line
    shift-ctrl-right  x   $KEY_SHIFT_CTRL_RIGHT select      end-of-line
    shift-ctrl-left   x   $KEY_SHIFT_CTRL_LEFT  select      beginning-of-line

    del               x   $KEY_CTRL_D           delselect   delete-char

    a                 x       'a'               insertchar  'a'
    b                 x       'b'               insertchar  'b'
    c                 x       'c'               insertchar  'c'
    d                 x       'd'               insertchar  'd'
    e                 x       'e'               insertchar  'e'
    f                 x       'f'               insertchar  'f'
    g                 x       'g'               insertchar  'g'
    h                 x       'h'               insertchar  'h'
    i                 x       'i'               insertchar  'i'
    j                 x       'j'               insertchar  'j'
    k                 x       'k'               insertchar  'k'
    l                 x       'l'               insertchar  'l'
    m                 x       'm'               insertchar  'm'
    n                 x       'n'               insertchar  'n'
    o                 x       'o'               insertchar  'o'
    p                 x       'p'               insertchar  'p'
    q                 x       'q'               insertchar  'q'
    r                 x       'r'               insertchar  'r'
    s                 x       's'               insertchar  's'
    t                 x       't'               insertchar  't'
    u                 x       'u'               insertchar  'u'
    v                 x       'v'               insertchar  'v'
    w                 x       'w'               insertchar  'w'
    x                 x       'x'               insertchar  'x'
    y                 x       'y'               insertchar  'y'
    z                 x       'z'               insertchar  'z'
    A                 x       'A'               insertchar  'A'
    B                 x       'B'               insertchar  'B'
    C                 x       'C'               insertchar  'C'
    D                 x       'D'               insertchar  'D'
    E                 x       'E'               insertchar  'E'
    F                 x       'F'               insertchar  'F'
    G                 x       'G'               insertchar  'G'
    H                 x       'H'               insertchar  'H'
    I                 x       'I'               insertchar  'I'
    J                 x       'J'               insertchar  'J'
    K                 x       'K'               insertchar  'K'
    L                 x       'L'               insertchar  'L'
    M                 x       'M'               insertchar  'M'
    N                 x       'N'               insertchar  'N'
    O                 x       'O'               insertchar  'O'
    P                 x       'P'               insertchar  'P'
    Q                 x       'Q'               insertchar  'Q'
    R                 x       'R'               insertchar  'R'
    S                 x       'S'               insertchar  'S'
    T                 x       'T'               insertchar  'T'
    U                 x       'U'               insertchar  'U'
    V                 x       'V'               insertchar  'V'
    W                 x       'W'               insertchar  'W'
    X                 x       'X'               insertchar  'X'
    Y                 x       'Y'               insertchar  'Y'
    Z                 x       'Z'               insertchar  'Z'
    0                 x       '0'               insertchar  '0'
    1                 x       '1'               insertchar  '1'
    2                 x       '2'               insertchar  '2'
    3                 x       '3'               insertchar  '3'
    4                 x       '4'               insertchar  '4'
    5                 x       '5'               insertchar  '5'
    6                 x       '6'               insertchar  '6'
    7                 x       '7'               insertchar  '7'
    8                 x       '8'               insertchar  '8'
    9                 x       '9'               insertchar  '9'

    exclamation-mark      x  '!'                insertchar  '!'
    hash-sign             x  '\#'               insertchar  '\#'
    dollar-sign           x  '$'                insertchar  '$'
    percent-sign          x  '%'                insertchar  '%'
    ampersand-sign        x  '\&'               insertchar  '\&'
    star                  x  '\*'               insertchar  '\*'
    plus                  x  '+'                insertchar  '+'
    comma                 x  ','                insertchar  ','
    dot                   x  '.'                insertchar  '.'
    forwardslash          x  '\\'               insertchar  '\\'
    backslash             x  '/'                insertchar  '/'
    colon                 x  ':'                insertchar  ':'
    semi-colon            x  '\;'               insertchar  '\;'
    left-angle-bracket    x  '\<'               insertchar  '\<'
    right-angle-bracket   x  '\>'               insertchar  '\>'
    equal-sign            x  '='                insertchar  '='
    question-mark         x  '\?'               insertchar  '\?'
    left-square-bracket   x  '['                insertchar  '['
    right-square-bracket  x  ']'                insertchar  ']'
    hat-sign              x  '^'                insertchar  '^'
    underscore            x  '_'                insertchar  '_'
    left-brace            x  '{'                insertchar  '{'
    right-brace           x  '\}'               insertchar  '\}'
    left-parenthesis      x  '\('               insertchar  '\('
    right-parenthesis     x  '\)'               insertchar  '\)'
    pipe                  x  '\|'               insertchar  '\|'
    tilde                 x  '\~'               insertchar  '\~'
    at-sign               x  '@'                insertchar  '@'
    dash                  x  '\-'               insertchar  '\-'
    double-quote          x  '\"'               insertchar  '\"'
    single-quote          x  "\'"               insertchar  "\'"
    backtick              x  '\`'               insertchar  '\`'
    whitespace            x  '\ '               insertchar  '\ '
) {
    eval "function widget::key-$keyname() {
        widget::util-$mode $widget \$@
    }"
    zle -N widget::key-$keyname
    bindkey $seq widget::key-$keyname
}

# suggested by "e.nikolov", fixes autosuggest completion being 
# overriden by keybindings: to have [zsh] autosuggest [plugin
# feature] complete visible suggestions, you can assign an array
# of shell functions to the `ZSH_AUTOSUGGEST_ACCEPT_WIDGETS` 
# variable. when these functions are triggered, they will also 
# complete any visible suggestion. Example:
export ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
    widget::key-right
    widget::key-shift-right
    widget::key-cmd-right
    widget::key-shift-cmd-right
)
奖励

有些人可能也会发现以下内容有用,虽然我没有在上面列出来,只需将它们添加到for循环数组中即可:

  1. Tab

        tab                   x  $'\x09'           insertchar  '\   '
    
    • note that tab completion will fail
  2. Other

        send              kEND   $'\E[1;2F'        select      end-of-line
        send2             x      $'\E[4;2~'        select      end-of-line
    
        shome             kHOM   $'\E[1;2H'        select      beginning-of-line
        shome2            x      $'\E[1;2~'        select      beginning-of-line
    
        end               kend   $'\EOF'           deselect    end-of-line
        end2              x      $'\E4~'           deselect    end-of-line
    
        home              khome  $'\EOH'           deselect    beginning-of-line
        home2             x      $'\E1~'           deselect    beginning-of-line
    
        csend             x      $'\E[1;6F'        select      end-of-line
        cshome            x      $'\E[1;6H'        select      beginning-of-line
    
        cleft             x      $'\E[1;5D'        deselect    backward-word
        cright            x      $'\E[1;5C'        deselect    forward-word
    
        del               kdch1   $'\E[3~'         delregion   delete-char
    

虽然有些内容已经过时,但对于一些人仍然可能有用

注意

某些键盘按键序列首先在终端应用程序(例如iTerm2)上进行配置,以发送特定信号给shell程序。以上代码中使用的绑定为:

 iTerm2
    Preferences
      Keys
        Key Bindings:
按键序列 按键绑定
cmd+shift+left 发送转义序列:a
cmd+shift+right 发送转义序列:e
ctrl+shift+a 发送转义序列:a
ctrl+shift+e 发送转义序列:e
cmd+left 发送十六进制代码:\x01
cmd+right 发送十六进制代码:\x05
cmd+a 发送转义序列:å
cmd+c 发送转义序列:ç
cmd+v 发送转义序列:
cmd+x 发送转义序列:
cmd+z 发送转义序列:Ω
cmd+shift+z 发送转义序列:¸

此步骤将终端键绑定到shell信号上,即告诉终端程序/应用程序(iTerm2)在按下某些键盘键序列时向shell程序(zsh)发送什么信号。根据您的终端程序和喜好,您可以随意绑定键,或使用默认信号。

keyboard  -->  cmd+z  -->  iTerm2  -->  ^[Ω  -->  zsh  -->  undo (widget)

然后上面的脚本将接收到的信号绑定到称为小部件的shell函数上。也就是说,我们告诉shell程序,在接收到指定的信号(键序列)时运行指定的小部件。因此,在您的命令行上按下键盘键序列时,终端会向shell发送相应的信号,并且shell调用相应的小部件(函数)。在这种情况下,我们要绑定到shell的函数是编辑命令行本身的函数,就像它是一个文件一样。
上面定义的小部件使用了zsh内置的zle(zsh行编辑器)模块API。有关更多信息,请参见官方文档:ZSH ➤ 18 Zsh Line Editor和官方指南:ZSH ➤ Chapter 4: The Z-Shell Line Editor
  • A neat trick to see what signals are received by the shell is running cat and then pressing keys:

    ❯ cat
    ^[[1;2D^[[1;2C^[Ω^[≈^[ç
    

    That was the output for after having pressed: shift-left, shift-right, cmd+z, cmd+x, and cmd+c.

    Certain keys might not appear. In this case, check your terminal's configurations, the key might be binded to some terminal functionality (e.g. cmd+n might open up a new terminal pane, cmd+t might open up a new terminal tab).

    Also see terminfo(5), another way of finding certain keys.

已知问题和问题修复

  • if you're on iTerm2, changing what cmd+v is binded to might make pasting on anything other than the command-line act different, and requires remapping on that specific program (e.g. in the search prompts for programs like less). if you want to avoid this, then don't change the mapping of iTerm2's cmd+v, and comment-out/remove the widget::paste.

  • esc might clash with oh-my-zsh's sudo plugin and give weird behavior. You can comment-out/remove the esc line in the array, or suggest a fix.

  • right clashes with zsh-autosuggestion, i.e it won't accept the suggestion. You can comment-out/remove right from the array, or suggest a fix. This is probably possible, I just currently don't know how, and spent enough time for now trying.

    I tried many things, I think the closest thing to working might of been something like:

    function widget::key-right() {
        REGION_ACTIVE=0
        zle autosuggest-accept
        zle forward-char
    }
    zle -N widget::key-right
    bindkey $'\eOC' widget::key-right
    

    But to no avail. It doesn't complete the suggestion. You could however always create a new keybinding for it:

    bindkey $'\e\'' autosuggest-accept
    

    I got autosuggestion-accept from the Github repo: zsh-users/zsh-autosuggestions.

    To fix right-key clashing with zsh-autosuggestion, and/or other clashes you might have, add to one of your shell initialization files (e.g. .zshrc) the following: export ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(<shell-function> ...) (suggested by @e.nikolov). see above for an example.


1
当我执行以下操作时,自动建议功能起作用了:export ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=widget::key-right - e.nikolov
1
可能应该这样写:export ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(widget::key-right widget::key-shift-right widget::key-shift-cmd-right widget::key-cmd-right)。如果它不是一个数组,那么可能会有其他兼容性问题。 - e.nikolov
哦,很酷 @e.nikolov,更好了。 - 8c6b5df0d16ade6c

5

这个页面上的所有解决方案要么不完整,要么过于侵入性,因此会对其他插件(例如zsh-autosuggestions或zsh-syntax-highlighting)造成负面影响。因此,我提出了一种不同的方法,它的效果显著更好。

https://github.com/jirutka/zsh-shift-select/

该插件不会覆盖任何现有的窗口小部件(widget),仅绑定按下shift键的组合键。它创建了一个新的 shift-select keymap,在调用 shift-select(使用任何定义的 shifted keys)时自动激活,并在按下未定义在 shift-select keymap 中的任何键时停用(当前 keymap 切换回主键盘映射)。由于这种方法,它不会干扰其他插件的使用(例如, 它可以与 zsh-autosuggestions 无需任何更改地一起使用)。


3

适用于Windows和WSL用户。

这是其他作者答案的结合,尊重他们的答案,在WSL中修改以与Window Terminal一起使用。行为与标准PowerShell相同:ctrl+shift+箭头选择,ctrl+z、x、c、v、a等。

# zsh-shift-select https://dev59.com/RW035IYBdhLWcg3wc_wm#30899296
r-delregion() {
  if ((REGION_ACTIVE)) then
     zle kill-region
  else
    local widget_name=$1
    shift
    zle $widget_name -- $@
  fi
}
r-deselect() {
  ((REGION_ACTIVE = 0))
  local widget_name=$1
  shift
  zle $widget_name -- $@
}
r-select() {
  ((REGION_ACTIVE)) || zle set-mark-command
  local widget_name=$1
  shift
  zle $widget_name -- $@
}
for key     kcap   seq        mode   widget (
    sleft   kLFT   $'\e[1;2D' select   backward-char
    sright  kRIT   $'\e[1;2C' select   forward-char
    sup     kri    $'\e[1;2A' select   up-line-or-history
    sdown   kind   $'\e[1;2B' select   down-line-or-history
    send    kEND   $'\E[1;2F' select   end-of-line
    send2   x      $'\E[4;2~' select   end-of-line
    shome   kHOM   $'\E[1;2H' select   beginning-of-line
    shome2  x      $'\E[1;2~' select   beginning-of-line
    left    kcub1  $'\EOD'    deselect backward-char
    right   kcuf1  $'\EOC'    deselect forward-char
    end     kend   $'\EOF'    deselect end-of-line
    end2    x      $'\E4~'    deselect end-of-line
    home    khome  $'\EOH'    deselect beginning-of-line
    home2   x      $'\E1~'    deselect beginning-of-line
    csleft  x      $'\E[1;6D' select   backward-word
    csright x      $'\E[1;6C' select   forward-word
    csend   x      $'\E[1;6F' select   end-of-line
    cshome  x      $'\E[1;6H' select   beginning-of-line
    cleft   x      $'\E[1;5D' deselect backward-word
    cright  x      $'\E[1;5C' deselect forward-word
    del     kdch1   $'\E[3~'  delregion delete-char
    bs      x       $'^?'     delregion backward-delete-char
  ) {
  eval "key-$key() {
    r-$mode $widget \$@
  }"
  zle -N key-$key
  bindkey ${terminfo[$kcap]-$seq} key-$key
}
# Fix zsh-autosuggestions https://dev59.com/RW035IYBdhLWcg3wc_wm#30899296
export ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
  key-right
)
# ctrl+x,c,v https://unix.stackexchange.com/a/634916/424080
function zle-clipboard-cut {
  if ((REGION_ACTIVE)); then
    zle copy-region-as-kill
    print -rn -- $CUTBUFFER | clip.exe
    zle kill-region
  fi
}
zle -N zle-clipboard-cut
function zle-clipboard-copy {
  if ((REGION_ACTIVE)); then
    zle copy-region-as-kill
    print -rn -- $CUTBUFFER | clip.exe
  else
    zle send-break
  fi
}
zle -N zle-clipboard-copy
function zle-clipboard-paste {
  if ((REGION_ACTIVE)); then
    zle kill-region
  fi
  LBUFFER+="$(cat clip.exe)"
}
zle -N zle-clipboard-paste
function zle-pre-cmd {
  stty intr "^@"
}
precmd_functions=("zle-pre-cmd" ${precmd_functions[@]})
function zle-pre-exec {
  stty intr "^C"
}
preexec_functions=("zle-pre-exec" ${preexec_functions[@]})
for key     kcap    seq           widget              arg (
    cx      _       $'^X'         zle-clipboard-cut   _
    cc      _       $'^C'         zle-clipboard-copy  _
    cv      _       $'^V'         zle-clipboard-paste _
) {
  if [ "${arg}" = "_" ]; then
    eval "key-$key() {
      zle $widget
    }"
  else
    eval "key-$key() {
      zle-$widget $arg \$@
    }"
  fi
  zle -N key-$key
  bindkey ${terminfo[$kcap]-$seq} key-$key
}
# ctrl+a https://dev59.com/RW035IYBdhLWcg3wc_wm#68987551
function widget::select-all() {
  local buflen=$(echo -n "$BUFFER" | wc -m | bc)
  CURSOR=$buflen  
  zle set-mark-command
  while [[ $CURSOR > 0 ]]; do
    zle beginning-of-line
  done
}
zle -N widget::select-all
bindkey '^a' widget::select-all
# ctrl+z
bindkey "^Z" undo

在WSL2 "Linux 5.15.79.1-microsoft-standard-WSL2"上测试,使用Windows终端 "1.15.3466.0"。


1
感谢这些答案,我会将它们整合并编写脚本,以便使用键盘进行选择和复制。
如果有人想要过滤器来使内容更加干净,我将不胜感激。
这是位于用户主目录下的~/.zshrc文件的一部分。
alias pbcopy="xclip -selection clipboard"

shift-arrow() {


((REGION_ACTIVE)) || zle set-mark-command 
  zle $1
}
for key  kcap seq        widget (
    left  LFT $'\e[1;2D' backward-char
    right RIT $'\e[1;2C' forward-char
    up    ri  $'\e[1;2A' up-line-or-history
    down  ind $'\e[1;2B' down-line-or-history
    super sup $'\ec' widget::copy-selection
  ) {
  functions[shift-$key]="shift-arrow $widget"
  zle -N shift-$key
  bindkey ${terminfo[k$kcap]-$seq} shift-$key
}
 
zle -N widget::copy-selection


# copy selected terminal text to clipboard
function widget::copy-selection {
    if ((REGION_ACTIVE)); then
        zle copy-region-as-kill
            printf "%s" $CUTBUFFER | pbcopy
    fi
}

我使用了Windows+C键来复制选定的字符。我正在使用Ubuntu 20.04,并配置键盘选项,将Win键用作元键!在这之后,在终端仿真器的首选项中,我将粘贴快捷键更改为Windows+V,因为我认为这样会更快,就像Control+X和Control+V一样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接