comparison mercurial/ui.py @ 31690:2d11d278279a

ui: defer setting pager related properties until the pager has spawned When --pager=on is given, dispatch.py spawns a pager before setting up color. If the pager failed to launch, ui.pageractive was left set to True, so color configured itself based on 'color.pagermode'. A typical MSYS setting would be 'color.mode=auto, color.pagermode=ansi'. In the failure case, this would print a warning, disable the pager, and then print the raw ANSI codes to the terminal. Care needs to be taken, because it appears that leaving ui.pageractive=True was the only thing that prevented an attempt at running the pager again from inside the command. This results in a double warning message, so pager is simply disabled on failure. The ui config settings didn't need to be moved to fix this, but it seemed like the right thing to do for consistency.
author Matt Harbison <matt_harbison@yahoo.com>
date Sat, 25 Mar 2017 21:12:00 -0400
parents c5fe0c7dad94
children c3ca0ad8ab9c
comparison
equal deleted inserted replaced
31689:57a22f699179 31690:2d11d278279a
854 if not pagercmd: 854 if not pagercmd:
855 return 855 return
856 856
857 self.debug('starting pager for command %r\n' % command) 857 self.debug('starting pager for command %r\n' % command)
858 self.flush() 858 self.flush()
859 self.pageractive = True 859
860 # Preserve the formatted-ness of the UI. This is important 860 wasformatted = self.formatted()
861 # because we mess with stdout, which might confuse
862 # auto-detection of things being formatted.
863 self.setconfig('ui', 'formatted', self.formatted(), 'pager')
864 self.setconfig('ui', 'interactive', False, 'pager')
865 if util.safehasattr(signal, "SIGPIPE"): 861 if util.safehasattr(signal, "SIGPIPE"):
866 signal.signal(signal.SIGPIPE, _catchterm) 862 signal.signal(signal.SIGPIPE, _catchterm)
867 self._runpager(pagercmd) 863 if self._runpager(pagercmd):
864 self.pageractive = True
865 # Preserve the formatted-ness of the UI. This is important
866 # because we mess with stdout, which might confuse
867 # auto-detection of things being formatted.
868 self.setconfig('ui', 'formatted', wasformatted, 'pager')
869 self.setconfig('ui', 'interactive', False, 'pager')
870 else:
871 # If the pager can't be spawned in dispatch when --pager=on is
872 # given, don't try again when the command runs, to avoid a duplicate
873 # warning about a missing pager command.
874 self.disablepager()
868 875
869 def _runpager(self, command): 876 def _runpager(self, command):
870 """Actually start the pager and set up file descriptors. 877 """Actually start the pager and set up file descriptors.
871 878
872 This is separate in part so that extensions (like chg) can 879 This is separate in part so that extensions (like chg) can
873 override how a pager is invoked. 880 override how a pager is invoked.
874 """ 881 """
875 if command == 'cat': 882 if command == 'cat':
876 # Save ourselves some work. 883 # Save ourselves some work.
877 return 884 return False
878 # If the command doesn't contain any of these characters, we 885 # If the command doesn't contain any of these characters, we
879 # assume it's a binary and exec it directly. This means for 886 # assume it's a binary and exec it directly. This means for
880 # simple pager command configurations, we can degrade 887 # simple pager command configurations, we can degrade
881 # gracefully and tell the user about their broken pager. 888 # gracefully and tell the user about their broken pager.
882 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%") 889 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
889 # determine which one to use. 896 # determine which one to use.
890 fullcmd = util.findexe(command) 897 fullcmd = util.findexe(command)
891 if not fullcmd: 898 if not fullcmd:
892 self.warn(_("missing pager command '%s', skipping pager\n") 899 self.warn(_("missing pager command '%s', skipping pager\n")
893 % command) 900 % command)
894 return 901 return False
895 902
896 command = fullcmd 903 command = fullcmd
897 904
898 try: 905 try:
899 pager = subprocess.Popen( 906 pager = subprocess.Popen(
902 stdout=util.stdout, stderr=util.stderr) 909 stdout=util.stdout, stderr=util.stderr)
903 except OSError as e: 910 except OSError as e:
904 if e.errno == errno.ENOENT and not shell: 911 if e.errno == errno.ENOENT and not shell:
905 self.warn(_("missing pager command '%s', skipping pager\n") 912 self.warn(_("missing pager command '%s', skipping pager\n")
906 % command) 913 % command)
907 return 914 return False
908 raise 915 raise
909 916
910 # back up original file descriptors 917 # back up original file descriptors
911 stdoutfd = os.dup(util.stdout.fileno()) 918 stdoutfd = os.dup(util.stdout.fileno())
912 stderrfd = os.dup(util.stderr.fileno()) 919 stderrfd = os.dup(util.stderr.fileno())
922 # restore original fds, closing pager.stdin copies in the process 929 # restore original fds, closing pager.stdin copies in the process
923 os.dup2(stdoutfd, util.stdout.fileno()) 930 os.dup2(stdoutfd, util.stdout.fileno())
924 os.dup2(stderrfd, util.stderr.fileno()) 931 os.dup2(stderrfd, util.stderr.fileno())
925 pager.stdin.close() 932 pager.stdin.close()
926 pager.wait() 933 pager.wait()
934
935 return True
927 936
928 def interface(self, feature): 937 def interface(self, feature):
929 """what interface to use for interactive console features? 938 """what interface to use for interactive console features?
930 939
931 The interface is controlled by the value of `ui.interface` but also by 940 The interface is controlled by the value of `ui.interface` but also by