예제 #1
0
def find_prev_lone_bracket(view, start, tags, unbalanced=0):
    # TODO: Extract common functionality from here and the % motion instead of
    # duplicating code.

    # XXX: refactor this
    if view.substr(start) == (tags[0][1] if len(tags[0]) > 1 else tags[0]):
        if not unbalanced and view.substr(start - 1) != '\\':
            return Region(start, start + 1)

    new_start = start
    for i in range(unbalanced or 1):
        prev_opening_bracket = reverse_search_by_pt(view, tags[0],
                                                    start=0,
                                                    end=new_start,
                                                    flags=IGNORECASE)

        if prev_opening_bracket is None:
            # Check whether the caret is exactly at a bracket.
            # Tag names may be escaped, so slice them.
            if (i == 0 and view.substr(start) == tags[0][-1] and
               view.substr(start - 1) != '\\'):
                    return Region(start, start + 1)
            # Unbalanced tags; nothing we can do.
            return

        while view.substr(prev_opening_bracket.begin() - 1) == '\\':
            prev_opening_bracket = reverse_search_by_pt(
                view,
                tags[0],
                start=0,
                end=prev_opening_bracket.begin(),
                flags=IGNORECASE
            )

            if prev_opening_bracket is None:
                return

        new_start = prev_opening_bracket.begin()

    nested = 0
    while True:
        next_closing_bracket = reverse_search_by_pt(view,
                                                    tags[1],
                                                    start=prev_opening_bracket.a,
                                                    end=start,
                                                    flags=IGNORECASE)
        if not next_closing_bracket:
            break

        nested += 1
        start = next_closing_bracket.begin()

    if nested > 0:
        return find_prev_lone_bracket(view,
                                      prev_opening_bracket.begin(),
                                      tags,
                                      nested)
    else:
        return prev_opening_bracket
예제 #2
0
def _get_text_object_sentence(view, s: Region, inclusive: bool,
                              count: int) -> Region:
    sentence_start = view.find_by_class(s.b,
                                        forward=False,
                                        classes=CLASS_EMPTY_LINE)
    sentence_start_2 = reverse_search_by_pt(view,
                                            r'[.?!:]\s+|[.?!:]$',
                                            start=0,
                                            end=s.b)
    if sentence_start_2:
        sentence_start = (sentence_start + 1 if
                          (sentence_start > sentence_start_2.b) else
                          sentence_start_2.b)
    else:
        sentence_start = sentence_start + 1

    sentence_end = find_in_range(view,
                                 r'([.?!:)](?=\s))|([.?!:)]$)',
                                 start=s.b,
                                 end=view.size())
    if not sentence_end:
        return s

    if inclusive:
        return Region(sentence_start, sentence_end.b)
    else:
        return Region(sentence_start, sentence_end.b)
예제 #3
0
def _get_text_object_quote(view, s: Region, inclusive: bool, count: int,
                           delims) -> Region:
    line = view.line(s)

    delim_open = delims[0]

    # FIXME: Escape sequences like \" are probably syntax-dependant.
    prev_quote = reverse_search_by_pt(view,
                                      r'(?<!\\\\)' + delim_open,
                                      start=line.a,
                                      end=s.b)
    next_quote = find_in_range(view,
                               r'(?<!\\\\)' + delim_open,
                               start=s.b,
                               end=line.b)

    if next_quote and not prev_quote:
        prev_quote = next_quote
        next_quote = find_in_range(view,
                                   r'(?<!\\\\)' + delim_open,
                                   start=prev_quote.b,
                                   end=line.b)

    if not (prev_quote and next_quote):
        return s

    if inclusive:
        return Region(prev_quote.a, next_quote.b)

    return Region(prev_quote.a + 1, next_quote.b - 1)
예제 #4
0
def _resolve_line_number(view, token, current: int) -> int:
    # Args:
    #   view (View): The view where the calculation is made.
    #   token (Token):
    #   current (int): Line number where we are now.
    if isinstance(token, TokenDot):
        return row_at(view, view.text_point(current, 0))

    if isinstance(token, TokenDigits):
        return max(int(token.content) - 1, -1)

    if isinstance(token, TokenPercent):
        return row_at(view, view.size())

    if isinstance(token, TokenDollar):
        return row_at(view, view.size())

    if isinstance(token, TokenOffset):
        return current + sum(token.content)

    if isinstance(token, TokenSearchForward):
        match = view.find(token.content, view.text_point(current, 0))
        if not match:
            raise ValueError('E385: Search hit BOTTOM without match for: ' + token.content)

        return row_at(view, match.a)

    if isinstance(token, TokenSearchBackward):
        match = reverse_search_by_pt(view, token.content, 0, view.text_point(current, 0))
        if not match:
            raise ValueError('E384: Search hit TOP without match for: ' + token.content)

        return row_at(view, match.a)

    if isinstance(token, TokenMark):
        if token.content == '<':
            sel = list(view.sel())[0]
            view.sel().clear()
            view.sel().add(sel)
            if sel.a < sel.b:
                return row_at(view, sel.a)
            else:
                return row_at(view, sel.a - 1)
        elif token.content == '>':
            sel = list(view.sel())[0]
            view.sel().clear()
            view.sel().add(sel)
            if sel.a < sel.b:
                return row_at(view, sel.b - 1)
            else:
                return row_at(view, sel.b)
        elif token.content in tuple('abcdefghijklmnopqrstuvwxyz'):
            mark = get_mark(view, token.content)
            if not isinstance(mark, Region):
                raise ValueError('E20: mark not set')

            return view.rowcol(mark.b)[0]

    raise NotImplementedError()
예제 #5
0
def _resolve_line_number(view, token, current):
    # type: (...) -> int
    # Args:
    #   view (View): The view where the calculation is made.
    #   token (Token):
    #   current (int): Line number where we are now.
    if isinstance(token, TokenDot):
        return row_at(view, view.text_point(current, 0))

    if isinstance(token, TokenDigits):
        return max(int(token.content) - 1, -1)

    if isinstance(token, TokenPercent):
        return row_at(view, view.size())

    if isinstance(token, TokenDollar):
        return row_at(view, view.size())

    if isinstance(token, TokenOffset):
        return current + sum(token.content)

    if isinstance(token, TokenSearchForward):
        match = view.find(token.content, view.text_point(current, 0))
        if not match:
            raise ValueError('pattern not found')

        return row_at(view, match.a)

    if isinstance(token, TokenSearchBackward):
        match = reverse_search_by_pt(view, token.content, 0,
                                     view.text_point(current, 0))
        if not match:
            raise ValueError('pattern not found')

        return row_at(view, match.a)

    if isinstance(token, TokenMark):
        if token.content == '<':
            sel = list(view.sel())[0]
            view.sel().clear()
            view.sel().add(sel)
            if sel.a < sel.b:
                return row_at(view, sel.a)
            else:
                return row_at(view, sel.a - 1)
        elif token.content == '>':
            sel = list(view.sel())[0]
            view.sel().clear()
            view.sel().add(sel)
            if sel.a < sel.b:
                return row_at(view, sel.b - 1)
            else:
                return row_at(view, sel.b)
        elif token.content in tuple('abcdefghijklmnopqrstuvwxyz'):
            return view.rowcol(
                get_mark_as_encoded_address(view, token.content).b)[0]

    raise NotImplementedError()
예제 #6
0
def previous_begin_tag(view, pattern, start=0, end=0):
    assert pattern, 'bad call'
    region = reverse_search_by_pt(view, pattern, start, end, IGNORECASE)
    if not region:
        return None, None, None

    match = re.search(pattern, view.substr(region))

    return (region, match.group(1), match.group(0)[1] != '/')
예제 #7
0
    def resolve_notation(self, view, token, current):
        """Return a line number."""
        if isinstance(token, TokenDot):
            pt = view.text_point(current, 0)
            return row_at(view, pt)

        if isinstance(token, TokenDigits):
            return max(int(str(token)) - 1, -1)

        if isinstance(token, TokenPercent):
            return row_at(view, view.size())

        if isinstance(token, TokenDollar):
            return row_at(view, view.size())

        if isinstance(token, TokenOffset):
            return current + sum(token.content)

        if isinstance(token, TokenSearchForward):
            start_pt = view.text_point(current, 0)
            match = view.find(str(token)[1:-1], start_pt)
            if not match:
                raise ValueError('pattern not found')

            return row_at(view, match.a)

        if isinstance(token, TokenSearchBackward):
            start_pt = view.text_point(current, 0)
            match = reverse_search_by_pt(view, str(token)[1:-1], 0, start_pt)
            if not match:
                raise ValueError('pattern not found')

            return row_at(view, match.a)

        if isinstance(token, TokenMark):
            return self.resolve_mark(view, token)

        raise NotImplementedError()
예제 #8
0
def get_text_object_region(view, s, text_object, inclusive=False, count=1):
    # type: (...) -> Region
    try:
        delims, type_ = PAIRS[text_object]
    except KeyError:
        return s

    if type_ == TAG:
        begin_tag, end_tag, _ = find_containing_tag(view, s.b)

        if not (begin_tag and end_tag):
            return s

        if inclusive:
            return Region(begin_tag.a, end_tag.b)
        else:
            return Region(begin_tag.b, end_tag.a)

    if type_ == PARAGRAPH:
        return find_paragraph_text_object(view, s, inclusive=inclusive, count=count)

    if type_ == BRACKET:
        b = resolve_insertion_point_at_b(s)
        opening = find_prev_lone_bracket(view, b, delims)
        closing = find_next_lone_bracket(view, b, delims)

        if not (opening and closing):
            return s

        if inclusive:
            return Region(opening.a, closing.b)

        a = opening.a + 1
        if view.substr(a) == '\n':
            a += 1

        return Region(a, closing.b - 1)

    if type_ == QUOTE:
        # Vim only operates on the current line.
        line = view.line(s)

        # FIXME: Escape sequences like \" are probably syntax-dependant.
        prev_quote = reverse_search_by_pt(view, r'(?<!\\\\)' + delims[0], start=line.a, end=s.b)
        next_quote = find_in_range(view, r'(?<!\\\\)' + delims[0], start=s.b, end=line.b)

        if next_quote and not prev_quote:
            prev_quote = next_quote
            next_quote = find_in_range(view, r'(?<!\\\\)' + delims[0], start=prev_quote.b, end=line.b)

        if not (prev_quote and next_quote):
            return s

        if inclusive:
            return Region(prev_quote.a, next_quote.b)

        return Region(prev_quote.a + 1, next_quote.b - 1)

    if type_ == WORD:
        w = a_word(view, s.b, inclusive=inclusive, count=count)
        if not w:
            return s

        if s.size() <= 1:
            return w

        return Region(s.a, w.b)

    if type_ == BIG_WORD:
        w = a_big_word(view, s.b, inclusive=inclusive, count=count)
        if not w:
            return s

        if s.size() <= 1:
            return w

        return Region(s.a, w.b)

    if type_ == SENTENCE:
        # FIXME: This doesn't work well.
        # TODO: Improve this.
        sentence_start = view.find_by_class(s.b, forward=False, classes=CLASS_EMPTY_LINE)
        sentence_start_2 = reverse_search_by_pt(view, r'[.?!:]\s+|[.?!:]$', start=0, end=s.b)
        if sentence_start_2:
            sentence_start = (sentence_start + 1 if (sentence_start > sentence_start_2.b) else sentence_start_2.b)
        else:
            sentence_start = sentence_start + 1

        sentence_end = find_in_range(view, r'([.?!:)](?=\s))|([.?!:)]$)', start=s.b, end=view.size())

        if not (sentence_end):
            return s

        if inclusive:
            return Region(sentence_start, sentence_end.b)
        else:
            return Region(sentence_start, sentence_end.b)

    if type_ == INDENT:
        start, end = find_indent_text_object(view, s, inclusive)

        return Region(start, end)

    if type_ == LINE:
        start, end = find_line_text_object(view, s)

        return Region(start, end)

    return s
예제 #9
0
def _resolve_line_number(view, token, current):
    # type: (...) -> int
    # Args:
    #   view (View): The view where the calculation is made.
    #   token (Token):
    #   current (int): Line number where we are now.
    if isinstance(token, TokenDot):
        return row_at(view, view.text_point(current, 0))

    if isinstance(token, TokenDigits):
        return max(int(token.content) - 1, -1)

    if isinstance(token, TokenPercent):
        return row_at(view, view.size())

    if isinstance(token, TokenDollar):
        return row_at(view, view.size())

    if isinstance(token, TokenOffset):
        return current + sum(token.content)

    if isinstance(token, TokenSearchForward):
        match = view.find(token.content, view.text_point(current, 0))
        if not match:
            raise ValueError('pattern not found')

        return row_at(view, match.a)

    if isinstance(token, TokenSearchBackward):
        match = reverse_search_by_pt(view, token.content, 0,
                                     view.text_point(current, 0))
        if not match:
            raise ValueError('pattern not found')

        return row_at(view, match.a)

    if isinstance(token, TokenMark):
        if token.content == '<':
            sel = list(view.sel())[0]
            view.sel().clear()
            view.sel().add(sel)
            if sel.a < sel.b:
                return row_at(view, sel.a)
            else:
                return row_at(view, sel.a - 1)
        elif token.content == '>':
            sel = list(view.sel())[0]
            view.sel().clear()
            view.sel().add(sel)
            if sel.a < sel.b:
                return row_at(view, sel.b - 1)
            else:
                return row_at(view, sel.b)
        elif token.content in tuple('abcdefghijklmnopqrstuvwxyz'):
            # The state class is intentionally imported here instead of at the
            # begining of the file to avoid circular imports errors. The State
            # needs to refactored and replaced with some less god-like
            from NeoVintageous.nv.state import State
            address = State(view).marks.get_as_encoded_address(token.content)

            return view.rowcol(address.b)[0]

    raise NotImplementedError()
예제 #10
0
def get_text_object_region(view, s, text_object, inclusive=False, count=1):
    # type: (...) -> Region
    try:
        delims, type_ = PAIRS[text_object]
    except KeyError:
        return s

    if type_ == TAG:
        begin_tag, end_tag, _ = find_containing_tag(view, s.begin())

        if not (begin_tag and end_tag):
            return s

        if inclusive:
            return Region(begin_tag.a, end_tag.b)
        else:
            return Region(begin_tag.b, end_tag.a)

    if type_ == PARAGRAPH:
        return find_paragraph_text_object(view, s, inclusive=inclusive, count=count)

    if type_ == BRACKET:
        opening = find_prev_lone_bracket(view, max(0, s.begin() - 1), delims)
        closing = find_next_lone_bracket(view, s.end(), delims)

        if not (opening and closing):
            return s

        if inclusive:
            return Region(opening.a, closing.b)

        a = opening.a + 1
        if view.substr(a) == '\n':
            a += 1

        b = closing.b - 1

        if b > a:
            line = view.line(b)
            if next_non_blank(view, line.a) + 1 == line.b:
                row_a, col_a = view.rowcol(a)
                row_b, col_b = view.rowcol(b)
                if (row_b - 1) > row_a:
                    line = view.full_line(view.text_point((row_b - 1), 0))

                    return Region(a, line.b)

        return Region(a, b)

    if type_ == QUOTE:
        # Vim only operates on the current line.
        line = view.line(s)

        # FIXME: Escape sequences like \" are probably syntax-dependant.
        prev_quote = reverse_search_by_pt(view, r'(?<!\\\\)' + delims[0], start=line.a, end=s.b)
        next_quote = find_in_range(view, r'(?<!\\\\)' + delims[0], start=s.b, end=line.b)

        if next_quote and not prev_quote:
            prev_quote = next_quote
            next_quote = find_in_range(view, r'(?<!\\\\)' + delims[0], start=prev_quote.b, end=line.b)

        if not (prev_quote and next_quote):
            return s

        if inclusive:
            return Region(prev_quote.a, next_quote.b)

        return Region(prev_quote.a + 1, next_quote.b - 1)

    if type_ == WORD:
        w = a_word(view, s.b, inclusive=inclusive, count=count)
        if s.size() <= 1:
            return w

        return Region(s.a, w.b)

    if type_ == BIG_WORD:
        w = a_big_word(view, s.b, inclusive=inclusive, count=count)
        if s.size() <= 1:
            return w

        return Region(s.a, w.b)

    if type_ == SENTENCE:
        # FIXME: This doesn't work well.
        # TODO: Improve this.
        sentence_start = view.find_by_class(s.b, forward=False, classes=CLASS_EMPTY_LINE)
        sentence_start_2 = reverse_search_by_pt(view, r'[.?!:]\s+|[.?!:]$', start=0, end=s.b)
        if sentence_start_2:
            sentence_start = (sentence_start + 1 if (sentence_start > sentence_start_2.b) else sentence_start_2.b)
        else:
            sentence_start = sentence_start + 1

        sentence_end = find_in_range(view, r'([.?!:)](?=\s))|([.?!:)]$)', start=s.b, end=view.size())
        if not sentence_end:
            return s

        if inclusive:
            return Region(sentence_start, sentence_end.b)
        else:
            return Region(sentence_start, sentence_end.b)

    # Support for a port of the Indent Object plugin:
    # https://github.com/michaeljsmith/vim-indent-object
    if type_ == INDENT:
        start, end = find_indent_text_object(view, s, inclusive)

        return Region(start, end)

    if type_ == LINE:
        start, end = find_line_text_object(view, s)

        return Region(start, end)

    return s