我正在尝试使用python模仿view.full_line
SublimeText函数,如果我们阅读docs,我们将会看到:
line(point):返回包含该点的线。
line(region):返回区域的修改后的副本,以使其开始于行的开头,并结束于行的结尾。请注意,它可能跨越几行。
full_line(point):与line()相同,但该区域包括结尾的换行符(如果有)。
full_line(region):与line()相同,但是该区域包括结尾的换行符(如果有)。
我尝试遵循该文档中的说明,这就是我得到的:
class Region(object):
__slots__ = ['a', 'b', 'xpos']
def __init__(self, a, b=None, xpos=-1):
if b is None:
b = a
self.a = a
self.b = b
self.xpos = xpos
def __str__(self):
return "(" + str(self.a) + ", " + str(self.b) + ")"
def __repr__(self):
return "(" + str(self.a) + ", " + str(self.b) + ")"
def begin(self):
if self.a < self.b:
return self.a
else:
return self.b
def end(self):
if self.a < self.b:
return self.b
else:
return self.a
def lskip_nonewlines(text, pt):
len_text = len(text)
while True:
if pt <= 0 or pt >= len_text:
break
if text[pt - 1] == "\n" or text[pt] == "\n":
break
pt -= 1
return pt
def rskip_nonewlines(text, pt):
len_text = len(text)
while True:
if pt <= 0 or pt >= len_text:
break
if text[pt] == "\n":
break
pt += 1
return pt
def full_line(text, x):
region = Region(x)
if region.a <= region.b:
# try:
# if text[region.a]=="\n":
# region.a-=1
# except Exception as e:
# pass
region.a = lskip_nonewlines(text, region.a)
region.b = rskip_nonewlines(text, region.b)
region.b = region.b + 1 if region.b < len(text) else region.b
else:
region.a = rskip_nonewlines(text, region.a)
region.b = lskip_nonewlines(text, region.b)
region.a = region.a + 1 if region.a < len(text) else region.a
return (region.begin(), region.end())
if __name__ == '__main__':
text = "# I'm a comment\n\n\ndef foo():\n print('# No comment')\n"
sublime_output = [
[0, (0, 16)],
[1, (0, 16)],
[2, (0, 16)],
[3, (0, 16)],
[4, (0, 16)],
[5, (0, 16)],
[6, (0, 16)],
[7, (0, 16)],
[8, (0, 16)],
[9, (0, 16)],
[10, (0, 16)],
[11, (0, 16)],
[12, (0, 16)],
[13, (0, 16)],
[14, (0, 16)],
[15, (0, 16)],
[16, (16, 17)],
[17, (17, 18)],
[18, (18, 29)],
[19, (18, 29)],
[20, (18, 29)],
[21, (18, 29)],
[22, (18, 29)],
[23, (18, 29)],
[24, (18, 29)],
[25, (18, 29)],
[26, (18, 29)],
[27, (18, 29)],
[28, (18, 29)],
[29, (29, 55)],
[30, (29, 55)],
[31, (29, 55)],
[32, (29, 55)],
[33, (29, 55)],
[34, (29, 55)],
[35, (29, 55)],
[36, (29, 55)],
[37, (29, 55)],
[38, (29, 55)],
[39, (29, 55)],
[40, (29, 55)],
[41, (29, 55)],
[42, (29, 55)],
[43, (29, 55)],
[44, (29, 55)],
[45, (29, 55)],
[46, (29, 55)],
[47, (29, 55)],
[48, (29, 55)],
[49, (29, 55)],
[50, (29, 55)],
[51, (29, 55)],
[52, (29, 55)],
[53, (29, 55)],
[54, (29, 55)],
]
for test in sublime_output:
pos, expected_output = test
output = full_line(text, pos)
try:
assert output == expected_output
except Exception as e:
print(f"Error at pos: {pos}, output {output}, expected output {expected_output}")
上面的mcve将输出与我从SublimeText本身得到的结果进行比较。您可以看到该函数的性能很好,但在某些极端情况下仍会失败:
Error at pos: 0, output (0, 1), expected output (0, 16)
Error at pos: 15, output (15, 16), expected output (0, 16)
Error at pos: 28, output (28, 29), expected output (18, 29)
Error at pos: 54, output (54, 55), expected output (29, 55)
那么,我该如何修复该例程,使其像SublimeText一样表现为1:1?
答案 0 :(得分:1)
您的代码中有几个相当明显的错误,导致其无法按预期进行。
Error at pos: 0, output (0, 1), expected output (0, 16)
这似乎表明,当代码从位置0向前扫描以确定行的结尾时,它将停止在位置1而不是位置16。
因此,查看您的代码,您在这里有这句话;它是if
的上部,因为您的测试区域总是以a == b
的方式制作:
region.a = lskip_nonewlines(text, region.a)
region.b = rskip_nonewlines(text, region.b)
region.b = region.b + 1 if region.b < len(text) else region.b
因此,查看此内容,找到结束位置是rskip_nonewlines()
的工作,然后,只要返回的区域小于文本,我们就将其向上推一位。因此,可以推断出此方法旨在返回找到的newline
字符的位置:
def rskip_nonewlines(text, pt):
len_text = len(text)
while True:
if pt <= 0 or pt >= len_text:
break
if text[pt] == "\n":
break
pt += 1
return pt
当您以pt
为0调用此函数时,首先要做的是确定0 <= 0
的值为True
,从而使它脱离while
循环,使它立即返回0
。然后,由于0小于文本,因此它添加了1
,最终结果为(0, 1)
。
如果您删除条件语句的pt <= 0 or
部分,它将正确地将换行符定位在位置15
处,向其添加1
,并在(0, 16)
结尾正如它应该。看来这可能是lskip_nonewlines()
的复制/粘贴错误。
Error at pos: 15, output (15, 16), expected output (0, 16)
这似乎表明,当代码从位置15向后扫描以确定该行的开始位置时,它将停止在位置15而不是位置0。
再次根据上述代码,lskip_nonewlines()
的工作是找到行的开始:
def lskip_nonewlines(text, pt):
len_text = len(text)
while True:
if pt <= 0 or pt >= len_text:
break
if text[pt - 1] == "\n" or text[pt] == "\n":
break
pt -= 1
return pt
在此循环中,我们首先要确保不会超出字符串的结尾,然后检查pt
之前的字符还是pt
处的字符换行符。
从前面的示例中我们已经知道位置15
是一个newline
字符,因此在循环的第一次迭代中,我们立即发现我们在换行符上并返回{{1 }}保持原样。最终结果为pt
。
在这种情况下,从条件中删除(15, 16)
可以阻止它立即确定行在其开始处结束,并允许它在决定停止之前向后扫描到位置0,这为您提供了所需的{{1 }}返回。
进行这两项更改后,您的测试程序不会产生任何错误。