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