Read about our upcoming Code of Conduct on this issue

Commit e5eacbf1 authored by Gregory Szorc's avatar Gregory Szorc
Browse files

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.
parent 61fbf5dc12b2
......@@ -94,9 +94,12 @@ class ui(object):
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 @@ class ui(object):
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 @@ class ui(object):
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):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment