Package plugins :: Package core :: Package complete :: Module main |
[hide private]
[frames] | no frames]

# Source Code for Module plugins.core.complete.main

      1  #-*- coding: utf8 -*
      2  #
      3  # Max E. Kuznecov <syhpoon@syhpoon.name> 2010
      4  #
      5
      6  import os
      7  import re
      8
      9  from libxyz.core import ODict
     10  from libxyz.core.utils import ustring, bstring, intersect, split_cmd
     11  from libxyz.core.plugins import BasePlugin
     12
     13  from libxyz.ui import lowui
     14  from libxyz.ui import ListEntry
     15  from libxyz.ui import Keys
     16  from libxyz.ui import XYZListBox
     17
     18 -class XYZPlugin(BasePlugin):
     19      "Auto-completion plugin"
     20
     21      NAME = u"complete"
     22      AUTHOR = u"Max E. Kuznecov <syhpoon@syhpoon.name>"
     23      VERSION = u"0.1"
     24      BRIEF_DESCRIPTION = u"Auto-completion system"
     25      FULL_DESCRIPTION = u"Plugin-based completion system"
     26      NAMESPACE = u"core"
     27      MIN_XYZ_VERSION = None
     28      DOC = u"Configuration variables:\n"\
     29            u"domains - List of domains to use.\n"\
     30            u"Default: ['binpath', 'fs']"
     31      HOMEPAGE = u"http://xyzcmd.syhpoon.name/"
     32      EVENTS = None
     33
     34 -    def __init__(self, xyz):
     35          super(XYZPlugin, self).__init__(xyz)
     36
     37          self._domains = ODict()
     38
     39          self.export(self.complete)
     40          self.export(self.smart_complete)
     41          self.export(self.dialog)
     42          self._keys = Keys()
     43
     44      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     45
     46 -    def prepare(self):
     47          # Domains initialization
     48          try:
     49
     50              for domain in self.conf["domains"]:
     51                  self._domains[domain] = self._init_domain(domain)
     52
     53              behaviour = []
     54
     55              for (pattern, domain) in self.conf["behaviour"]:
     56                  behaviour.append((re.compile(pattern), domain))
     57
     58              self.conf["behaviour"] = behaviour
     59          except Exception, e:
     60              xyzlog.warning(_(u"Error ininitalizing plugin: %s") %
     61                             (ustring(str(e))))
     62
     63      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     64
     65 -    def finalize(self):
     66          for domain in self._domains:
     67              try:
     68                  domain.finalize()
     69              except Exception:
     70                  pass
     71
     72      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     73
     74 -    def complete(self, buf, domains=None):
     75          """
     76          Take current buffer and return list of list-generator each entry for
     77          each domain.
     78
     79          @param buf: Current buffer
     80          @param domains: List of domains to search in,
     81                 if None search in all available domains.
     82          @return: List of list-generator each entry for each domain
     83          """
     84
     85          buf = bstring(buf)
     86
     87          result = []
     88
     89          if domains is None:
     90              domains = self._domains.keys()
     91
     92          for d in domains:
     93              result.append(self._domains[d].complete(buf))
     94
     95          return result
     96
     97      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     98
     99 -    def smart_complete(self, cmd):
    100          """
    101          Try to guess what completion domain can be used based on
    102          provided buffer and defined behaviour.
    103
    104          @param cmd: Current buffer
    105          @return: List of list-generator each entry for each domain
    106          """
    107
    108          if not cmd:
    109              return
    110
    111          data = None
    112          pat_used = False
    113          buf = bstring(split_cmd(cmd)[-1])
    114
    115          # First check the behaviour patterns
    116          for (pattern, domain) in self.conf["behaviour"]:
    117              if pattern.search(cmd) is not None:
    118                  pat_used = True
    119                  data = self.dialog(cmd, [domain])
    120                  break
    121
    122          # Else default fallback behaviour applied
    123          if pat_used == False:
    124              # Treat as path
    125              if buf.startswith(os.path.sep) or buf.startswith("./") or \
    126                     buf.startswith("../"):
    127                  data = self.dialog(buf, ["fs"])
    128              else:
    129                  # Treat as command in $PATH
    130                  data = self.dialog(buf, ["binpath"])
    131
    132          if data is not None:
    133              data = intersect(buf, data)
    134              self.xyz.pm.from_load(":sys:cmd", "append")(data)
    135
    136      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    137
    138 -    def dialog(self, buf, domains=None):
    139          """
    140          Show a window containing matched entries
    141          and return user selected one
    142          """
    143
    144          match = self.complete(buf, domains)
    145
    146          _chdir = self.xyz.pm.from_load(u":sys:panel", u"chdir")
    147
    148          _sel_attr = self.xyz.skin.attr(CompleteWindow.resolution, u"selected")
    149          _wdata = []
    150
    151          for gen in match:
    152              for entry in sorted(gen):
    153                  _wdata.append(ListEntry(entry, _sel_attr))
    154
    155          if len(_wdata) == 1:
    156              return _wdata[0].text
    157
    158          _walker = lowui.PollingListWalker(_wdata)
    159          _dim = tuple([x - 2 for x in self.xyz.screen.get_cols_rows()])
    160
    161          return CompleteWindow(self.xyz, self.xyz.top, _walker, _(u"Complete"),
    162                                _dim, extra=_wdata, buf=buf).show()
    163
    164      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    165
    166 -    def _init_domain(self, dom):
    167          """
    168          Try to load and initialize domain class
    169
    170          @param dom: Domain name string. Class name is constructed by
    171                      prepending domain_ prefix
    172          """
    173
    174          domain_class = __import__("domain_%s" % dom, globals(), locals(), [])
    175          domain_obj = domain_class.Domain()
    176
    177          domain_obj.prepare()
    178
    179          return domain_obj
    180
    181  #++++++++++++++++++++++++++++++++++++++++++++++++
    182
    183 -class CompleteWindow(XYZListBox):
    184 -    def __init__(self, *args, **kwargs):
    185          if "extra" in kwargs:
    186              self._extra = kwargs["extra"]
    187              del(kwargs["extra"])
    188          else:
    189              self._extra = None
    190
    191          if "buf" in kwargs:
    192              self._buf = kwargs["buf"]
    193              del(kwargs["buf"])
    194          else:
    195              self._buf = u""
    196
    197          super(CompleteWindow, self).__init__(*args, **kwargs)
    198
    199      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    200
    201 -    def show(self, dim=None, exit_keys=None):
    202          """
    203          Show window. Update contents as user types
    204          """
    205
    206          exit_keys = exit_keys or []
    207
    208          if dim is None:
    209              dim = self.xyz.screen.get_cols_rows()
    210
    211          _buf = u""
    212          _title = self.title
    213
    214          while True:
    215              self.xyz.screen.draw_screen(dim, self.render(dim, True))
    216
    217              _i = self.xyz.input.get()
    218
    219              if self.xyz.input.WIN_RESIZE in _i:
    220                  dim = self.xyz.screen.get_cols_rows()
    221                  continue
    222
    223              update = False
    224
    225              if _i:
    226                  for _k in _i:
    227                      if _k == self._keys.ESC:
    228                          return
    229                      elif _k == self._keys.BACKSPACE:
    230                          _buf = _buf[:-1]
    231                          update = True
    232                      elif _k == self._keys.ENTER:
    233                          entry, _ = self.listbox.get_focus()
    234                          return entry.text
    235                      elif len(ustring(_k)) == 1:
    236                          _buf += _k
    237                          update = True
    238                      else:
    239                          self._listbox.keypress(dim, _k)
    240
    241                  if update:
    242                      _b = bstring(_buf)
    243                      self.set_title(u"%s: %s" % (_title, _buf))
    244
    245                      self.listbox.body.contents = [x for x in self._extra
    246                                                    if _b in x._text]
    247