Logo Search packages:      
Sourcecode: ranger version File versions  Download package

keybuffer.py

# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import curses.ascii
from collections import deque
from string import digits
from ranger.ext.keybinding_parser import parse_keybinding, \
            DIRKEY, ANYKEY, PASSIVE_ACTION
from ranger.container.keymap import Binding, KeyMap # mainly for assertions

MAX_ALIAS_RECURSION = 20
digitlist = set(ord(n) for n in digits)

00026 class KeyBuffer(object):
      """The evaluator and storage for pressed keys"""
      def __init__(self, keymap, direction_keys):
            self.assign(keymap, direction_keys)

00031       def assign(self, keymap, direction_keys):
            """Change the keymap and direction keys of the keybuffer"""
            self.keymap = keymap
            self.direction_keys = direction_keys

00036       def add(self, key):
            """Add a key and evaluate it"""
            assert isinstance(key, int)
            assert key >= 0
            self.all_keys.append(key)
            self.key_queue.append(key)
            while self.key_queue:
                  key = self.key_queue.popleft()

                  # evaluate quantifiers
                  if self.eval_quantifier and self._do_eval_quantifier(key):
                        return

                  # evaluate the command
                  if self.eval_command and self._do_eval_command(key):
                        return

                  # evaluate (the first number of) the direction-quantifier
                  if self.eval_quantifier and self._do_eval_quantifier(key):
                        return

                  # evaluate direction keys {j,k,gg,pagedown,...}
                  if not self.eval_command:
                        self._do_eval_direction(key)

      def _do_eval_direction(self, key):
            try:
                  assert isinstance(self.dir_tree_pointer, dict)
                  self.dir_tree_pointer = self.dir_tree_pointer[key]
            except KeyError:
                  self.failure = True
            else:
                  self._direction_try_to_finish()

      def _direction_try_to_finish(self):
            if self.max_alias_recursion <= 0:
                  self.failure = True
                  return None
            match = self.dir_tree_pointer
            assert isinstance(match, (Binding, dict, KeyMap))
            if isinstance(match, KeyMap):
                  self.dir_tree_pointer = self.dir_tree_pointer._tree
                  match = self.dir_tree_pointer
            if isinstance(self.dir_tree_pointer, Binding):
                  if match.alias:
                        self.key_queue.extend(parse_keybinding(match.alias))
                        self.dir_tree_pointer = self.direction_keys._tree
                        self.max_alias_recursion -= 1
                  else:
                        direction = match.actions['dir'].copy()
                        if self.direction_quant is not None:
                              direction.multiply(self.direction_quant)
                        self.directions.append(direction)
                        self.direction_quant = None
                        self.eval_command = True
                        self._try_to_finish()

      def _do_eval_quantifier(self, key):
            if self.eval_command:
                  tree = self.tree_pointer
            else:
                  tree = self.dir_tree_pointer
            if key in digitlist and ANYKEY not in tree:
                  attr = self.eval_command and 'quant' or 'direction_quant'
                  if getattr(self, attr) is None:
                        setattr(self, attr, 0)
                  setattr(self, attr, getattr(self, attr) * 10 + key - 48)
            else:
                  self.eval_quantifier = False
                  return None
            return True

      def _do_eval_command(self, key):
            assert isinstance(self.tree_pointer, dict), self.tree_pointer
            try:
                  self.tree_pointer = self.tree_pointer[key]
            except TypeError:
                  self.failure = True
                  return None
            except KeyError:
                  try:
                        key in digitlist or self.direction_keys._tree[key]
                        self.tree_pointer = self.tree_pointer[DIRKEY]
                  except KeyError:
                        try:
                              self.tree_pointer = self.tree_pointer[ANYKEY]
                        except KeyError:
                              self.failure = True
                              return None
                        else:
                              self.matches.append(key)
                              assert isinstance(self.tree_pointer, (Binding, dict))
                              self._try_to_finish()
                  else:
                        assert isinstance(self.tree_pointer, (Binding, dict))
                        self.eval_command = False
                        self.eval_quantifier = True
                        self.dir_tree_pointer = self.direction_keys._tree
            else:
                  if isinstance(self.tree_pointer, dict):
                        try:
                              self.command = self.tree_pointer[PASSIVE_ACTION]
                        except (KeyError, TypeError):
                              self.command = None
                  self._try_to_finish()

      def _try_to_finish(self):
            if self.max_alias_recursion <= 0:
                  self.failure = True
                  return None
            assert isinstance(self.tree_pointer, (Binding, dict, KeyMap))
            if isinstance(self.tree_pointer, KeyMap):
                  self.tree_pointer = self.tree_pointer._tree
            if isinstance(self.tree_pointer, Binding):
                  if self.tree_pointer.alias:
                        keys = parse_keybinding(self.tree_pointer.alias)
                        self.key_queue.extend(keys)
                        self.tree_pointer = self.keymap._tree
                        self.max_alias_recursion -= 1
                  else:
                        self.command = self.tree_pointer
                        self.done = True

00159       def clear(self):
            """Reset the keybuffer.  Do this once before the first usage."""
            self.max_alias_recursion = MAX_ALIAS_RECURSION
            self.failure = False
            self.done = False
            self.quant = None
            self.matches = []
            self.command = None
            self.direction_quant = None
            self.directions = []
            self.all_keys = []
            self.tree_pointer = self.keymap._tree
            self.dir_tree_pointer = self.direction_keys._tree

            self.key_queue = deque()

            self.eval_quantifier = True
            self.eval_command = True

00178       def __str__(self):
            """returns a concatenation of all characters"""
            return "".join("{0:c}".format(c) for c in self.all_keys)

Generated by  Doxygen 1.6.0   Back to index