Mercurial > hg > crecord
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)