comparison mercurial/ui.py @ 27106:6ef17697b03d

ui: track label expansion when creating buffers As part of profiling `hg log` performance, I noticed a lot of time is spent in buffered writes to ui instances. This patch starts a series that refactors buffered writes with the eventual intent to improve performance. Currently, labels are expanded when buffers are popped. This means we have to preserve the original text and the label until we render the final output. This is avoidable overhead and adds complexity since we're retaining state. This patch adds functionality to ui.pushbuffer() to declare whether label expansion should be active for the buffer. Labels are still evaluated during buffer pop. This will change in a subsequent patch. Since we'll need to access the "expand labels" flag on future write() operations, we prematurely optimize how the current value is stored to optimize for rapid retrieval.
author Gregory Szorc <gregory.szorc@gmail.com>
date Sun, 22 Nov 2015 14:10:48 -0800
parents 9eeca021a803
children a93d53f79e6e
comparison
equal deleted inserted replaced
27101:61fbf5dc12b2 27106:6ef17697b03d
92 92
93 class ui(object): 93 class ui(object):
94 def __init__(self, src=None): 94 def __init__(self, src=None):
95 # _buffers: used for temporary capture of output 95 # _buffers: used for temporary capture of output
96 self._buffers = [] 96 self._buffers = []
97 # _bufferstates: 97 # 3-tuple describing how each buffer in the stack behaves.
98 # should the temporary capture include stderr and subprocess output 98 # Values are (capture stderr, capture subprocesses, apply labels).
99 self._bufferstates = [] 99 self._bufferstates = []
100 # When a buffer is active, defines whether we are expanding labels.
101 # This exists to prevent an extra list lookup.
102 self._bufferapplylabels = None
100 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False 103 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
101 self._reportuntrusted = True 104 self._reportuntrusted = True
102 self._ocfg = config.config() # overlay 105 self._ocfg = config.config() # overlay
103 self._tcfg = config.config() # trusted 106 self._tcfg = config.config() # trusted
104 self._ucfg = config.config() # untrusted 107 self._ucfg = config.config() # untrusted
570 573
571 @util.propertycache 574 @util.propertycache
572 def paths(self): 575 def paths(self):
573 return paths(self) 576 return paths(self)
574 577
575 def pushbuffer(self, error=False, subproc=False): 578 def pushbuffer(self, error=False, subproc=False, labeled=False):
576 """install a buffer to capture standard output of the ui object 579 """install a buffer to capture standard output of the ui object
577 580
578 If error is True, the error output will be captured too. 581 If error is True, the error output will be captured too.
579 582
580 If subproc is True, output from subprocesses (typically hooks) will be 583 If subproc is True, output from subprocesses (typically hooks) will be
581 captured too.""" 584 captured too.
582 self._buffers.append([])
583 self._bufferstates.append((error, subproc))
584
585 def popbuffer(self, labeled=False):
586 '''pop the last buffer and return the buffered output
587 585
588 If labeled is True, any labels associated with buffered 586 If labeled is True, any labels associated with buffered
589 output will be handled. By default, this has no effect 587 output will be handled. By default, this has no effect
590 on the output returned, but extensions and GUI tools may 588 on the output returned, but extensions and GUI tools may
591 handle this argument and returned styled output. If output 589 handle this argument and returned styled output. If output
592 is being buffered so it can be captured and parsed or 590 is being buffered so it can be captured and parsed or
593 processed, labeled should not be set to True. 591 processed, labeled should not be set to True.
592 """
593 self._buffers.append([])
594 self._bufferstates.append((error, subproc, labeled))
595 self._bufferapplylabels = labeled
596
597 def popbuffer(self, labeled=False):
598 '''pop the last buffer and return the buffered output
599
600 If labeled is True, any labels associated with buffered
601 output will be handled. By default, this has no effect
602 on the output returned, but extensions and GUI tools may
603 handle this argument and returned styled output. If output
604 is being buffered so it can be captured and parsed or
605 processed, labeled should not be set to True.
594 ''' 606 '''
595 self._bufferstates.pop() 607 self._bufferstates.pop()
608 if self._bufferstates:
609 self._bufferapplylabels = self._bufferstates[-1][2]
610 else:
611 self._bufferapplylabels = None
612
596 return "".join(self._buffers.pop()) 613 return "".join(self._buffers.pop())
597 614
598 def write(self, *args, **opts): 615 def write(self, *args, **opts):
599 '''write args to output 616 '''write args to output
600 617