【发布时间】:2011-08-23 14:49:04
【问题描述】:
我想设置两种折叠方法
-
:set foldmethod=indent并保留其所有功能 -
隐藏cmets
:set foldmethod=marker:set foldmarker=/*,*/
我发现这是不可能的。是否可以实现所需的折叠并将其设置在.vimrc 或为此使用一些脚本或插件?
【问题讨论】:
我想设置两种折叠方法
:set foldmethod=indent 并保留其所有功能隐藏cmets
:set foldmethod=marker
:set foldmarker=/*,*/
我发现这是不可能的。是否可以实现所需的折叠并将其设置在.vimrc 或为此使用一些脚本或插件?
【问题讨论】:
我和你有同样的要求,这是我的不完美的解决方案
我的 maker 对是 #(或 pycharm 中的 #region 和 #endregion)
let b:inBlock=0
let b:lastLineNum=0
let b:lastLevel=0
let b:lastGoodLine=0
let b:lastGoodBlock=0
let b:startFoldingMark='^\s*.\?#<==*\|^\s*.\?#region'
let b:endFoldingMark='^\s*.\?#=*=>\|^\s*.\?#endregion'
function! MyFold(linenum)
let linetext = getline(a:linenum)
let level = indent(a:linenum) / &shiftwidth
"the first line have 0 fold level
if (a:linenum == 1)
if linetext =~ b:startFoldingMark
let b:inBlock = 1
let b:lastLineNum=a:linenum
let b:lastGoodLine=0
let b:lastGoodBlock=0
let b:lastLevel=level
return level
endif
let b:inBlock=0
let b:lastInBlock=0
let b:lastLineNum=a:linenum
let b:lastGoodLine=0
let b:lastGoodBlock=b:inBlock
let b:lastLevel=level + b:inBlock
return level + b:inBlock
endif
" not calculate from the mid of text
if ((b:lastLineNum+1) != a:linenum)
let level = indent(a:linenum) / &shiftwidth
let lastGoodNum = a:linenum-1
while (lastGoodNum>1 && getline(lastGoodNum) =~? '\v^\s*$' )
let lastGoodNum -= 1
endwhile
if (foldlevel(lastGoodNum)==-1)
let b:inBlock=b:lastGoodBlock
else
let lastlevel = indent(lastGoodNum) / &shiftwidth
let lastlinetext = getline(lastGoodNum)
let lastlinelevel = foldlevel(lastGoodNum)
if lastlinetext =~ b:startFoldingMark
let b:inBlock = lastlinelevel - lastlevel + 1
elseif lastlinetext =~ b:endFoldingMark
let b:inBlock = lastlinelevel - lastlevel - 1
else
let b:inBlock = lastlinelevel - lastlevel
endif
endif
endif
"blank lines have undefined fold level
if getline(a:linenum) =~? '\v^\s*$'
let b:lastLineNum=a:linenum
let b:lastLevel=-1
return -1
endif
"if next line is a start of new marker block, inBlock ++
if linetext =~ b:startFoldingMark
let b:lastLineNum=a:linenum
if (b:lastLevel != -1)
let b:lastGoodLine=a:linenum
let b:lastGoodBlock=b:inBlock
endif
let b:lastLevel=level + b:inBlock - 1
return level + b:inBlock - 1
"if next line is an end of new marker block, inBlock -
elseif linetext =~ b:endFoldingMark
let b:inBlock = b:inBlock - 1
let b:lastLineNum=a:linenum
let b:lastGoodLine=a:linenum
let b:lastGoodBlock=b:inBlock
let b:lastLevel=level + b:inBlock + 1
return level + b:inBlock + 1
endif
let b:lastLineNum=a:linenum
if (b:lastLevel != -1)
let b:lastGoodLine=a:linenum
let b:lastGoodBlock=b:inBlock
endif
let b:lastLevel=level + b:inBlock
return level+b:inBlock
endfunction
现在,我可以在使用缩进折叠方法时保留所有功能, 我可以折叠每个 # 标记块, 此外,行的缩进折叠关系仍然保留在每个块中。
在这个函数中,我避免使用“a1”、“s1”和“=”级别,这将导致这个函数的迭代,并且对于大文件可能会很慢。 但是,当你更新行时,折叠级别的计算可能不正确(因为 vim 可能不会从头更新所有折叠级别,因此 inBlock 值不正确)
您可以使用 zx 手动更新折叠级别。
【讨论】:
与我在对您的问题的不同答案中建议的基于 expr 的方法相比,基于语法的折叠可能是获得所需内容的更好方法。检查:h fold-syn 了解更多信息。我认为基于 c 的折叠可能已经有一些好的解决方案。不知道它有多好,但这里有一个支持基于语法的折叠的 c-syntax 文件:
http://www.vim.org/scripts/script.php?script_id=234
另一个:
http://www.vim.org/scripts/script.php?script_id=925
上述解决方案完全基于语法,不涉及使用缩进来确定折叠级别。但是如果需要,您可以修改基于语法的折叠以通过缩进区域进行主要折叠。如果你根据句法元素缩进,结果可能是一样的。
这里有一个提示,展示了如何折叠 c 样式的 cmets(不是实际代码) http://vim.wikia.com/wiki/Fold_C-style_comments
【讨论】:
在同一个缓冲区中不可能有不同的折叠方法类型。 Vim 如何知道有一些缩进级别与您想要视为不同(更高编号)级别的其他文本的缩进级别?
我相信你可以通过将 foldmethod 设置为 'expr' 来实现你想要的。这是在 Vim 中进行折叠的最灵活的方式,但根据您的需要可能会变得复杂(和/或缓慢)。不过,我认为它对于您的用例来说相当容易。
首先,在你的 vimrc 或 vimscripts 的某个地方,你需要确保 foldexpr 被定义为有问题的文件类型。
set foldexpr=MyFoldLevel(v:lnum)
set foldmethod=expr
" and for last code example
let b:previous_level = 0
然后您必须充实您的 foldexpr 函数,以便它以导致您想要的行为的方式分配级别。在每个注释行都有前缀符号的情况下(即不是您的情况),类似下面的代码可能会接近工作,但我希望它需要一些调整。 h: fold-expr 是寻求帮助的好地方:
function! MyFoldLevel(linenum)
" assign levels based on spaces indented and tabstop of 4
let level = indent(a:linenum) / 4
if getline(a:linenum) =~ [put line-based comment prefix pattern here]
let level = 20
endif
endfunction
需要修改以按照您想要的方式为注释开始和结束标记之间的行分配更高的级别:
function! MyFoldLevel(linenum)
let linetext = getline(a:linenum)
if linetext =~ [put line-based comment prefix pattern here]
let level = 20
elseif linetext =~ '^\s*/\*'
let level = 20
elseif linetext =~ '^\s*\*/'
let level = 21
else
if b:previous_level == 20
let level = 20
else
"assuming code is space-indented with tabstop of 4
let level = indent(a:linenum) / 4
endif
endif
let b:previous_level = level
return level
endfunction
我不希望我编写的 foldmethod 函数会完全按照编写的那样工作。但他们确实指出了可行的方法。
请注意,对 cmets 使用 '20' 级别只是允许它们被折叠的任意级别,而所有(可能是较低级别的)缩进代码都是可见的。注释部分最后一行的 '21' 只是为了将其与前几行级别为 20 的 cmets 区分开来,以了解下一行应视为常规代码行。
此外,像“zc”和“zo”这样的关键操作在设置为比周围代码高得多的级别时,在 cmets 上将无法正常工作。希望使用像:set foldlevel=21 这样的直接命令来显示所有注释行。
不漂亮,我希望它可以简化一点,但我认为这样的东西是你想要的。
实际上,仔细考虑一下,我认为您希望任何注释块的第一行与非注释行处于同一级别,只有同一块中的后续注释行需要更高级别让它们“折叠”到起始注释行中。在我给出的代码中,如果它可以工作或几乎可以工作,我认为 vim 会将 all 前面的非注释行后面的注释行折叠起来,这不是你想要的,但是不幸的是,我没有更多的时间专注于这个小谜题。 . .我已经做了很多次这种自定义折叠,并且通常总是要经过一些试验和错误才能得到我想要的。
【讨论】:
zO 会对您有所帮助 - 它递归地打开光标下的所有折叠(同样,zC 关闭所有折叠)。