dropshell/source/src/utils/tableprint.cpp
Your Name b278e81533
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
info for table print
2025-05-18 14:03:12 +12:00

302 lines
10 KiB
C++

#include "tableprint.hpp"
#include <iomanip>
#include <algorithm>
#include <locale>
#include <cwchar>
#include <string>
#include <sstream>
#include <codecvt>
#include <iostream>
#include <map>
#include "output.hpp"
enum kTextColors {
kTextColor_Default,
kTextColor_Red,
kTextColor_Green,
kTextColor_Yellow,
kTextColor_Blue,
kTextColor_Magenta,
kTextColor_Cyan,
kTextColor_White,
kTextColor_DarkGrey,
kTextColor_DarkYellow,
kTextColor_LightGrey
};
struct coloredText {
std::string text;
kTextColors color;
};
const std::map<std::string, coloredText> kReplacements = {
{":tick:", {"+", kTextColor_Green}},
{":cross:", {"x", kTextColor_Red}},
{":warning:", {"!", kTextColor_Yellow}},
{":dropshell::info:", {"i", kTextColor_Blue}},
{":check:", {"+", kTextColor_Green}},
{":x:", {"x", kTextColor_Red}},
{":error:", {"!", kTextColor_Red}},
{":question:", {"?", kTextColor_DarkGrey}},
{":greytick:", {"+", kTextColor_LightGrey}},
{":greycross:", {"x", kTextColor_LightGrey}}
};
// Helper function to get ANSI color code
std::string get_color_code(kTextColors color) {
switch (color) {
case kTextColor_Red: return "\033[1;31m";
case kTextColor_Green: return "\033[1;32m";
case kTextColor_Yellow: return "\033[1;33m";
case kTextColor_Blue: return "\033[1;34m";
case kTextColor_Magenta: return "\033[1;35m";
case kTextColor_Cyan: return "\033[1;36m";
case kTextColor_White: return "\033[1;37m";
case kTextColor_DarkGrey: return "\033[90m";
case kTextColor_DarkYellow: return "\033[38;5;142m";
case kTextColor_LightGrey: return "\033[38;5;250m";
default: return "\033[0m";
}
}
int get_codepoints(const std::string& str) {
int num_code_points = 0;
for (char byte: str) {
if((byte & 0xC0) != 0x80) {
num_code_points++;
}
}
return num_code_points;
}
int get_visible_length(const std::string& str) {
int length = get_codepoints(str);
size_t pos = 0;
while (pos < str.length()) {
for (const auto& [key, value] : kReplacements) {
if (str.compare(pos, key.length(), key) == 0) {
length = length - get_codepoints(key) + get_codepoints(value.text);
pos += key.length();
break;
}
}
pos++;
}
return length;
}
std::string process_cell(std::string str,std::string rowcolor) {
std::string result;
size_t pos = 0;
while (pos < str.length()) {
bool found_replacement = false;
for (const auto& [key, value] : kReplacements) {
if (str.compare(pos, key.length(), key) == 0) {
found_replacement = true;
result += get_color_code(value.color) + value.text + rowcolor;
pos += key.length();
break;
}
}
if (!found_replacement) {
result += str[pos];
pos++;
}
}
return result;
}
std::string width_print_centered(std::string str,int width, std::string rowcolor) {
size_t padding = (width - get_visible_length(str));
size_t lpad = padding/2;
size_t rpad = padding - lpad;
std::ostringstream oss;
oss << rowcolor << std::string(lpad, ' ') << process_cell(str, rowcolor) <<
std::string(rpad, ' ') << "\033[0m";
// dropshell::info << "str = "<<str <<std::endl;
// dropshell::info << "width = "<<width <<std::endl;
// dropshell::info << "padding = "<<padding <<std::endl;
// dropshell::info << "get_visible_length(str) = "<<get_visible_length(str) <<std::endl;
// dropshell::info << "get_codepoints(str) = "<<get_codepoints(str) <<std::endl;
// dropshell::info << "oss.str() = ["<<oss.str() <<"]"<<std::endl;
return oss.str();
}
std::string width_print_left(std::string str,int width, std::string rowcolor) {
size_t padding = (width - get_visible_length(str));
size_t lpad = (padding>1 ? 1 : 0);
size_t rpad = padding - lpad;
std::ostringstream oss;
oss << rowcolor << std::string(lpad, ' ') << process_cell(str, rowcolor)<< std::string(rpad, ' ') << "\033[0m";
return oss.str();
}
tableprint::tableprint(const std::string title, bool compact) : title(title), mCompact(compact) {
// Set locale for wide character support
std::setlocale(LC_ALL, "");
}
tableprint::~tableprint() {}
void tableprint::set_title(const std::string title) {
this->title = title;
}
void tableprint::add_row(const std::vector<std::string>& row) {
std::vector<std::string> trimmed_row;
for (const auto& cell : row) {
// Trim whitespace from start and end
auto start = cell.find_first_not_of(" \t");
auto end = cell.find_last_not_of(" \t");
if (start == std::string::npos) {
trimmed_row.push_back("");
} else {
trimmed_row.push_back(cell.substr(start, end - start + 1));
}
}
rows.push_back(trimmed_row);
}
void tableprint::print() {
if (rows.empty()) return;
// Calculate column widths based on header text
std::vector<size_t> col_widths(rows[0].size(), 0);
for (size_t i = 0; i < rows[0].size(); ++i) {
col_widths[i] = rows[0][i].length();
}
// Adjust widths for any cells with replacements
for (size_t row_idx = 1; row_idx < rows.size(); ++row_idx) {
const auto& row = rows[row_idx];
for (size_t i = 0; i < row.size(); ++i) {
int l = get_visible_length(row[i]);
if (l > col_widths[i])
col_widths[i] = l;
}
}
// Debug output
// std::cerr << "Column widths: ";
// for (size_t width : col_widths) {
// std::cerr << width << " ";
// }
// std::cerr << std::endl;
// Calculate total table width
size_t total_width = 0;
for (size_t width : col_widths) {
total_width += width + 2; // +2 for padding
}
total_width += col_widths.size() - 1; // Add space for vertical borders
// Print title if it exists
if (!title.empty()) {
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "+";
for (size_t i = 0; i < rows[0].size(); ++i) {
dropshell::info << std::string(col_widths[i] + 2, '-');
if (i < rows[0].size() - 1) dropshell::info << "-";
}
dropshell::info << "+" << std::endl;
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "|"; // White color for title
size_t title_width = 0;
for (size_t width : col_widths) {
title_width += width + 2; // +2 for padding
}
title_width += col_widths.size() - 1; // Add space for vertical borders
dropshell::info << width_print_centered(title,title_width,"\033[1;37m");
dropshell::info << "\033[90m|" << std::endl;
// Use └─┴─┘ for bottom of title box to connect with table
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "+";
for (size_t i = 0; i < rows[0].size(); ++i) {
dropshell::info << std::string(col_widths[i] + 2, '-');
if (i < rows[0].size() - 1) dropshell::info << "-";
}
dropshell::info << "+" << std::endl;
} else {
// Print top border if no title
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "+";
for (size_t i = 0; i < rows[0].size(); ++i) {
dropshell::info << std::string(col_widths[i] + 2, '-');
if (i < rows[0].size() - 1) dropshell::info << "+";
}
dropshell::info << "+" << std::endl;
}
// Print header
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "|";
for (size_t i = 0; i < rows[0].size(); ++i) {
dropshell::info << width_print_centered(rows[0][i],col_widths[i]+2,"\033[1;36m");
if (i < rows[0].size() - 1) {
dropshell::info << "\033[90m|\033[1;36m"; // Border color then back to cyan
} else {
dropshell::info << "\033[90m|"; // Just border color for last column
}
}
dropshell::info << std::endl;
// Print header separator
if (!mCompact) {
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "+";
for (size_t i = 0; i < rows[0].size(); ++i) {
for (size_t j = 0; j < col_widths[i] + 2; ++j) {
dropshell::info << "-";
}
if (i < rows[0].size() - 1) dropshell::info << "+";
}
dropshell::info << "+" << std::endl;
}
// Print rows
for (size_t row_idx = 1; row_idx < rows.size(); ++row_idx) {
const auto& row = rows[row_idx];
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "|";
for (size_t i = 0; i < row.size(); ++i) {
// Set the appropriate color for the row
std::string rowcolor = (row_idx % 2 == 1) ? "\033[38;5;142m" : "\033[38;5;250m";
dropshell::info << width_print_left(row[i],col_widths[i]+2,rowcolor);
dropshell::info << "\033[90m" << "|";
}
dropshell::info << std::endl;
// Print row separator if not the last row
if (row_idx < rows.size() - 1 && !mCompact) {
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "+";
for (size_t i = 0; i < rows[0].size(); ++i) {
for (size_t j = 0; j < col_widths[i] + 2; ++j) {
dropshell::info << "-";
}
if (i < rows[0].size() - 1) dropshell::info << "+";
}
dropshell::info << "+" << std::endl;
}
}
// Print bottom border
dropshell::info << "\033[90m"; // Dark grey color for borders
dropshell::info << "+";
for (size_t i = 0; i < rows[0].size(); ++i) {
for (size_t j = 0; j < col_widths[i] + 2; ++j) {
dropshell::info << "-";
}
if (i < rows[0].size() - 1) dropshell::info << "+";
}
dropshell::info << "+" << "\033[0m" << std::endl; // Reset color
}