Example #1
0
def _get_text_object_tag(view, s: Region, inclusive: bool, count: int) -> Region:
    # When the active cursor position is on leading whitespace before a tag on
    # the same line then the start point of the text object is the tag.
    line = view.line(get_insertion_point_at_b(s))
    tag_in_line = view_find_in_range(view, '^\\s*<[^>]+>', line.begin(), line.end())
    if tag_in_line:
        if s.b >= s.a and s.b < tag_in_line.end():
            if s.empty():
                s.a = s.b = tag_in_line.end()
            else:
                s.a = tag_in_line.end()
                s.b = tag_in_line.end() + 1

    begin_tag, end_tag, _ = find_containing_tag(view, s.begin())
    if not (begin_tag and end_tag):
        return s

    # The normal method is to select a <tag> until the matching </tag>. For "at"
    # the tags are included, for "it" they are excluded. But when "it" is
    # repeated the tags will be included (otherwise nothing would change).
    if not inclusive:
        if s == Region(begin_tag.end(), end_tag.begin()):
            inclusive = True

    if inclusive:
        return Region(begin_tag.a, end_tag.b)
    else:
        return Region(begin_tag.b, end_tag.a)
Example #2
0
    def run(self, edit, mode, count, search=None, forward=True, save=True):
        if len(self.view.sel()) != 1:
            ui_bell('sneak does not support multiple cursors')
            return

        if not search:
            search = _get_last_sneak_search(self.view)
            if not search:
                ui_bell('no previous sneak search')
                return

        clear_search_highlighting(self.view)

        flags = _get_search_flags(self.view, search)
        s = self.view.sel()[0]
        start_pt = get_insertion_point_at_b(s)

        if forward:
            occurrences = view_find_all_in_range(self.view,
                                                 search, start_pt + 1,
                                                 self.view.size(), flags)
        else:
            occurrences = list(
                view_rfind_all(self.view, search, start_pt, flags))

        occurrences = occurrences[count - 1:]

        if not occurrences:
            ui_bell('not found: %s' % search)
            return

        target = occurrences[0].a

        if mode == NORMAL:
            s.a = s.b = target
        elif mode == VISUAL:
            resolve_visual_target(s, target)
        elif mode == VISUAL_LINE:
            resolve_visual_line_target(self.view, s, target)
        elif mode == INTERNAL_NORMAL:
            s.b = target
        else:
            return

        set_selection(self.view, s)
        add_search_highlighting(self.view, occurrences)

        if save:
            set_last_char_search_command(
                self.view, 'sneak_s' if forward else 'sneak_big_s')
            _set_last_sneak_search(self.view, search)
Example #3
0
def _get_text_object_word(view, s: Region, inclusive: bool,
                          count: int) -> Region:
    if s.size() == 1:
        pt = get_insertion_point_at_b(s)
    else:
        if s.b < s.a:
            pt = max(0, s.b - 1)
        else:
            pt = s.b

    w = _a_word(view, pt, inclusive=inclusive, count=count)
    if s.size() <= 1:
        return w

    if s.b >= s.a:
        return Region(s.a, w.b)
    else:
        return Region(s.a, w.a)

    return s
Example #4
0
def set_mark(view, name: str) -> None:
    pt = get_insertion_point_at_b(view.sel()[0])
    view.add_regions(_get_key(name), [Region(pt)])
Example #5
0
def find_next_item_match_pt(view, s: Region):
    pt = get_insertion_point_at_b(s)

    # Note that some item targets are added later in relevant contexts, for
    # example the item targets <> are added only when in a HTML/XML context.
    # TODO Default targets should be configurable, see :h 'matchpairs'.
    targets = '(){}[]'

    # If in a HTML/XML context, check if the cursor position is within a valid
    # tag e.g. <name>, and find the matching tag e.g. if inside an opening tag
    # then find the closing tag, if in a closing tag find the opening one.
    if view.match_selector(0, 'text.(html|xml)'):
        # Add valid HTML/XML item targets.
        targets += '<>'

        # Find matching HTML/XML items, but only if cursor is within a tag.
        if view.substr(pt) not in targets:
            closest_tag = get_closest_tag(view, pt)
            if closest_tag:
                if closest_tag.contains(pt):
                    begin_tag, end_tag, _ = find_containing_tag(view, pt)
                    if begin_tag:
                        return begin_tag.a if end_tag.contains(pt) else end_tag.a

    # Find the next item after or under the cursor.
    bracket = view_find(view, '|'.join(map(re_escape, targets)), pt)
    if not bracket:
        return

    # Only accept items found within the current cursor line.
    if bracket.b > view.line(pt).b:
        return

    target = view.substr(bracket)
    target_index = targets.index(target)
    targets_open = targets[::2]
    forward = True if target in targets_open else False

    if target in targets_open:
        target_pair = (targets[target_index], targets[target_index + 1])
    else:
        target_pair = (targets[target_index - 1], targets[target_index])

    accepted_selector = 'punctuation|text.plain'

    if forward:
        counter = 0
        start = bracket.b
        while True:
            bracket = view_find(view, '|'.join(map(re_escape, target_pair)), start)
            if not bracket:
                return

            if view.match_selector(bracket.a, accepted_selector):
                if view.substr(bracket) == target:
                    counter += 1
                else:
                    if counter == 0:
                        return bracket.a

                    counter -= 1

            start = bracket.b
    else:
        # Note the use of view_rfind_all(), because Sublime doesn't have any
        # APIs to do reverse searches and finding *all* matches before a point
        # is easier and more effiecient, at least in a userland implemention.
        counter = 0
        start = bracket.a
        for bracket in view_rfind_all(view, '|'.join(map(re_escape, target_pair)), start):
            if view.match_selector(bracket.a, accepted_selector):
                if view.substr(bracket) == target:
                    counter += 1
                else:
                    if counter == 0:
                        return bracket.a

                    counter -= 1

            start = bracket.a
Example #6
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 = get_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

        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