comparison mercurial/crecord.py @ 38047:dabc2237963c

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
author Kyle Lippincott <spectral@google.com>
date Thu, 17 May 2018 23:11:24 -0700
parents f0b6fbea00cf
children 1978abdb216c
comparison
equal deleted inserted replaced
38046:ee7b6fa52d9d 38047:dabc2237963c
62 curses.error 62 curses.error
63 except ImportError: 63 except ImportError:
64 # wcurses is not shipped on Windows by default, or python is not 64 # wcurses is not shipped on Windows by default, or python is not
65 # compiled with curses 65 # compiled with curses
66 curses = False 66 curses = False
67
68 class fallbackerror(error.Abort):
69 """Error that indicates the client should try to fallback to text mode."""
70 # Inherits from error.Abort so that existing behavior is preserved if the
71 # calling code does not know how to fallback.
67 72
68 def checkcurses(ui): 73 def checkcurses(ui):
69 """Return True if the user wants to use curses 74 """Return True if the user wants to use curses
70 75
71 This method returns True if curses is found (and that python is built with 76 This method returns True if curses is found (and that python is built with
527 origsigtstp = sentinel = object() 532 origsigtstp = sentinel = object()
528 if util.safehasattr(signal, 'SIGTSTP'): 533 if util.safehasattr(signal, 'SIGTSTP'):
529 origsigtstp = signal.getsignal(signal.SIGTSTP) 534 origsigtstp = signal.getsignal(signal.SIGTSTP)
530 try: 535 try:
531 curses.wrapper(chunkselector.main) 536 curses.wrapper(chunkselector.main)
532 if chunkselector.initerr is not None: 537 if chunkselector.initexc is not None:
533 raise error.Abort(chunkselector.initerr) 538 raise chunkselector.initexc
534 # ncurses does not restore signal handler for SIGTSTP 539 # ncurses does not restore signal handler for SIGTSTP
535 finally: 540 finally:
536 if origsigtstp is not sentinel: 541 if origsigtstp is not sentinel:
537 signal.signal(signal.SIGTSTP, origsigtstp) 542 signal.signal(signal.SIGTSTP, origsigtstp)
538 return chunkselector.opts 543 return chunkselector.opts
1716 1721
1717 def _main(self, stdscr): 1722 def _main(self, stdscr):
1718 self.stdscr = stdscr 1723 self.stdscr = stdscr
1719 # error during initialization, cannot be printed in the curses 1724 # error during initialization, cannot be printed in the curses
1720 # interface, it should be printed by the calling code 1725 # interface, it should be printed by the calling code
1721 self.initerr = None 1726 self.initexc = None
1722 self.yscreensize, self.xscreensize = self.stdscr.getmaxyx() 1727 self.yscreensize, self.xscreensize = self.stdscr.getmaxyx()
1723 1728
1724 curses.start_color() 1729 curses.start_color()
1725 try: 1730 try:
1726 curses.use_default_colors() 1731 curses.use_default_colors()
1749 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1 1754 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1750 1755
1751 try: 1756 try:
1752 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize) 1757 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1753 except curses.error: 1758 except curses.error:
1754 self.initerr = _('this diff is too large to be displayed') 1759 self.initexc = fallbackerror(
1760 _('this diff is too large to be displayed'))
1755 return 1761 return
1756 # initialize selecteditemendline (initial start-line is 0) 1762 # initialize selecteditemendline (initial start-line is 0)
1757 self.selecteditemendline = self.getnumlinesdisplayed( 1763 self.selecteditemendline = self.getnumlinesdisplayed(
1758 self.currentselecteditem, recursechildren=False) 1764 self.currentselecteditem, recursechildren=False)
1759 1765