Skip to content

[Feature Request] show special key combinations instead of bytes: <Esc> instead of ^[ #78

@juanMarinero

Description

@juanMarinero

Quoting next vi.stackexchange answer:

Special key combinations, such as Ctrl+→, are translated into terminal keycodes like ^[[1;5C, then into vim keycodes like <C-Right>, and finally into a sequence of bytes starting with 0x80 like <80><fd>V (<80> is 0x80). This is a special case where the vim keycode is different than the terminal keycode.
After assigning the register q like this:
let @q="\<C-Right>"
the sequence of bytes is what's actually saved:
:registers q
Type Name Content
c "q <80><fd>V
Press ENTER or type command to continue

In vim-peekaboo the output is also byted as in :registers command.

But one can overwrite (in command line) a register through user friendly key-strokes (Vim ones as in mapping, not byted). Quoting this:

For readability purpose, it's possible to use the proper key-notation tags such as <Esc> or <CR> instead of ^[ or ^M
You would need to escape the tag <Esc> with a single \ and use double quotes instead of single quotes which would result in "\<Esc>". The following examples are equivalent
:let @e='^[I<e>^[A</e>'
:let @e="\<Esc><e>\<Esc>A</e>"

So my plugin feature request is this. To show the Vim mappings, not the sequence of bytes.

Related to this I tried to make a function, just to (not byte sequence) edit a register quickly in command line. With few easy examples it is working (see screenshot), BUT I did test just easy cases, a lot lot more is TODO. Furthermore, I don't know:

  • if is reasonable to translate every byte to user friendly characters (and therefore if my plugin future request is reasonable) or pro Vim users are just used to byte sequences (or find it easily in documentation)
  • and if it's reasonable, then I doubt if I make a right approach in my next function (same approach as my feature request could adapt)
  • or maybe if in next Vim upgrades are already working on this

Thanks in advance!

Screenshot example:
2021-05-20_00-17

[registers_friendly_overwrite__vim.txt](https://github.com/junegunn/vim-peekaboo/files/6511854/registers_friendly_overwrite__vim.txt)

Note: in next pasted code, the bytes sequences are not recognized in github, for example ^[ is transformed to . Rather download previous file, and change extension to .vim (github neither allows to upload .vim files in comments [banging head on wall emoji]).

nnoremap <leader>z <C-u>:call X_Register_echo('d')<CR>:let @d="<C-r>*"<Left>
fun! X_Register_echo(reg)

    " toogle to 1 to debug current function (otherwise 0)
    let debug_bool = 0

    " use 'a:' before arg input variable to use it https://learnvimscriptthehardway.stevelosh.com/chapters/24.html
    " echo a:reg
    let regText = getreg(a:reg, 1, 1)
    let regTextStr01 = string(regText) " list to string
    if strlen(l:regTextStr01) > 0
        if debug_bool
          echo "regTextStr01 --> "regTextStr01
        endif

        " check if has substring
        " echo stridx(regTextStr, str_match)

        " replace <Esc> alike for \<Esc>
            " https://stackoverflow.com/questions/2943555/how-to-save-a-vim-macro-that-contains-escape-key-presses
            " Ctrl + V-Esc for "�"

            " double or tiple value just as one (<Esc><Esc> is equivalent to just <Esc>)
            let str_match = "���"
            let str_replacement = "�"
            let regTextStr01 = substitute(l:regTextStr01, l:str_match, l:str_replacement, "g") " substitute all (flag 'g')
            let str_match = "��"
            let regTextStr01 = substitute(l:regTextStr01, l:str_match, l:str_replacement, "g") " substitute all (flag 'g')
            if debug_bool
              echo "regTextStr01 --> "regTextStr01
            endif

            let str_match = "�"
            let str_replacement = '\\<Esc>'
            " let str_replacement = "\\\\\<Esc\>"

            " https://stackoverflow.com/questions/4864073/using-substitute-on-a-variable
            let regTextStr02 = substitute(l:regTextStr01, l:str_match, l:str_replacement, "g") " substitute all (flag 'g')
            if debug_bool
              echo "regTextStr02 --> "regTextStr02
            endif

        " remove <fd><80>a alike --> added to macro changing from insert to normal mode (i.e. not always added)
        " https://vi.stackexchange.com/questions/19465/whats-the-difference-between-qp-and-c-rc-rq-when-you-store-a-macro
        " Opt.A search and remove ( I cannot even find it, so no removing it this way)
            " " Ctrl+V u0080 for <80>
            " " Ctrl+V u00fd for <fd>
            " " let str_match = 'a'
            " " let str_match = "�ýa"
            " let str_match = '�'
            " " let str_match = '<80>'
            " " let str_match = '�ý'
            " let str_replacement = ''
            " 
            " " not even finding it
            " echo stridx(regTextStr02, str_match)
            " echo stridx(regTextStr02, '\\�')
            " 
            " " let regTextStr03 = substitute(l:regTextStr02, l:str_match, l:str_replacement, "")
            " let regTextStr03 = s:strreplace(l:regTextStr02, l:str_match, l:str_replacement)
            " echo "regTextStr03 --> "regTextStr03
        
        " Opt. B: remove next 6 bytes after \<Esc>
            " remove ONLY if not already removed before
            if l:regTextStr01 == l:regTextStr02
                let l:removed_already_bool = 1
            else
                let l:removed_already_bool = 0
            endif

            " remove just 1st match:
            "  if l:removed_already_bool == 0
            "      let l:match_str ='\<Esc>'
            "      let l:match_len = strlen(l:match_str)
            "      let l:match_index = stridx(regTextStr02, match_str) " cannot be -1
            "      if debug_bool
            "          echo "regTextStr02 --> "regTextStr02
            "          echo "removed_already_bool --> "removed_already_bool
            "          echo "match_index --> "match_index
            "      endif
            "      " VimL counts the bytes instead of the characters for indexing --> https://stackoverflow.com/a/64780270/9391770
            "      let l:match_index += l:match_len -1
            "      let l:regTextStr03 = regTextStr02[2:match_index] . regTextStr02[match_index+3:]
            "      if debug_bool
            "        " echo "n --> "n
            "        echo "regTextStr03 --> "regTextStr03
            "        echo "-----------------\n"
            "      endif
            "  endif

            " previous just remove first <fd><80>a alike
            " ... to remove all we shall loop 
            if l:removed_already_bool == 0
                let l:match_str ='\<Esc>'
                let l:match_len = strlen(l:match_str)
                let l:cnt = 1
                let l:regTextStr_shorted = l:regTextStr02
                while l:cnt > 0
                    if debug_bool
                        echo "regTextStr_shorted --> "regTextStr_shorted
                    endif
                    let l:match_index = stridx(regTextStr_shorted, match_str) " cannot be -1 the 1st time
                    if debug_bool
                      echo "match_index --> "match_index
                    endif
                    let l:match_index_prev = l:match_index
                    if l:match_index > -1

                        " it's not always alike: ��<fd><80>a
                        " ... sometimes is just alike: ��
                        let l:match_char_after = regTextStr_shorted[match_index+match_len:match_index+match_len]
                        if debug_bool
                          echo "match_char_after --> "match_char_after
                        endif
                        let l:remove_fd80a_bool = 1
                        if "qwertyuiopasdfghjklzxcvbnm" =~ l:match_char_after
                          let l:remove_fd80a_bool = 0
                        endif

                        if l:cnt == 1
                            let l:regTextStr03 = regTextStr_shorted[2:match_index+match_len-1]
                        else
                            if l:remove_fd80a_bool
                                let l:regTextStr03 = l:regTextStr03 . regTextStr_shorted[0:match_index+match_len-1]
                            else
                                let l:regTextStr03 = l:regTextStr03 . regTextStr_shorted[1:match_index+match_len-1]
                            endif
                        endif
                        if l:remove_fd80a_bool
                            let l:regTextStr_shorted = regTextStr_shorted[match_index+match_len+2:]
                        else
                            let l:regTextStr_shorted = regTextStr_shorted[match_index+match_len:]
                        endif
                        let l:cnt+=1
                        if l:cnt > 9
                          " avoid infinite loop debugging
                          break
                        endif
                    else
                        let l:cnt=0
                    endif
                    if debug_bool
                        echo "regTextStr03 --> "regTextStr03."\n"
                    endif
                endwhile
            else
              let l:regTextStr03 = l:regTextStr01[1:-3]
            endif

        " echo "Register '" a:reg "' values: " l:regText
        echon "Register "
                    \ | :echon "'"
                    \ | :call EchoWarning(a:reg)
                    \ | :echon "'"
                    \ | :echon " now values: '"
                    \ | :call EchoGreen(l:regTextStr03)
                    \ | :echon "'"
        echo "Register "
                    \ | :echon "'"
                    \ | :call EchoWarning(a:reg)
                    \ | :echon "'"
                    \ | :echon " did value:  '"
                    \ | :call EchoGreen(l:regTextStr01[2:-3])
                    \ | :echon "'"
        echo "Tip: "
                    \ | :echo "\tRun @@ab to copy reg 'a' into 'b' (if same vim process)"
                    \ | :echo "\tOR copy inbetween single quotes of previous echo of reg. "
                    \ | :call EchoWarning(a:reg)
                    \ | :echon " and execute in other terminal: "
                    \ | :call EchoBlue(':let @')
                    \ | :call EchoWarning(a:reg)
                    \ | :call EchoBlue('="<C-r>+"')

        " overwrite register
        let [regText, regType] = [getreg(a:reg, 1, 1), getregtype(a:reg)]
        if debug_bool
            echo "regText --> "regText
        endif
        " call setreg(a:reg, l:regTextStr03, l:regType)
          " setreg() would need double quotes, not single quotes to make \Esc work
          " https://stackoverflow.com/a/56506589/9391770
            " For readability purpose, it's possible to use the proper key-notation tags such as <Esc> or <CR> instead of ^[ or ^M
            " You would need to escape the tag <Esc> with a single \ and use double quotes instead of single quotes which would result in "\<Esc>". The following examples are equivalent
            " :let @e='^[I<e>^[A</e>'
            " :let @e="\<Esc><e>\<Esc>A</e>"
        " alternative:
        let l:x_reg_echo_aux = l:regTextStr03
    else
        let l:x_reg_echo_aux = "empty register"
    endif
    call setreg('*', l:x_reg_echo_aux, l:regType)
endfun


function! EchoWarning(msg)
    echohl WarningMsg
    echon a:msg
    echohl None
endfunction
                                                
" EchoGreen
hi X_ColorGreen guifg=#00ff00 ctermfg=darkgreen
function! EchoGreen(msg)
    echohl X_ColorGreen
    echon a:msg
    echohl None
endfunction
                                                
" EchoBlue
hi X_ColorBlue guifg=#00ff00 ctermfg=blue
function! EchoBlue(msg)
    echohl X_ColorBlue
    echon a:msg
    echohl None
endfunction

Btw if anyone is interested, for the @@ab map that I mention in screenshot check this or this:

nnoremap @@ab <C-u>:call CopyRegister('a','b')<cr>
fun! CopyRegister(reg_origin,reg_destiny)
    let [regText, regType] = [getreg(a:reg_origin, 1, 1), getregtype(a:reg_origin)]
    call setreg(a:reg_destiny, l:regText, l:regType)
    " echo "Register '" a:reg_destiny "' changed to value of register'" a:reg_origin "': " l:regText
endfun

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions