| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/debug/zxdb/console/format_table.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include "src/developer/debug/zxdb/console/output_buffer.h" |
| #include "src/developer/debug/zxdb/console/string_util.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| template <typename CellType> |
| bool SpansRemainingCols(const std::vector<ColSpec>& spec, const std::vector<CellType>& row, |
| size_t column) { |
| // The last item in a row will span all remaining columns if there are more |
| // in the column spec. |
| return column == row.size() - 1 && column < spec.size() - 1; |
| } |
| |
| // Character width for both cell types. |
| int CellWidth(const std::string& str) { return static_cast<int>(UnicodeCharWidth(str)); } |
| int CellWidth(const OutputBuffer& buf) { return static_cast<int>(buf.UnicodeCharWidth()); } |
| |
| // Appends the given cell type to the OutputBuffer. The Syntax is ignored |
| // in the OutputBuffer->OutputBuffer variant because it will contain its own |
| // syntax. |
| void AppendCell(const std::string& str, Syntax syntax, OutputBuffer* out) { |
| out->Append(syntax, str); |
| } |
| void AppendCell(const OutputBuffer& buf, Syntax, OutputBuffer* out) { out->Append(buf); } |
| |
| // Appends the given string to the output, padding with spaces to the width |
| // as necessary. |
| template <typename CellType> |
| void AppendPadded(const CellType& cell, int width, Align align, Syntax syntax, bool is_last_col, |
| OutputBuffer* out) { |
| int pad = std::max(0, width - CellWidth(cell)); |
| if (pad > 0 && align == Align::kRight) |
| out->Append(std::string(pad, ' ')); |
| |
| AppendCell(cell, syntax, out); |
| |
| // Padding on the right. Don't add for the last col. |
| if (pad > 0 && !is_last_col && align == Align::kLeft) |
| out->Append(std::string(pad, ' ')); |
| |
| // Separator after columns for all but the last. |
| if (!is_last_col) |
| out->Append(std::string(1, ' ')); |
| } |
| |
| // Backend for FormatColumns variants. The requirements are that CellWidth() |
| // and AppendCell() are define for CellType. |
| template <typename CellType> |
| void FormatTableT(const std::vector<ColSpec>& spec, const std::vector<std::vector<CellType>>& rows, |
| OutputBuffer* out) { |
| std::vector<int> max; // Max width of each column. |
| |
| // Max widths of headings. |
| bool has_head = false; |
| for (const auto& col : spec) { |
| max.push_back(UnicodeCharWidth(col.head)); |
| has_head |= !col.head.empty(); |
| } |
| |
| // Max widths of contents. |
| for (const auto& row : rows) { |
| FX_DCHECK(row.size() <= max.size()) |
| << "Column spec size is too small (" << row.size() << " vs " << max.size() << ")."; |
| for (size_t i = 0; i < row.size(); i++) { |
| // Only count the ones that don't overflow (either because they span the |
| // remaining columns, or because they're beyond max_width). |
| if (!SpansRemainingCols(spec, row, i)) { |
| int cell_size = CellWidth(row[i]); |
| if (spec[i].max_width == 0 || cell_size <= spec[i].max_width) |
| max[i] = std::max(max[i], cell_size); |
| } |
| } |
| } |
| |
| // Print heading. |
| if (has_head) { |
| for (size_t i = 0; i < max.size(); i++) { |
| const ColSpec& col = spec[i]; |
| if (col.pad_left) |
| out->Append(Syntax::kNormal, std::string(col.pad_left, ' ')); |
| AppendPadded(col.head, max[i], col.align, Syntax::kHeading, i == max.size() - 1, out); |
| } |
| out->Append("\n"); |
| } |
| |
| // Print rows. |
| for (const auto& row : rows) { |
| std::string text; |
| for (size_t i = 0; i < row.size(); i++) { |
| const ColSpec& col = spec[i]; |
| if (SpansRemainingCols(spec, row, i)) { |
| AppendCell(row[i], col.syntax, out); |
| } else { |
| if (col.pad_left) |
| out->Append(Syntax::kNormal, std::string(col.pad_left, ' ')); |
| AppendPadded(row[i], max[i], col.align, col.syntax, i == max.size() - 1, out); |
| } |
| } |
| out->Append("\n"); |
| } |
| } |
| |
| } // namespace |
| |
| void FormatTable(const std::vector<ColSpec>& spec, |
| const std::vector<std::vector<std::string>>& rows, OutputBuffer* out) { |
| FormatTableT<std::string>(spec, rows, out); |
| } |
| |
| void FormatTable(const std::vector<ColSpec>& spec, |
| const std::vector<std::vector<OutputBuffer>>& rows, OutputBuffer* out) { |
| FormatTableT<OutputBuffer>(spec, rows, out); |
| } |
| |
| } // namespace zxdb |