1# Copyright (c) 2010, Tomohiro Kusumi 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# 1. Redistributions of source code must retain the above copyright notice, this 8# list of conditions and the following disclaimer. 9# 2. Redistributions in binary form must reproduce the above copyright notice, 10# this list of conditions and the following disclaimer in the documentation 11# and/or other materials provided with the distribution. 12# 13# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 24from __future__ import division 25 26from . import candidate 27from . import history 28from . import kbd 29from . import literal 30from . import screen 31from . import util 32 33class Operand (object): 34 def __init__(self): 35 self.__history = history.History(None) 36 self.__prev = util.Namespace(key=kbd.ERROR, opc='', arg='', raw=[]) 37 self.init([], [], []) 38 l = "e", "w", "wneg", "wq", "split", "vsplit", "bdelete", "open_b64e", \ 39 "open_b64d", "open_b32e", "open_b32d", "open_b16e", "open_b16d", \ 40 "open_b85e", "open_b85d" # XXX 41 self.__path_li = tuple(getattr(literal, "s_" + _) for _ in l) 42 self.__path_li_str = tuple(_.str for _ in self.__path_li) 43 44 def init(self, rl, fl, sl): 45 self.__regxs = rl 46 self.__fasts = fl 47 self.__slows = sl 48 _li_cand.init(self.__slows) 49 self.__cand = {None : _li_cand} 50 _arg_cand.init(literal.get_arg_literals()) 51 for li in self.__slows: 52 if li is literal.s_set: 53 self.__cand[li.str] = _arg_cand 54 if li in self.__path_li: 55 self.__cand[li.str] = _path_cand 56 self.clear() 57 58 def cleanup(self): 59 self.__history.flush() 60 61 def clear(self): 62 self.__pos = 0 63 self.__amp = [] 64 self.__buf = [] 65 self.__opc = [] 66 self.__arg = [] # list of strings 67 assert len(self.__buf) == self.__pos 68 69 def rewind(self): 70 end = self.__pos 71 beg = end - len(self.__opc) 72 l = self.__amp + self.__buf[:beg] + self.__buf[end:] 73 self.clear() 74 for x in l: 75 self.process_incoming(x) 76 77 def get_prev_history(self, c): 78 return self.__history.get_latest(c) 79 80 def __add_history(self): 81 key = chr(self.__buf[0]) 82 s = _to_string(self.__buf) 83 if s != key: 84 if self.__history.get_latest(key) != s: 85 self.__history.append(key, s) 86 self.__history.reset_cursor(key) 87 88 def __get_older_history(self): 89 buf = self.__prev.raw 90 assert buf and buf[0] in literal.get_slow_ords() 91 x = _to_string(buf) 92 s = self.__history.get_older(x[0], x) 93 if not s: 94 s = self.__history.get_newer(x[0], x) 95 if not s: 96 s = x 97 assert s[0] == x[0] 98 self.__set_string(s) 99 100 def __get_newer_history(self): 101 buf = self.__prev.raw 102 assert buf and buf[0] in literal.get_slow_ords() 103 x = _to_string(buf) 104 s = self.__history.get_newer(x[0], x) 105 if not s: 106 s = x 107 assert s[0] == x[0] 108 self.__set_string(s) 109 110 def __clear_candidate(self): 111 self.__cand[None].clear() 112 for o in self.__cand.values(): 113 o.clear() 114 115 def __get_min_cursor(self): 116 return 1 117 118 def __at_min_cursor(self): 119 return self.__pos <= self.__get_min_cursor() 120 121 def __get_max_cursor(self): 122 return screen.get_size_x() - 2 123 124 def __at_max_cursor(self): 125 return self.__pos >= self.__get_max_cursor() 126 127 def __get_tail_cursor(self): 128 return len(self.__buf) 129 130 def __at_tail_cursor(self): 131 return self.__pos == self.__get_tail_cursor() 132 133 def __add_buffer(self, c): 134 self.__buf.insert(self.__pos, c) 135 self.__pos += 1 136 self.__parse_buffer() 137 138 def __set_buffer(self, l): 139 self.clear() 140 self.__buf = list(l) 141 self.__pos = len(self.__buf) 142 self.__parse_buffer() 143 144 def __set_string(self, s): 145 self.__set_buffer([ord(x) for x in s]) 146 147 def __parse_buffer(self): 148 self.__arg = [] 149 if not _is_slow(self.__buf): 150 self.__opc = self.__buf 151 else: 152 l = _to_string(self.__buf).split(' ') 153 for i, s in enumerate(l): 154 if i == 0: 155 self.__opc = [ord(x) for x in s] 156 elif s: 157 self.__arg.append(s) 158 159 def process_incoming(self, x): 160 if _is_null(self.__buf) and not self.__scan_amp(x): 161 return None, None, None, None, None, None, -1 162 163 typ = _get_type(self.__buf) 164 if typ == _null: 165 ret = self.__scan_null(x) 166 elif typ == _fast: 167 ret = self.__scan_fast(x) 168 else: 169 ret = self.__scan_slow(x) 170 if not ret: 171 if typ != _fast: 172 msg = _to_string(self.__buf) 173 else: 174 msg = None 175 return None, None, None, None, None, msg, self.__pos 176 177 typ = _get_type(self.__buf) 178 if typ == _null: 179 li = self.__match_null() 180 elif typ == _fast: 181 li = self.__match_fast() 182 else: 183 li = self.__match_slow() 184 185 if self.__amp: 186 s = _to_string(self.__amp) 187 if s == '+': 188 amp = 1 189 elif s == '-': 190 amp = -1 191 else: 192 amp = int(s) 193 if amp > util.MAX_INT: 194 amp = util.MAX_INT 195 elif amp < util.MIN_INT: 196 amp = util.MIN_INT 197 else: 198 amp = None 199 if util.isprints(self.__opc): 200 opc = _to_string(self.__opc) 201 else: 202 opc = '' 203 if _is_slow(self.__buf): 204 msg = '' 205 else: 206 msg = None 207 return li, amp, opc, self.__arg[:], self.__buf[:], msg, self.__pos 208 209 def __scan_amp(self, x): 210 if self.__amp: 211 if not _is_digit(x): 212 return True 213 elif x == ord('+') or x == ord('-'): 214 self.__amp.append(x) 215 return False 216 elif x == ord('0') or not _is_digit(x): 217 return True # 0 for literal.zero 218 self.__amp.append(x) 219 return False 220 221 def __scan_null(self, x): 222 if x == kbd.ESCAPE: 223 self.clear() 224 return True 225 elif x == kbd.MOUSE: 226 self.__set_buffer(literal.mouse.seq) 227 return True 228 elif x == kbd.RESIZE: 229 self.__set_buffer(literal.resize.seq) 230 return True 231 else: 232 self.__add_buffer(x) 233 return not _is_slow(self.__buf) 234 235 def __scan_fast(self, x): 236 if x == kbd.ESCAPE: 237 self.clear() 238 return True 239 elif x == kbd.MOUSE: 240 self.__set_buffer(literal.mouse.seq) 241 return True 242 elif x == kbd.RESIZE: 243 self.__set_buffer(literal.resize.seq) 244 return True 245 else: 246 self.__add_buffer(x) 247 return True 248 249 def __scan_slow(self, x): 250 self.__scan_slow_prev(x) 251 if x == kbd.ESCAPE: 252 self.__add_history() 253 self.__clear_candidate() 254 self.clear() 255 return True 256 elif x == kbd.MOUSE: 257 self.__set_buffer(literal.mouse.seq) 258 return True 259 elif x == kbd.RESIZE: 260 self.__set_buffer(literal.resize.seq) 261 return True 262 elif x == kbd.TAB: 263 return self.__scan_slow_tab() 264 elif x in (kbd.UP, util.ctrl('p')): 265 self.__get_older_history() 266 return False 267 elif x in (kbd.DOWN, util.ctrl('n')): 268 self.__get_newer_history() 269 return False 270 elif x == kbd.LEFT: 271 if self.__pos > self.__get_min_cursor(): 272 self.__pos -= 1 273 return False 274 elif x == kbd.RIGHT: 275 if self.__pos < self.__get_tail_cursor(): 276 self.__pos += 1 277 return False 278 elif x in kbd.get_backspaces(): 279 self.__backspace_slow() 280 return False 281 elif x == kbd.DELETE: 282 self.__delete_slow() 283 return False 284 elif x == util.ctrl('a'): 285 self.__pos = self.__get_min_cursor() 286 return False 287 elif x == util.ctrl('e'): 288 self.__pos = self.__get_tail_cursor() 289 return False 290 elif x == util.ctrl('k'): 291 self.__buf[self.__pos:] = '' 292 self.__parse_buffer() 293 return False 294 elif x == kbd.ENTER: 295 self.__parse_buffer() 296 self.__add_history() 297 self.__clear_candidate() 298 return True 299 elif util.isprint(x): 300 if len(self.__opc) < self.__get_max_cursor(): 301 self.__add_buffer(x) 302 return False 303 else: 304 return False 305 306 def __scan_slow_prev(self, x): 307 prev_key = self.__prev.key 308 if prev_key != kbd.TAB and x == kbd.TAB: 309 self.__parse_buffer() 310 self.__prev.opc = _to_string(self.__opc) 311 self.__prev.arg = self.__arg[:] 312 elif prev_key not in _arrows and x in _arrows: 313 self.__parse_buffer() 314 self.__prev.raw = self.__buf[:] 315 elif prev_key == kbd.TAB and x != kbd.TAB: 316 self.__prev.opc = '' 317 self.__prev.arg = '' 318 elif prev_key in _arrows and x not in _arrows: 319 self.__prev.raw = [] 320 self.__prev.key = x 321 322 def __scan_slow_tab(self): 323 opc = self.__prev.opc 324 arg = self.__prev.arg 325 326 # handle a special case (ends with space) first 327 if self.__buf[self.__get_tail_cursor() - 1] == ord(' '): 328 if not arg and opc in self.__path_li_str: 329 # e.g. ":e <TAB>" 330 self.__set_string(opc + " ./") 331 self.__parse_buffer_update_arg() 332 return self.__scan_slow_tab() 333 else: 334 return False 335 # ignore unless at tail, but test this after above 336 if not self.__at_tail_cursor(): 337 return False 338 339 if not arg: 340 s = self.__cand[None].get(opc) 341 if s: 342 self.__set_string(s) 343 elif len(arg) == 1 and opc in self.__cand: 344 cand = self.__cand[opc] 345 if isinstance(cand, candidate.PathCandidate): 346 l = cand.get(arg[0]) 347 if l is not None: 348 s, n = l 349 if s: 350 self.__set_string(opc + " " + s) 351 if n == 1: 352 # This is the only candidate, so update buffer for 353 # the next iteration which may need to start from 354 # beginning with a different input (e.g. change from 355 # a dir path without trailing / to with trailing /). 356 self.__parse_buffer_update_arg() 357 else: 358 s = cand.get(arg[0]) 359 if s: 360 self.__set_string(opc + " " + s) 361 return False 362 363 # XXX needs refactoring, should avoid updating __prev here 364 def __parse_buffer_update_arg(self): 365 self.__parse_buffer() 366 self.__prev.arg = self.__arg[:] 367 368 def __delete_slow(self): 369 key = chr(self.__buf[0]) 370 if self.__at_tail_cursor(): 371 self.__backspace_slow() 372 else: 373 del self.__buf[self.__pos] 374 if not self.__buf: 375 self.clear() 376 self.__history.reset_cursor(key) 377 self.__parse_buffer() 378 379 def __backspace_slow(self): 380 key = chr(self.__buf[0]) 381 if self.__at_min_cursor() and not self.__at_tail_cursor(): 382 return 383 del self.__buf[self.__pos - 1] 384 self.__pos -= 1 385 if not self.__buf: 386 self.clear() 387 self.__history.reset_cursor(key) 388 self.__parse_buffer() 389 390 def __match_null(self): 391 return literal.escape 392 393 def __match_fast(self): 394 beg = 0 395 end = len(self.__fasts) - 1 396 seq = tuple(self.__opc) 397 while True: 398 i = (beg + end) // 2 399 o = self.__fasts[i] 400 if o.match(seq): 401 return o 402 elif o.match_incomplete(seq): 403 return None 404 if seq < o.seq: 405 end = i - 1 406 else: 407 beg = i + 1 408 if beg > end: 409 break 410 411 for o in self.__regxs: 412 if o.match(seq): 413 return o 414 elif o.match_incomplete(seq): 415 return None 416 self.clear() 417 return None 418 419 def __match_slow(self): 420 l = [] 421 for o in self.__slows: 422 if o.match(self.__opc): 423 return o 424 elif o.match_incomplete(self.__opc): 425 l.append(o) 426 if len(l) == 1: 427 s = l[0].str + " " + ' '.join(self.__arg) 428 self.__set_string(s.rstrip()) 429 return l[0] 430 else: 431 s = _to_string(self.__buf) 432 return literal.InvalidLiteral(s, None, '') 433 434_arrows = list(kbd.get_arrows()) 435_arrows.append(util.ctrl('p')) 436_arrows.append(util.ctrl('n')) 437_null, _fast, _slow = range(3) 438 439def _to_string(l): 440 return ''.join([chr(x) for x in l]) 441 442def _is_digit(x): 443 return ord('0') <= x <= ord('9') 444 445def _is_null(l): 446 return _get_type(l) == _null 447 448def _is_slow(l): 449 return _get_type(l) == _slow 450 451def _get_type(l): 452 if not l: 453 return _null 454 elif l[0] in literal.get_slow_ords(): 455 return _slow 456 else: 457 return _fast 458 459_li_cand = candidate.LiteralCandidate() 460_arg_cand = candidate.LiteralCandidate() 461_path_cand = candidate.PathCandidate() 462