| // 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 |