在vim中粘贴时如何使用转义字符

7
我在vim中将输出缓冲区的内容复制到C++代码中。这些输出通常会被放入字符串中。如果能自动转义所有控制字符而不是返回并手动编辑粘贴的片段,那就太好了。
例如,我可能会复制以下内容:
error in file "foo.dat"

需要将其放入类似于这样的东西中。
std::string expected_error = "error in file \"foo.dat\""

我认为可以对上次粘贴的正文应用替换函数,使用上次粘贴的起始和结束标记,但我不确定如何实现。
更新:
Joey Mazzarelli建议使用
`[v`]h:%s/\%V"/\\"/g

粘贴后。

由于没有解释这是什么,我最初觉得有点简洁,但在评论中很难解释,所以我想在这里解释一下我认为它的作用:

`[  : Move to start of last paste
v   : Start visual mode
`]  : Move to end of last paste
h   : adjust cursor one position left
:%  : apply on the lines of the selection
s/  : replace
\%V : within the visual area
"   : "
/   : with
\\" : \"
/g  : all occurrences

这似乎是一个不错的方法,但只处理了一个字符“”,我希望它可以处理换行符、制表符和其他可能出现在文本中的内容。(可能不包括一般的Unicode)我理解这在问题定义中可能不太清楚。

这个应该放在超级用户上吧? - zmbush
@zipcodeman 我认为SO是可以的,因为它专门用于编码任务中使用vim。如果是用vim进行系统管理任务,我可能会使用SU。 - Michael Anderson
4个回答

5
以下是几个vimscript函数,应该可以满足您的要求。
- `EscapeText()`将任意文本转换为C转义等效形式。它将换行符转换为`\n`,制表符转换为`\t`,控制字符G转换为`\a`等,并为没有友好名称的特殊字符生成八进制转义(如`\o032`)。
- `PasteEscapedRegister()`转义名为`v:register`的寄存器的内容,然后将其插入到当前缓冲区中。(当函数返回时,寄存器将被恢复,因此可以重复调用该函数而不会多次转义寄存器内容。)
还包括一些关键映射,使`PasteEscapedRegister()`易于交互使用。`P`在光标位置之前粘贴转义的寄存器内容,`p`在之后粘贴。都可以加上一个寄存器规范前缀,例如`"a\P`以粘贴寄存器a的转义内容。
以下是代码:
function! EscapeText(text)

    let l:escaped_text = a:text

    " Map characters to named C backslash escapes. Normally, single-quoted
    " strings don't require double-backslashing, but these are necessary
    " to make the substitute() call below work properly.
    "
    let l:charmap = {
    \   '"'     : '\\"',
    \   "'"     : '\\''',
    \   "\n"    : '\\n',
    \   "\r"    : '\\r',
    \   "\b"    : '\\b',
    \   "\t"    : '\\t',
    \   "\x07"  : '\\a',
    \   "\x0B"  : '\\v',
    \   "\f"    : '\\f',
    \   }

    " Escape any existing backslashes in the text first, before
    " generating new ones. (Vim dictionaries iterate in arbitrary order,
    " so this step can't be combined with the items() loop below.)
    "
    let l:escaped_text = substitute(l:escaped_text, "\\", '\\\', 'g')

    " Replace actual returns, newlines, tabs, etc., with their escaped
    " representations.
    "
    for [original, escaped] in items(charmap)
        let l:escaped_text = substitute(l:escaped_text, original, escaped, 'g')
    endfor

    " Replace any other character that isn't a letter, number,
    " punctuation, or space with a 3-digit octal escape sequence. (Octal
    " is used instead of hex, since octal escapes terminate after 3
    " digits. C allows hex escapes of any length, so it's possible for
    " them to run up against subsequent characters that might be valid
    " hex digits.)
    "
    let l:escaped_text = substitute(l:escaped_text,
    \   '\([^[:alnum:][:punct:] ]\)',
    \   '\="\\o" . printf("%03o",char2nr(submatch(1)))',
    \   'g')

    return l:escaped_text

endfunction


function! PasteEscapedRegister(where)

    " Remember current register name, contents, and type,
    " so they can be restored once we're done.
    "
    let l:save_reg_name     = v:register
    let l:save_reg_contents = getreg(l:save_reg_name, 1)
    let l:save_reg_type     = getregtype(l:save_reg_name)

    echo "register: [" . l:save_reg_name . "] type: [" . l:save_reg_type . "]"

    " Replace the contents of the register with the escaped text, and set the
    " type to characterwise (so pasting into an existing double-quoted string,
    " for example, will work as expected).
    " 
    call setreg(l:save_reg_name, EscapeText(getreg(l:save_reg_name)), "c")

    " Build the appropriate normal-mode paste command.
    " 
    let l:cmd = 'normal "' . l:save_reg_name . (a:where == "before" ? "P" : "p")

    " Insert the escaped register contents.
    "
    exec l:cmd

    " Restore the register to its original value and type.
    " 
    call setreg(l:save_reg_name, l:save_reg_contents, l:save_reg_type)

endfunction

" Define keymaps to paste escaped text before or after the cursor.
"
nmap <Leader>P :call PasteEscapedRegister("before")<cr>
nmap <Leader>p :call PasteEscapedRegister("after")<cr>

还没有尝试过,但看起来完美(甚至读起来也很好... vimscript 最终可能会看起来像 Perl ;)) - Michael Anderson

1

这至少可以让你开始...

粘贴后:

`[v`]h:%s/\%V"/\\"/g

你显然可以将其映射到更容易输入的内容。


这似乎是一个不错的方法,但只处理了一个字符“,”我希望它能处理换行符、制表符和其他可能出现在文本中的内容。(可能不包括通用Unicode)我理解在问题定义中可能没有表述清楚。 - Michael Anderson

0

虽然 Joey 的解决方案看起来可能可扩展以涵盖我需要的所有情况,但我想分享一下我的部分解决方案,使用 vim 的 Python 集成(因为我比 vim 脚本更熟悉 Python)

# FILE : tocstring.py
import vim
def setRegister(reg, value):
  vim.command( "let @%s='%s'" % (reg, value.replace("'","''") ) )

def getRegister(reg):
  return vim.eval("@%s" % reg )

def transformChar( map, c):
  if c in map:
    return map[c]
  return c

def transformText( map, text ):
  return ''.join( [ transformChar(map,c) for c in text ] )

cmap={}
cmap["\\"]="\\\\"
cmap["\n"]="\\n" 
cmap["\t"]=r"\\t"
cmap['"']="\\\""

def convertToCString( inregister, outregister ):
  setRegister(outregister, transformText( cmap, getRegister(inregister) ) )

然后在我的 .vimrc 或其他配置文件中,我可以放置以下内容

# FILE cpp.vim
python import tocstring
# C-Escape and paste the currently yanked content
nmap <Leader>P :python tocstring.convertToCString("@","z")<CR>"zP
# C-Escape and paste the current visual selection
vmap <Leader>P "zd:python tocstring.convertToCString("z","z")<CR>"zP

如果我能让第一个函数工作,那就太好了,这样"a\"就可以粘贴“a”寄存器的转换内容,我认为这可以通过v:register以某种方式实现,但我不知道如何做到。

可以制作与Joeys解决方案相同的版本,以相同的方式工作。

nmap <Leader>P `[v`]"zd:python tocstring.convertToCString("z","z")<CR>"zP

致谢:本文使用了Can you access registers from python functions in vim的代码,用于从Vim的Python中与寄存器进行交互。

0

对于Java/JavaScript类型的转义,可以使用json_encode

nmap <leader>jp :call setreg('e', json_encode(@+))\| normal "ep<CR>

json_encode(@+) - 将寄存器+的内容进行JSON编码(映射到剪贴板)

setreg('e',...) - 将其写入寄存器e

normal "ep - 粘贴寄存器e的内容


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