junegunn / vim-peekaboo

:eyes: " / @ / CTRL-R
1.13k stars 39 forks source link

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

Open juanMarinero opened 3 years ago

juanMarinero commented 3 years ago

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:

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
juanMarinero commented 3 years ago

Maybe these could be workarounds to deal blindly with byte sequences. Quoting:

nnoremap <Leader>c :normal [your macro] <Enter>

function TransformNodeCallback()
    normal [your macro]
endfunction
nnoremap <Leader>c :call TransformNodeCallback()^M