def find_sentences_backward(view, start_pt: int, count: int = 1) -> Region: if isinstance(start_pt, Region): start_pt = start_pt.a pt = prev_non_ws(view, start_pt) sen = Region(pt) prev = sen while True: sen = view.expand_by_class(sen, CLASS_LINE_END | CLASS_PUNCTUATION_END) if sen.a <= 0 or view.substr(sen.begin() - 1) in ('.', '\n', '?', '!'): if view.substr(sen.begin() - 1) == '.' and not view.substr(sen.begin()) == ' ': continue if prev == sen: break prev = sen if sen: pt = next_non_blank(view, sen.a) if pt < sen.b and pt != start_pt: if view.substr(pt) == '\n': if pt + 1 != start_pt: pt += 1 return Region(pt) if pt > 0: continue return sen return sen
def _get_text_object_bracket(view, s: Region, inclusive: bool, count: int, delims: tuple) -> Region: 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 - 1) row_b, col_b = view.rowcol(b + 1) 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)
def _find_sentence_forward(view, start: int): char = view.substr(start) if char == '\n': next_sentence = view.find('\\s+', start) else: next_sentence = view.find('[\\.\\?\\!][\\)\\]"\']*\\s', start) if next_sentence: return next_non_blank(view, next_sentence.b)
def word_starts(view, start, count=1, internal=False): assert start >= 0 assert count > 0 pt = start for i in range(count): # On the last motion iteration, we must do some special stuff if we are still on the # starting line of the motion. if (internal and (i == count - 1) and (view.line(start) == view.line(pt))): if view.substr(pt) == '\n': return pt + 1 return next_word_start(view, pt, internal=True) pt = next_word_start(view, pt) if not internal or (i != count - 1): pt = next_non_blank(view, pt) while not (view.size() == pt or view.line(pt).empty() or view.substr(view.line(pt)).strip()): pt = next_word_start(view, pt) pt = next_non_blank(view, pt) if (internal and (view.line(start) != view.line(pt)) and (start != view.line(start).a and not view.substr(view.line(pt - 1)).isspace()) and at_eol(view, pt - 1)): # FIXME # noqa: E501 pt -= 1 return pt
def f(view, s): if mode == NORMAL: pt = next_non_blank(view, dest) if view.substr(pt) == EOF: pt = max(pt - 1, 0) return Region(pt) elif mode == INTERNAL_NORMAL: start_line = view.full_line(s.a) dest_line = view.full_line(dest) if start_line.a == dest_line.a: return dest_line elif start_line.a < dest_line.a: return Region(start_line.a, dest_line.b) else: return Region(start_line.b, dest_line.a) elif mode == VISUAL: dest_non_blank = next_non_blank(view, dest) if dest_non_blank < s.a and s.a < s.b: return Region(s.a + 1, dest_non_blank) elif dest_non_blank < s.a: return Region(s.a, dest_non_blank) elif dest_non_blank > s.b and s.a > s.b: return Region(s.a - 1, dest_non_blank + 1) return Region(s.a, dest_non_blank + 1) elif mode == VISUAL_LINE: if dest < s.a and s.a < s.b: return Region(view.full_line(s.a).b, dest) elif dest < s.a: return Region(s.a, dest) elif dest >= s.a and s.a > s.b: return Region( view.full_line(s.a - 1).a, view.full_line(dest).b) return Region(s.a, view.full_line(dest).b) return s
def inner_lines(view, s, count=1): """ Return a region spanning @count inner lines. Inner lines are lines excluding leading/trailing whitespace at outer ends. Assumes we're operating in INTERNAL_NORMAL mode. @view Target view. @s Selection in @view taken as starting point. @count Number of lines to include in returned region. """ end = view.text_point(row_at(view, s.b) + (count - 1), 0) begin = view.line(s.b).a begin = next_non_blank(view, begin) return Region(begin, view.line(end).b)
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