# Copyright 2000-2004 Michael Hudson mwh@python.net
#
# All Rights Reserved
#
# Portions Copyright (c) 2005 Nokia Corporation
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose is hereby granted without fee,
# provided that the above copyright notice appear in all copies and
# that both that copyright notice and this permission notice appear in
# supporting documentation.
#
# THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import ascii
import sys, os
# Catgories of actions:
# killing
# yanking
# motion
# editing
# history
# finishing
# [completion]
class Command(object):
finish = 0
kills_digit_arg = 1
def __init__(self, reader, (event_name, event)):
self.reader = reader
self.event = event
self.event_name = event_name
def do(self):
pass
class KillCommand(Command):
def kill_range(self, start, end):
if start == end:
return
r = self.reader
b = r.buffer
text = b[start:end]
del b[start:end]
if is_kill(r.last_command):
if start < r.pos:
r.kill_ring[-1] = text + r.kill_ring[-1]
else:
r.kill_ring[-1] = r.kill_ring[-1] + text
else:
r.kill_ring.append(text)
r.pos = start
r.dirty = 1
class YankCommand(Command):
pass
class MotionCommand(Command):
pass
class EditCommand(Command):
pass
class FinishCommand(Command):
finish = 1
pass
def is_kill(command):
return command and issubclass(command, KillCommand)
def is_yank(command):
return command and issubclass(command, YankCommand)
# etc
class digit_arg(Command):
kills_digit_arg = 0
def do(self):
r = self.reader
c = self.event[-1]
if c == "-":
if r.arg is not None:
r.arg = -r.arg
else:
r.arg = -1
else:
d = int(c)
if r.arg is None:
r.arg = d
else:
if r.arg < 0:
r.arg = 10*r.arg - d
else:
r.arg = 10*r.arg + d
r.dirty = 1
class clear_screen(Command):
def do(self):
r = self.reader
r.console.clear()
r.dirty = 1
class refresh(Command):
def do(self):
self.reader.dirty = 1
class repaint(Command):
def do(self):
self.reader.dirty = 1
self.reader.console.repaint_prep()
class kill_line(KillCommand):
def do(self):
r = self.reader
b = r.buffer
eol = r.eol()
for c in b[r.pos:eol]:
if not ascii.isspace(c):
self.kill_range(r.pos, eol)
return
else:
self.kill_range(r.pos, eol+1)
class unix_line_discard(KillCommand):
def do(self):
r = self.reader
self.kill_range(r.bol(), r.pos)
# XXX unix_word_rubout and backward_kill_word should actually
# do different things...
class unix_word_rubout(KillCommand):
def do(self):
r = self.reader
for i in range(r.get_arg()):
self.kill_range(r.bow(), r.pos)
class kill_word(KillCommand):
def do(self):
r = self.reader
for i in range(r.get_arg()):
self.kill_range(r.pos, r.eow())
class backward_kill_word(KillCommand):
def do(self):
r = self.reader
for i in range(r.get_arg()):
self.kill_range(r.bow(), r.pos)
class yank(YankCommand):
def do(self):
r = self.reader
if not r.kill_ring:
r.error("nothing to yank")
return
r.insert(r.kill_ring[-1])
class yank_pop(YankCommand):
def do(self):
r = self.reader
b = r.buffer
if not r.kill_ring:
r.error("nothing to yank")
return
if not is_yank(r.last_command):
r.error("previous command was not a yank")
return
repl = len(r.kill_ring[-1])
r.kill_ring.insert(0, r.kill_ring.pop())
t = r.kill_ring[-1]
b[r.pos - repl:r.pos] = t
r.pos = r.pos - repl + len(t)
r.dirty = 1
class interrupt(FinishCommand):
def do(self):
#import signal
self.reader.console.finish()
#os.kill(os.getpid(), signal.SIGINT)
class suspend(Command):
def do(self):
raise "suspend not implemented"
class up(MotionCommand):
def do(self):
r = self.reader
for i in range(r.get_arg()):
bol1 = r.bol()
if bol1 == 0:
r.pos = 0
r.error("start of buffer")
return
bol2 = r.bol(bol1-1)
line_pos = r.pos - bol1
if line_pos > bol1 - bol2 - 1:
r.sticky_y = line_pos
r.pos = bol1 - 1
else:
r.pos = bol2 + line_pos
class down(MotionCommand):
def do(self):
r = self.reader
b = r.buffer
for i in range(r.get_arg()):
bol1 = r.bol()
eol1 = r.eol()
if eol1 == len(b):
r.pos = len(b)
r.error("end of buffer")
return
eol2 = r.eol(eol1+1)
if r.pos - bol1 > eol2 - eol1 - 1:
r.pos = eol2
else:
r.pos = eol1 + (r.pos - bol1) + 1
class left(MotionCommand):
def do(self):
r = self.reader
for i in range(r.get_arg()):
p = r.pos - 1
if p >= 0:
r.pos = p
else:
self.reader.error("start of buffer")
class right(MotionCommand):
def do(self):
r = self.reader
b = r.buffer
for i in range(r.get_arg()):
p = r.pos + 1
if p <= len(b):
r.pos = p
else:
self.reader.error("end of buffer")
class beginning_of_line(MotionCommand):
def do(self):
self.reader.pos = self.reader.bol()
class end_of_line(MotionCommand):
def do(self):
r = self.reader
self.reader.pos = self.reader.eol()
class home(MotionCommand):
def do(self):
self.reader.pos = 0
class end(MotionCommand):
def do(self):
self.reader.pos = len(self.reader.buffer)
class forward_word(MotionCommand):
def do(self):
r = self.reader
for i in range(r.get_arg()):
r.pos = r.eow()
class backward_word(MotionCommand):
def do(self):
r = self.reader
for i in range(r.get_arg()):
r.pos = r.bow()
class self_insert(EditCommand):
def do(self):
r = self.reader
r.insert(self.event * r.get_arg())
class insert_nl(EditCommand):
def do(self):
r = self.reader
r.insert("\n" * r.get_arg())
class transpose_characters(EditCommand):
def do(self):
r = self.reader
b = r.buffer
s = r.pos - 1
if s < 0:
r.error("cannot transpose at start of buffer")
else:
if s == len(b):
s -= 1
t = min(s + r.get_arg(), len(b) - 1)
c = b[s]
del b[s]
b.insert(t, c)
r.pos = t
r.dirty = 1
class backspace(EditCommand):
def do(self):
r = self.reader
b = r.buffer
for i in range(r.get_arg()):
if r.pos > 0:
r.pos -= 1
del b[r.pos]
r.dirty = 1
else:
self.reader.error("can't backspace at start")
class delete(EditCommand):
def do(self):
r = self.reader
b = r.buffer
if ( r.pos == 0 and len(b) == 0 # this is something of a hack
and self.event[-1] == "\004"):
r.console.finish()
raise EOFError
for i in range(r.get_arg()):
if r.pos != len(b):
del b[r.pos]
r.dirty = 1
else:
self.reader.error("end of buffer")
class accept(FinishCommand):
def do(self):
pass
class help(Command):
def do(self):
self.reader.msg = self.reader.help_text
self.reader.dirty = 1
class invalid_key(Command):
def do(self):
pending = self.reader.console.getpending()
s = ''.join(self.event) + pending.data
self.reader.error("`%r' not bound"%s)
class invalid_command(Command):
def do(self):
s = self.event_name
self.reader.error("command `%s' not known"%s)
class qIHelp(Command):
def do(self):
r = self.reader
r.insert((self.event + r.console.getpending().data) * r.get_arg())
r.pop_input_trans()
from pyrepl import input
class QITrans(object):
def push(self, evt):
self.evt = evt
def get(self):
return ('qIHelp', self.evt.raw)
class quoted_insert(Command):
kills_digit_arg = 0
def do(self):
self.reader.push_input_trans(QITrans())
class save_history(Command):
def do(self):
self.reader.save_history()
self.reader.message("history saved to file")