colify handles ansi color input directly; no more decorator.

This commit is contained in:
Todd Gamblin 2014-12-02 21:56:22 -08:00
parent 40b4fa5443
commit 11cffff943
5 changed files with 55 additions and 47 deletions

View file

@ -145,18 +145,16 @@ def get_yes_or_no(prompt, **kwargs):
def hline(label=None, **kwargs): def hline(label=None, **kwargs):
"""Draw an optionally colored or labeled horizontal line. """Draw a labeled horizontal line.
Options: Options:
char Char to draw the line with. Default '-' char Char to draw the line with. Default '-'
color Color of the label. Default is no color.
max_width Maximum width of the line. Default is 64 chars. max_width Maximum width of the line. Default is 64 chars.
See tty.color for possible color formats.
""" """
char = kwargs.get('char', '-') char = kwargs.pop('char', '-')
color = kwargs.get('color', '') max_width = kwargs.pop('max_width', 64)
max_width = kwargs.get('max_width', 64) if kwargs:
raise TypeError("'%s' is an invalid keyword argument for this function."
% next(kwargs.iterkeys()))
rows, cols = terminal_size() rows, cols = terminal_size()
if not cols: if not cols:
@ -166,15 +164,12 @@ def hline(label=None, **kwargs):
cols = min(max_width, cols) cols = min(max_width, cols)
label = str(label) label = str(label)
prefix = char * 2 + " " + label + " " prefix = char * 2 + " "
suffix = (cols - len(prefix)) * char suffix = " " + (cols - len(prefix) - clen(label)) * char
out = StringIO() out = StringIO()
if color:
prefix = char * 2 + " " + color + cescape(label) + "@. "
cwrite(prefix, stream=out, color=True)
else:
out.write(prefix) out.write(prefix)
out.write(label)
out.write(suffix) out.write(suffix)
print out.getvalue() print out.getvalue()

View file

@ -33,6 +33,7 @@
from StringIO import StringIO from StringIO import StringIO
from llnl.util.tty import terminal_size from llnl.util.tty import terminal_size
from llnl.util.tty.color import clen
class ColumnConfig: class ColumnConfig:
@ -40,7 +41,8 @@ def __init__(self, cols):
self.cols = cols self.cols = cols
self.line_length = 0 self.line_length = 0
self.valid = True self.valid = True
self.widths = [0] * cols self.widths = [0] * cols # does not include ansi colors
self.cwidths = [0] * cols # includes ansi colors
def __repr__(self): def __repr__(self):
attrs = [(a,getattr(self, a)) for a in dir(self) if not a.startswith("__")] attrs = [(a,getattr(self, a)) for a in dir(self) if not a.startswith("__")]
@ -62,7 +64,10 @@ def config_variable_cols(elts, console_width, padding, cols=0):
raise ValueError("cols must be non-negative.") raise ValueError("cols must be non-negative.")
# Get a bound on the most columns we could possibly have. # Get a bound on the most columns we could possibly have.
lengths = [len(elt) for elt in elts] # 'clen' ignores length of ansi color sequences.
lengths = [clen(e) for e in elts]
clengths = [len(e) for e in elts]
max_cols = max(1, console_width / (min(lengths) + padding)) max_cols = max(1, console_width / (min(lengths) + padding))
max_cols = min(len(elts), max_cols) max_cols = min(len(elts), max_cols)
@ -71,17 +76,16 @@ def config_variable_cols(elts, console_width, padding, cols=0):
# Determine the most columns possible for the console width. # Determine the most columns possible for the console width.
configs = [ColumnConfig(c) for c in col_range] configs = [ColumnConfig(c) for c in col_range]
for elt, length in enumerate(lengths): for i, length in enumerate(lengths):
for conf in configs: for conf in configs:
if conf.valid: if conf.valid:
col = elt / ((len(elts) + conf.cols - 1) / conf.cols) col = i / ((len(elts) + conf.cols - 1) / conf.cols)
padded = length p = padding if col < (conf.cols - 1) else 0
if col < (conf.cols - 1):
padded += padding
if conf.widths[col] < padded: if conf.widths[col] < (length + p):
conf.line_length += padded - conf.widths[col] conf.line_length += length + p - conf.widths[col]
conf.widths[col] = padded conf.widths[col] = length + p
conf.cwidths[col] = clengths[i] + p
conf.valid = (conf.line_length < console_width) conf.valid = (conf.line_length < console_width)
try: try:
@ -105,12 +109,17 @@ def config_uniform_cols(elts, console_width, padding, cols=0):
if cols < 0: if cols < 0:
raise ValueError("cols must be non-negative.") raise ValueError("cols must be non-negative.")
max_len = max(len(elt) for elt in elts) + padding # 'clen' ignores length of ansi color sequences.
max_len = max(clen(e) for e in elts) + padding
max_clen = max(len(e) for e in elts) + padding
if cols == 0: if cols == 0:
cols = max(1, console_width / max_len) cols = max(1, console_width / max_len)
cols = min(len(elts), cols) cols = min(len(elts), cols)
config = ColumnConfig(cols) config = ColumnConfig(cols)
config.widths = [max_len] * cols config.widths = [max_len] * cols
config.cwidths = [max_clen] * cols
return config return config
@ -139,9 +148,8 @@ def colify(elts, **options):
Variable-width columns are tighter, uniform columns are all the Variable-width columns are tighter, uniform columns are all the
same width and fit less data on the screen. same width and fit less data on the screen.
decorator=<func> Function to add decoration (such as color) after columns have len=<func> Function to use for calculating string length.
already been fitted. Useful for fitting based only on Useful for ignoring ansi color. Default is 'len'.
positive-width characters.
""" """
# Get keyword arguments or set defaults # Get keyword arguments or set defaults
cols = options.pop("cols", 0) cols = options.pop("cols", 0)
@ -151,7 +159,6 @@ def colify(elts, **options):
tty = options.pop('tty', None) tty = options.pop('tty', None)
method = options.pop("method", "variable") method = options.pop("method", "variable")
console_cols = options.pop("width", None) console_cols = options.pop("width", None)
decorator = options.pop("decorator", lambda x:x)
if options: if options:
raise TypeError("'%s' is an invalid keyword argument for this function." raise TypeError("'%s' is an invalid keyword argument for this function."
@ -162,13 +169,10 @@ def colify(elts, **options):
if not elts: if not elts:
return (0, ()) return (0, ())
# Use only one column if not a tty.
if not tty: if not tty:
if tty is False or not output.isatty(): if tty is False or not output.isatty():
for elt in elts: cols = 1
output.write("%s\n" % elt)
maxlen = max(len(str(s)) for s in elts)
return (1, (maxlen,))
# Specify the number of character columns to use. # Specify the number of character columns to use.
if not console_cols: if not console_cols:
@ -186,7 +190,7 @@ def colify(elts, **options):
raise ValueError("method must be one of: " + allowed_methods) raise ValueError("method must be one of: " + allowed_methods)
cols = config.cols cols = config.cols
formats = ["%%-%ds" % width for width in config.widths[:-1]] formats = ["%%-%ds" % width for width in config.cwidths[:-1]]
formats.append("%s") # last column has no trailing space formats.append("%s") # last column has no trailing space
rows = (len(elts) + cols - 1) / cols rows = (len(elts) + cols - 1) / cols
@ -196,7 +200,7 @@ def colify(elts, **options):
output.write(" " * indent) output.write(" " * indent)
for col in xrange(cols): for col in xrange(cols):
elt = col * rows + row elt = col * rows + row
output.write(decorator(formats[col] % elts[elt])) output.write(formats[col] % elts[elt])
output.write("\n") output.write("\n")
row += 1 row += 1

View file

@ -149,6 +149,11 @@ def colorize(string, **kwargs):
return re.sub(color_re, match_to_ansi(color), string) return re.sub(color_re, match_to_ansi(color), string)
def clen(string):
"""Return the length of a string, excluding ansi color sequences."""
return len(re.sub(r'\033[^m]*m', '', string))
def cwrite(string, stream=sys.stdout, color=None): def cwrite(string, stream=sys.stdout, color=None):
"""Replace all color expressions in string with ANSI control """Replace all color expressions in string with ANSI control
codes and write the result to the stream. If color is codes and write the result to the stream. If color is

View file

@ -25,6 +25,7 @@
from external import argparse from external import argparse
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.tty.color import colorize
from llnl.util.tty.colify import colify from llnl.util.tty.colify import colify
from llnl.util.lang import index_by from llnl.util.lang import index_by
@ -96,9 +97,12 @@ def compiler_info(args):
def compiler_list(args): def compiler_list(args):
tty.msg("Available compilers") tty.msg("Available compilers")
index = index_by(spack.compilers.all_compilers(), 'name') index = index_by(spack.compilers.all_compilers(), 'name')
for name, compilers in index.items(): for i, (name, compilers) in enumerate(index.items()):
tty.hline(name, char='-', color=spack.spec.compiler_color) if i >= 1: print
colify(reversed(sorted(compilers)), indent=4)
cname = "%s{%s}" % (spack.spec.compiler_color, name)
tty.hline(colorize(cname), char='-')
colify(reversed(sorted(compilers)))
def compiler(parser, args): def compiler(parser, args):

View file

@ -79,13 +79,16 @@ def find(parser, args):
# Traverse the index and print out each package # Traverse the index and print out each package
for i, (architecture, compiler) in enumerate(sorted(index)): for i, (architecture, compiler) in enumerate(sorted(index)):
if i > 0: print if i > 0: print
tty.hline("%s / %s" % (compiler, architecture), char='-')
header = "%s{%s} / %s{%s}" % (
spack.spec.architecture_color, architecture,
spack.spec.compiler_color, compiler)
tty.hline(colorize(header), char='-')
specs = index[(architecture,compiler)] specs = index[(architecture,compiler)]
specs.sort() specs.sort()
abbreviated = [s.format('$_$@$+$#', color=True) for s in specs] abbreviated = [s.format('$_$@$+$#', color=True) for s in specs]
if args.paths: if args.paths:
# Print one spec per line along with prefix path # Print one spec per line along with prefix path
width = max(len(s) for s in abbreviated) width = max(len(s) for s in abbreviated)
@ -99,7 +102,4 @@ def find(parser, args):
for spec in specs: for spec in specs:
print spec.tree(indent=4, format='$_$@$+', color=True), print spec.tree(indent=4, format='$_$@$+', color=True),
else: else:
max_len = max([len(s.name) for s in specs]) colify(s.format('$-_$@$+$#', color=True) for s in specs)
max_len += 4
colify((s.format('$-_$@$+$#') for s in specs), decorator=spack.spec.colorize_spec)