# HG changeset patch # User Gregory Szorc # Date 1448230248 28800 # Node ID 6ef17697b03d8180eff95f53edb575b1a54d704a # Parent 61fbf5dc12b23e7a2a30cf04ebd9f096c42a1f61 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. diff -r 61fbf5dc12b2 -r 6ef17697b03d mercurial/ui.py --- a/mercurial/ui.py Tue Nov 24 21:41:12 2015 +0000 +++ b/mercurial/ui.py Sun Nov 22 14:10:48 2015 -0800 @@ -94,9 +94,12 @@ def __init__(self, src=None): # _buffers: used for temporary capture of output self._buffers = [] - # _bufferstates: - # should the temporary capture include stderr and subprocess output + # 3-tuple describing how each buffer in the stack behaves. + # Values are (capture stderr, capture subprocesses, apply labels). self._bufferstates = [] + # When a buffer is active, defines whether we are expanding labels. + # This exists to prevent an extra list lookup. + self._bufferapplylabels = None self.quiet = self.verbose = self.debugflag = self.tracebackflag = False self._reportuntrusted = True self._ocfg = config.config() # overlay @@ -572,15 +575,24 @@ def paths(self): return paths(self) - def pushbuffer(self, error=False, subproc=False): + def pushbuffer(self, error=False, subproc=False, labeled=False): """install a buffer to capture standard output of the ui object If error is True, the error output will be captured too. If subproc is True, output from subprocesses (typically hooks) will be - captured too.""" + captured too. + + If labeled is True, any labels associated with buffered + output will be handled. By default, this has no effect + on the output returned, but extensions and GUI tools may + handle this argument and returned styled output. If output + is being buffered so it can be captured and parsed or + processed, labeled should not be set to True. + """ self._buffers.append([]) - self._bufferstates.append((error, subproc)) + self._bufferstates.append((error, subproc, labeled)) + self._bufferapplylabels = labeled def popbuffer(self, labeled=False): '''pop the last buffer and return the buffered output @@ -593,6 +605,11 @@ processed, labeled should not be set to True. ''' self._bufferstates.pop() + if self._bufferstates: + self._bufferapplylabels = self._bufferstates[-1][2] + else: + self._bufferapplylabels = None + return "".join(self._buffers.pop()) def write(self, *args, **opts):