changeset 78:5618ae4accb4

- uses default editor for editing commit-messages now
author Mark Edgington <edgimar@gmail.com>
date Fri, 11 Feb 2011 00:27:39 +0100
parents d4a4f739a039
children 15cf4a0a5ab6
files crecord/chunk_selector.py crecord/crecord_core.py crecord/crpatch.py crecord/textpad.py
diffstat 4 files changed, 23 insertions(+), 222 deletions(-) [+]
line wrap: on
line diff
--- a/crecord/chunk_selector.py
+++ b/crecord/chunk_selector.py
@@ -18,6 +18,7 @@
 import struct
 import termios
 import signal
+import textwrap
 
 from crpatch import Patch, header, hunk, HunkLine
 
@@ -28,9 +29,6 @@
     # I have no idea if wcurses works with crecord...
     import wcurses as curses
 
-#from curses import textpad
-import textpad # customized textpad
-
 try:
     curses
 except NameError:
@@ -54,20 +52,22 @@
     return h, w
 
 
-def chunkselector(opts, headerList):
+def chunkselector(opts, headerList, ui):
     """
     Curses interface to get selection of chunks, and mark the applied flags
     of the chosen chunks.
 
     """
 
-    chunkSelector = CursesChunkSelector(headerList)
+    chunkSelector = CursesChunkSelector(headerList, ui)
     curses.wrapper(chunkSelector.main, opts)
 
 class CursesChunkSelector(object):
-    def __init__(self, headerList):
+    def __init__(self, headerList, ui):
         # put the headers into a patch object
         self.headerList = Patch(headerList)
+        
+        self.ui = ui
 
         # list of all chunks
         self.chunkList = []
@@ -920,27 +920,22 @@
 
     def commitMessageWindow(self):
         "Create a temporary commit message editing window on the screen."
-        # In Python versions < 2.6, there is no insert mode (only overwrite) :(
-        def keyFilter(key):
-            "provide keymappings to emacs-style keys"
-            if key in (7,):
-                # diable keys we're re-mapping
-                return ""
-            elif key in (24, 3): # CTRL-X,  3 = CTRL-C
-                return 7 # CTRL-G (i.e. exit comment window)
-            else:
-                return key
-        statusline = curses.newwin(2,0,0,0)
-        statusLineText = (" Begin/resume editing commit message."
-                          " CTRL-C/-X returns to patch view.")
-        self.printString(statusline, statusLineText, pairName="legend")
-        statusline.refresh()
-        helpwin = curses.newwin(self.yScreenSize-1,0,1,0)
-        t = textpad.Textbox(helpwin)
+        if self.commentText == "":
+            self.commentText = textwrap.dedent("""
+            HG: Enter/resume commit message.  Lines beginning with 'HG:' are removed.
+            HG: You can save this message, and edit it again later before committing.
+            HG: After exiting the editor, you will return to the crecord patch view.
+            HG: --
+            HG: user: %s""" % self.ui.username())
+            
         curses.raw()
-        self.commentText = t.edit(keyFilter, self.commentText).rstrip(" \n")
+        curses.def_prog_mode()
+        curses.endwin()
+        self.commentText = self.ui.edit(self.commentText, self.ui.username())
         curses.cbreak()
-
+        self.stdscr.refresh()
+        self.stdscr.keypad(1) # allow arrow-keys to continue to function
+        
     def confirmCommit(self, review=False):
         "Ask for 'Y' to be pressed to confirm commit. Return True if confirmed."
         if review:
--- a/crecord/crecord_core.py
+++ b/crecord/crecord_core.py
@@ -56,7 +56,7 @@
         # 1. filter patch, so we have intending-to apply subset of it
         chunks = crpatch.filterpatch(opts,
                                      crpatch.parsepatch(changes, fp),
-                                     chunk_selector.chunkselector)
+                                     chunk_selector.chunkselector, ui)
         del fp
 
         contenders = set()
--- a/crecord/crpatch.py
+++ b/crecord/crpatch.py
@@ -635,7 +635,7 @@
         state = newstate
     return p.finished()
 
-def filterpatch(opts, chunks, chunk_selector):
+def filterpatch(opts, chunks, chunk_selector, ui):
     """Interactively filter patch chunks into applied-only chunks"""
     chunks = list(chunks)
     # convert chunks list into structure suitable for displaying/modifying
@@ -647,7 +647,7 @@
         return []
 
     # let user choose headers/hunks/lines, and mark their applied flags accordingly
-    chunk_selector(opts, headers)
+    chunk_selector(opts, headers, ui)
 
     appliedHunkList = []
     for hdr in headers:
deleted file mode 100644
--- a/crecord/textpad.py
+++ /dev/null
@@ -1,194 +0,0 @@
-"""Simple textbox editing widget with Emacs-like keybindings."""
-
-import curses
-import curses.ascii
-
-def rectangle(win, uly, ulx, lry, lrx):
-    """Draw a rectangle with corners at the provided upper-left
-    and lower-right coordinates.
-    """
-    win.vline(uly+1, ulx, curses.ACS_VLINE, lry - uly - 1)
-    win.hline(uly, ulx+1, curses.ACS_HLINE, lrx - ulx - 1)
-    win.hline(lry, ulx+1, curses.ACS_HLINE, lrx - ulx - 1)
-    win.vline(uly+1, lrx, curses.ACS_VLINE, lry - uly - 1)
-    win.addch(uly, ulx, curses.ACS_ULCORNER)
-    win.addch(uly, lrx, curses.ACS_URCORNER)
-    win.addch(lry, lrx, curses.ACS_LRCORNER)
-    win.addch(lry, ulx, curses.ACS_LLCORNER)
-
-class Textbox:
-    """Editing widget using the interior of a window object.
-     Supports the following Emacs-like key bindings:
-
-    Ctrl-A      Go to left edge of window.
-    Ctrl-B      Cursor left, wrapping to previous line if appropriate.
-    Ctrl-D      Delete character under cursor.
-    Ctrl-E      Go to right edge (stripspaces off) or end of line (stripspaces on).
-    Ctrl-F      Cursor right, wrapping to next line when appropriate.
-    Ctrl-G      Terminate, returning the window contents.
-    Ctrl-H      Delete character backward.
-    Ctrl-J      Terminate if the window is 1 line, otherwise insert newline.
-    Ctrl-K      If line is blank, delete it, otherwise clear to end of line.
-    Ctrl-L      Refresh screen.
-    Ctrl-N      Cursor down; move down one line.
-    Ctrl-O      Insert a blank line at cursor location.
-    Ctrl-P      Cursor up; move up one line.
-
-    Move operations do nothing if the cursor is at an edge where the movement
-    is not possible.  The following synonyms are supported where possible:
-
-    KEY_LEFT = Ctrl-B, KEY_RIGHT = Ctrl-F, KEY_UP = Ctrl-P, KEY_DOWN = Ctrl-N
-    KEY_BACKSPACE = Ctrl-h
-    """
-    def __init__(self, win, insert_mode=False):
-        self.win = win
-        self.insert_mode = insert_mode
-        (self.maxy, self.maxx) = win.getmaxyx()
-        self.maxy = self.maxy - 1
-        self.maxx = self.maxx - 1
-        self.stripspaces = 1
-        self.lastcmd = None
-        win.keypad(1)
-
-    def _end_of_line(self, y):
-        """Go to the location of the first blank on the given line,
-        returning the index of the last non-blank character."""
-        last = self.maxx
-        while True:
-            if curses.ascii.ascii(self.win.inch(y, last)) != curses.ascii.SP:
-                last = min(self.maxx, last+1)
-                break
-            elif last == 0:
-                break
-            last = last - 1
-        return last
-
-    def _insert_printable_char(self, ch):
-        (y, x) = self.win.getyx()
-        if y < self.maxy or x < self.maxx:
-            if self.insert_mode:
-                oldch = self.win.inch()
-            # The try-catch ignores the error we trigger from some curses
-            # versions by trying to write into the lowest-rightmost spot
-            # in the window.
-            try:
-                self.win.addch(ch)
-            except curses.error:
-                pass
-            if self.insert_mode:
-                (backy, backx) = self.win.getyx()
-                if curses.ascii.isprint(oldch):
-                    self._insert_printable_char(oldch)
-                    self.win.move(backy, backx)
-
-    def do_command(self, ch):
-        "Process a single editing command."
-        (y, x) = self.win.getyx()
-        self.lastcmd = ch
-        if curses.ascii.isprint(ch):
-            if y < self.maxy or x < self.maxx:
-                self._insert_printable_char(ch)
-        elif ch == curses.ascii.SOH:                           # ^a
-            self.win.move(y, 0)
-        elif ch in (curses.ascii.STX,curses.KEY_LEFT, curses.ascii.BS,
-                    curses.KEY_BACKSPACE):
-            if x > 0:
-                self.win.move(y, x-1)
-            elif y == 0:
-                pass
-            elif self.stripspaces:
-                self.win.move(y-1, self._end_of_line(y-1))
-            else:
-                self.win.move(y-1, self.maxx)
-            if ch in (curses.ascii.BS, curses.KEY_BACKSPACE):
-                self.win.delch()
-        elif ch == curses.ascii.EOT:                           # ^d
-            self.win.delch()
-        elif ch == curses.ascii.ENQ:                           # ^e
-            if self.stripspaces:
-                self.win.move(y, self._end_of_line(y))
-            else:
-                self.win.move(y, self.maxx)
-        elif ch in (curses.ascii.ACK, curses.KEY_RIGHT):       # ^f
-            if x < self.maxx:
-                self.win.move(y, x+1)
-            elif y == self.maxy:
-                pass
-            else:
-                self.win.move(y+1, 0)
-        elif ch == curses.ascii.BEL:                           # ^g
-            return 0
-        elif ch == curses.ascii.NL:                            # ^j
-            if self.maxy == 0:
-                return 0
-            elif y < self.maxy:
-                self.win.move(y+1, 0)
-        elif ch == curses.ascii.VT:                            # ^k
-            if x == 0 and self._end_of_line(y) == 0:
-                self.win.deleteln()
-            else:
-                # first undo the effect of self._end_of_line
-                self.win.move(y, x)
-                self.win.clrtoeol()
-        elif ch == curses.ascii.FF:                            # ^l
-            self.win.refresh()
-        elif ch in (curses.ascii.SO, curses.KEY_DOWN):         # ^n
-            if y < self.maxy:
-                self.win.move(y+1, x)
-                if x > self._end_of_line(y+1):
-                    self.win.move(y+1, self._end_of_line(y+1))
-        elif ch == curses.ascii.SI:                            # ^o
-            self.win.insertln()
-        elif ch in (curses.ascii.DLE, curses.KEY_UP):          # ^p
-            if y > 0:
-                self.win.move(y-1, x)
-                if x > self._end_of_line(y-1):
-                    self.win.move(y-1, self._end_of_line(y-1))
-        return 1
-
-    def gather(self):
-        "Collect and return the contents of the window."
-        result = ""
-        for y in range(self.maxy+1):
-            self.win.move(y, 0)
-            stop = self._end_of_line(y)
-            if stop == 0 and self.stripspaces:
-                continue
-            for x in range(self.maxx+1):
-                if self.stripspaces and x > stop:
-                    break
-                result = result + chr(curses.ascii.ascii(self.win.inch(y, x)))
-            if self.maxy > 0:
-                result = result + "\n"
-        return result
-
-    def edit(self, validate=None, initial_text=None):
-        "Edit in the widget window and collect the results."
-        if initial_text is None:
-            initial_text = ""
-        initial_text_list = list(initial_text)
-        while 1:
-            if initial_text_list:
-                curses.ungetch(ord(initial_text_list.pop(0)))
-            ch = self.win.getch()
-            if validate:
-                ch = validate(ch)
-            if not ch:
-                continue
-            if not self.do_command(ch):
-                break
-            self.win.refresh()
-        return self.gather()
-
-if __name__ == '__main__':
-    def test_editbox(stdscr):
-        ncols, nlines = 9, 4
-        uly, ulx = 15, 20
-        stdscr.addstr(uly-2, ulx, "Use Ctrl-G to end editing.")
-        win = curses.newwin(nlines, ncols, uly, ulx)
-        rectangle(stdscr, uly-1, ulx-1, uly + nlines, ulx + ncols)
-        stdscr.refresh()
-        return Textbox(win).edit()
-
-    str = curses.wrapper(test_editbox)
-    print 'Contents of text box:', repr(str)