Beispiel #1
0
def reset_command_data(view) -> None:
    # Resets all temp data needed to build a command or partial command.
    motion = get_motion(view)
    action = get_action(view)

    if _must_update_xpos(motion, action):
        update_xpos(view)

    if _must_scroll_into_view(motion, action):
        # Intentionally using the active view because the previous command
        # may have switched views and view would be the previous one.
        active_view = active_window().active_view()
        _scroll_into_view(active_view, get_mode(active_view))

    action and action.reset()
    set_action(view, None)
    motion and motion.reset()
    set_motion(view, None)
    set_action_count(view, '')
    set_motion_count(view, '')
    set_sequence(view, '')
    set_partial_sequence(view, '')
    set_register(view, '"')
    set_must_capture_register_name(view, False)
    reset_status_line(view, get_mode(view))
Beispiel #2
0
def mappings_resolve(view,
                     sequence: str = None,
                     mode: str = None,
                     check_user_mappings: bool = True):
    # Look at the current global state and return the command mapped to the available sequence.
    #
    # Args:
    #   sequence (str): The command sequence. If a sequence is passed, it is
    #       used instead of the global state's. This is necessary for some
    #       commands that aren't name spaces but act as them (for example,
    #       ys from the surround plugin).
    #   mode (str): If different than None, it will be used instead of the
    #       global state's. This is necessary when we are in operator
    #       pending mode and we receive a new action. By combining the
    #       existing action's name with name of the action just received we
    #       could find a new action.
    #   check_user_mappings (bool):
    #
    # Returns:
    #   Mapping:
    #   ViMissingCommandDef: If not found.

    # We usually need to look at the partial sequence, but some commands do
    # weird things, like ys, which isn't a namespace but behaves as such
    # sometimes.
    seq = sequence or get_partial_sequence(view)

    command = None

    if check_user_mappings:
        # Resolve the full sequence rather than the "bare" sequence, because the
        # user may have defined some mappings that start with numbers (counts),
        # or " (register character), which are stripped from the bare sequences.
        # See https://github.com/NeoVintageous/NeoVintageous/issues/434.

        # XXX The reason these does not pass the mode, and instead uses the
        # get_mode(), is because implementation of commands like dd are a bit
        # hacky. For example, the dd definition does is not assigned to operator
        # pending mode, the second d is instead caught by the feed key command
        # and resolved by specifying NORMAL mode explicitly, which resolves the
        # delete line command definition. Commands like this can probably be
        # fixed by allowing the definitions to handle the OPERATOR PENDING and
        # let the definition handle any special-cases itself instead of passing
        # off the responsibility to the feed key command.

        command = _seq_to_mapping(get_mode(view), seq)

    if not command:
        command = _seq_to_command(view, to_bare_command_name(seq), mode
                                  or get_mode(view))

    _log.info('resolved %s mode=%s sequence=%s %s', command, mode, sequence,
              command.__class__.__mro__)

    return command
Beispiel #3
0
def is_runnable(view) -> bool:
    # Returns:
    #   True if motion and/or action is in a runnable state, False otherwise.
    # Raises:
    #   ValueError: Invlid mode.
    action = get_action(view)
    motion = get_motion(view)

    if must_collect_input(view, motion, action):
        return False

    mode = get_mode(view)

    if action and motion:
        if mode != NORMAL:
            raise ValueError('invalid mode')

        return True

    if (action and (not action.motion_required or is_visual_mode(mode))):
        if mode == OPERATOR_PENDING:
            raise ValueError('action has invalid mode')

        return True

    if motion:
        if mode == OPERATOR_PENDING:
            raise ValueError('motion has invalid mode')

        return True

    return False
Beispiel #4
0
 def translate(self, view):
     return {
         'action': 'nv_enter_select_mode',
         'action_args': {
             'mode': get_mode(view)
         }
     }
Beispiel #5
0
def update_status_line(view) -> None:
    mode_name = mode_to_name(get_mode(view))
    if mode_name:
        view.set_status('vim-mode',
                        '-- {} --'.format(mode_name) if mode_name else '')

    view.set_status('vim-seq', get_sequence(view))
Beispiel #6
0
 def translate(self, view):
     return {
         'action': 'nv_vi_select_k',
         'action_args': {
             'mode': get_mode(view),
             'count': get_count(view)
         }
     }
Beispiel #7
0
 def translate(self, view):
     return {
         'action': '_nv_surround',
         'action_args': {
             'action': 'ys',
             'mode': get_mode(view),
             'replacement': self.inp
         }
     }
Beispiel #8
0
 def translate(self, view):
     return {
         'action': '_nv_surround',
         'action_args': {
             'action': 'ds',
             'mode': get_mode(view),
             'target': self.inp
         }
     }
 def translate(self, view):
     return {
         'action': '_nv_commentary',
         'action_args': {
             'action': 'C',
             'mode': get_mode(view),
             'count': get_count(view)
         }
     }
Beispiel #10
0
 def translate(self, view):
     return {
         'action': '_nv_unimpaired',
         'action_args': {
             'mode': get_mode(view),
             'count': get_count(view),
             'action': 'context_next'
         }
     }
Beispiel #11
0
 def translate(self, view):
     return {
         'action': '_nv_unimpaired',
         'action_args': {
             'mode': get_mode(view),
             'count': get_count(view),
             'action': 'goto_next_conflict_marker'
         }
     }
Beispiel #12
0
 def translate(self, view):
     return {
         'action': '_nv_unimpaired',
         'action_args': {
             'mode': get_mode(view),
             'count': get_count(view),
             'action': 'blank_down'
         }
     }
Beispiel #13
0
 def translate(self, view):
     return {
         'action': '_nv_unimpaired',
         'action_args': {
             'mode': get_mode(view),
             'count': get_count(view),
             'action': 'tabprevious'
         }
     }
Beispiel #14
0
 def translate(self, view):
     return {
         'motion': '_nv_sneak',
         'motion_args': {
             'mode': get_mode(view),
             'count': get_count(view),
             'search': self.inp.rstrip()
         }
     }
Beispiel #15
0
 def translate(self, view):
     return {
         'action': 'nv_unimpaired',
         'action_args': {
             'mode': get_mode(view),
             'count': get_count(view),
             'action': 'move_up'
         }
     }
 def translate(self, view):
     return {
         'action': 'nv_surround',
         'action_args': {
             'action': 'cs',
             'mode': get_mode(view),
             'target': self.inp[0],
             'replacement': self.inp[1:]
         }
     }
Beispiel #17
0
    def on_text_command(self, view, command: str, args: dict):
        # Called when a text command is issued.
        #
        # The listener may return a (command, arguments) tuple to rewrite the
        # command, or None to run the command unmodified.

        if command == 'drag_select':

            # Updates the mode based on mouse events. For example, a double
            # click will select a word and enter VISUAL mode. A triple click
            # will select a line and enter VISUAL LINE mode.
            #
            # The command is rewritten by returning a chain of commands that
            # executes the original drag_select command followed by entering the
            # correct mode.

            mode = get_mode(view)

            if mode in (VISUAL, VISUAL_LINE, VISUAL_BLOCK):
                if (args.get('extend') or (args.get('by') == 'words')
                        or args.get('additive')):
                    return
                elif args.get('by') == 'lines':
                    # Triple click: enter VISUAL LINE.
                    return ('nv_run_cmds', {
                        'commands':
                        [['drag_select', args],
                         ['nv_enter_visual_line_mode', {
                             'mode': mode
                         }]]
                    })
                elif not args.get('extend'):
                    # Single click: enter NORMAL.
                    return ('nv_run_cmds', {
                        'commands': [['drag_select', args],
                                     ['nv_enter_normal_mode', {
                                         'mode': mode
                                     }]]
                    })

            elif mode == NORMAL:
                # TODO Dragging the mouse does not seem to fire a different event than simply clicking. This makes it hard to update the xpos. See https://github.com/SublimeTextIssues/Core/issues/2117.  # noqa: E501
                if args.get('extend') or (args.get('by') == 'words'):
                    # Double click: enter VISUAL.
                    return ('nv_run_cmds', {
                        'commands': [['drag_select', args],
                                     ['nv_enter_visual_mode', {
                                         'mode': mode
                                     }]]
                    })
Beispiel #18
0
 def translate(self, view):
     return {
         'action': '_nv_surround',
         'action_args': {
             'action': 'ys',
             'mode': get_mode(view),
             'motion': {
                 'motion': '_vi_select_text_object',
                 'motion_args': {
                     'mode': INTERNAL_NORMAL,
                     'count': 1,
                     'inclusive': False,
                     'text_object': 'l'
                 }
             },
             'replacement': self.inp
         }
     }
Beispiel #19
0
    def on_query_context(self, view, key, operator, operand, match_all):
        # Called when determining to trigger a key binding with the given context key.
        #
        # If the plugin knows how to respond to the context, it should return
        # either True of False. If the context is unknown, it should return
        # None.
        #
        # Args:
        #   view (View):
        #   key (str):
        #   operator (int):
        #   operand (bool):
        #   match_all (bool):
        #
        # Returns:
        #   bool: If the context is known.
        #   None: If the context is unknown.
        if key == 'nv_handle_key':
            handle_keys = get_setting(view, 'handle_keys')
            if handle_keys:
                try:
                    # Check if the key (no mode prefix; all modes) should be handled.
                    return bool(handle_keys[operand])
                except KeyError:
                    # Check if the key should be handled only for a specific mode.
                    # The format is "{mode}_{key}" e.g. "n_<C-w>", "v_<C-w>"
                    # meaning NORMAL, VISUAL respectively. No prefix implies all
                    # modes. See mode_to_char() for a list of valid mode prefixes.
                    cur_mode_char = mode_to_char(get_mode(view))
                    if cur_mode_char:
                        try:
                            return bool(handle_keys['%s_%s' %
                                                    (cur_mode_char, operand)])
                        except KeyError:
                            pass

            # By default all keys are handled.
            return True

        try:
            return _query_contexts[key](view, operator, operand, match_all)
        except KeyError:
            pass
Beispiel #20
0
    def on_activated(self, view):

        # Clear any visual selections in the view we are leaving. This mirrors
        # Vim behaviour. We can't put this functionality in the
        # view.on_deactivate() event, because that event is triggered when the
        # user right button clicks the view with the mouse, and we don't want
        # visual selections to be cleared on mouse right button clicks.
        if is_view(view):
            window = view.window()
            if window:
                active_group = window.active_group()
                for group in range(window.num_groups()):
                    if group != active_group:
                        other_view = window.active_view_in_group(group)
                        if other_view and other_view != view:
                            sel = other_view.sel()
                            if len(sel) > 0 and any(
                                [not s.empty() for s in sel]):
                                enter_normal_mode(other_view,
                                                  get_mode(other_view))

        # Initialise view.
        init_state(view)
Beispiel #21
0
def init_state(view) -> None:
    # Initialise view state.
    #
    # Runs every time a view is activated, loaded, etc.

    # Don't initialise if we get a console, widget, panel, or any other view
    # where Vim modes are not relevant. Some related initialised settings that
    # may cause unexpected behaviours if they exist are erased "cleaned" too.
    if not is_view(view):
        try:
            # TODO "cleaning" views that are not initialised shouldn't be necessary?
            clean_view(view)
        except Exception:
            _log.debug(
                'could not clean an object: console, widget, panel, etc.')
        finally:
            return

    if not get_reset_during_init(view):
        # Probably exiting from an input panel, like when using '/'. Don't reset
        # the global state, as it may contain data needed to complete the
        # command that's being built.
        set_reset_during_init(view, True)
        return

    mode = get_mode(view)

    # Does user want to reset mode (to normal mode) when initialising state?
    if mode not in (NORMAL, UNKNOWN) and not get_setting(
            view, 'reset_mode_when_switching_tabs'):
        return

    # Fix malformed selection: if we have no selections, add one.
    if len(view.sel()) == 0:
        view.sel().add(0)

    if get_setting(view, 'default_mode') == 'insert':
        if mode in (NORMAL, UNKNOWN):
            enter_insert_mode(view, mode)
    elif mode in (VISUAL, VISUAL_LINE, VISUAL_BLOCK):
        # Visual modes are not reset (to normal mode), because actions like
        # pressing the super key or opening a command-palette/overlay will cause
        # the active view to lose focus and when focus is received again it
        # triggers the on_activated() event, this in turn initialises the view'
        # state, which would reset the visual mode to normal mode, therefore,
        # for example, any command run from the command palette that expects to
        # operate on a visual selection wouldn't work because the visual
        # selection is reset to normal mode before the command has time to run.
        # See https://github.com/NeoVintageous/NeoVintageous/issues/547
        pass
    elif mode in (INSERT, REPLACE):
        # NOTE that the mode is not passed as an argument because it causes the
        # cursor to move back one point from it's current position, for example
        # when pressing i<Esc>i<Esc>i<Esc> the cursor moves one point each time,
        # which is expected, but not expected when initialising state. But not
        # passing the mode may also be causing some other hidden bugs too.
        view.window().run_command('_enter_normal_mode', {'from_init': True})
    elif mode != VISUAL and view.has_non_empty_selection_region():
        # Try to fixup a malformed visual state. For example, apparently this
        # can happen when a search is performed via a search panel and "Find
        # All" is pressed. In that case, multiple selections may need fixing.
        view.window().run_command('_enter_visual_mode', {'mode': mode})
    else:
        # This may be run when we're coming from cmdline mode.
        mode = VISUAL if view.has_non_empty_selection_region() else mode
        view.window().run_command('_enter_normal_mode', {
            'mode': mode,
            'from_init': True
        })

    reset_command_data(view)
Beispiel #22
0
def evaluate_state(view) -> None:
    _log.debug('evaluating...')
    if not is_runnable(view):
        _log.debug('not runnable!')
        return

    action = get_action(view)
    motion = get_motion(view)

    if action and motion:

        # Evaluate action with motion: runs the action with the motion as an
        # argument. The motion's mode is set to INTERNAL_NORMAL and is run
        # by the action internally to make the selection it operates on. For
        # example the motion commands can be used after an operator command,
        # to have the command operate on the text that was moved over.

        action_cmd = action.translate(view)
        motion_cmd = motion.translate(view)

        _log.debug('action: %s', action_cmd)
        _log.debug('motion: %s', motion_cmd)

        set_mode(view, INTERNAL_NORMAL)

        if 'mode' in action_cmd['action_args']:
            action_cmd['action_args']['mode'] = INTERNAL_NORMAL

        if 'mode' in motion_cmd['motion_args']:
            motion_cmd['motion_args']['mode'] = INTERNAL_NORMAL

        args = action_cmd['action_args']

        args['count'] = 1

        # Let the action run the motion within its edit object so that we
        # don't need to worry about grouping edits to the buffer.
        args['motion'] = motion_cmd

        if get_glue_until_normal_mode(
                view) and not is_processing_notation(view):
            run_window_command('mark_undo_groups_for_gluing')

        add_macro_step(view, action_cmd['action'], args)

        run_window_command(action_cmd['action'], args)

        if is_interactive(view) and get_action(view).repeatable:
            set_repeat_data(
                view, ('vi', str(get_sequence(view)), get_mode(view), None))

        reset_command_data(view)

        return  # Nothing more to do.

    if motion:

        # Evaluate motion: Run it.

        motion_cmd = motion.translate(view)

        _log.debug('motion: %s', motion_cmd)

        add_macro_step(view, motion_cmd['motion'], motion_cmd['motion_args'])

        run_motion(view, motion_cmd)

    if action:

        # Evaluate action. Run it.

        action_cmd = action.translate(view)

        _log.debug('action: %s', action_cmd)

        if get_mode(view) == NORMAL:
            set_mode(view, INTERNAL_NORMAL)

            if 'mode' in action_cmd['action_args']:
                action_cmd['action_args']['mode'] = INTERNAL_NORMAL

        elif is_visual_mode(get_mode(view)):
            # Special-case exclusion: saving the previous selection would
            # overwrite the previous selection needed e.g. gv in a VISUAL
            # mode needs to expand or contract to previous selection.
            if action_cmd['action'] != '_vi_gv':
                save_previous_selection(view, get_mode(view))

        # Some commands, like 'i' or 'a', open a series of edits that need
        # to be grouped together unless we are gluing a larger sequence
        # through _nv_process_notation. For example, aFOOBAR<Esc> should be
        # grouped atomically, but not inside a sequence like
        # iXXX<Esc>llaYYY<Esc>, where we want to group the whole sequence
        # instead.
        if get_glue_until_normal_mode(
                view) and not is_processing_notation(view):
            run_window_command('mark_undo_groups_for_gluing')

        sequence = get_sequence(view)
        visual_repeat_data = get_visual_repeat_data(view, get_mode(view))
        action = get_action(view)

        add_macro_step(view, action_cmd['action'], action_cmd['action_args'])

        run_action(active_window(), action_cmd)

        if not (is_processing_notation(view)
                and get_glue_until_normal_mode(view)) and action.repeatable:
            set_repeat_data(
                view, ('vi', sequence, get_mode(view), visual_repeat_data))

    if get_mode(view) == INTERNAL_NORMAL:
        set_mode(view, NORMAL)

    reset_command_data(view)
Beispiel #23
0
 def on_post_save(self, view):
     if is_view(view):
         # Ensure the carets are within valid bounds. For instance, this is a
         # concern when 'trim_trailing_white_space_on_save' is set to true.
         fix_eol_cursor(view, get_mode(view))