src/extras/pyrepl/commands.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     1 #   Copyright 2000-2004 Michael Hudson mwh@python.net
       
     2 #
       
     3 #                        All Rights Reserved
       
     4 #
       
     5 # Portions Copyright (c) 2005 Nokia Corporation 
       
     6 #
       
     7 # Permission to use, copy, modify, and distribute this software and
       
     8 # its documentation for any purpose is hereby granted without fee,
       
     9 # provided that the above copyright notice appear in all copies and
       
    10 # that both that copyright notice and this permission notice appear in
       
    11 # supporting documentation.
       
    12 #
       
    13 # THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
       
    14 # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       
    15 # AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
       
    16 # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
       
    17 # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
       
    18 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
       
    19 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       
    20 
       
    21 import ascii
       
    22 import sys, os
       
    23 
       
    24 # Catgories of actions:
       
    25 #  killing
       
    26 #  yanking
       
    27 #  motion
       
    28 #  editing
       
    29 #  history
       
    30 #  finishing
       
    31 # [completion]
       
    32 
       
    33 class Command(object):
       
    34     finish = 0
       
    35     kills_digit_arg = 1
       
    36     def __init__(self, reader, (event_name, event)):
       
    37         self.reader = reader
       
    38         self.event = event
       
    39         self.event_name = event_name
       
    40     def do(self):
       
    41         pass
       
    42 
       
    43 class KillCommand(Command):
       
    44     def kill_range(self, start, end):
       
    45         if start == end:
       
    46             return
       
    47         r = self.reader
       
    48         b = r.buffer
       
    49         text = b[start:end]
       
    50         del b[start:end]
       
    51         if is_kill(r.last_command):
       
    52             if start < r.pos:
       
    53                 r.kill_ring[-1] = text + r.kill_ring[-1]
       
    54             else:
       
    55                 r.kill_ring[-1] = r.kill_ring[-1] + text
       
    56         else:
       
    57             r.kill_ring.append(text)
       
    58         r.pos = start
       
    59         r.dirty = 1
       
    60 
       
    61 class YankCommand(Command):
       
    62     pass
       
    63 
       
    64 class MotionCommand(Command):
       
    65     pass
       
    66 
       
    67 class EditCommand(Command):
       
    68     pass
       
    69 
       
    70 class FinishCommand(Command):
       
    71     finish = 1
       
    72     pass
       
    73 
       
    74 def is_kill(command):
       
    75     return command and issubclass(command, KillCommand)
       
    76 
       
    77 def is_yank(command):
       
    78     return command and issubclass(command, YankCommand)
       
    79 
       
    80 # etc
       
    81 
       
    82 class digit_arg(Command):
       
    83     kills_digit_arg = 0
       
    84     def do(self):
       
    85         r = self.reader
       
    86         c = self.event[-1]
       
    87         if c == "-":
       
    88             if r.arg is not None:
       
    89                 r.arg = -r.arg
       
    90             else:
       
    91                 r.arg = -1
       
    92         else:
       
    93             d = int(c)
       
    94             if r.arg is None:
       
    95                 r.arg = d
       
    96             else:
       
    97                 if r.arg < 0:
       
    98                     r.arg = 10*r.arg - d
       
    99                 else:
       
   100                     r.arg = 10*r.arg + d
       
   101         r.dirty = 1
       
   102 
       
   103 class clear_screen(Command):
       
   104     def do(self):
       
   105         r = self.reader
       
   106         r.console.clear()
       
   107         r.dirty = 1
       
   108 
       
   109 class refresh(Command):
       
   110     def do(self):
       
   111         self.reader.dirty = 1
       
   112 
       
   113 class repaint(Command):
       
   114     def do(self):
       
   115         self.reader.dirty = 1
       
   116         self.reader.console.repaint_prep()
       
   117 
       
   118 class kill_line(KillCommand):
       
   119     def do(self):
       
   120         r = self.reader
       
   121         b = r.buffer
       
   122         eol = r.eol()
       
   123         for c in b[r.pos:eol]:
       
   124             if not ascii.isspace(c):
       
   125                 self.kill_range(r.pos, eol)
       
   126                 return
       
   127         else:
       
   128             self.kill_range(r.pos, eol+1)
       
   129 
       
   130 class unix_line_discard(KillCommand):
       
   131     def do(self):
       
   132         r = self.reader
       
   133         self.kill_range(r.bol(), r.pos)
       
   134 
       
   135 # XXX unix_word_rubout and backward_kill_word should actually
       
   136 # do different things...
       
   137 
       
   138 class unix_word_rubout(KillCommand):
       
   139     def do(self):
       
   140         r = self.reader
       
   141         for i in range(r.get_arg()):
       
   142             self.kill_range(r.bow(), r.pos)
       
   143 
       
   144 class kill_word(KillCommand):
       
   145     def do(self):
       
   146         r = self.reader
       
   147         for i in range(r.get_arg()):
       
   148             self.kill_range(r.pos, r.eow())
       
   149 
       
   150 class backward_kill_word(KillCommand):
       
   151     def do(self):
       
   152         r = self.reader
       
   153         for i in range(r.get_arg()):
       
   154             self.kill_range(r.bow(), r.pos)
       
   155 
       
   156 class yank(YankCommand):
       
   157     def do(self):
       
   158         r = self.reader
       
   159         if not r.kill_ring:
       
   160             r.error("nothing to yank")
       
   161             return
       
   162         r.insert(r.kill_ring[-1])
       
   163 
       
   164 class yank_pop(YankCommand):
       
   165     def do(self):
       
   166         r = self.reader
       
   167         b = r.buffer
       
   168         if not r.kill_ring:
       
   169             r.error("nothing to yank")
       
   170             return
       
   171         if not is_yank(r.last_command):
       
   172             r.error("previous command was not a yank")
       
   173             return
       
   174         repl = len(r.kill_ring[-1])
       
   175         r.kill_ring.insert(0, r.kill_ring.pop())
       
   176         t = r.kill_ring[-1]
       
   177         b[r.pos - repl:r.pos] = t
       
   178         r.pos = r.pos - repl + len(t)
       
   179         r.dirty = 1
       
   180 
       
   181 class interrupt(FinishCommand):
       
   182     def do(self):
       
   183         #import signal
       
   184         self.reader.console.finish()
       
   185         #os.kill(os.getpid(), signal.SIGINT)
       
   186 
       
   187 class suspend(Command):
       
   188     def do(self):
       
   189         raise "suspend not implemented"
       
   190 
       
   191 class up(MotionCommand):
       
   192     def do(self):
       
   193         r = self.reader
       
   194         for i in range(r.get_arg()):
       
   195             bol1 = r.bol()
       
   196             if bol1 == 0:
       
   197                 r.pos = 0
       
   198                 r.error("start of buffer")
       
   199                 return
       
   200             bol2 = r.bol(bol1-1)
       
   201             line_pos = r.pos - bol1
       
   202             if line_pos > bol1 - bol2 - 1:
       
   203                 r.sticky_y = line_pos
       
   204                 r.pos = bol1 - 1
       
   205             else:
       
   206                 r.pos = bol2 + line_pos
       
   207 
       
   208 class down(MotionCommand):
       
   209     def do(self):
       
   210         r = self.reader
       
   211         b = r.buffer
       
   212         for i in range(r.get_arg()):
       
   213             bol1 = r.bol()
       
   214             eol1 = r.eol()
       
   215             if eol1 == len(b):
       
   216                 r.pos = len(b)
       
   217                 r.error("end of buffer")
       
   218                 return
       
   219             eol2 = r.eol(eol1+1)
       
   220             if r.pos - bol1 > eol2 - eol1 - 1:
       
   221                 r.pos = eol2
       
   222             else:
       
   223                 r.pos = eol1 + (r.pos - bol1) + 1
       
   224 
       
   225 class left(MotionCommand):
       
   226     def do(self):
       
   227         r = self.reader
       
   228         for i in range(r.get_arg()):        
       
   229             p = r.pos - 1
       
   230             if p >= 0:
       
   231                 r.pos = p
       
   232             else:
       
   233                 self.reader.error("start of buffer")
       
   234 
       
   235 class right(MotionCommand):
       
   236     def do(self):
       
   237         r = self.reader
       
   238         b = r.buffer
       
   239         for i in range(r.get_arg()):
       
   240             p = r.pos + 1
       
   241             if p <= len(b):
       
   242                 r.pos = p
       
   243             else:
       
   244                 self.reader.error("end of buffer")
       
   245 
       
   246 class beginning_of_line(MotionCommand):
       
   247     def do(self):
       
   248         self.reader.pos = self.reader.bol()
       
   249 
       
   250 class end_of_line(MotionCommand):
       
   251     def do(self):
       
   252         r = self.reader
       
   253         self.reader.pos = self.reader.eol()
       
   254 
       
   255 class home(MotionCommand):
       
   256     def do(self):
       
   257         self.reader.pos = 0
       
   258         
       
   259 class end(MotionCommand):
       
   260     def do(self):
       
   261         self.reader.pos = len(self.reader.buffer)
       
   262         
       
   263 class forward_word(MotionCommand):
       
   264     def do(self):
       
   265         r = self.reader
       
   266         for i in range(r.get_arg()):
       
   267             r.pos = r.eow()
       
   268     
       
   269 class backward_word(MotionCommand):
       
   270     def do(self):
       
   271         r = self.reader
       
   272         for i in range(r.get_arg()):
       
   273             r.pos = r.bow()
       
   274 
       
   275 class self_insert(EditCommand):
       
   276     def do(self):
       
   277         r = self.reader
       
   278         r.insert(self.event * r.get_arg())
       
   279 
       
   280 class insert_nl(EditCommand):
       
   281     def do(self):
       
   282         r = self.reader
       
   283         r.insert("\n" * r.get_arg())
       
   284 
       
   285 class transpose_characters(EditCommand):
       
   286     def do(self):
       
   287         r = self.reader
       
   288         b = r.buffer
       
   289         s = r.pos - 1
       
   290         if s < 0:
       
   291             r.error("cannot transpose at start of buffer")
       
   292         else:
       
   293             if s == len(b):
       
   294                 s -= 1
       
   295             t = min(s + r.get_arg(), len(b) - 1)
       
   296             c = b[s]
       
   297             del b[s]
       
   298             b.insert(t, c)
       
   299             r.pos = t
       
   300             r.dirty = 1
       
   301 
       
   302 class backspace(EditCommand):
       
   303     def do(self):
       
   304         r = self.reader
       
   305         b = r.buffer
       
   306         for i in range(r.get_arg()):
       
   307             if r.pos > 0:
       
   308                 r.pos -= 1
       
   309                 del b[r.pos]
       
   310                 r.dirty = 1
       
   311             else:
       
   312                 self.reader.error("can't backspace at start")
       
   313 
       
   314 class delete(EditCommand):
       
   315     def do(self):
       
   316         r = self.reader
       
   317         b = r.buffer
       
   318         if  ( r.pos == 0 and len(b) == 0 # this is something of a hack
       
   319               and self.event[-1] == "\004"):
       
   320             r.console.finish()
       
   321             raise EOFError
       
   322         for i in range(r.get_arg()):
       
   323             if r.pos != len(b):
       
   324                 del b[r.pos]
       
   325                 r.dirty = 1
       
   326             else:
       
   327                 self.reader.error("end of buffer")
       
   328 
       
   329 class accept(FinishCommand):
       
   330     def do(self):
       
   331         pass
       
   332 
       
   333 class help(Command):
       
   334     def do(self):
       
   335         self.reader.msg = self.reader.help_text
       
   336         self.reader.dirty = 1
       
   337 
       
   338 class invalid_key(Command):
       
   339     def do(self):
       
   340         pending = self.reader.console.getpending()
       
   341         s = ''.join(self.event) + pending.data
       
   342         self.reader.error("`%r' not bound"%s)
       
   343 
       
   344 class invalid_command(Command):
       
   345     def do(self):
       
   346         s = self.event_name
       
   347         self.reader.error("command `%s' not known"%s)
       
   348 
       
   349 class qIHelp(Command):
       
   350     def do(self):
       
   351         r = self.reader
       
   352         r.insert((self.event + r.console.getpending().data) * r.get_arg())
       
   353         r.pop_input_trans()
       
   354 
       
   355 from pyrepl import input
       
   356 
       
   357 class QITrans(object):
       
   358     def push(self, evt):
       
   359         self.evt = evt
       
   360     def get(self):
       
   361         return ('qIHelp', self.evt.raw)
       
   362 
       
   363 class quoted_insert(Command):
       
   364     kills_digit_arg = 0
       
   365     def do(self):
       
   366         self.reader.push_input_trans(QITrans())
       
   367 
       
   368 class save_history(Command):
       
   369     def do(self):
       
   370         self.reader.save_history()
       
   371         self.reader.message("history saved to file")