Skip to content

Commit

Permalink
Moved printing layouts to UI file
Browse files Browse the repository at this point in the history
  • Loading branch information
davidswarbrick committed Mar 27, 2022
1 parent 3ccd555 commit e684e41
Show file tree
Hide file tree
Showing 2 changed files with 342 additions and 371 deletions.
225 changes: 198 additions & 27 deletions beets/ui/__init__.py
Expand Up @@ -73,14 +73,12 @@ class UserError(Exception):


def _in_encoding():
"""Get the encoding to use for *inputting* strings from the console.
"""
"""Get the encoding to use for *inputting* strings from the console."""
return _stream_encoding(sys.stdin)


def _out_encoding():
"""Get the encoding to use for *outputting* strings to the console.
"""
"""Get the encoding to use for *outputting* strings to the console."""
return _stream_encoding(sys.stdout)


Expand Down Expand Up @@ -149,8 +147,7 @@ def print_(*strings, **kwargs):


def _bool_fallback(a, b):
"""Given a boolean or None, return the original value or a fallback.
"""
"""Given a boolean or None, return the original value or a fallback."""
if a is None:
assert isinstance(b, bool)
return b
Expand Down Expand Up @@ -616,7 +613,7 @@ def colorize(color_name, text):
if color_def in LEGACY_COLORS:
color_def = LEGACY_COLORS[color_def]
else:
raise ValueError('no such color %s', color_def)
raise ValueError("no such color %s", color_def)

COLORS[name] = color_def
# In case a 3rd party plugin is still passing the actual color ('red')
Expand All @@ -631,8 +628,7 @@ def colorize(color_name, text):


def uncolorize(colored_text):
"""Remove colors from a string.
"""
"""Remove colors from a string."""
# Define a regular expression to match ANSI codes.
# See: http://stackoverflow.com/a/2187024/1382707
# Explanation of regular expression:
Expand Down Expand Up @@ -753,8 +749,7 @@ def get_path_formats(subview=None):


def get_replacements():
"""Confuse validation function that reads regex/string pairs.
"""
"""Confuse validation function that reads regex/string pairs."""
replacements = []
for pattern, repl in config["replace"].get(dict).items():
repl = repl or ""
Expand Down Expand Up @@ -813,21 +808,21 @@ def split_into_lines(string, width_tuple):
words_raw = []
for word in string.split():
word_raw = uncolorize(word)
if word_raw != u' ':
if word_raw != u" ":
words.append(word)
words_raw.append(word_raw)
result = {'col': [], 'raw': []}
next_substr_raw = u''
next_substr = u''
result = {"col": [], "raw": []}
next_substr_raw = u""
next_substr = u""

# Iterate over all words.
for i in range(len(words_raw)):
if i == 0:
pot_substr_raw = words_raw[i]
pot_substr = words[i]
else:
pot_substr_raw = ' '.join([next_substr_raw, words_raw[i]])
pot_substr = ' '.join([next_substr, words[i]])
pot_substr_raw = " ".join([next_substr_raw, words_raw[i]])
pot_substr = " ".join([next_substr, words[i]])

# Find out if the pot(ential)_substr fits into the next substring.
fits_first = (
Expand Down Expand Up @@ -860,6 +855,184 @@ def split_into_lines(string, width_tuple):
return result


def print_column_layout(indent_str, left, right, separator=u" -> ", max_width=term_width()):
"""Print left & right data, with separator inbetween
'left' and 'right' have a structure of:
{'prefix':u'','contents':u'','suffix':u'','width':0}
In a column layout the printing will be:
{indent_str}{lhs0}{separator}{rhs0}
{lhs1 / padding }{rhs1}
...
The first line of each column (i.e. {lhs0} or {rhs0}) is:
{prefix}{part of contents}{suffix}
With subsequent lines (i.e. {lhs1}, {rhs1} onwards) being the
rest of contents, wrapped if the width would be otherwise exceeded.
"""
first_line_no_wrap = (
indent_str
+ left["prefix"]
+ left["contents"]
+ left["suffix"]
+ separator
+ right["prefix"]
+ right["contents"]
+ right["suffix"]
)
if color_len(first_line_no_wrap) < max_width:
# Everything fits, print out line.
print_(first_line_no_wrap)
else:
# Wrap into columns
if "width" not in left or "width" not in right:
# If widths have not been defined, set to share space.
left["width"] = (max_width - len(indent_str) - color_len(separator)) // 2
right["width"] = (max_width - len(indent_str) - color_len(separator)) // 2
# On the first line, account for suffix as well as prefix
left_width = (
left["width"] - color_len(left["prefix"]) - color_len(left["suffix"]),
left["width"] - color_len(left["prefix"]),
left["width"] - color_len(left["prefix"]),
)

left_split = split_into_lines(left["contents"], left_width)
right_width = (
right["width"] - color_len(right["prefix"]) - color_len(right["suffix"]),
right["width"] - color_len(right["prefix"]),
right["width"] - color_len(right["prefix"]),
)

right_split = split_into_lines(right["contents"], right_width)
max_line_count = max(len(left_split["col"]), len(right_split["col"]))

out = u""
for i in range(max_line_count):
# indentation
out += indent_str

# Prefix or indent_str for line
if i == 0:
out += left["prefix"]
else:
out += indent(color_len(left["prefix"]))

# Line i of left hand side contents.
if i < len(left_split["col"]):
out += left_split["col"][i]
left_part_len = len(left_split["raw"][i])
else:
left_part_len = 0

# Padding until end of column.
# Note: differs from original
# column calcs in not -1 afterwards for space
# in track number as that is included in 'prefix'
padding = left["width"] - color_len(left["prefix"]) - left_part_len

# Remove some padding on the first line to display
# length
if i == 0:
padding -= color_len(left["suffix"])

out += indent(padding)

if i == 0:
out += left["suffix"]

# Separator between columns.
if i == 0:
out += separator
else:
out += indent(color_len(separator))

# Right prefix, contents, padding, suffix
if i == 0:
out += right["prefix"]
else:
out += indent(color_len(right["prefix"]))

# Line i of right hand side.
if i < len(right_split["col"]):
out += right_split["col"][i]
right_part_len = len(right_split["raw"][i])
else:
right_part_len = 0

# Padding until end of column
padding = right["width"] - color_len(right["prefix"]) - right_part_len
# Remove some padding on the first line to display
# length
if i == 0:
padding -= color_len(right["suffix"])
out += indent(padding)
# Length in first line
if i == 0:
out += right["suffix"]

# Linebreak, except in the last line.
if i < max_line_count - 1:
out += u"\n"

# Constructed all of the columns, now print
print_(out)


def print_newline_layout(
indent_str, left, right, separator=u" -> ", max_width=term_width()
):
"""Prints using a newline separator between left & right if
they go over their allocated widths. The datastructures are
shared with the column layout. In contrast to the column layout,
the prefix and suffix are printed at the beginning and end of
the contents. If no wrapping is required (i.e. everything fits) the
first line will look exactly the same as the column layout:
{indent}{lhs0}{separator}{rhs0}
However if this would go over the width given, the layout now becomes:
{indent}{lhs0}
{indent}{separator}{rhs0}
If {lhs0} would go over the maximum width, the subsequent lines are
indented a second time for ease of reading.
"""
first_line_no_wrap = (
indent_str
+ left["prefix"]
+ left["contents"]
+ left["suffix"]
+ separator
+ right["prefix"]
+ right["contents"]
+ right["suffix"]
)
if color_len(first_line_no_wrap) < max_width:
# Everything fits, print out line.
print_(first_line_no_wrap)
else:
# Newline separation, with wrapping
empty_space = max_width - len(indent_str)
# On lower lines we will double the indent for clarity
left_width = (empty_space, empty_space - len(indent_str), empty_space - len(indent_str))
left_str = left["prefix"] + left["contents"] + left["suffix"]
left_split = split_into_lines(left_str, left_width)
# Repeat calculations for rhs, including separator on first line
right_width = (
empty_space - color_len(separator),
empty_space - len(indent_str),
empty_space - len(indent_str),
)
right_str = right["prefix"] + right["contents"] + right["suffix"]
right_split = split_into_lines(right_str, right_width)
for i, line in enumerate(left_split["col"]):
if i == 0:
print_(indent_str + line)
elif line != u"":
# Ignore empty lines
print_(indent_str * 2 + line)
for i, line in enumerate(right_split["col"]):
if i == 0:
print_(indent_str + separator + line)
elif line != u"":
print_(indent_str * 2 + line)


FLOAT_EPSILON = 0.01


Expand Down Expand Up @@ -1010,7 +1183,7 @@ def _store_dict(option, opt_str, value, parser):
value = util.text_string(value, util.arg_encoding())

try:
key, value = value.split('=', 1)
key, value = value.split("=", 1)
if not (key and value):
raise ValueError
except ValueError:
Expand Down Expand Up @@ -1144,8 +1317,7 @@ def add_format_option(self, flags=("-f", "--format"), target=None):
self.add_option(opt)

def add_all_common_options(self):
"""Add album, path and format options.
"""
"""Add album, path and format options."""
self.add_album_option()
self.add_path_option()
self.add_format_option()
Expand Down Expand Up @@ -1221,8 +1393,7 @@ def __init__(self, *args, **kwargs):
self.subcommands = []

def add_subcommand(self, *cmds):
"""Adds a Subcommand object to the parser's list of commands.
"""
"""Adds a Subcommand object to the parser's list of commands."""
for cmd in cmds:
cmd.root_parser = self
self.subcommands.append(cmd)
Expand Down Expand Up @@ -1334,6 +1505,7 @@ def parse_subcommand(self, args):

# The main entry point and bootstrapping.


def _load_plugins(options, config):
"""Load the plugins specified on the command line or in the configuration.
"""
Expand All @@ -1346,6 +1518,7 @@ def _load_plugins(options, config):

# Extend the `beetsplug` package to include the plugin paths.
import beetsplug

beetsplug.__path__ = paths + list(beetsplug.__path__)

# For backwards compatibility, also support plugin paths that
Expand All @@ -1354,10 +1527,9 @@ def _load_plugins(options, config):

# If we were given any plugins on the command line, use those.
if options.plugins is not None:
plugin_list = (options.plugins.split(',')
if len(options.plugins) > 0 else [])
plugin_list = options.plugins.split(",") if len(options.plugins) > 0 else []
else:
plugin_list = config['plugins'].as_str_seq()
plugin_list = config["plugins"].as_str_seq()

plugins.load_plugins(plugin_list)
return plugins
Expand Down Expand Up @@ -1402,8 +1574,7 @@ def _setup(options, lib=None):


def _configure(options):
"""Amend the global configuration object with command line options.
"""
"""Amend the global configuration object with command line options."""
# Add any additional config files specified with --config. This
# special handling lets specified plugins get loaded before we
# finish parsing the command line.
Expand Down

0 comments on commit e684e41

Please sign in to comment.