Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    def testFromNormalModeToNormalMode(self):
        set_text(self.view, 'abc\nxxx\nabc\nabc')
        add_selection(self.view, self.R((1, 0), (1, 0)))

        state = VintageState(self.view)
        state.enter_normal_mode()

        self.view.run_command('vi_enter_normal_mode')
        prev_mode = state.mode

        self.view.run_command('ex_move', {'address': '3'})

        state = VintageState(self.view)
        new_mode = state.mode
        self.assertEqual(prev_mode, new_mode)
Ejemplo n.º 3
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
Ejemplo n.º 4
0
    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])
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
        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
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
 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)
Ejemplo n.º 10
0
    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')
Ejemplo n.º 11
0
    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 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)
Ejemplo n.º 13
0
    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')
Ejemplo n.º 14
0
    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)
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
    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
Ejemplo n.º 17
0
 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()
Ejemplo n.º 18
0
    def run(self, edit):
        if self.view.score_selector(0, 'text.excmdline') == 0:
            return

        cmd, prefix, only_dirs = parse(self.view.substr(self.view.line(0)))
        if not cmd:
            return
        if not FsCompletion.prefix and prefix:
            FsCompletion.prefix = prefix
            FsCompletion.is_stale = True
        elif not FsCompletion.prefix:
            state = VintageState(self.view)
            FsCompletion.prefix = state.settings.vi['_cmdline_cd'] + '/'

        if not FsCompletion.items or FsCompletion.is_stale:
            FsCompletion.items = iter_paths(FsCompletion.prefix,
                                            only_dirs=only_dirs)
            FsCompletion.is_stale = False

        try:
            self.view.run_command('write_fs_completion', {
                'cmd': cmd,
                'completion': next(FsCompletion.items)
            })
        except StopIteration:
            try:
                FsCompletion.items = iter_paths(FsCompletion.prefix,
                                                only_dirs=only_dirs)
                self.view.run_command('write_fs_completion', {
                    'cmd': cmd,
                    'completion': next(FsCompletion.items)
                })
            except StopIteration:
                return
Ejemplo n.º 19
0
    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)
Ejemplo n.º 20
0
 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()
Ejemplo n.º 21
0
 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
Ejemplo n.º 22
0
 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
Ejemplo n.º 23
0
    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)))

        VintageState(self.view).mode = MODE_VISUAL

        self.view.run_command('vi_enter_normal_mode', {})
        self.assertEqual(self.R((1, 0), (1, 0)), first_sel(self.view))
Ejemplo n.º 24
0
    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.range['left_ref'] = "'<"
        self.range['right_ref'] = "'>"
        self.range['text_range'] = "'<,'>"
        self.view.run_command('ex_delete', {'line_range': self.range})

        state = VintageState(self.view)
        new_mode = state.mode
        self.assertNotEqual(prev_mode, new_mode)
        self.assertEqual(new_mode, MODE_NORMAL)
Ejemplo n.º 25
0
    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
Ejemplo n.º 26
0
    def testFromNormalModeToNormalMode(self):
        set_text(self.view, 'abc\nxxx\nabc\nabc')
        add_sel(self.view, self.R((1, 0), (1, 0)))

        state = VintageState(self.view)
        state.enter_normal_mode()

        self.view.run_command('vi_enter_normal_mode')
        prev_mode = state.mode

        self.range['left_offset'] = 2
        self.range['text_range'] = '2'
        self.view.run_command('ex_delete', {'line_range': self.range})

        state = VintageState(self.view)
        new_mode = state.mode
        self.assertEqual(prev_mode, new_mode)
Ejemplo n.º 27
0
    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()
Ejemplo n.º 28
0
    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()
Ejemplo n.º 29
0
    def run(self, edit, register=None, count=1):
        state = VintageState(self.view)
        register = register or '"'
        fragments = state.registers[register]
        if not fragments:
            print("Vintageous: Nothing in register \".")
            return

        sels = list(self.view.sel())
        # If we have the same number of pastes and selections, map 1:1. Otherwise paste paste[0]
        # to all target selections.
        if len(sels) == len(fragments):
            sel_to_frag_mapped = zip(sels, fragments)
        else:
            sel_to_frag_mapped = zip(sels, [
                fragments[0],
            ] * len(sels))

        # FIXME: Fix this mess. Separate linewise from charwise pasting.
        pasting_linewise = True
        offset = 0
        paste_locations = []
        for selection, fragment in reversed(list(sel_to_frag_mapped)):
            fragment = self.prepare_fragment(fragment)
            if fragment.startswith('\n'):
                # Pasting linewise...
                # If pasting at EOL or BOL, make sure we paste before the newline character.
                if (utils.is_at_eol(self.view, selection)
                        or utils.is_at_bol(self.view, selection)):
                    l = self.paste_all(edit, selection,
                                       self.view.line(selection.b).b, fragment,
                                       count)
                    paste_locations.append(l)
                else:
                    l = self.paste_all(edit, selection,
                                       self.view.line(selection.b - 1).b,
                                       fragment, count)
                    paste_locations.append(l)
            else:
                pasting_linewise = False
                # Pasting charwise...
                # If pasting at EOL, make sure we don't paste after the newline character.
                if self.view.substr(selection.b) == '\n':
                    l = self.paste_all(edit, selection, selection.b + offset,
                                       fragment, count)
                    paste_locations.append(l)
                else:
                    l = self.paste_all(edit, selection,
                                       selection.b + offset + 1, fragment,
                                       count)
                    paste_locations.append(l)
                offset += len(fragment) * count

        if pasting_linewise:
            self.reset_carets_linewise()
        else:
            self.reset_carets_charwise(paste_locations, len(fragment))
Ejemplo n.º 30
0
 def run(self, character=None):
     state = VintageState(self.view)
     if character is None:
         state.motion = 'vi_big_t'
         state.expecting_user_input = True
     else:
         state.user_input = character
         state.expecting_user_input = False
         state.eval()