Как работает full_line в SublimeText? - PullRequest
0 голосов
/ 07 мая 2019

Я пытаюсь имитировать view.full_line функцию SublimeText, используя python, если мы прочитаем документы , мы увидим:

line (point): возвращает строкусодержит точку.

строка (регион): возвращает измененную копию области, которая начинается в начале строки и заканчивается в конце строки.Обратите внимание, что он может занимать несколько строк.

full_line (точка): как строка (), но область включает в себя завершающий символ новой строки, если таковой имеется.

full_line (region): как строка (), но регион включает в себя завершающий символ новой строки, если таковой имеется.

Я пытался следовать объяснениям из этих документов, и вот что я получил:

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)

Итак, как я могу исправить подпрограмму, чтобы она вела себя 1: 1, как SublimeText?

1 Ответ

1 голос
/ 07 мая 2019

У вас есть несколько довольно очевидных ошибок в коде, которые мешают ему делать то, что вы хотите.

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, поэтому здесь, на первой итерации цикла, мы сразу обнаруживаем, что мы находимся на новой строке и возвращаем pt без изменений.выдача конечного результата (15, 16).

В этом случае удаление or text[pt] == "\n" из условного предложения не позволяет ему сразу определить, что линия заканчивается там, где она началась, и позволяет сканировать назад в положение 0, прежде чем принять решение оостановка, которая дает вам желаемый (0, 16) возврат.

С этими двумя изменениями ваша тестовая программа не выдает никаких ошибок.

...