def run(self, edit, mode, current_iteration, total_iterations): def f(view, s): delta = 1 if mode == MODE_VISUAL: delta = 1 if utils.is_region_reversed(view, s) else 2 text_before_caret = view.substr(sublime.Region(view.line(s.b).a, s.b - delta)) first_char_is_space = view.substr(s.b - delta).isspace() if (view.line(s.b).a == s.b - delta) else False if mode == MODE_NORMAL: if text_before_caret.isspace() or first_char_is_space: pt = utils.previous_non_white_space_char(self.view, s.b - 1, white_space='\t ') if view.line(pt).empty(): return sublime.Region(s.a , pt + 1) elif view.word(pt).size() == 1: return sublime.Region(pt + 1, pt + 1) return sublime.Region(pt, pt) # At BOL. # XXX: Use a general function instead of spelling out the computation. elif view.line(s.b).a == s.b and not view.line(s.b - 1).empty(): return sublime.Region(s.b - 1, s.b - 1) elif mode == MODE_VISUAL: if utils.is_region_reversed(view, s): if text_before_caret.isspace() or first_char_is_space: pt = utils.previous_non_white_space_char(self.view, s.b - delta, white_space='\t ') # PREVIOUSLINE empty; don't go past it. if view.line(pt).empty(): return sublime.Region(s.a , pt + 1) return sublime.Region(s.a, pt) elif utils.is_at_bol(view, s) and not view.line(s.b - 1).empty(): # Single-character words are a special case; we don't want to skip over # them. if view.word(s.b - 1).size() > 1: return sublime.Region(s.a, s.b - 1) else: # Non-reversed region. Note that .b here is at NEXTCHAR, not CURRENTCHAR. if text_before_caret.isspace() or first_char_is_space: pt = utils.previous_non_white_space_char(self.view, s.b - delta, white_space='\t ') if view.line(pt).empty(): return sublime.Region(s.a , pt + 1) # XXX: I don't think this branch is necessary. # On new WORD; make sure motion doesn't skip it. elif view.substr(pt) not in ('\t \n'): return sublime.Region(s.a, pt + 1) return sublime.Region(s.a, pt) # At WORDBEGIN or at any non-ALPHANUMERICCHAR. elif (view.word(s.b - 1).a == s.b - 1) or not view.substr(s.b - 1).isalnum(): return sublime.Region(s.a, s.b - 1) return s regions_transformer(self.view, f)
def run(self, edit, extend=False, character=None, mode=None, count=1): def f(view, s): line_text = view.substr(sublime.Region(view.line(s.b).a, s.b)) offset = 0 a, b = view.line(s.b).a, s.b final_offset = -1 try: for i in range(count): line_text = view.substr(sublime.Region(a, b)) match_in_line = line_text.rindex(character) final_offset = match_in_line b = view.line(s.a).a + final_offset except ValueError: pass if final_offset > -1: pt = view.line(s.b).a + final_offset state = VintageState(view) if state.mode == MODE_VISUAL or mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, pt) return sublime.Region(pt, pt) return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False, count=None): def f(view, s): if mode == MODE_NORMAL: pt = self.col_to_pt(pt=s.b, nr=count) return sublime.Region(pt, pt) elif mode == MODE_VISUAL: pt = self.col_to_pt(pt=s.b - 1, nr=count) if s.a < s.b: if pt < s.a: return sublime.Region(s.a + 1, pt) else: return sublime.Region(s.a, pt + 1) else: if pt > s.a: return sublime.Region(s.a - 1, pt + 1) else: return sublime.Region(s.a, pt) elif mode == _MODE_INTERNAL_NORMAL: pt = self.col_to_pt(pt=s.b, nr=count) if s.a < s.b: return sublime.Region(s.a, pt) else: return sublime.Region(s.a + 1, pt) return s regions_transformer(self.view, f)
def run(self, edit, count=None, extend=False, mode=None): def f(view, s): if mode == MODE_NORMAL: return sublime.Region(target, target) elif mode == _MODE_INTERNAL_NORMAL: if s.b >= target: return sublime.Region(s.a + 1, target) return sublime.Region(s.a, target) elif mode == MODE_VISUAL: if s.b >= target: new_target = utils.next_non_white_space_char(view, target) return sublime.Region(s.a + 1, new_target) new_target = utils.next_non_white_space_char(view, target) return sublime.Region(s.a, new_target + 1) else: return s r = self.view.visible_region() row_a, _ = self.view.rowcol(r.a) row_b, _ = self.view.rowcol(r.b) row = ((row_a + row_b) / 2) target = self.view.text_point(row, 0) regions_transformer(self.view, f) self.view.show(target)
def run(self, edit, mode=None, count=1, extend=False): def f(view, s): pattern = r'\b{0}\b'.format(query) flags = sublime.IGNORECASE if mode == _MODE_INTERNAL_NORMAL: match = view.find(pattern, view.word(s.end()).end(), flags) else: match = view.find(pattern, view.word(s).end(), flags) if match: if mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, match.begin()) elif state.mode == MODE_VISUAL: return sublime.Region(s.a, match.begin()) elif state.mode == MODE_NORMAL: return sublime.Region(match.begin(), match.begin()) return s state = VintageState(self.view) # TODO: make sure we swallow any leading white space. query = self.view.substr(self.view.word(self.view.sel()[0].end())) if query: state.last_buffer_search = query regions_transformer(self.view, f)
def run(self): # We define our own transformer here because we want to handle undo as a special case. # TODO: I don't know if it needs to be an special case in reality. def f(view, s): # Compensates the move issued below. if s.a < s.b : return sublime.Region(s.a + 1, s.a + 1) else: return sublime.Region(s.a, s.a) state = VintageState(self.view) for i in range(state.count): self.view.run_command('undo') if self.view.has_non_empty_selection_region(): regions_transformer(self.view, f) # !! HACK !! ///////////////////////////////////////////////////////// # This is a hack to work around an issue in Sublime Text: # When undoing in normal mode, Sublime Text seems to prime a move by chars # forward that has never been requested by the user or Vintageous. # As far as I can tell, Vintageous isn't at fault here, but it seems weird # to think that Sublime Text is wrong. self.view.run_command('move', {'by': 'characters', 'forward': False}) # //////////////////////////////////////////////////////////////////// state.update_xpos() # Ensure that we wipe the count, if any. state.reset()
def run(self, edit, mode=None, character=None, extend=False): def f(view, s): if mode == MODE_VISUAL: if s.a <= s.b: if address.b < s.b: return sublime.Region(s.a + 1, address.b) else: return sublime.Region(s.a, address.b) else: return sublime.Region(s.a + 1, address.b) elif mode == MODE_NORMAL: return address elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, address.b) return s state = VintageState(self.view) address = state.marks.get_as_encoded_address(character) if address is None: return if isinstance(address, str): if not address.startswith('<command'): self.view.window().open_file(address, sublime.ENCODED_POSITION) else: # We get a command in this form: <command _vi_double_quote> self.view.run_command(address.split(' ')[1][:-1]) return # This is a motion in a composite command. regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: full_current_line = view.full_line(s.b) target = full_current_line.b - 1 full_next_line = view.full_line(full_current_line.b) two_lines = sublime.Region(full_current_line.a, full_next_line.b) # Text without \n. first_line_text = view.substr(view.line(full_current_line.a)) next_line_text = view.substr(full_next_line) if len(next_line_text) > 1: next_line_text = next_line_text.lstrip() sep = '' if first_line_text and not first_line_text.endswith(' '): sep = ' ' view.replace(edit, two_lines, first_line_text + sep + next_line_text) if first_line_text: return sublime.Region(target, target) return s else: return s regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: if view.line(s).empty(): return s elif mode == MODE_VISUAL: if view.line(s.b - 1).empty() and s.size() == 1: return s state = VintageState(self.view) autoindent = state.settings.vi['autoindent'] if mode == _MODE_INTERNAL_NORMAL: if not autoindent: return self.view.line(s) else: pt = utils.next_non_white_space_char(view, self.view.line(s).a) return sublime.Region(pt, self.view.line(s).b) elif mode == MODE_VISUAL: if not autoindent: return self.view.line(sublime.Region(s.a, s.b - 1)) else: pt = utils.next_non_white_space_char(view, self.view.line(s.a).a) return sublime.Region(pt, self.view.line(s.b - 1).b) regions_transformer(self.view, f)
def run(self, edit, count=None, extend=False, mode=None): def f(view, s): if mode == MODE_NORMAL: return sublime.Region(target, target) elif mode == _MODE_INTERNAL_NORMAL: if s.b >= target: return sublime.Region(s.a + 1, target) return sublime.Region(s.a, target) elif mode == MODE_VISUAL: if s.b >= target: new_target = utils.next_non_white_space_char(view, target) return sublime.Region(s.a + 1, new_target) new_target = utils.next_non_white_space_char(view, target) return sublime.Region(s.a, new_target + 1) else: return s r = self.view.visible_region() row, _ = self.view.rowcol(r.b) row -= count + 1 # XXXX: Subtract 1 so that Sublime Text won't attempt to scroll the line into view, which # would be quite annoying. target = self.view.text_point(row - 1, 0) regions_transformer(self.view, f) self.view.show(target)
def run(self, edit): def f(view, s): a = view.line(s.a).a b = view.line(s.b - 1).b return sublime.Region(a, b + 1) regions_transformer(self.view, f)
def run(self, edit, **kwargs): def f(view, s): # Consider 2d$. This command should delete two lines and this class helps with that. # In some cases, though, we can't possibly know whether .b is at SOMELINE HARDEOL # or at SOMELINE HARDBOL. For example, if .b is at CURRENTLINE HARDEOL and NEXTLINE is # shorter, 2d$ may cause the caret to land at NEXTLINE HARDEOL, which is the same # point as HARDBOL two lines down. In such case, we don't need to extend .b to # HARDEOL, but with the available data to this function, we can't know that. # # For now, we'll consider the example above as an exception and let Vintageous do the # wrong thing. It's more important that the command mentioned above works well when # the caret is in the middle of a line or at HARDBOL. state = VintageState(view) if state.mode == MODE_VISUAL: if s.a < s.b and not view.line(s.b - 1).empty(): hard_eol = self.view.full_line(s.b - 1).b return sublime.Region(s.a, hard_eol) elif state.mode == MODE_NORMAL: pass return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False, exact_word=True): def f(view, s): if exact_word: pattern = r'\b{0}\b'.format(query) else: pattern = query flags = sublime.IGNORECASE if mode == _MODE_INTERNAL_NORMAL: match = reverse_search(view, pattern, 0, current_sel.a, flags) else: match = reverse_search(view, pattern, 0, current_sel.a, flags) if match: if mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.b, match.begin()) elif state.mode == MODE_VISUAL: return sublime.Region(s.b, match.begin()) elif state.mode == MODE_NORMAL: return sublime.Region(match.begin(), match.begin()) return s state = VintageState(self.view) # TODO: make sure we swallow any leading white space. query = self.view.substr(self.view.word(self.view.sel()[0].end())) if query: state.last_buffer_search = query current_sel = self.view.sel()[0] regions_transformer(self.view, f)
def run(self, edit, count=None, extend=False, mode=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: x_limit = max(view.line(s.b).a, s.b - count) return sublime.Region(s.a, x_limit) elif mode == MODE_VISUAL: if s.a < s.b: x_limit = max(view.line(s.b - 1).a + 1, s.b - count) if view.line(s.a) == view.line(s.b - 1) and count >= s.size(): x_limit = max(view.line(s.b - 1).a, s.b - count - 1) return sublime.Region(s.a + 1, x_limit) return sublime.Region(s.a, x_limit) if s.a > s.b: x_limit = max(view.line(s.b).a, s.b - count) return sublime.Region(s.a, x_limit) elif mode == MODE_NORMAL: x_limit = max(view.line(s.b).a, s.b - count) return sublime.Region(x_limit, x_limit) # XXX: We should never reach this. return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False): def f(view, s): # TODO: must skip empty paragraphs. start = utils.previous_non_white_space_char(view, s.b - 1, white_space='\n \t') par_as_region = view.expand_by_class(start, sublime.CLASS_EMPTY_LINE) if mode == MODE_NORMAL: return sublime.Region(par_as_region.a, par_as_region.a) elif mode == MODE_VISUAL: return sublime.Region(s.a + 1, par_as_region.a) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, par_as_region.a) elif mode == MODE_VISUAL_LINE: if s.a <= s.b: if par_as_region.a < s.a: return sublime.Region(view.full_line(s.a).b, par_as_region.a) return sublime.Region(s.a, par_as_region.a + 1) else: return sublime.Region(s.a, par_as_region.a) return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False): def f(view, s): # TODO: must skip empty paragraphs. start = utils.next_non_white_space_char(view, s.b, white_space='\n \t') par_as_region = view.expand_by_class(start, sublime.CLASS_EMPTY_LINE) if mode == MODE_NORMAL: return sublime.Region(min(par_as_region.b, view.size() - 1), min(par_as_region.b, view.size() - 1)) elif mode == MODE_VISUAL: return sublime.Region(s.a, par_as_region.b + 1) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, par_as_region.b - 1) elif mode == MODE_VISUAL_LINE: if s.a <= s.b: return sublime.Region(s.a, par_as_region.b + 1) else: if par_as_region.b > s.a: return sublime.Region(view.line(s.a - 1).a, par_as_region.b + 1) return sublime.Region(s.a, par_as_region.b) return s regions_transformer(self.view, f)
def run(self, edit): def f(view, s): return sublime.Region(s.b, view.line(s.b).a) regions_transformer(self.view, f) self.view.run_command('left_delete')
def run(self, edit, mode=None, count=None): def f(view, s): if mode == MODE_NORMAL: if view.line(s.b).empty(): return s x_limit = min(view.line(s.b).b - 1, s.b + count, view.size()) return sublime.Region(x_limit, x_limit) if mode == _MODE_INTERNAL_NORMAL: x_limit = min(view.line(s.b).b, s.b + count) x_limit = max(0, x_limit) return sublime.Region(s.a, x_limit) if mode in (MODE_VISUAL, MODE_VISUAL_BLOCK): if s.a < s.b: x_limit = min(view.full_line(s.b - 1).b, s.b + count) return sublime.Region(s.a, x_limit) if s.a > s.b: x_limit = min(view.full_line(s.b).b - 1, s.b + count) if view.substr(s.b) == '\n': return s if view.line(s.a) == view.line(s.b) and count >= s.size(): x_limit = min(view.full_line(s.b).b, s.b + count + 1) return sublime.Region(s.a - 1, x_limit) return sublime.Region(s.a, x_limit) return s regions_transformer(self.view, f)
def run(self, edit, search_string, mode=None, count=1, extend=False): def f(view, s): if mode == MODE_VISUAL: return sublime.Region(s.a, match.a + 1) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, match.a) elif mode == MODE_NORMAL: return sublime.Region(match.a, match.a) return s # This happens when we attempt to repeat the search and there's no search term stored yet. if search_string is None: return # We want to start searching right after the current selection. current_sel = self.view.sel()[0] start = current_sel.b if not current_sel.empty() else current_sel.b + 1 wrapped_end = self.view.size() # TODO: What should we do here? Case-sensitive or case-insensitive search? Configurable? # Search wrapping around the end of the buffer. match = find_wrapping(self.view, search_string, start, wrapped_end, flags=0, times=count) if not match: return regions_transformer(self.view, f) self.hilite(search_string)
def run(self, edit): def f(view, s): line = view.line(s.b) pt = utils.next_non_white_space_char(view, line.a) return sublime.Region(pt, pt) regions_transformer(self.view, f)
def run(self, edit, search_string, mode=None, count=1, extend=False): def f(view, s): # FIXME: Readjust carets if we searched for '\n'. if mode == MODE_VISUAL: return sublime.Region(s.end(), found.a) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.end(), found.a) elif mode == MODE_NORMAL: return sublime.Region(found.a, found.a) return s # This happens when we attempt to repeat the search and there's no search term stored yet. if search_string is None: return # FIXME: What should we do here? Case-sensitive or case-insensitive search? Configurable? found = reverse_find_wrapping( self.view, term=search_string, start=0, end=self.view.sel()[0].b, flags=0, times=count ) if not found: print("Vintageous: Pattern not found.") return regions_transformer(self.view, f) self.hilite(search_string)
def run(self, edit, forward=True, mode=None): def f(view, s): if mode != MODE_VISUAL_LINE: if s.end() - s.begin() == 1: if forward: if s.b < s.a: return sublime.Region(s.b, s.a) else: return s else: if s.b > s.a: return sublime.Region(s.b, s.a) else: return s else: return s else: if forward: if self.view.full_line(s.b).a == s.b and self.view.full_line(s.b).b == s.a: return sublime.Region(s.b, s.a) else: return s elif self.view.full_line(s.b - 1).a == s.a and self.view.full_line(s.b - 1).b == s.b: r = sublime.Region(self.view.full_line(s.a).b, s.a) return r else: return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False, globally=False): def f(view, s): if mode == MODE_NORMAL: return sublime.Region(location, location) elif mode == MODE_VISUAL: return sublime.Region(s.a + 1, location) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, location) return s current_sel = self.view.sel()[0] self.view.sel().clear() self.view.sel().add(current_sel) location = self.find_symbol(current_sel, globally=globally) if not location: return if globally: # Global symbol; simply open the file; not a motion. # TODO: Perhaps must be a motion if the target file happens to be the current one? self.view.window().open_file(location[0] + ':' + ':'.join([str(x) for x in location[2]]), sublime.ENCODED_POSITION) return # Local symbol; select. location = self.view.text_point(*location) regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: current_line = view.line(s.b) return sublime.Region(current_line.a, current_line.a) return s regions_transformer(self.view, f)
def run(self, edit, **kwargs): def f(view, s): if (s.a < s.b and self.view.full_line(s.b - 1).b == s.b): return sublime.Region(s.a, s.b - 1) else: return s regions_transformer(self.view, f)
def run(self, edit, **kwargs): def f(view, s): if is_at_eol(self.view, s) and not self.view.line(s.b).empty(): return back_one_char(s) else: return s regions_transformer(self.view, f)
def run(self, edit): def f(view, s): if not s.empty(): return sublime.Region(s.begin(), s.begin()) else: return s regions_transformer(self.view, f)
def run(self, edit, **kwargs): def f(view, s): if is_at_eol(self.view, s) and not self.view.line(s.b).empty(): s = back_one_char(s) # s = next_non_white_space_char(self.view, s.b) return s regions_transformer(self.view, f)
def run(self, edit): def f(view, s): if not is_on_empty_line(self.view, s) and is_at_eol(self.view, s): return back_one_char(s) else: return s regions_transformer(self.view, f)
def run(self, edit, **kwargs): def f(view, s): if view.size() > 0 and is_at_eol(self.view, s): return forward_one_char(s) else: return s regions_transformer(self.view, f)
def run(self, edit): def f(view, s): if s.size() > 0: if view.full_line(s.b - 1).b == s.b: return self.view.full_line(sublime.Region(s.a, s.b - 1)) else: return self.view.full_line(s) else: return self.view.full_line(s) regions_transformer(self.view, f)
def run(self, edit): def f(view, s): if not s.empty(): if s.a < s.b: return sublime.Region(s.b - 1, s.b - 1) else: return sublime.Region(s.b, s.b) else: return s regions_transformer(self.view, f)
def run(self, edit, extend=False, percent=None, mode=None): if percent == None: def move_to_bracket(view, s): def find_bracket_location(pt): bracket, brackets, bracket_pt = self.find_a_bracket(pt) if not bracket: return if bracket == brackets[0]: return self.find_balanced_closing_bracket( bracket_pt + 1, brackets) else: return self.find_balanced_opening_bracket( bracket_pt, brackets) if mode == MODE_VISUAL: # TODO: Improve handling of s.a < s.b and s.a > s.b cases. a = find_bracket_location(s.b - 1) if a is not None: a = a + 1 if a > s.b else a if a == s.a: a += 1 return sublime.Region(s.a, a) elif mode == MODE_NORMAL: a = find_bracket_location(s.b) if a is not None: return sublime.Region(a, a) # TODO: According to Vim we must swallow brackets in this case. elif mode == _MODE_INTERNAL_NORMAL: a = find_bracket_location(s.b) if a is not None: return sublime.Region(s.a, a) return s regions_transformer(self.view, move_to_bracket) return row = self.view.rowcol(self.view.size())[0] * (percent / 100) def f(view, s): pt = view.text_point(row, 0) return sublime.Region(pt, pt) regions_transformer(self.view, f) # FIXME: Bringing the selections into view will be undesirable in many cases. Maybe we # should have an optional .scroll_selections_into_view() step during command execution. self.view.show(self.view.sel()[0])
def run(self, edit, mode=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: if view.line(s).empty(): return s elif mode == MODE_VISUAL: if view.line(s.b - 1).empty(): return s return sublime.Region(s.a, self.view.line(s).b) regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: if view.substr(s.b) != '\n': return sublime.Region(s.a, view.line(s.b).b) elif mode == MODE_VISUAL: if view.substr(s.b) != '\n': return sublime.Region(s.a, view.line(s.b).b) return s regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: line = view.line(s.b) if view.substr(line).isupper(): view.replace(edit, line, view.substr(line).lower()) else: view.replace(edit, line, view.substr(line).upper()) return line return s regions_transformer(self.view, f)
def run(self, edit, character=None, mode=None): def f(view, s): next_row = view.rowcol(s.b - 1)[0] + 1 pt = view.text_point(next_row, 0) return sublime.Region(pt, pt) if mode == _MODE_INTERNAL_NORMAL: for s in self.view.sel(): self.view.replace(edit, s, character * s.size()) if character == '\n': regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): # We've made a selection with _vi_big_s_motion just before this. if mode == _MODE_INTERNAL_NORMAL: pt = utils.next_non_white_space_char(view, s.a, white_space=' \t') view.erase(edit, sublime.Region(pt, view.line(s.b).b)) return sublime.Region(pt, pt) return s regions_transformer(self.view, f)
def run(self, edit, count=None, mode=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: x_limit = max(view.line(s.b).a, s.b - count) return sublime.Region(s.a, x_limit) # TODO: Split handling of the two modes for clarity. elif mode in (MODE_VISUAL, MODE_VISUAL_BLOCK): if s.a < s.b: if mode == MODE_VISUAL_BLOCK and self.view.rowcol( s.b - 1)[1] == baseline: return s x_limit = max(view.line(s.b - 1).a + 1, s.b - count) if view.line( s.a) == view.line(s.b - 1) and count >= s.size(): x_limit = max(view.line(s.b - 1).a, s.b - count - 1) return sublime.Region(s.a + 1, x_limit) return sublime.Region(s.a, x_limit) if s.a > s.b: x_limit = max(view.line(s.b).a, s.b - count) return sublime.Region(s.a, x_limit) elif mode == MODE_NORMAL: x_limit = max(view.line(s.b).a, s.b - count) return sublime.Region(x_limit, x_limit) # XXX: We should never reach this. return s # For jagged selections (on the rhs), only those sticking out need to move leftwards. # Example ([] denotes the selection): # # 10 foo bar foo [bar] # 11 foo bar foo [bar foo bar] # 12 foo bar foo [bar foo] # # Only lines 11 and 12 should move when we press h. baseline = 0 if mode == MODE_VISUAL_BLOCK: sel = self.view.sel()[0] if sel.a < sel.b: min_ = min( self.view.rowcol(r.b - 1)[1] for r in self.view.sel()) if any( self.view.rowcol(r.b - 1)[1] != min_ for r in self.view.sel()): baseline = min_ regions_transformer(self.view, f)
def run(self, edit, mode=None, current_iteration=None, total_iterations=None): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: if view.substr(s.b) == '\n': return sublime.Region(s.b + 1, s.b + 1) return s regions_transformer(self.view, f)
def run(self, edit, mode=None, current_iteration=None, total_iterations=None): def f(view, s): if view.substr(s.b - 1) == '\n': # FIXME: Actually, we should go back to the first \n; we may have run over # multiple ones. return sublime.Region(s.a, s.b - 1) return s regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if mode == MODE_VISUAL: if s.a < s.b: a = view.line(s.a).a b = view.full_line(s.b - 1).b else: a = view.full_line(s.a - 1).b b = view.full_line(s.b).a return sublime.Region(a, b) return s regions_transformer(self.view, f)
def testCanTransformRegions(self): TestsState.view.sel().clear() TestsState.view.sel().add(sublime.Region(0, 100)) TestsState.view.sel().add(sublime.Region(200, 300)) TestsState.view.sel().add(sublime.Region(400, 500)) transf = lambda view, x: sublime.Region(x.a, x.b - (x.size() - 1)) regions_transformer(TestsState.view, transf) regions = list(TestsState.view.sel()) for r in regions: self.assertEqual(r.size(), 1)
def run(self, edit): def f(view, s): if s.a < s.b: # going forward if utils.is_at_hard_eol(self.view, s) and \ not utils.visual_is_on_empty_line_forward(self.view, s): return utils.back_end_one_char(s) else: return s else: # Moving down by lines. return s regions_transformer(self.view, f)
def run(self, edit): def f(view, s): # TODO: This will confuse users, but otherwise they will be even more confused, because # the caret will disappear until they press h, l, etc. # Alternatively, we could abort the mode change? if view.size() == 0: return s if s.empty(): return sublime.Region(s.a, s.b + 1) else: return s regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if mode == MODE_VISUAL: line = view.line(s.b - 1) pt = utils.next_non_white_space_char(view, line.a) return sublime.Region(s.a, pt + 1) elif mode != MODE_VISUAL_LINE: line = view.line(s.b) pt = utils.next_non_white_space_char(view, line.a) return sublime.Region(pt, pt) return s regions_transformer(self.view, f)
def run(self, edit, extend=False, line=None): line = line if line > 0 else 1 dest = self.view.text_point(line - 1, 0) def f(view, s): if not extend: return sublime.Region(dest, dest) else: return sublime.Region(s.a, dest) regions_transformer(self.view, f) # FIXME: Bringing the selections into view will be undesirable in many cases. Maybe we # should have an optional .scroll_selections_into_view() step during command execution. self.view.show(self.view.sel()[0])
def run(self, edit, xpos=-1): def f(view, s): row, col = view.rowcol(s.b) # We assume every time we need to adjust xpos it's because the old one is smaller. if (s.a >= s.b) and col < xpos: limit = view.line(s.b).size() new_col = min(xpos + 1, limit) return sublime.Region(s.a, view.text_point(row, new_col)) return s if xpos < 0: return regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if mode == MODE_NORMAL: line = view.line(s.b) return sublime.Region(line.a, line.a) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(view.line(s.b).a, view.full_line(s.b).b) elif mode == MODE_VISUAL: # TODO: This is sloppy. We're reorienting the caret here. We need to use the # existing mechanism for that or imrpove it (using a normal hook as for # other cases.) return sublime.Region(s.end(), s.begin()) return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False): def f(view, s): if mode == MODE_NORMAL: pass elif mode == MODE_VISUAL: if s.a > s.b: return sublime.Region(s.a, s.b - 1) elif mode == _MODE_INTERNAL_NORMAL: pass return s regions_transformer(self.view, f)
def run(self, edit, extend=False, character=None, mode=None, count=1): def f(view, s): eol = view.line(s.b).end() if not s.empty(): eol = view.line(s.b - 1).end() match = s offset = 1 if count > 1 else 0 for i in range(count): # Define search range as 'rest of the line to the right'. if state.mode != MODE_VISUAL: search_range = sublime.Region( min(match.b + 1 + offset, eol), eol) else: search_range = sublime.Region(min(match.b + offset, eol), eol) match = find_in_range(view, character, search_range.a, search_range.b, sublime.LITERAL) # Count too high or simply no match; break. if match is None: match = s break if state.mode == MODE_VISUAL or mode == _MODE_INTERNAL_NORMAL: if match == s: # FIXME: It won't blink because the current light can't be highlighted right # now (we are in command mode and there is a selection on the screen. Perhaps # we can make the gutter blink instead.) utils.blink() return s return sublime.Region(s.a, match.b - 1) if match == s: utils.blink() return s return sublime.Region(match.a - 1, match.a - 1) # TODO: Give feedback to the user that the search failed? if character is None: return else: state = VintageState(self.view) state.last_character_search = character regions_transformer(self.view, f)
def run(self, edit, mode=None): def f(view, s): if s.a < s.b: # TODO: Wait until regions can compare themselves neatly. if (self.view.line(s.b).a == self.view.line(s.b - 1).a and self.view.line(s.b).b == self.view.line(s.b - 1).b): return sublime.Region(s.a, self.view.full_line(s.b).b) else: return s else: if self.view.line(s.b).a != s.b: r = sublime.Region(s.a, self.view.full_line(s.b).a) return r else: return s regions_transformer(self.view, f)
def run(self, edit, current_iteration=None, total_iterations=None, mode=None): def f(view, s): is_last_iteration = (current_iteration == total_iterations - 1) if mode == _MODE_INTERNAL_NORMAL: # If we're at BOL one LINE down; move to NEXTWORD WORDEND inclusive. if utils.is_at_bol(self.view, s): next = utils.next_non_white_space_char(self.view, s.b, white_space='\t \n') next = self.view.word(next) return sublime.Region(s.a, next.b) else: return s elif mode == MODE_NORMAL: # If we're at BOL one LINE down; move to NEXTWORD WORDEND exclusive. if utils.is_at_bol(self.view, s): next = utils.next_non_white_space_char(self.view, s.b, white_space='\t \n') next = self.view.word(next) return sublime.Region(next.b - 1, next.b - 1) # Last motion; ensure caret ends up at a WORDEND exclusive. The native 'move' # command will have left us on the next character. elif is_last_iteration: return sublime.Region(s.a - 1, s.b - 1) else: return s elif mode == MODE_VISUAL: # If we're at BOL one LINE down, move to NEXTWORD WORDEND inclusive. if utils.is_at_bol(self.view, s): next = utils.next_non_white_space_char(self.view, s.b, white_space='\t \n') next = self.view.word(next) return sublime.Region(s.a, next.b) else: return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False): def f(view, s): # TODO: must skip empty paragraphs. sen = self.find_previous_sentence_end(s) if mode == MODE_NORMAL: return sublime.Region(sen.a, sen.a) elif mode == MODE_VISUAL: return sublime.Region(s.a + 1, sen.a + 1) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, sen.a + 1) return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False): def f(view, s): if mode == MODE_NORMAL: pt = self.first_non_white_space_char(s.b) pt = min((pt, self.view.line(pt).b)) return sublime.Region(pt, pt) elif mode == MODE_VISUAL: pt = self.first_non_white_space_char(s.b) return sublime.Region(s.a, pt) elif mode == _MODE_INTERNAL_NORMAL: # Same as dj, dk delete two entire lines, here we do a similar thing. return sublime.Region(s.a, self.view.line(s.b).a) return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False): def f(view, s): if mode == _MODE_INTERNAL_NORMAL: if s.a <= s.b: if view.substr(s.b) != '\n': end = view.word(s.b).b end = utils.next_non_white_space_char( view, end, white_space='\t ') if end == s.b: end = view.expand_by_class( s.b, sublime.CLASS_PUNCTUATION_END).b return sublime.Region(s.a, end) elif view.line(s.b).empty(): return sublime.Region(s.a, s.b + 1) return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False): def f(view, s): # TODO: must skip empty paragraphs. sen = self.find_next_sentence_end(s) if mode == MODE_NORMAL: target = min(sen.b, view.size() - 1) return sublime.Region(target, target) elif mode == MODE_VISUAL: # TODO: Must encompass new line char too? return sublime.Region(s.a, sen.b) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, sen.b) return s regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False, exact_word=True): def f(view, s): if exact_word: pattern = r'\b{0}\b'.format(query) else: pattern = query flags = sublime.IGNORECASE if mode == _MODE_INTERNAL_NORMAL: match = reverse_find_wrapping(view, term=pattern, start=0, end=current_sel.a, flags=0, times=1) else: match = reverse_find_wrapping(view, term=pattern, start=0, end=current_sel.a, flags=0, times=1) if match: if mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.b, match.begin()) elif state.mode == MODE_VISUAL: return sublime.Region(s.b, match.begin()) elif state.mode == MODE_NORMAL: return sublime.Region(match.begin(), match.begin()) return s state = VintageState(self.view) # TODO: make sure we swallow any leading white space. query = self.view.substr(self.view.word(self.view.sel()[0].end())) if query: state.last_buffer_search = query current_sel = self.view.sel()[0] regions_transformer(self.view, f)
def run(self, edit, extend=False, character=None, mode=None, count=1): def f(view, s): # TODO: Refactor this mess. line_text = view.substr(sublime.Region(view.line(s.b).a, s.b)) offset = 0 a, b = view.line(s.b).a, s.b final_offset = -1 try: for i in range(count): line_text = view.substr(sublime.Region(a, b)) match_in_line = line_text.rindex(character) final_offset = match_in_line b = view.line(s.a).a + final_offset except ValueError: pass if final_offset > -1: pt = view.line(s.b).a + final_offset state = VintageState(view) if state.mode == MODE_VISUAL or mode == _MODE_INTERNAL_NORMAL: if sublime.Region(s.b, pt) == s: utils.blink() return s return sublime.Region(s.a, pt) if pt == s.b: utils.blink() return s return sublime.Region(pt, pt) return s if character is None: return else: state = VintageState(self.view) state.last_character_search = character regions_transformer(self.view, f)
def run(self, edit, mode=None, extend=False, count=None): def f(view, s): if mode == MODE_NORMAL: return previous elif mode == MODE_VISUAL: return sublime.Region(s.a, previous.b) elif mode == _MODE_INTERNAL_NORMAL: return sublime.Region(s.a, previous.b) elif mode == MODE_VISUAL_LINE: return sublime.Region(s.a, self.view.full_line(previous.b).b) return s previous, scroll_amount = self.prev_half_page(count) regions_transformer(self.view, f) self.view.run_command('scroll_lines', {'amount': scroll_amount})