コード例 #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.
    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=sublime.IGNORECASE)
        if prev_opening_bracket is None:
            # Unbalanced tags; nothing we can do.
            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=sublime.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
ファイル: text_objects.py プロジェクト: trkoch/Vintageous
def find_prev_lone_bracket(view, start, tags, unbalanced=0):
    # TODO: Extract common functionality from here and the % motion instead of duplicating code.
    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=sublime.IGNORECASE)
        if prev_opening_bracket is None:
            # Unbalanced tags; nothing we can do.
            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=sublime.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
コード例 #3
0
    def find_balanced_opening_bracket(self, start, brackets, unbalanced=0):
        new_start = start
        for i in range(unbalanced or 1):
            prev_opening_bracket = reverse_search_by_pt(
                self.view, brackets[0], start=0, end=new_start, flags=sublime.LITERAL
            )
            if prev_opening_bracket is None:
                # Unbalanced brackets; nothing we can do.
                return
            new_start = prev_opening_bracket.begin()

        nested = 0
        while True:
            next_closing_bracket = reverse_search_by_pt(
                self.view, brackets[1], start=prev_opening_bracket.a, end=start, flags=sublime.LITERAL
            )
            if not next_closing_bracket:
                break
            nested += 1
            start = next_closing_bracket.begin()

        if nested > 0:
            return self.find_balanced_opening_bracket(prev_opening_bracket.begin(), brackets, nested)
        else:
            return prev_opening_bracket.begin()
コード例 #4
0
ファイル: motion_cmds.py プロジェクト: typopl/Vintageous
    def find_balanced_opening_bracket(self, start, brackets, unbalanced=0):
        new_start = start
        for i in range(unbalanced or 1):
            prev_opening_bracket = reverse_search_by_pt(self.view,
                                                        brackets[0],
                                                        start=0,
                                                        end=new_start,
                                                        flags=sublime.LITERAL)
            if prev_opening_bracket is None:
                # Unbalanced brackets; nothing we can do.
                return
            new_start = prev_opening_bracket.begin()

        nested = 0
        while True:
            next_closing_bracket = reverse_search_by_pt(
                self.view,
                brackets[1],
                start=prev_opening_bracket.a,
                end=start,
                flags=sublime.LITERAL)
            if not next_closing_bracket:
                break
            nested += 1
            start = next_closing_bracket.begin()

        if nested > 0:
            return self.find_balanced_opening_bracket(
                prev_opening_bracket.begin(), brackets, nested)
        else:
            return prev_opening_bracket.begin()
コード例 #5
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 sublime.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=sublime.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 sublime.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=sublime.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=sublime.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
コード例 #6
0
def previous_begin_tag(view, pattern, start=0, end=0):
    assert pattern, 'bad call'
    region = reverse_search_by_pt(view, RX_ANY_TAG, start, end,
                                  sublime.IGNORECASE)
    if not region:
        return None, None, None
    match = re.search(RX_ANY_TAG, view.substr(region))
    return (region, match.group(1), match.group(0)[1] != '/')
コード例 #7
0
def previous_begin_tag(view, pattern, start=0, end=0):
    assert pattern, 'bad call'
    region = reverse_search_by_pt(view, RX_ANY_TAG, start, end,
                                  sublime.IGNORECASE)
    if not region:
        return None, None, None
    match = re.search(RX_ANY_TAG, view.substr(region))
    return (region, match.group(1), match.group(0)[1] != '/')
コード例 #8
0
ファイル: nodes.py プロジェクト: xjx79/Vintageous
    def resolve_notation(self, view, token, current):
        '''
        Returns 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:
                # TODO: Convert this to a VimError or something like that.
                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:
                # TODO: Convert this to a VimError or something like that.
                raise ValueError('pattern not found')
            return row_at(view, match.a)

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

        raise NotImplementedError()
コード例 #9
0
ファイル: nodes.py プロジェクト: xlinkerz/Vintageous
    def resolve_notation(self, view, token, current):
        """
        Returns 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:
                # TODO: Convert this to a VimError or something like that.
                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:
                # TODO: Convert this to a VimError or something like that.
                raise ValueError("pattern not found")
            return row_at(view, match.a)

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

        raise NotImplementedError()
コード例 #10
0
ファイル: text_objects.py プロジェクト: trkoch/Vintageous
def get_text_object_region(view, s, text_object, inclusive=False):
    try:
        delims, type_ = PAIRS[text_object]
    except KeyError:
        return s

    if type_ == TAG:
        return find_tag_text_object(view, s, inclusive)

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

        if not (opening and closing):
            return s

        if inclusive:
            return sublime.Region(opening.a, closing.b)
        return sublime.Region(opening.a + 1, closing.b - 1)

    if type_ == QUOTE:
        prev_quote = reverse_search_by_pt(view, delims[0],
                                                start=0,
                                                end=s.b,
                                                flags=sublime.IGNORECASE)

        next_quote = find_in_range(view, delims[0],
                                         start=s.b,
                                         end=view.size(),
                                         flags=sublime.IGNORECASE)

        if not (prev_quote and next_quote):
            return s

        if inclusive:
            return sublime.Region(prev_quote.a, next_quote.b)
        return sublime.Region(prev_quote.a + 1, next_quote.b - 1)

    if type_ == WORD:
        # TODO: Improve this -- specify word separators.
        word_start = view.find_by_class(s.b,
                                        forward=True,
                                        classes=sublime.CLASS_WORD_START)
        w = view.word(s.b)

        # XXX: I don't think this is necessary?
        if not w:
            return s

        if inclusive:
            return sublime.Region(w.a, word_start)
        else:
            return w

    if type_ == SENTENCE:
        # FIXME: This doesn't work well.
        # TODO: Improve this.
        sentence_start = view.find_by_class(s.b,
                                            forward=False,
                                            classes=sublime.CLASS_EMPTY_LINE)
        sentence_start_2 = reverse_search_by_pt(view, "[.?!:]\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, "[.?!:)](?=\s)|[.?!:)]$",
                                     start=s.b,
                                     end=view.size())

        if not (sentence_end):
            return s

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


    return s
コード例 #11
0
def get_text_object_region(view, s, text_object, inclusive=False, count=1):
    try:
        delims, type_ = PAIRS[text_object]
    except KeyError:
        return s

    if type_ == TAG:
        begin_tag, end_tag, _ = find_containing_tag(view, s.b)
        if inclusive:
            return sublime.Region(begin_tag.a, end_tag.b)
        else:
            return sublime.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, s.b, delims)
        closing = find_next_lone_bracket(view, s.b, delims)

        if not (opening and closing):
            return s

        if inclusive:
            return sublime.Region(opening.a, closing.b)
        return sublime.Region(opening.a + 1, 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 sublime.Region(prev_quote.a, next_quote.b)
        return sublime.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 sublime.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 sublime.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=sublime.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 sublime.Region(sentence_start, sentence_end.b)
        else:
            return sublime.Region(sentence_start, sentence_end.b)


    return s
コード例 #12
0
def get_text_object_region(view, s, text_object, inclusive=False, count=1):
    try:
        delims, type_ = PAIRS[text_object]
    except KeyError:
        return s

    if type_ == TAG:
        return find_tag_text_object(view, s, inclusive)

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

        if not (opening and closing):
            return s

        if inclusive:
            return sublime.Region(opening.a, closing.b)
        return sublime.Region(opening.a + 1, 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, '(?<!\\\\)' + delims[0],
                                          start=line.a, end=s.b)

        next_quote = find_in_range(view, '(?<!\\\\)' + 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, '(?<!\\\\)' + delims[0],
                                       start=prev_quote.b, end=line.b)

        if not (prev_quote and next_quote):
            return s

        if inclusive:
            return sublime.Region(prev_quote.a, next_quote.b)
        return sublime.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
        return w

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

    if type_ == SENTENCE:
        # FIXME: This doesn't work well.
        # TODO: Improve this.
        sentence_start = view.find_by_class(s.b,
                                            forward=False,
                                            classes=sublime.CLASS_EMPTY_LINE)
        sentence_start_2 = reverse_search_by_pt(view, "[.?!:]\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, "[.?!:)](?=\s)|[.?!:)]$",
                                     start=s.b,
                                     end=view.size())

        if not (sentence_end):
            return s

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


    return s
コード例 #13
0
ファイル: text_objects.py プロジェクト: amclain/Vintageous
def find_tag_text_object(view, s, inclusive=False):

    if (view.score_selector(s.b, 'text.html') == 0 and
        view.score_selector(s.b, 'text.xml') == 0):
            # TODO: What happens with other xml formats?
            return s

    # TODO: Receive the actual mode in the parameter list?
    current_pt = (s.b - 1) if view.has_non_empty_selection_region() else s.b
    start_pt = utils.previous_white_space_char(view, current_pt,
                                               white_space=' \t\n') + 1

    if view.substr(sublime.Region(start_pt, start_pt + 2)) == '</':
        closing_tag = view.find(RX_ANY_END_TAG, start_pt, sublime.IGNORECASE)
        name = get_tag_name(view.substr(closing_tag))
        start_tag_pattern = r'<({0}).*?>'.format(name)
        start_tag = search.reverse_search_by_pt(view, start_tag_pattern, 0,
                                                start_pt)
    elif view.substr(start_pt) == '<':
        start_tag = view.find(RX_ANY_START_TAG, start_pt, sublime.IGNORECASE)
        if start_tag.a != start_pt:
            return s
    else:
        start_tag = search.reverse_search_by_pt(view, RX_ANY_START_TAG, 0,
                                                start_pt)

    if not start_tag:
        return s

    tag_name = get_tag_name(view.substr(start_tag))

    literal_end_tag = r'</{0}>'.format(tag_name)
    end_tag = None
    current_pt = start_tag.b
    while True:
        temp_end_tag = view.find(literal_end_tag, current_pt,
                                 sublime.IGNORECASE)
        if not end_tag and not temp_end_tag:
            return s
        elif not temp_end_tag:
            break

        end_tag = temp_end_tag
        current_pt = end_tag.b

        where = view.substr(sublime.Region(start_pt, end_tag.end()))
        opening_tags = re.findall(r'<{0}.*?>'.format(tag_name), where,
                                  re.IGNORECASE)
        closing_tags = re.findall(literal_end_tag, where, sublime.IGNORECASE)

        if len(opening_tags) == len(closing_tags):
            break

    if not end_tag:
        return s

    # Perhaps this should be handled further up by the command itself?
    was_visual = view.has_non_empty_selection_region()
    if not inclusive:
        if not was_visual:
            return sublime.Region(start_tag.b, end_tag.a)
        else:
            if start_tag.b == end_tag.a:
                return sublime.Region(start_tag.b, start_tag.b + 1)
            else:
                return sublime.Region(start_tag.b, end_tag.a)

    if not was_visual:
        return sublime.Region(start_tag.a, end_tag.b)
    else:
        if start_tag.a == end_tag.b:
            return sublime.Region(start_tag.a, start_tag.a + 1)
        else:
            return sublime.Region(start_tag.a, end_tag.b)
コード例 #14
0
ファイル: text_objects.py プロジェクト: jianzw/Vintageous
def get_text_object_region(view, s, text_object, inclusive=False, count=1):
    try:
        delims, type_ = PAIRS[text_object]
    except KeyError:
        return s

    if type_ == TAG:
        return find_tag_text_object(view, s, inclusive)

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

        if not (opening and closing):
            return s

        if inclusive:
            return sublime.Region(opening.a, closing.b)
        return sublime.Region(opening.a + 1, closing.b - 1)

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

        next_quote = find_in_range(view, '(?<!\\\\)' + delims[0],
                                   start=s.b, end=view.size())

        if not (prev_quote and next_quote):
            return s

        if inclusive:
            return sublime.Region(prev_quote.a, next_quote.b)
        return sublime.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
        return w

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

    if type_ == SENTENCE:
        # FIXME: This doesn't work well.
        # TODO: Improve this.
        sentence_start = view.find_by_class(s.b,
                                            forward=False,
                                            classes=sublime.CLASS_EMPTY_LINE)
        sentence_start_2 = reverse_search_by_pt(view, "[.?!:]\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, "[.?!:)](?=\s)|[.?!:)]$",
                                     start=s.b,
                                     end=view.size())

        if not (sentence_end):
            return s

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


    return s
コード例 #15
0
def get_text_object_region(view, s, text_object, inclusive=False):
    try:
        delims, type_ = PAIRS[text_object]
    except KeyError:
        return s

    if type_ == TAG:
        return find_tag_text_object(view, s, inclusive)

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

        if not (opening and closing):
            return s

        if inclusive:
            return sublime.Region(opening.a, closing.b)
        return sublime.Region(opening.a + 1, closing.b - 1)

    if type_ == QUOTE:
        prev_quote = reverse_search_by_pt(view,
                                          delims[0],
                                          start=0,
                                          end=s.b,
                                          flags=sublime.IGNORECASE)

        next_quote = find_in_range(view,
                                   delims[0],
                                   start=s.b,
                                   end=view.size(),
                                   flags=sublime.IGNORECASE)

        if not (prev_quote and next_quote):
            return s

        if inclusive:
            return sublime.Region(prev_quote.a, next_quote.b)
        return sublime.Region(prev_quote.a + 1, next_quote.b - 1)

    if type_ == WORD:
        # TODO: Improve this -- specify word separators.
        word_start = view.find_by_class(s.b,
                                        forward=True,
                                        classes=sublime.CLASS_WORD_START)
        w = view.word(s.b)

        # XXX: I don't think this is necessary?
        if not w:
            return s

        if inclusive:
            return sublime.Region(w.a, word_start)
        else:
            return w

    if type_ == SENTENCE:
        # FIXME: This doesn't work well.
        # TODO: Improve this.
        sentence_start = view.find_by_class(s.b,
                                            forward=False,
                                            classes=sublime.CLASS_EMPTY_LINE)
        sentence_start_2 = reverse_search_by_pt(view,
                                                "[.?!:]\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,
                                     "[.?!:)](?=\s)|[.?!:)]$",
                                     start=s.b,
                                     end=view.size())

        if not (sentence_end):
            return s

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

    return s
コード例 #16
0
def find_tag_text_object(view, s, inclusive=False):

    if (view.score_selector(s.b, 'text.html') == 0
            and view.score_selector(s.b, 'text.xml') == 0):
        # TODO: What happens with other xml formats?
        return s

    # TODO: Receive the actual mode in the parameter list?
    current_pt = (s.b - 1) if view.has_non_empty_selection_region() else s.b
    start_pt = utils.previous_white_space_char(
        view, current_pt, white_space=' \t\n') + 1

    if view.substr(sublime.Region(start_pt, start_pt + 2)) == '</':
        closing_tag = view.find(RX_ANY_END_TAG, start_pt, sublime.IGNORECASE)
        name = get_tag_name(view.substr(closing_tag))
        start_tag_pattern = r'<({0}).*?>'.format(name)
        start_tag = search.reverse_search_by_pt(view, start_tag_pattern, 0,
                                                start_pt)
    elif view.substr(start_pt) == '<':
        start_tag = view.find(RX_ANY_START_TAG, start_pt, sublime.IGNORECASE)
        if start_tag.a != start_pt:
            return s
    else:
        start_tag = search.reverse_search_by_pt(view, RX_ANY_START_TAG, 0,
                                                start_pt)

    if not start_tag:
        return s

    tag_name = get_tag_name(view.substr(start_tag))

    literal_end_tag = r'</{0}>'.format(tag_name)
    end_tag = None
    current_pt = start_tag.b
    while True:
        temp_end_tag = view.find(literal_end_tag, current_pt,
                                 sublime.IGNORECASE)
        if not end_tag and not temp_end_tag:
            return s
        elif not temp_end_tag:
            break

        end_tag = temp_end_tag
        current_pt = end_tag.b

        where = view.substr(sublime.Region(start_pt, end_tag.end()))
        opening_tags = re.findall(r'<{0}.*?>'.format(tag_name), where,
                                  re.IGNORECASE)
        closing_tags = re.findall(literal_end_tag, where, sublime.IGNORECASE)

        if len(opening_tags) == len(closing_tags):
            break

    if not end_tag:
        return s

    # Perhaps this should be handled further up by the command itself?
    was_visual = view.has_non_empty_selection_region()
    if not inclusive:
        if not was_visual:
            return sublime.Region(start_tag.b, end_tag.a)
        else:
            if start_tag.b == end_tag.a:
                return sublime.Region(start_tag.b, start_tag.b + 1)
            else:
                return sublime.Region(start_tag.b, end_tag.a)

    if not was_visual:
        return sublime.Region(start_tag.a, end_tag.b)
    else:
        if start_tag.a == end_tag.b:
            return sublime.Region(start_tag.a, start_tag.a + 1)
        else:
            return sublime.Region(start_tag.a, end_tag.b)