假设我正在vim中编辑一些Python代码,如下所示:

myobj.myfunc("Some string parameter that goes on and on and on and on and sometimes doesn't"
             "split very"
             "neatly over different lines so that"
             "it is formatted attractively")


我希望将其重新格式化,以便重新设置为我设置的textwidth

myobj.myfunc("Some string parameter that goes on and on and "
             "on and on and sometimes doesn't split very "
             "neatly over different lines so that it is "
             "formatted attractively")


有简单的方法吗?如果这是常规的文本段落,则gqip或类似的内容将很有用,但这将不能处理用于修饰字符串的引号。

注意:我在这里专门询问Python,但理想情况下,此答案与允许字符串连续的许多类型的编程语言有关。

评论

这里类似的问题:vim.1045645.n5.nabble.com/…

您是否还考虑过将代码重写为““”多行字符串“”“”?

@ 200_success,很好的提示,谢谢。我完全不了解Python的功能。似乎大部分都可以正常工作-尽管该函数的所有参数均已重排,但这并不理想。我认为我的问题对于我拥有的字符串类型仍然有效,当然对于其他语言也是如此。

请注意,多行字符串将添加空格字符,如果字符串中的空格很重要,则可能会出现问题。

非常相关的问题:vi.stackexchange.com/questions/2135/…...

#1 楼

哦,那是一个艰难的过程。我认为最好的方法可能是宏,但更可能是插件。这是我举个例子(我需要一个更好的爱好)。它似乎为我工作,但需要python缩进插件正确缩进。试试吧。

function ReformatMultiLines()
  let brx = '^\s*"'
  let erx = '"\s*$'
  let fullrx = brx . '\(.\+\)' . erx
  let startLine = line(".")
  let endLine   = line(".")
  while getline(startLine) =~ fullrx
    let startLine -= 1
  endwhile
  if getline(endLine) =~ erx
    let endLine += 1
  endif
  while getline(endLine) =~ fullrx
    let endLine += 1
  endwhile
  if startLine != endLine
    exec endLine . ' s/' . brx . '//'
    exec startLine . ' s/' . erx . '//'
    exec startLine . ',' . endLine . ' s/' . fullrx . '//'
    exec startLine . ',' . endLine . ' join'
  endif
  exec startLine
  let orig_tw = &tw
  if &tw == 0
    let &tw = &columns
    if &tw > 79
      let &tw = 79
    endif
  endif
  let &tw -= 3 " Adjust for missing quotes and space characters
  exec "normal A%-%\<Esc>gqq"
  let &tw = orig_tw
  let endLine = search("%-%$")
  exec endLine . ' s/%-%$//'
  if startLine == endLine
    return
  endif
  exec endLine
  exec 'normal I"'
  exec startLine
  exec 'normal A "'
  if endLine - startLine == 1
    return
  endif
  let startLine += 1
  while startLine != endLine
    exec startLine
    exec 'normal I"'
    exec 'normal A "'
    let startLine += 1
  endwhile
endfunction


然后您可以将其与:call ReformatMultiLines()一起使用和/或在映射中使用。

#2 楼

如果这是经常发生的情况,那么最好是寻找插件或使用@Sukima的函数。但是,如果我是即时执行此操作,则可能会执行以下操作:



在开始括号后和结束括号前添加换行符,以便字符串在单独的行上。

myobj.myfunc(
             "Some string parameter that goes on and on and on and on and sometimes doesn't"
             "split very"
             "neatly over different lines so that"
             "it is formatted attractively"
)


选择带有字符串的行并删除周围的引号::norm ^x$x
减小文本宽度(以解决引号引起的问题) -选择并格式化::set tw-=2

修复文本宽度:gvgq

重新添加引号::set tw+=2。而不是gv:norm I"<Esc>A",您想通过键入ctrl-v和后跟转义键来插入文字转义。由于我将jj映射为转义,因此通常只在此处键入jj。
也可以选择使用J重新连接前两行/后两行。当我在python这样的字符串中有一个很长的字符串时,我通常更喜欢在下一行开始它,并且只将其缩进一个级别,而不是上一行。这为弦乐提供了更多的水平空间,让我感觉更自然。或者,您可以将字符串保存到上方的某个变量中。假设它是一个静态字符串,您甚至可以将其保存到全局变量中,以使其缩进级别低得多,并且可以容纳更少的行。请注意,要在同一行上重新加入结束括号,您可能希望将文本宽度减小/增加3,而不是2。


评论


谢谢。我只是试过了,但E487:set tw- = 2结果:参数必须为正:tw- = 2。语法不正确吗?

–安德鲁·费里尔(Andrew Ferrier)
15年3月23日在11:30

用:set tw?检查当前的textwidth值。我的猜测是您将textwidth设置为0。这些步骤假定textwidth开始时是每行所需的字符数。

–马特·博姆(Matt Boehm)
2015年3月23日14:05



在您的示例中,您可能想要s / 2/3 /并在右引号之前添加一个空格,您的长字符串在单词之间没有适当的间距。

–cdosborn
18-09-24在18:28

#3 楼

解析和格式化代码非常困难,尤其是对于诸如Python之类的动态语言。

我建议您使用诸如yapf之类的外部格式化程序,而不是尝试在Vimscript中解析Python。使用我的答案,您可以在使用以下代码自动编写该代码:如果发现错误,则不必在两个地方进行更新:-)

还可以将s:write_cmd()设置为formatprg并使用yapf手动对其进行格式化。

评论


你好有用的答案,但我很不满意,因为我认为它不能回答这个问题:这个问题非常具体地要求重新格式化多行字符串,而yapf似乎不这样做。不过,我同意它看起来像是一个有用的工具。

–安德鲁·费里尔(Andrew Ferrier)
17年1月7日在14:28

@AndrewFerrier足够公平;我发誓我前几天进行测试时看到它重新格式化了字符串,但现在也不能使它工作:-/猜猜我很困惑。

–马丁·图尔诺伊(Martin Tournoij)
17年1月8日在16:36

#4 楼

以下功能和对应的映射为我解决了此问题:

function! BreakMultlineString(quote)
let c = 100
while c == 100
    normal 100|
    let c = virtcol('.')
    if c == 100
        execute "normal ," . a:quote
    endif
endwhile
endfunction

function! JoinMultilineString()
    let c = "'"
    while c == "'" || c == '"'
        normal gj^
        let c = matchstr(getline('.'), '\%' . col('.') . 'c.')
        normal k
        if c == "'" || c == '"'
            normal ,j
        endif
    endwhile
endfunction

" break lines
nnoremap <Leader>" 100\|Bi"<CR>"<Esc>
nnoremap <Leader><Leader>" :call BreakMultlineString('"')<CR>
nnoremap <Leader>' 100\|Bi'<CR>'<Esc>
nnoremap <Leader><Leader>' :call BreakMultlineString("'")<CR>

" join lines
nmap <Leader>j Jds'ds"x
nnoremap <Leader>J :call JoinMultilineString()<CR>