Vim: 创建一个接受移动命令的自定义命令

7
我经常将文本行转换为数组。例如,这样的文本:
Monday
Tuesday
Wednesday

Becomes:

[
  'Monday',
  'Tuesday',
  'Wednesday',
]

我可以制作一张地图,更改其中的一行(例如将Monday更改为'Monday',),如下所示: :nnoremap gsa ^i'<Esc>A, 我想要做的是让该命令接收一个移动或文本对象,以便我可以像执行gsaipgsip3j这样的命令。
我该如何实现这个功能?
4个回答

7
您可以使用'operatorfunc'g@来创建一个带有运动的映射。在:map-operator主题下,帮助文档提供了完整的解释和示例。基本上,您需要在映射中设置要调用的函数,并使用g@。Vim将等待运动,然后设置标记'['']'并调用您的函数。
在您的函数内部,您可以发挥创意。这里我只是举了一个快速示例,说明您如何将此原理应用于您所需的内容。
:nnoremap <silent> gsa :set opfunc=TransformToArray<cr>g@

function! TransformToArray(type)
  if a:type == 'line'
    let s=line("'[") " saving beginning mark
    ']s/\(\s*\).*\zs/\r\1]
    exec s ",']-1s/\\s*\\zs.*/  '&',"
    exec s 's/\(\s*\)\zs\ze  /[\r\1'
  elseif a:type == 'char'
    " ...
  endif
endfunction

1
@Meninx-メネンック斯,对我来说是必需的,因为它涉及缩进设置。由于前一行缩进了2个空格,所以在插入闭合括号之前,我使用bs将其删除。这只是一个快速解决方案,需要更多的思考来改进 :) - sidyll
1
@Meninx-メネンックス 是的,但我的意图只是为了演示您可以检测它是否按行或字符方式调用。如果是后者,可能需要不同的东西。感谢您的评论!如果您愿意,随时可以将其编辑掉。我对此持中立态度。 - sidyll
1
不,没关系!这仍然取决于 OP,但我想使用 operatorfunc 是最好的选择。 - Meninx - メネンックス
@user9397302 当你执行 gsaip 时,ip 是一个文本对象,这是有效的。你能举个例子说明一下,哪些情况下会出现问题吗?谢谢! - sidyll
1
哦,好的。我在:map-operator的文档中读到了“尽管“block”很少出现,因为它只能从Visual模式下产生,而“g@”无法使用。”的内容,所以认为它不会像那样工作。看起来这是一种方法,但我最终使用了这篇文章中描述的策略 -> http://vim.wikia.com/wiki/Act_on_text_objects_with_custom_functions - sdeleon28
显示剩余4条评论

1
将以下内容添加到您的.vimrc文件中:
vnoremap <silent> gsa :call Brackets()<CR>

function! Brackets()
    execute "normal! I'"
    if line(".") == a:lastline
        execute "normal! A'\<cr>]"
        execute a:firstline."s:^:[\r:" 
    else
        execute "normal! A',"
    endif
endfunction

选择您想要的视觉行块,例如vip,然后按下gsa

0

准确来说,您似乎想将映射应用于运动或文本对象覆盖的每一行。

您可以通过可视模式建立这样的范围vipv2j。然后,您可以使用:normal gsa(键入;如果您从可视模式进入命令行模式,则Vim会自动插入所选范围(:'<,'>))将您的自定义映射应用于每一行(根据:help :normal-range,光标位于每行的第一列)。


0
如果你真的想在最后一个项目后面添加逗号,我会这样做,以便不改变寄存器。
function! s:to_list() range abort
  let words = getline(a:firstline, a:lastline)
  exe a:firstline.','.a:lastline.'d _' " remove the "_" to fill the unnamed register
  let lines = ['['] + map(words, '"  ".string(v:val).","') + [']']
  call append(a:firstline-1, lines)
endfunction

command! -nargs=0 -range=% ToList <line1>,<line2>call s:to_list()

为了去掉最后两行,我编辑了map()结果的最后一项或使用了join()
  let lines = "[\n" . join(map(words, '"  ".string(v:val)'), ",\n") . "\n]"
  put!=lines

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