crecord: fallback to text mode if diffs are too big for curses mode
crecord uses curses.newpad to create a region that we can then scroll around in
by moving the main 'screen' as a veiwport into the (probably larger than the
actual screen) pad. Internally, at least in ncurses, pads are implemented using
windows, which have their dimensions limited to a certain size. Depending on
compilation options for ncurses, this size might be pretty small: (signed)
short, or it might be larger ((signed) int).
crecord wants to have enough room to have all of the contents of the main area
of the chunkselector in the pad; this means that the full size with everything
expanded must be less than these (undocumented, afaict) limits.
It's not easy to write tests for this because the limits are platform- and
installation- dependent and undocumented / unqueryable, as far as I can tell.
Differential Revision: https://phab.mercurial-scm.org/D3577
--- a/mercurial/cmdutil.py Thu May 17 15:33:28 2018 -0700
+++ b/mercurial/cmdutil.py Thu May 17 23:11:24 2018 -0700
@@ -197,17 +197,21 @@
return oldwrite
def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
- if usecurses:
- if testfile:
- recordfn = crecordmod.testdecorator(testfile,
- crecordmod.testchunkselector)
- else:
- recordfn = crecordmod.chunkselector
-
- return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
-
- else:
- return patch.filterpatch(ui, originalhunks, operation)
+ try:
+ if usecurses:
+ if testfile:
+ recordfn = crecordmod.testdecorator(
+ testfile, crecordmod.testchunkselector)
+ else:
+ recordfn = crecordmod.chunkselector
+
+ return crecordmod.filterpatch(ui, originalhunks, recordfn,
+ operation)
+ except crecordmod.fallbackerror as e:
+ ui.warn('%s\n' % e.message)
+ ui.warn(_('falling back to text mode\n'))
+
+ return patch.filterpatch(ui, originalhunks, operation)
def recordfilter(ui, originalhunks, operation=None):
""" Prompts the user to filter the originalhunks and return a list of
--- a/mercurial/crecord.py Thu May 17 15:33:28 2018 -0700
+++ b/mercurial/crecord.py Thu May 17 23:11:24 2018 -0700
@@ -65,6 +65,11 @@
# compiled with curses
curses = False
+class fallbackerror(error.Abort):
+ """Error that indicates the client should try to fallback to text mode."""
+ # Inherits from error.Abort so that existing behavior is preserved if the
+ # calling code does not know how to fallback.
+
def checkcurses(ui):
"""Return True if the user wants to use curses
@@ -529,8 +534,8 @@
origsigtstp = signal.getsignal(signal.SIGTSTP)
try:
curses.wrapper(chunkselector.main)
- if chunkselector.initerr is not None:
- raise error.Abort(chunkselector.initerr)
+ if chunkselector.initexc is not None:
+ raise chunkselector.initexc
# ncurses does not restore signal handler for SIGTSTP
finally:
if origsigtstp is not sentinel:
@@ -1718,7 +1723,7 @@
self.stdscr = stdscr
# error during initialization, cannot be printed in the curses
# interface, it should be printed by the calling code
- self.initerr = None
+ self.initexc = None
self.yscreensize, self.xscreensize = self.stdscr.getmaxyx()
curses.start_color()
@@ -1751,7 +1756,8 @@
try:
self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
except curses.error:
- self.initerr = _('this diff is too large to be displayed')
+ self.initexc = fallbackerror(
+ _('this diff is too large to be displayed'))
return
# initialize selecteditemendline (initial start-line is 0)
self.selecteditemendline = self.getnumlinesdisplayed(