From f691831481a28d7d902524d650cd76e28ac939f9 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 7 Apr 2017 13:40:14 +0200 Subject: libsmartcols: column width reduction refactoring The current implementation is unreadable... Let's write it again with more obvious semantic. Signed-off-by: Karel Zak --- libsmartcols/src/table.c | 17 ++++++++ libsmartcols/src/table_print.c | 95 ++++++++++++++++++++++++++++-------------- 2 files changed, 80 insertions(+), 32 deletions(-) (limited to 'libsmartcols') diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c index 3672114b2..066a1e9a7 100644 --- a/libsmartcols/src/table.c +++ b/libsmartcols/src/table.c @@ -326,6 +326,23 @@ int scols_table_move_column(struct libscols_table *tb, * relative width is used as a hint only. It's possible that column will be * narrow if the specified size is too large for column data. * + * + * If the width of all columns is greater than terminal width then library + * tries to reduce width of the individual columns. It's done in three stages: + * + * #1 reduce columns with SCOLS_FL_TRUNC flag and with relative width if the + * width is greater than width defined by @whint (@whint * terminal_width) + * + * #2 reduce all columns with SCOLS_FL_TRUNC flag + * + * #3 reduce all columns with relative width + * + * The next stage is always used if the previous stage is unsuccessful. Note + * that SCOLS_FL_WRAP is interpreted as SCOLS_FL_TRUNC when calculate column + * width (if custom wrap function is not specified), but the final text is not + * truncated, but wrapped to multi-line cell. + * + * * The column is necessary to address by sequential number. The first defined * column has the colnum = 0. For example: * diff --git a/libsmartcols/src/table_print.c b/libsmartcols/src/table_print.c index 1fe700273..6b4b727c0 100644 --- a/libsmartcols/src/table_print.c +++ b/libsmartcols/src/table_print.c @@ -1090,7 +1090,7 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf struct libscols_column *cl; struct libscols_iter itr; size_t width = 0, width_min = 0; /* output width */ - int trunc_only, rc = 0; + int stage, rc = 0; int extremes = 0; size_t colsepsz; @@ -1220,60 +1220,91 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf } } - /* bad, we have to reduce output width, this is done in two steps: - * 1) reduce columns with a relative width and with truncate flag - * 2) reduce columns with a relative width without truncate flag + /* bad, we have to reduce output width, this is done in three stages: + * + * 1) trunc relative with trunc flag if the column width is greater than + * expected column width (it means "width_hint * terminal_width"). + * + * 2) trunc all with trunc flag + * + * 3) trunc relative without trunc flag + * + * Note that SCOLS_FL_WRAP (if no custom wrap function is specified) is + * interpreted as SCOLS_FL_TRUNC. */ - trunc_only = 1; - while (width > tb->termwidth) { - size_t org = width; + for (stage = 1; width > tb->termwidth && stage <= 3; ) { + size_t org_width = width; - DBG(TAB, ul_debugobj(tb, " reduce width (current=%zu, " - "wanted=%zu, mode=%s)", - width, tb->termwidth, - trunc_only ? "trunc-only" : "all-relative")); + DBG(TAB, ul_debugobj(tb, " reduce width - #%d stage (current=%zu, wanted=%zu)", + stage, width, tb->termwidth)); scols_reset_iter(&itr, SCOLS_ITER_FORWARD); while (scols_table_next_column(tb, &itr, &cl) == 0) { - DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)", + int trunc_flag = 0; + + DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)", cl->header.data, cl->width, cl->width_treeart)); if (scols_column_is_hidden(cl)) continue; if (width <= tb->termwidth) break; - if (cl->width_hint > 1 && !scols_column_is_trunc(cl)) - continue; /* never truncate columns with absolute sizes */ + + /* never truncate if already minimal width */ + if (cl->width == cl->width_min) + continue; + + /* never truncate the tree */ if (scols_column_is_tree(cl) && width <= cl->width_treeart) - continue; /* never truncate the tree */ - if (trunc_only && !(scols_column_is_trunc(cl) || - (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl)))) continue; - if (cl->width == cl->width_min) + + /* nothing to truncate */ + if (cl->width == 0 || width == 0) continue; - DBG(TAB, ul_debugobj(tb, " trying to reduce: %s (width=%zu)", cl->header.data, cl->width)); + trunc_flag = scols_column_is_trunc(cl) + || (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl)); - /* truncate column with relative sizes */ - if (cl->width_hint < 1 && cl->width > 0 && width > 0 && - cl->width >= (size_t) (cl->width_hint * tb->termwidth)) { + switch (stage) { + /* #1 stage - trunc relative with TRUNC flag */ + case 1: + if (!trunc_flag) /* ignore: missing flag */ + break; + if (cl->width_hint >= 1) /* ignore: no relative */ + break; + if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) /* ignore: smaller than expected width */ + break; + + DBG(TAB, ul_debugobj(tb, " reducing (relative with flag)")); cl->width--; width--; - } + break; - /* truncate column with absolute size */ - if (cl->width_hint > 1 && cl->width > 0 && width > 0 && - !trunc_only) { + /* #2 stage - trunc all with TRUNC flag */ + case 2: + if (!trunc_flag) /* ignore: missing flag */ + break; + + DBG(TAB, ul_debugobj(tb, " reducing (all with flag)")); cl->width--; width--; - } - } - if (org == width) { - if (trunc_only) - trunc_only = 0; - else break; + + /* #3 stage - trunc relative without flag */ + case 3: + if (cl->width_hint >= 1) /* ignore: no relative */ + break; + + DBG(TAB, ul_debugobj(tb, " reducing (relative without flag)")); + cl->width--; + width--; + break; + } } + + /* the current stage is without effect, go to the next */ + if (org_width == width) + stage++; } /* ignore last column(s) or force last column to be truncated if -- cgit v1.2.3-55-g7522