import curses, sys from buffer import Buffer, EditError from editwindow import EditWindow, CAttr, UnhandledKey, setup_screen from editwindow import CloseEditor, cursorattr class ResizeEvent(Exception): pass class StatusbarMessage(Exception): pass class ShpEd: def __init__(self, scr, buffer): self.current_timeout = -1 self.screen = scr self.mycursornb = getattr(buffer, 'mycursornb', 0) self.mainwindow = EditWindow(buffer) self.last_saved_version = buffer.version self.setup_layout() def setup_layout(self): h, w = self.screen.getmaxyx() self.screen.clear() self.screensize = w, h self.mainwindow.geometry = (0, 0, w, h-1) def statuslineattr(self): return cursorattr(self.mycursornb) | curses.A_REVERSE | curses.A_BOLD def drawstatusbar(self, statusmsg='', error=False): scr = self.screen stdscr_x, stdscr_y = self.screensize scr.move(stdscr_y-1, 0) attr = self.statuslineattr() if statusmsg: line = statusmsg[:stdscr_x-1].ljust(stdscr_x-1) if error: attr = CAttr.statusline_error else: statusmsg = ' [^K] Cut [^U] Paste [^R] Read [^W] Write [^X] Exit ' line = statusmsg.rjust(stdscr_x-1)[-(stdscr_x-1):] scr.addstr(line, attr) scr.clrtoeol() def nextkey(self, timeout=-1): self.screen.refresh() if timeout != self.current_timeout: self.screen.timeout(timeout) self.current_timeout = timeout c = self.screen.getch() # Get a keystroke if c == curses.KEY_RESIZE or c == 12: # special-case Ctrl-L raise ResizeEvent return c def keyreader(self): scr = self.screen stdscr_x, stdscr_y = self.screensize scr.move(stdscr_y-1, stdscr_x-1) haspoll = hasattr(self.mainwindow.buffer, 'poll') if haspoll: timeout = 25 else: timeout = -1 while True: c = self.nextkey(timeout) if c == -1: # timeout if haspoll and self.mainwindow.buffer.poll(): break continue break self.drawstatusbar() return c def run(self): statusmsg = '' error = False mw = self.mainwindow while True: mw.cursor.color = self.mycursornb self.drawstatusbar(statusmsg, error) statusmsg = '' error = False try: try: mw.run(self.screen, self.keyreader) except UnhandledKey, e: self.dispatchkeyname(e.args[0]) except KeyboardInterrupt: statusmsg = "KeyboardInterrupt" error = True except CloseEditor, e: if (str(e) == 'force' or self.mainwindow.buffer.version == self.last_saved_version): break statusmsg = "changes not saved (^X again to quit anyway)" error = True except (EditError, IOError, OSError), e: curses.beep() statusmsg = str(e) error = True except StatusbarMessage, e: statusmsg = str(e) except ResizeEvent: self.setup_layout() def dispatchkeyname(self, keyname): try: meth = getattr(self, keyname) except AttributeError: raise EditError("unknown key (%s)" % keyname) else: meth() def KEY_CTRL_R(self): filename = self.interactive_input('Read file:') if not filename: raise EditError("no file name") f = open(filename, 'r') text = f.read() f.close() buf = self.mainwindow.buffer was_empty = len(buf) == 0 and not buf.filename self.mainwindow.insert(text, move_cursor=False) if was_empty: buf.filename = filename self.last_saved_version = buf.version def KEY_CTRL_W(self): buf = self.mainwindow.buffer buf.filename = self.interactive_input('Write file:', buf.filename or '') if not buf.filename: raise EditError("no file name") f = open(buf.filename, 'w') f.write(buf[:]) f.close() self.last_saved_version = buf.version raise StatusbarMessage("%r saved." % (buf.filename,)) # ____________________________________________________________ def interactive_input(self, prompt, default=''): scr = self.screen def localkeyreader(): stdscr_x, stdscr_y = self.screensize scr.move(stdscr_y-1, 0) return self.nextkey() w, h = self.screensize prompt = (' %s ' % (prompt,))[:w//2] n = len(prompt)+1 scr.move(h-1, 0) scr.addstr(prompt, self.statuslineattr()) scr.clrtoeol() buf = Buffer(default) subedit = MiniEditWindow(buf, geometry=(n, h-1, w-n-1, 1)) subedit.cursor.color = self.mycursornb subedit.cursor.pos = len(buf) try: while True: try: subedit.run(self.screen, localkeyreader) except (EditError, UnhandledKey): curses.beep() except CloseEditor, e: if str(e) == 'done': return buf[:] else: raise EditError("cancelled") class MiniEditWindow(EditWindow): show_missing_lines = False def KEY_CTRL_J(self): # enter raise CloseEditor('done') def initialize(): if len(sys.argv) == 1: buf = Buffer() elif len(sys.argv) == 2: filename = sys.argv[1] buf = Buffer(open(filename).read(), filename=filename) elif len(sys.argv) == 3: import py, socket import execnetbuffer, execnetconference port = int(sys.argv[2]) if sys.argv[1] in ('localhost', '127.0.0.1', socket.gethostname()): # no need to go through an ssh connexion for the local host gateway = py.execnet.PopenGateway() else: # remote ssh connexion gateway = py.execnet.SshGateway(sys.argv[1]) gateway = execnetconference.conference(gateway, port) buf = execnetbuffer.ExecnetBuffer(gateway) else: raise ValueError, "too many command-line arguments" return buf def main(stdscr, buf): setup_screen(stdscr) ShpEd(stdscr, buf).run() if __name__=='__main__': buf = initialize() try: # Initialize curses stdscr=curses.initscr() # Turn off echoing of keys, and enter cbreak mode, # where no buffering is performed on keyboard input curses.noecho() ; curses.cbreak() # In keypad mode, escape sequences for special keys # (like the cursor keys) will be interpreted and # a special value like curses.KEY_LEFT will be returned stdscr.keypad(1) main(stdscr, buf) # Enter the main loop # Set everything back to normal finally: # In the event of an error, restore the terminal # to a sane state. stdscr.keypad(0) curses.echo() ; curses.nocbreak() curses.endwin() # Terminate curses print