列位置可视化逐行选择

Column position visual selection line by line

例子:

text téxt text |text| text
text text text |text| text
text téxt téxt |text| text
text téxt text |text| téxt

我正在选择一段文本“|text|”以上行数

我想知道的是每一行的startcolumn和endcolumn的列位置。

如果我使用:

echo col('"<') --> 17  
echo col('">') --> 20  

它给了我块第一行的开始列和块最后一行的结束列。

然而在每一行中我的块选择之前都有多字节字符。我想知道每一行的 col('"<') 和 col('">'):

17、20
16、19
18、21
17、20

如何获得这些?

我们从视觉(按块)选择的开始和结束列开始。这些是 屏幕列 ,即计算屏幕单元格。您想将它们转换为字节索引,对于每个覆盖的行。

因此,我们需要一种将屏幕列转换为字节索引的方法。如果我们使用游标,有一些方便的函数可以将它定位在特定的屏幕列上([N]| 命令),以及查询字节索引的函数(col())。因此,整个算法将通过 virtcol() 获得的选择开始和结束的屏幕列(分别标记为 '<'>)通过定位光标转换为字节索引.

这是一种实现方法:

:'<,'>global/^/
\   let positions = [] |
\   for mark in ['<', '>'] |
\       exe 'normal!' virtcol("'" . mark) . '|' |
\       call add(positions, col('.')) |
\   endfor |
\   echo positions
[17, 20]
[16, 19]
[18, 21]
[17, 20]

选择的开始和结束屏幕列可以通过virtcol()获得。然后我们可以将光标定位到开始位置,然后定位到结束位置,通过col('.').

获取字节索引

为简单起见,我在这里使用 :global 遍历所有选定的行,并将列放入列表中。

作为 virtcol({mark}) returns 字符索引,您可以将其用作正则表达式中的计数器。

例如,我们有:

let first_col = virtcol("'<")
let last_col = virtcol("'>")

从这里开始,每行的3个部分可以用

得到
let lead      = matchstr(getline(n), '\v^.{'.(first_col-1).'}')
let selection = matchstr(getline(n), '\v^.{'.(first_col-1).'}\zs.{'.(last_col-first_col+1).'}')
let tail      = matchstr(getline(n), '\v^.{'.(last_col).'}\zs.*')

或者我们也可以使用matchlist()

let [all, lead, selection, tail; dummy] = matchlist(getline('.'), '\v^(.{'.(first_col-1).'})(.{'.(last_col-first_col+1).'})(.*)')

我想我们应该也可以使用 split(),但它会很复杂。

请注意,由于这种手动计数,我们有了一个正则表达式,因此我们可以直接使用 :'<,'>substitute。那样的话,直接用\%V in the pattern. But beware, it's a little bit tricky to use correctly: to signal the end of the match, we need to negate the pattern. See this discussion on vi.SE来稍微解释一下就更简单了。

:'<,'>s/\v^(.{-})%V(.{-})%V@!(.*)/####/

或者,如果您希望在每个匹配项上应用函数,#### 可以变为:\=(func1(submatch(1)).func2(submatch(2)).func3(submatch(3))。这样,您可以保留 python 部分来实现各种 funcn() 功能。请注意,如果需要,您可以将多个参数传递给这些函数,甚至可以使用单个匹配项调用单个函数:\=singlefunc(submatch(1), submatch(2), submatch(3))