From: Arvin Schnell Date: Thu, 14 Mar 2024 13:29:21 +0000 (+0100) Subject: - trim trailing spaces in number columns if possible X-Git-Tag: v0.11.0~14^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F886%2Fhead;p=thirdparty%2Fsnapper.git - trim trailing spaces in number columns if possible --- diff --git a/client/cmd-list.cc b/client/cmd-list.cc index 96e6e51a..5f503074 100644 --- a/client/cmd-list.cc +++ b/client/cmd-list.cc @@ -615,8 +615,12 @@ namespace snapper formatter.header().push_back(header_for(list_mode, column)); + formatter.trim().push_back(Id::NUMBER); + formatter.trim().push_back(Id::PRE_NUMBER); + formatter.trim().push_back(Id::POST_NUMBER); + if (global_options.abbreviate()) - formatter.abbrev().push_back(Id::DESCRIPTION); + formatter.abbreviate().push_back(Id::DESCRIPTION); } for (const ProxySnapshot& snapshot : output_helper.snapshots) diff --git a/client/utils/Table.cc b/client/utils/Table.cc index c83aef55..7eb8e56e 100644 --- a/client/utils/Table.cc +++ b/client/utils/Table.cc @@ -39,14 +39,18 @@ namespace snapper OutputInfo(const Table& table); void calculate_hidden(const Table& table, const Table::Row& row); - void calculate_widths(const Table& table, const Table::Row& row, unsigned indent); + void calculate_trims(const Table& table, const Table::Row& row); + void calculate_widths(const Table& table, const Table::Row& row, bool is_header, unsigned indent); size_t calculate_total_width(const Table& table) const; void calculate_abbriviated_widths(const Table& table); + string trimmed(const string& s, Align align, size_t trim) const; + struct ColumnVars { bool hidden = false; size_t width = 0; + size_t trim = -1; }; vector column_vars; @@ -71,16 +75,21 @@ namespace snapper for (const Table::Row& row : table.rows) calculate_hidden(table, row); + // calculate trims + + for (const Table::Row& row : table.rows) + calculate_trims(table, row); + // calculate widths for (size_t idx = 0; idx < table.column_params.size(); ++idx) column_vars[idx].width = table.column_params[idx].min_width; if (table.show_header) - calculate_widths(table, table.header, 0); + calculate_widths(table, table.header, true, 0); for (const Table::Row& row : table.rows) - calculate_widths(table, row, 0); + calculate_widths(table, row, false, 0); calculate_abbriviated_widths(table); } @@ -106,7 +115,67 @@ namespace snapper void - Table::OutputInfo::calculate_widths(const Table& table, const Table::Row& row, unsigned indent) + Table::OutputInfo::calculate_trims(const Table& table, const Table::Row& row) + { + const vector& columns = row.get_columns(); + + for (size_t idx = 0; idx < columns.size(); ++idx) + { + if (!table.column_params[idx].trim || column_vars[idx].hidden) + continue; + + if (column_vars[idx].trim == 0) + continue; + + const string& column = columns[idx]; + if (column.empty()) + continue; + + size_t trim = 0; + + switch (table.column_params[idx].align) + { + case Align::RIGHT: + trim = column.size() - column.find_last_not_of(" ") - 1; + break; + + case Align::LEFT: + trim = column.find_first_not_of(" "); + break; + } + + column_vars[idx].trim = min(column_vars[idx].trim, trim); + } + + for (const Table::Row& subrow : row.get_subrows()) + calculate_trims(table, subrow); + } + + + string + Table::OutputInfo::trimmed(const string& s, Align align, size_t trim) const + { + string ret = s; + + switch (align) + { + case Align::RIGHT: + if (ret.size() >= trim) + ret.erase(ret.size() - trim, trim); + break; + + case Align::LEFT: + if (ret.size() >= trim) + ret.erase(0, trim); + break; + } + + return ret; + } + + + void + Table::OutputInfo::calculate_widths(const Table& table, const Table::Row& row, bool is_header, unsigned indent) { const vector& columns = row.get_columns(); @@ -115,7 +184,12 @@ namespace snapper if (column_vars[idx].hidden) continue; - size_t width = mbs_width(columns[idx]); + string column = columns[idx]; + + if (!is_header && table.column_params[idx].trim) + column = trimmed(column, table.column_params[idx].align, column_vars[idx].trim); + + size_t width = mbs_width(column); if (idx == table.tree_idx) width += 2 * indent; @@ -124,7 +198,7 @@ namespace snapper } for (const Table::Row& subrow : row.get_subrows()) - calculate_widths(table, subrow, indent + 1); + calculate_widths(table, subrow, false, indent + 1); } @@ -180,7 +254,8 @@ namespace snapper void - Table::output(std::ostream& s, const OutputInfo& output_info, const Table::Row& row, const vector& lasts) const + Table::output(std::ostream& s, const OutputInfo& output_info, const Table::Row& row, bool is_header, + const vector& lasts) const { s << string(global_indent, ' '); @@ -193,6 +268,9 @@ namespace snapper string column = idx < columns.size() ? columns[idx] : ""; + if (!is_header && column_params[idx].trim) + column = output_info.trimmed(column, column_params[idx].align, output_info.column_vars[idx].trim); + bool first = idx == 0; bool last = idx == output_info.column_vars.size() - 1; @@ -254,7 +332,7 @@ namespace snapper { vector sub_lasts = lasts; sub_lasts.push_back(i == subrows.size() - 1); - output(s, output_info, subrows[i], sub_lasts); + output(s, output_info, subrows[i], false, sub_lasts); } } @@ -379,6 +457,14 @@ namespace snapper } + void + Table::set_trim(Id id, bool trim) + { + size_t idx = id_to_idx(id); + column_params[idx].trim = trim; + } + + void Table::set_tree_id(Id id) { @@ -396,13 +482,13 @@ namespace snapper // output header and rows if (table.show_header) - table.output(s, output_info, table.header, {}); + table.output(s, output_info, table.header, true, {}); if (table.show_header && table.show_grid) table.output(s, output_info); for (const Table::Row& row : table.rows) - table.output(s, output_info, row, {}); + table.output(s, output_info, row, false, {}); return s; } diff --git a/client/utils/Table.h b/client/utils/Table.h index 2570955f..ac7ad197 100644 --- a/client/utils/Table.h +++ b/client/utils/Table.h @@ -148,9 +148,17 @@ namespace snapper void set_style(Style style) { Table::style = style; } void set_global_indent(size_t global_indent) { Table::global_indent = global_indent; } void set_screen_width(size_t screen_width) { Table::screen_width = screen_width; } + void set_min_width(Id id, size_t min_width); void set_visibility(Id id, Visibility visibility); void set_abbreviate(Id id, bool abbreviate); + + /** + * Allow to trim a column without breaking alignment by removing the same amount of + * spaces from all rols (excluding the header). + */ + void set_trim(Id id, bool trim); + void set_tree_id(Id id); friend std::ostream& operator<<(std::ostream& s, const Table& Table); @@ -178,6 +186,7 @@ namespace snapper size_t min_width = 0; Visibility visibility = Visibility::ON; bool abbreviate = false; + bool trim = false; }; vector column_params; @@ -186,7 +195,7 @@ namespace snapper struct OutputInfo; - void output(std::ostream& s, const OutputInfo& output_info, const Table::Row& row, + void output(std::ostream& s, const OutputInfo& output_info, const Table::Row& row, bool is_header, const vector& lasts) const; void output(std::ostream& s, const OutputInfo& output_info) const; diff --git a/client/utils/TableFormatter.cc b/client/utils/TableFormatter.cc index 435d28f1..d7c69ea7 100644 --- a/client/utils/TableFormatter.cc +++ b/client/utils/TableFormatter.cc @@ -36,10 +36,14 @@ namespace snapper table.set_style(table_formatter.style); - for (Id id : table_formatter._abbrev) + for (Id id : table_formatter._abbreviate) if (table.has_id(id)) table.set_abbreviate(id, true); + for (Id id : table_formatter._trim) + if (table.has_id(id)) + table.set_trim(id, true); + for (const vector& row : table_formatter._rows) { Table::Row table_row(table); diff --git a/client/utils/TableFormatter.h b/client/utils/TableFormatter.h index cd02d9b0..3cbada30 100644 --- a/client/utils/TableFormatter.h +++ b/client/utils/TableFormatter.h @@ -50,7 +50,8 @@ namespace snapper TableFormatter& operator=(const TableFormatter&) = delete; vector& header() { return _header; } - vector& abbrev() { return _abbrev; } + vector& abbreviate() { return _abbreviate; } + vector& trim() { return _trim; } vector>& rows() { return _rows; } friend ostream& operator<<(ostream& stream, const TableFormatter& table_formatter); @@ -60,7 +61,8 @@ namespace snapper const Style style; vector _header; - vector _abbrev; + vector _abbreviate; + vector _trim; vector> _rows; }; diff --git a/testsuite/table.cc b/testsuite/table.cc index 3dd548f1..0efb6706 100644 --- a/testsuite/table.cc +++ b/testsuite/table.cc @@ -128,3 +128,80 @@ BOOST_AUTO_TEST_CASE(test4) check(table, output); } + + +BOOST_AUTO_TEST_CASE(test5) +{ + Table table({ Cell("#", Id::NUMBER, Align::RIGHT), Cell("Description", Id::DESCRIPTION) }); + + table.set_style(Style::LIGHT); + table.set_trim(Id::NUMBER, true); + + Table::Row row1(table, { "1 ", "boot" }); + table.add(row1); + + Table::Row row2(table, { "2 " }); + table.add(row2); + + Table::Row row3(table, { "3 " }); + table.add(row3); + + vector output = { + "# │ Description", + "──┼────────────", + "1 │ boot", + "2 │", + "3 │" + }; + + check(table, output); +} + + +BOOST_AUTO_TEST_CASE(test6) +{ + Table table({ Cell("#", Id::NUMBER, Align::RIGHT), Cell("Description", Id::DESCRIPTION) }); + + table.set_style(Style::LIGHT); + table.set_trim(Id::NUMBER, true); + + Table::Row row1(table, { "1 ", "boot" }); + table.add(row1); + + Table::Row row2(table, { "2+" }); + table.add(row2); + + Table::Row row3(table, { "3 " }); + table.add(row3); + + vector output = { + " # │ Description", + "───┼────────────", + "1 │ boot", + "2+ │", + "3 │" + }; + + check(table, output); +} + + +BOOST_AUTO_TEST_CASE(test7) +{ + Table table({ Cell("#", Id::NUMBER, Align::RIGHT), Cell("Description", Id::DESCRIPTION) }); + + table.set_style(Style::LIGHT); + table.set_trim(Id::NUMBER, true); + table.set_trim(Id::DESCRIPTION, true); + + Table::Row row1(table, { "" }); + table.add(row1); + + vector output = { + "# │ Description", + "──┼────────────", + " │", + }; + + check(table, output); +}