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)) 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 + 1) return sublime.Region(pt + 1, pt + 1) 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, 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): state = VintageState(self.view) try: cmd, args, _ = state.repeat_command except TypeError: # Unreachable. return if not cmd: return elif cmd == 'vi_run': args['next_mode'] = MODE_NORMAL args['follow_up_mode'] = 'vi_enter_normal_mode' args['count'] = state.count * args['count'] self.view.run_command(cmd, args) elif cmd == 'sequence': for i, _ in enumerate(args['commands']): # Access this shape: {"commands":[['vi_run', {"foo": 100}],...]} args['commands'][i][1]['next_mode'] = MODE_NORMAL args['commands'][i][1][ 'follow_up_mode'] = 'vi_enter_normal_mode' # TODO: Implement counts properly for 'sequence' command. for i in range(state.count): self.view.run_command(cmd, args) # Ensure we wipe count data if any. state.reset() # XXX: Needed here? Maybe enter_... type commands should be IrreversibleTextCommands so we # must/can call them whenever we need them withouth affecting the undo stack. self.view.run_command('vi_enter_normal_mode')
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, line_range=None): if not line_range['text_range']: # No-op: user issued ":". return ranges, _ = ex_range.new_calculate_range(self.view, line_range) a, b = ranges[0] # FIXME: This should be handled by the parser. # FIXME: In Vim, 0 seems to equal 1 in ranges. b = b if line_range['text_range'] != '0' else 1 state = VintageState(self.view) # FIXME: In Visual mode, goto line does some weird stuff. if state.mode == MODE_NORMAL: # TODO: push all this code down to ViGoToLine? self.view.window().run_command('vi_add_to_jump_list') self.view.run_command('vi_go_to_line', { 'line': b, 'mode': MODE_NORMAL }) self.view.window().run_command('vi_add_to_jump_list') self.view.show(self.view.sel()[0]) elif state.mode in (MODE_VISUAL, MODE_VISUAL_LINE) and line_range['right_offset']: # TODO: push all this code down to ViGoToLine? self.view.run_command('vi_enter_normal_mode') self.view.window().run_command('vi_add_to_jump_list') # FIXME: The parser fails with '<,'>100. 100 is not the right_offset, but an argument. b = self.view.rowcol(self.view.sel()[0].b - 1)[0] + line_range['right_offset'] + 1 self.view.run_command('vi_go_to_line', { 'line': b, 'mode': MODE_NORMAL }) self.view.window().run_command('vi_add_to_jump_list') self.view.show(self.view.sel()[0]) state.display_partial_command()
def run(self, inclusive=False): state = VintageState(self.view) if inclusive: state.motion = 'vi_inclusive_text_object' else: state.motion = 'vi_exclusive_text_object' state.expecting_user_input = True
def run(self, edit, line_range=None): if not line_range['text_range']: # No-op: user issued ":". return ranges, _ = ex_range.new_calculate_range(self.view, line_range) a, b = ranges[0] # FIXME: This should be handled by the parser. # FIXME: In Vim, 0 seems to equal 1 in ranges. b = b if line_range['text_range'] != '0' else 1 state = VintageState(self.view) # FIXME: In Visual mode, goto line does some weird stuff. if state.mode == MODE_NORMAL: # TODO: push all this code down to ViGoToLine? self.view.window().run_command('vi_add_to_jump_list') self.view.run_command('vi_go_to_line', {'line': b, 'mode': MODE_NORMAL}) self.view.window().run_command('vi_add_to_jump_list') self.view.show(self.view.sel()[0]) elif state.mode in (MODE_VISUAL, MODE_VISUAL_LINE) and line_range['right_offset']: # TODO: push all this code down to ViGoToLine? self.view.run_command('vi_enter_normal_mode') self.view.window().run_command('vi_add_to_jump_list') # FIXME: The parser fails with '<,'>100. 100 is not the right_offset, but an argument. b = self.view.rowcol(self.view.sel()[0].b - 1)[0] + line_range['right_offset'] + 1 self.view.run_command('vi_go_to_line', {'line': b, 'mode': MODE_NORMAL}) self.view.window().run_command('vi_add_to_jump_list') self.view.show(self.view.sel()[0]) state.display_partial_command()
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)
class Test_parse_action(unittest.TestCase): def setUp(self): TestsState.view.settings().erase('vintage') TestsState.view.window().settings().erase('vintage') TestsState.view.settings().erase('is_widget') self.state = VintageState(TestsState.view) def tearDown(self): self.state.view.sel().clear() self.state.view.sel().add(sublime.Region(0, 0)) def testCantParseWithUnknownMotion(self): self.state.action = 'foobar' cmd_data = CmdData(self.state) self.assertRaises(AttributeError, self.state.parse_action, cmd_data) def testCanParseNoneMotion(self): cmd_data = CmdData(self.state) self.assertRaises(TypeError, self.state.parse_action, cmd_data) def testCmdDataIsntModifiedIfThereIsNoAction(self): # FIXME: The following creates a dependency in this test. self.state.action = 'vi_d' new_cmd_data = self.state.parse_action(CmdData(self.state)) self.assertEqual(new_cmd_data['action']['command'], 'left_delete') def testCanRunActionWhenThereAreNonEmptySelections(self): self.state.action = 'vi_d' self.state.view.sel().clear() self.state.view.sel().add(sublime.Region(10, 20)) new_cmd_data = self.state.parse_action(CmdData(self.state)) self.assertFalse(new_cmd_data['motion_required'])
def run(self, edit, character=None): state = VintageState(self.view) if character is None: state.expecting_register = True else: state.register = character state.expecting_register = False
def run(self, edit): state = VintageState(self.view) state.enter_insert_mode() self.view.run_command('collapse_to_direction') # 5i and friends don't enter INSERTMODE through this command, so it's fine resetting here. # We need to reset so that things like CTRL+R,ESC (INSERTMODE) cancel pending state. state.reset()
def setUp(self): sublime.set_clipboard('') registers._REGISTER_DATA = {} TestsState.view.settings().erase('vintage') TestsState.view.settings().erase('vintageous_use_sys_clipboard') self.regs = VintageState(TestsState.view).registers self.regs.view = mock.Mock()
def run(self): state = VintageState(self.view) try: cmd, args, _ = state.repeat_command except TypeError: # Unreachable. return if not cmd: return elif cmd == 'vi_run': args['next_mode'] = MODE_NORMAL args['follow_up_mode'] = 'vi_enter_normal_mode' args['count'] = state.count * args['count'] self.view.run_command(cmd, args) elif cmd == 'sequence': for i, _ in enumerate(args['commands']): # We can have either 'vi_run' commands or plain insert mode commands. if args['commands'][i][0] == 'vi_run': # Access this shape: {"commands":[['vi_run', {"foo": 100}],...]} args['commands'][i][1]['next_mode'] = MODE_NORMAL args['commands'][i][1]['follow_up_mode'] = 'vi_enter_normal_mode' # TODO: Implement counts properly for 'sequence' command. for i in range(state.count): self.view.run_command(cmd, args) # Ensure we wipe count data if any. state.reset() # XXX: Needed here? Maybe enter_... type commands should be IrreversibleTextCommands so we # must/can call them whenever we need them withouth affecting the undo stack. self.view.run_command('vi_enter_normal_mode')
def do_action(self, vi_cmd_data): self.debug("Vintageous: Action command: ", vi_cmd_data["action"]) if vi_cmd_data["action"]: # Populate registers if we have to. if vi_cmd_data["can_yank"]: state = VintageState(self.view) if vi_cmd_data["register"]: state.registers[vi_cmd_data["register"]] = self.get_selected_text(vi_cmd_data) else: # TODO: Encapsulate this in registers.py. We could have a # .yank() method there that knows about all the details. state.registers[REG_UNNAMED] = self.get_selected_text(vi_cmd_data) # XXX: Small register delete. Improve this implementation. if vi_cmd_data["populates_small_delete_register"]: state = VintageState(self.view) is_same_line = lambda r: self.view.line(r.begin()) == self.view.line(r.end() - 1) if all(is_same_line(x) for x in list(self.view.sel())): state.registers[REG_SMALL_DELETE] = self.get_selected_text(vi_cmd_data) cmd = vi_cmd_data["action"]["command"] args = vi_cmd_data["action"]["args"] # Some actions that don't take a motion apply the count to the action. For example, # > in visual mode. i = vi_cmd_data["count"] if vi_cmd_data["_repeat_action"] else 1 for t in range(i): self.view.run_command(cmd, args)
def on_done(self, idx): """Save selected value to `"` register.""" if idx == -1: return state = VintageState(self.view) value = list(state.registers.to_dict().values())[idx] state.registers['"'] = value
def setUp(self): sublime.set_clipboard('') registers._REGISTER_DATA = {} TestsState.view.settings().erase('vintage') TestsState.view.settings().erase('vintageous_use_sys_clipboard') # self.regs = Registers(view=TestsState.view, # settings=SettingsManager(view=TestsState.view)) self.regs = VintageState(TestsState.view).registers
def run(self, edit, **vi_cmd_data): self.debug("Data in ViRunCommand:", vi_cmd_data) try: if vi_cmd_data["restore_original_carets"]: self.save_caret_pos() # XXX: Fix this. When should we run the motion exactly? if vi_cmd_data["action"]: # If no motion is present, we know we just have to run the action (like ctrl+w, v). if (vi_cmd_data["motion"]) or ( vi_cmd_data["motion_required"] and not view.has_non_empty_selection_region() ): self.enter_visual_mode_if_needed(vi_cmd_data) self.do_whole_motion(vi_cmd_data) # The motion didn't change the selections: abort action if so required. # TODO: What to do with .post_action() and .do_follow_up_mode() in this event? if ( vi_cmd_data["mode"] == _MODE_INTERNAL_NORMAL and all([v.empty() for v in self.view.sel()]) and vi_cmd_data["cancel_action_if_motion_fails"] ): return self.do_action(vi_cmd_data) else: self.do_whole_motion(vi_cmd_data) finally: # XXX: post_action should be run only if do_action succeeds (?). self.do_post_action(vi_cmd_data) if vi_cmd_data["must_update_xpos"]: state = VintageState(self.view) first_sel = self.view.sel()[0] xpos = 0 if state.mode == MODE_VISUAL: if first_sel.a < first_sel.b: xpos = self.view.rowcol(first_sel.b - 1)[1] elif first_sel.a > first_sel.b: xpos = self.view.rowcol(first_sel.b)[1] elif state.mode == MODE_NORMAL: xpos = self.view.rowcol(first_sel.b)[1] state.xpos = xpos self.do_modify_selections(vi_cmd_data) self.do_follow_up_mode(vi_cmd_data) if vi_cmd_data["scroll_into_view"]: # TODO: If moving by lines, scroll the minimum amount to display the new sels. self.view.show(self.view.sel()[0]) state = VintageState(self.view) state.reset(next_mode=vi_cmd_data["next_mode"])
def run(self, edit): state = VintageState(self.view) if state.mode == MODE_VISUAL: state.store_visual_selections() self.view.run_command('collapse_to_direction') self.view.run_command('dont_stay_on_eol_backward') state.enter_normal_mode()
def run(self, edit, extend=False): def f(view, s): hard_eol = self.view.line(s.b).end() return sublime.Region(hard_eol, hard_eol) regions_transformer(self.view, f) state = VintageState(self.view) state.enter_insert_mode()
def run(self, edit, commands): for cmd, args in commands: self.view.run_command(cmd, args) # XXX: Sequence is a special case in that it doesn't run through vi_run, so we need to # ensure the next mode is correct. Maybe we can improve this by making it more similar to # regular commands? state = VintageState(self.view) state.enter_normal_mode()
def run(self, edit): # Handling multiple visual blocks seems quite hard, so ensure we only have one. first = list(self.view.sel())[0] self.view.sel().clear() self.view.sel().add(first) state = VintageState(self.view) state.enter_visual_block_mode() self.view.run_command('extend_to_minimal_width')
def run(self): state = VintageState(self.view) for i in range(state.count): self.view.run_command('redo') state.update_xpos() # Ensure that we wipe the count, if any. state.reset() self.view.run_command('vi_enter_normal_mode')
def run(self, edit, character=None): state = VintageState(self.view) if character is None: state.expecting_register = True else: if character not in (REG_EXPRESSION,): state.register = character state.expecting_register = False else: self.view.run_command('vi_expression_register')
def run(self, edit, extend=False): def f(view, s): line = view.line(s.b) pt = utils.next_non_white_space_char(view, line.a) return sublime.Region(pt, pt) state = VintageState(self.view) state.enter_insert_mode() regions_transformer(self.view, f)
def run(self, edit, character=None): state = VintageState(self.view) if character is None: state.expecting_register = True else: if character not in (REG_EXPRESSION, ): state.register = character state.expecting_register = False else: self.view.run_command('vi_expression_register')
def run(self, edit, count=None): def f(view, s): return sublime.Region(s.a, s.b + (count - 1)) state = VintageState(self.view) state.enter_visual_mode() self.view.run_command('extend_to_minimal_width') if count > 1: regions_transformer(self.view, f)
def run(self, edit, line_range, register=None, count=None): if not register: register = '"' regs = get_region_by_range(self.view, line_range) text = '\n'.join([self.view.substr(line) for line in regs]) + '\n' state = VintageState(self.view) state.registers[register] = [text] if register == '"': state.registers['0'] = [text]
def on_done(self, s): state = VintageState(self.view) state.motion = 'vi_question_mark' state.user_input = s # Equivalent to ?<CR>, which must repeat the last search. if s == '': state.user_input = state.last_buffer_search if s != '': state.last_buffer_search = s state.eval()
def run(self, edit): state = VintageState(self.view) if state.mode == MODE_VISUAL: state.store_visual_selections() # When returning to normal mode from select mode, we want to keep the non-Vintageous # selections just created unless it's the last one. if not (state.mode == MODE_SELECT and len(self.view.sel()) > 1): self.view.run_command('collapse_to_direction') self.view.run_command('dont_stay_on_eol_backward') state.enter_normal_mode()
def on_done(self, s): self.view.erase_regions("vi_inc_search") state = VintageState(self.view) state.motion = "vi_question_mark" state.user_input = s # Equivalent to ?<CR>, which must repeat the last search. if s == "": state.user_input = state.last_buffer_search if s != "": state.last_buffer_search = s state.eval()
def run(self, edit, extend=False, mode=None): def f(view, s): if mode == MODE_VISUAL_BLOCK: return sublime.Region(s.end()) elif mode != _MODE_INTERNAL_NORMAL: return s hard_eol = self.view.line(s.b).end() return sublime.Region(hard_eol, hard_eol) regions_transformer(self.view, f) state = VintageState(self.view) state.enter_insert_mode()
def on_done(self, s): self.view.erase_regions('vi_inc_search') state = VintageState(self.view) state.motion = 'vi_forward_slash' state.user_input = s # Equivalent to /<CR>, which must repeat the last search. if s == '': state.user_input = state.last_buffer_search if s != '': state.last_buffer_search = s state.eval()
def on_done(s): state = VintageState(self.view) try: rv = [str(eval(s, None, None)),] if not insert: # TODO: We need to sort out the values received and sent to registers. When pasting, # we assume a list... This should be encapsulated in Registers. state.registers[REG_EXPRESSION] = rv else: self.view.run_command('insert_snippet', {'contents': str(rv[0])}) state.reset() except: sublime.status_message("Vintageous: Invalid expression.") on_cancel()
def testFromVisualModeToNormalMode(self): set_text(self.view, 'abc\nxxx\nabc\nabc') add_sel(self.view, self.R((1, 0), (1, 1))) state = VintageState(self.view) state.enter_visual_mode() prev_mode = state.mode self.view.run_command('ex_copy', {'address': '3'}) state = VintageState(self.view) new_mode = state.mode self.assertNotEqual(prev_mode, new_mode) self.assertEqual(new_mode, MODE_NORMAL)
def testFromVisualModeToNormalMode(self): self.write("abc\nxxx\nabc\nabc") self.clear_sel() self.add_sel(self.R((1, 0), (1, 1))) state = VintageState(self.view) state.enter_visual_mode() prev_mode = state.mode self.view.run_command("ex_move", {"address": "3"}) state = VintageState(self.view) new_mode = state.mode self.assertNotEqual(prev_mode, new_mode)
def select(self, regions, register): self.view.sel().clear() to_store = [] for r in regions: self.view.sel().add(r) if register: to_store.append(self.view.substr(self.view.full_line(r))) if register: text = ''.join(to_store) if not text.endswith('\n'): text = text + '\n' state = VintageState(self.view) state.registers[register] = [text]
def run(self, edit): state = VintageState(self.view) # We've recorded what the user has typed into the buffer. Turn macro recording off. self.view.run_command('glue_marked_undo_groups') # FIXME: We can't repeat 5ifoo<esc> after we're done. for i in range(state.count - 1): self.view.run_command('repeat') # Ensure the count will be deleted. state.mode = MODE_NORMAL # Delete the count now. state.reset() self.view.run_command('vi_enter_normal_mode_from_insert_mode')
def testCaretEndsInExpectedRegion(self): set_text(self.view, ''.join(('foo bar\nfoo bar\nfoo bar\n', ))) add_sel(self.view, self.R((1, 3), (1, 0))) state = VintageState(self.view) state.enter_visual_mode() # TODO: we should bypass vi_r and define the values directly. data = CmdData(state) data = vi_r(data) data['action']['args']['character'] = 'X' self.view.run_command('vi_run', data) self.assertEqual(self.R((1, 0), (1, 0)), first_sel(self.view))
class Test_push_action_digit(unittest.TestCase): def setUp(self): TestsState.view.settings().erase('vintage') TestsState.view.window().settings().erase('vintage') TestsState.view.settings().erase('is_widget') self.state = VintageState(TestsState.view) def testCanAddOneItemWhenListIsEmpty(self): self.state.push_action_digit("1") self.assertEqual(self.state.action_digits, ["1"]) def testCanAppendMoreDigits(self): self.state.push_action_digit("1") self.state.push_action_digit("1") self.assertEqual(self.state.action_digits, ["1", "1"])
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, path=None, forced=False): if self.view.is_dirty() and not forced: ex_error.display_error(ex_error.ERR_UNSAVED_CHANGES) return state = VintageState(self.view) if not path: state.settings.vi['_cmdline_cd'] = os.path.expanduser("~") self.view.run_command('ex_print_working_dir') return # TODO: It seems there a few symbols that are always substituted when they represent a # filename. We should have a global method of substiting them. if path == '%:h': fname = self.view.file_name() if fname: state.settings.vi['_cmdline_cd'] = os.path.dirname(fname) self.view.run_command('ex_print_working_dir') return path = os.path.realpath(os.path.expandvars(os.path.expanduser(path))) if not os.path.exists(path): # TODO: Add error number in ex_error.py. ex_error.display_error(ex_error.ERR_CANT_FIND_DIR_IN_CDPATH) return state.settings.vi['_cmdline_cd'] = path self.view.run_command('ex_print_working_dir')
def f(view, s): line_text = view.substr(sublime.Region(view.line(s.b).a, s.b)) 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 + 1) return sublime.Region(pt + 1, pt + 1) return s
def run(self): Vintageous.state._dont_reset_during_init = True state = VintageState(self.view) on_change = self.on_change if state.settings.vi['incsearch'] else None self.view.window().show_input_panel('', '', self.on_done, on_change, self.on_cancel)
def on_cancel(self): self.view.erase_regions('vi_inc_search') state = VintageState(self.view) state.reset() if not self.view.visible_region().contains(self.view.sel()[0]): self.view.show(self.view.sel()[0])
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
def run(self, edit): state = VintageState(self.view) pairs = [ '"{0} {1}'.format(k, v) for k, v in state.registers.to_dict().items() if v ] self.view.window().show_quick_panel(pairs, self.on_done)
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 inner(*args, **kwargs): try: state = VintageState(args[0].view) except AttributeError: state = VintageState(args[0].window.active_view()) old = os.getcwd() try: # FIXME: Under some circumstances, like when switching projects to # a file whose _cmdline_cd has not been set, _cmdline_cd might # return 'None'. In such cases, change to the actual current # directory as a last measure. (We should probably fix this anyway). os.chdir(state.settings.vi['_cmdline_cd'] or old) f(*args, **kwargs) finally: os.chdir(old)
def run(self, edit, register=None, count=1): state = VintageState(self.view) if register: fragments = state.registers[register] else: # TODO: There should be a simpler way of getting the unnamed register's content. fragments = state.registers['"'] sels = list(self.view.sel()) if len(sels) == len(fragments): sel_frag = zip(sels, fragments) else: sel_frag = zip(sels, [ fragments[0], ] * len(sels)) offset = 0 for s, text in sel_frag: if text.endswith('\n'): if utils.is_at_eol(self.view, s) or utils.is_at_bol( self.view, s): self.paste_all(edit, s, self.view.line(s.b).a, text, count) else: self.paste_all(edit, s, self.view.line(s.b - 1).a, text, count) else: self.paste_all(edit, s, s.b + offset, text, count) offset += len(text) * count
def paste_all(self, edit, sel, at, text, count): state = VintageState(self.view) if state.mode not in (MODE_VISUAL, MODE_VISUAL_LINE): # TODO: generate string first, then insert? # Make sure we can paste at EOF. at = at if at <= self.view.size() else self.view.size() for x in range(count): self.view.insert(edit, at, text) # Return position at which we have just pasted. return at else: if text.startswith('\n'): text = text * count if not text.endswith('\n'): text = text + '\n' else: text = text * count if state.mode == MODE_VISUAL_LINE: if text.startswith('\n'): text = text[1:] self.view.replace(edit, sel, text) # Return position at which we have just pasted. return sel.a
def run(self, edit, extend=False): state = VintageState(self.view) state.enter_insert_mode() self.view.run_command('collapse_to_direction') sels = list(self.view.sel()) self.view.sel().clear() new_sels = [] for s in sels: hard_eol = self.view.line(s.b).end() new_sels.append(sublime.Region(hard_eol, hard_eol)) for s in new_sels: self.view.sel().add(s)