# HG changeset patch # User Kyle Lippincott # Date 1526623884 25200 # Node ID dabc2237963c212775f5653353868a466a2d697b # Parent ee7b6fa52d9d7317484969a9ec31384ef456984a 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 diff -r ee7b6fa52d9d -r dabc2237963c mercurial/cmdutil.py --- 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 diff -r ee7b6fa52d9d -r dabc2237963c mercurial/crecord.py --- 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(