Sublime Forum

[Bug] full_line() API returns another next line with it also if region given to it ends in a new newline also

#1

I think I have found a bug in SublimeText3 API full_line() function which is causing aberrant behavior in some of my plugins.

API full_line(region) returns the next line also if the line region given to it ends in a newline.
In this case - it should just return those lines - and not also add the next line also.

From API Documentation:
full_line(region) - As line(), but the region includes the trailing newline character, if any.

See attached screencast recording test case reproduce case.

0 Likes

#2

I think this is behaving correctly. If you put the cursor at the very beginning of the line, full_line will return the entire line including the new line character (112345\n). Therefore, it stands to reason that if your selection ends at the very beginning of a new line, that it’d include that second line as well.

0 Likes

#3

I havent put the cursor at the beginning of the next line - that is a selection awkwardy way of showing things - that it shows the cursor at the beginning of the next line instead of at the end of the \n newline char of the line its on (because the newline character is not a visible character showable).

Observe:

  1. I am selecting 12345\n
  2. So what should be the full_line of this selection be?
  3. The result of the full_line() of this should be 12345\n
0 Likes

#4

The most important part of your example is the text of the view:

12345\n6789

There is only one selection position between \n and 6. If you had a single empty selection at this position, it would look like this, where | denotes the cursor:

12345
|6789

What should be the result of view.substr(view.full_line(view.sel()[0]))? I claim that it should be 6789, not 12345. The behavior you observe follows from this.

0 Likes

#5

full_line(point) Region - As line(), but the region includes the trailing newline character, if any.
full_line(region) Region - As line(), but the region includes the trailing newline character, if any.

https://www.sublimetext.com/docs/3/api_reference.html

0 Likes

#6

line(Region) Returns a modified copy of region such that it starts at the beginning of a line, and ends at the end of a line. Note that it may span several lines.

so if the region passed in to full_line ends after a new line char - i.e. is at the beginning of a line, full_line will expand the region to include… the full line

or in other words, executing full_line with a region previously returned from full_line will not return the same region, but an expanded one. If you don’t like this behavior, there are plenty of other ways to achieve what you want :wink:

0 Likes

#7

I’ve wasted some time in the past trying to figure out how full_line works and here’s what I’ve come up with:

def lskip_nonewlines(text, pt):
    len_text = len(text)

    if pt < 0:
        return pt

    while True:
        if pt <= 0 or pt >= len_text:
            break
        if text[pt - 1] == "\n":
            break
        pt -= 1

    return pt


def rskip_nonewlines(text, pt):
    len_text = len(text)

    if pt < 0:
        pt = 0

    while True:
        if pt >= len_text:
            break
        if text[pt] == "\n":
            break
        pt += 1

    return pt
    
def full_line(self, x):
    region = Region(x)

    text = self.text()

    if region.a <= region.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
    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(region.begin(), region.end())

In any case, as @kingkeith said, if you don’t like this behaviour there are zillions ways to achieve what you want… in fact, you’re not even forced to use full_line at all… which btw, no, it’s not buggy :wink:

As an analogy… saying full_line is buggy it’s like suggesting that f(x) = x*x is buggy because f(2)!=5:smiley:

Ps. Speaking of which, gonna self-promote a SO thread I’d opened related to a similar subject https://stackoverflow.com/questions/56025185/how-does-full-line-works-in-sublimetext/56027488

0 Likes

#8

Thanks. Ive come up with your code - a solution for now as per your recommended to use my own implementation.of full_line as I think it should be. I still think this is a bug and the full_line implementation should be changed to that presented following in my plugin KeyboardNavigation.

KeyboardNavigation.py Line 583 in package KeyboardNavigation - (search for the line “def KnFullLine”)

0 Likes

#9

Today I’ve found this gem living in swap_line.py:

def expand_to_line(view, region):
    """
    As view.full_line, but doesn't expand to the next line if a full line is
    already selected
    """

    if not (region.a == region.b) and view.substr(region.end() - 1) == '\n':
        return sublime.Region(view.line(region).begin(), region.end())
    else:
        return view.full_line(region)

Hope that helps

0 Likes