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
def mappings_resolve(state, sequence=None, mode=None, check_user_mappings=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 = to_bare_command_name(sequence or state.partial_sequence) # TODO: Use same structure as in mappings (nested dict). command = None if check_user_mappings: # TODO: We should be able to force a mode here too as, below. command = _expand_first(state.mode, seq) if not command: command = seq_to_command(state, seq, mode=mode) _log.debug('resolved %s -> %s -> %s', sequence, seq, command) return command
def run(self, key, repeat_count=None, do_eval=True, check_user_mappings=True): _log.info( 'key evt: %s repeat_count=%s do_eval=%s check_user_mappings=%s', key, repeat_count, do_eval, check_user_mappings) # noqa: E501 state = self.state # If the user has made selections with the mouse, we may be in an # inconsistent state. Try to remedy that. if (state.view.has_non_empty_selection_region() and state.mode not in (VISUAL, VISUAL_LINE, VISUAL_BLOCK, SELECT)): init_state(state.view) if key.lower() == '<esc>': self.window.run_command('_enter_normal_mode', {'mode': state.mode}) state.reset_command_data() return state.sequence += key state.display_status() if state.must_capture_register_name: _log.debug('capturing register name...') state.register = key state.partial_sequence = '' return if state.must_collect_input: _log.debug('collecting input...') state.process_input(key) if state.runnable(): _log.debug('state is runnable') if do_eval: _log.debug('evaluating state...') state.eval() state.reset_command_data() return if repeat_count: state.action_count = str(repeat_count) if self._handle_count(state, key, repeat_count): _log.debug('handled count') return state.partial_sequence += key if check_user_mappings and mappings_is_incomplete( state.mode, state.partial_sequence): _log.debug('found incomplete mapping') return command = mappings_resolve(state, check_user_mappings=check_user_mappings) if isinstance(command, ViOpenRegister): _log.debug('opening register...') state.must_capture_register_name = True return # XXX: This doesn't seem to be correct. If we are in OPERATOR_PENDING mode, we should # most probably not have to wipe the state. if isinstance(command, Mapping): _log.debug('found user mapping...') if do_eval: _log.debug('evaluating user mapping...') new_keys = command.mapping if state.mode == OPERATOR_PENDING: new_keys = state.sequence[:-len(state.partial_sequence )] + command.mapping reg = state.register acount = state.action_count mcount = state.motion_count state.reset_command_data() state.register = reg state.motion_count = mcount state.action_count = acount _log.info('user mapping %s -> %s', key, new_keys) # Support for basic Command-line mode mappings: # # `:Command<CR>` maps to Sublime Text command (starts with uppercase letter). # `:command<CR>` maps to Command-line mode command. if ':' in new_keys: match = re.match( '^\\:(?P<cmd_line_command>[a-zA-Z][a-zA-Z_]*)\\<CR\\>', new_keys) if match: cmd_line_command = match.group('cmd_line_command') if cmd_line_command[0].isupper(): # run regular sublime text command def _coerce_to_snakecase(string): string = re.sub(r"([A-Z]+)([A-Z][a-z])", r'\1_\2', string) string = re.sub(r"([a-z\d])([A-Z])", r'\1_\2', string) string = string.replace("-", "_") return string.lower() command = _coerce_to_snakecase(cmd_line_command) command_args = {} else: command = 'vi_colon_input' command_args = {'cmd_line': ':' + cmd_line_command} _log.info('run command -> %s %s', command, command_args) return self.window.run_command(command, command_args) if ':' == new_keys: return self.window.run_command('vi_colon_input') return console_message( 'invalid command line mapping %s -> %s (only `:[a-zA-Z][a-zA-Z_]*<CR>` is supported)' % (command.head, command.mapping)) # noqa: E501 self.window.run_command('process_notation', { 'keys': new_keys, 'check_user_mappings': False }) return if isinstance(command, ViOpenNameSpace): # Keep collecting input to complete the sequence. For example, we # may have typed 'g' _log.info('opening namespace') return elif isinstance(command, ViMissingCommandDef): _log.info('found missing command...') bare_seq = to_bare_command_name(state.sequence) if state.mode == OPERATOR_PENDING: # We might be looking at a command like 'dd'. The first 'd' is # mapped for normal mode, but the second is missing in # operator pending mode, so we get a missing command. Try to # build the full command now. # # Exclude user mappings, since they've already been given a # chance to evaluate. command = mappings_resolve(state, sequence=bare_seq, mode=NORMAL, check_user_mappings=False) else: command = mappings_resolve(state, sequence=bare_seq) if isinstance(command, ViMissingCommandDef): _log.debug('unmapped sequence %s', state.sequence) state.mode = NORMAL state.reset_command_data() return ui_blink() if (state.mode == OPERATOR_PENDING and isinstance(command, ViOperatorDef)): _log.info('found operator pending...') # TODO: This may be unreachable code by now. ??? # we're expecting a motion, but we could still get an action. # For example, dd, g~g~ or g~~ # remove counts action_seq = to_bare_command_name(state.sequence) _log.debug('action sequence %s', action_seq) command = mappings_resolve(state, sequence=action_seq, mode=NORMAL) # TODO: Make _missing a command. if isinstance(command, ViMissingCommandDef): _log.debug('unmapped sequence %s', state.sequence) state.reset_command_data() return if not command['motion_required']: state.mode = NORMAL state.set_command(command) if state.mode == OPERATOR_PENDING: state.reset_partial_sequence() if do_eval: _log.info('evaluating state...') state.eval()
def _feed_key(self, key, repeat_count=None, do_eval=True, check_user_mappings=True): # Args: # key (str): Key pressed. # repeat_count (int): Count to be used when repeating through the '.' command. # do_eval (bool): Whether to evaluate the global state when it's in a # runnable state. Most of the time, the default value of `True` should # be used. Set to `False` when you want to manually control the global # state's evaluation. For example, this is what the _nv_feed_key # command does. # check_user_mappings (bool): state = self.state mode = state.mode _log.debug('mode: %s', mode) # If the user has made selections with the mouse, we may be in an # inconsistent state. Try to remedy that. if (state.view.has_non_empty_selection_region() and mode not in (VISUAL, VISUAL_LINE, VISUAL_BLOCK, SELECT)): init_state(state.view) if key.lower() == '<esc>': self.window.run_command('_enter_normal_mode', {'mode': mode}) state.reset_command_data() return state.sequence += key state.display_status() if state.must_capture_register_name: _log.debug('capturing register name...') state.register = key state.partial_sequence = '' return if state.must_collect_input: _log.debug('collecting input...') state.process_input(key) if state.runnable(): _log.debug('state is runnable') if do_eval: _log.debug('evaluating state...') state.eval() state.reset_command_data() return if repeat_count: state.action_count = str(repeat_count) if self._handle_count(state, key, repeat_count): _log.debug('handled count') return state.partial_sequence += key if check_user_mappings and mappings_is_incomplete( state.mode, state.partial_sequence): _log.debug('found incomplete mapping') return command = mappings_resolve(state, check_user_mappings=check_user_mappings) _log.debug('command %s %s', command, command.__class__.__mro__) if isinstance(command, ViOpenRegister): _log.debug('opening register...') state.must_capture_register_name = True return # XXX: This doesn't seem to be correct. If we are in OPERATOR_PENDING mode, we should # most probably not have to wipe the state. if isinstance(command, Mapping): _log.debug('found user mapping...') if do_eval: _log.debug('evaluating user mapping (mode=%s)...', state.mode) new_keys = command.mapping if state.mode == OPERATOR_PENDING: new_keys = state.sequence[:-len(state.partial_sequence )] + command.mapping reg = state.register acount = state.action_count mcount = state.motion_count state.reset_command_data() state.register = reg state.motion_count = mcount state.action_count = acount _log.info('user mapping %s -> %s', command.sequence, new_keys) if ':' in new_keys: do_ex_user_cmdline(self.window, new_keys) return self.window.run_command('_nv_process_notation', { 'keys': new_keys, 'check_user_mappings': False }) return if isinstance(command, ViOpenNameSpace): # Keep collecting input to complete the sequence. For example, we # may have typed 'g' _log.info('opening namespace') return elif isinstance(command, ViMissingCommandDef): _log.info('found missing command...') bare_seq = to_bare_command_name(state.sequence) if state.mode == OPERATOR_PENDING: # We might be looking at a command like 'dd'. The first 'd' is # mapped for normal mode, but the second is missing in # operator pending mode, so we get a missing command. Try to # build the full command now. # # Exclude user mappings, since they've already been given a # chance to evaluate. command = mappings_resolve(state, sequence=bare_seq, mode=NORMAL, check_user_mappings=False) else: command = mappings_resolve(state, sequence=bare_seq) if isinstance(command, ViMissingCommandDef): _log.debug('unmapped sequence %s', state.sequence) state.mode = NORMAL state.reset_command_data() return ui_blink() if (state.mode == OPERATOR_PENDING and isinstance(command, ViOperatorDef)): _log.info('found operator pending...') # TODO: This may be unreachable code by now. ??? # we're expecting a motion, but we could still get an action. # For example, dd, g~g~ or g~~ # remove counts action_seq = to_bare_command_name(state.sequence) _log.debug('action sequence %s', action_seq) command = mappings_resolve(state, sequence=action_seq, mode=NORMAL) if isinstance(command, ViMissingCommandDef): _log.debug('unmapped sequence %s', state.sequence) state.reset_command_data() return if not command['motion_required']: state.mode = NORMAL state.set_command(command) if state.mode == OPERATOR_PENDING: state.reset_partial_sequence() if do_eval: _log.info('evaluating state...') state.eval()