From 10c3746a6df45dd154955a9e91e662260f36363f Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Tue, 14 Apr 2026 14:38:42 +0800 Subject: refactor: adjust code structure --- src/render/table_renderer.cc | 147 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/render/table_renderer.cc (limited to 'src/render/table_renderer.cc') diff --git a/src/render/table_renderer.cc b/src/render/table_renderer.cc new file mode 100644 index 0000000..37e6bfb --- /dev/null +++ b/src/render/table_renderer.cc @@ -0,0 +1,147 @@ +/* + * Ricochet - A minimalist CLI web browser for fast, distraction-free navigation. + * Copyright (C) 2026 Richard Qin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ricochet/render/table_renderer.hh" +#include "ricochet/render/internal.hh" +#include "ricochet/render/utils.hh" +#include + +namespace ricochet::render { + +namespace { + +std::size_t get_node_text_len( const parser::DomNode& node ) // NOLINT(misc-no-recursion) +{ + std::size_t len = get_visible_length( decode_entities( node.text_content ) ); + for ( const auto& child : node.children ) { + len += get_node_text_len( child ); + } + return len; +} + +void process_table_row_widths( const parser::DomNode& row, std::vector& widths ) +{ + std::size_t col_idx = 0; + for ( const auto& cell : row.children ) { + const std::size_t space_pos = cell.tag_name.find( ' ' ); + const std::string tag + = ( space_pos == std::string::npos ) ? cell.tag_name : cell.tag_name.substr( 0, space_pos ); + if ( tag == "td" || tag == "th" ) { + const std::size_t len = get_node_text_len( cell ) + 2; + if ( col_idx >= widths.size() ) { + widths.push_back( len ); + } else if ( len > widths[col_idx] ) { + widths[col_idx] = len; + } + col_idx++; + } + } +} + +std::vector calc_table_widths( const parser::DomNode& table_node ) +{ + std::vector widths; + for ( const auto& child : table_node.children ) { + const std::size_t space_pos = child.tag_name.find( ' ' ); + const std::string tag + = ( space_pos == std::string::npos ) ? child.tag_name : child.tag_name.substr( 0, space_pos ); + if ( tag == "tr" ) { + process_table_row_widths( child, widths ); + } else if ( tag == "tbody" || tag == "thead" ) { + for ( const auto& row : child.children ) { + if ( row.tag_name.starts_with( "tr" ) ) { + process_table_row_widths( row, widths ); + } + } + } + } + for ( auto& w : widths ) { + w = std::min( w, 30 ); + } + return widths; +} + +void render_table_row( const parser::DomNode& row, + RenderResult& result, + const FormContext& ctx, + const std::vector& widths ) +{ + std::string& output = result.text; + output += "\033[90m|\033[0m "; + std::size_t col_idx = 0; + for ( const auto& cell : row.children ) { + const std::size_t cell_space = cell.tag_name.find( ' ' ); + const std::string c_tag + = ( cell_space == std::string::npos ) ? cell.tag_name : cell.tag_name.substr( 0, cell_space ); + if ( c_tag != "td" && c_tag != "th" ) { + continue; + } + + const std::size_t text_start = output.size(); + if ( c_tag == "th" ) { + output += "\033[1;36m"; + } + + for ( const auto& cell_child : cell.children ) { + render_node( cell_child, result, ctx ); + } + + if ( c_tag == "th" ) { + output += "\033[0m"; + } + + const std::string added = output.substr( text_start ); + const std::size_t vis_len = get_visible_length( added ); + const std::size_t tw = ( col_idx < widths.size() ) ? widths[col_idx] : 10; + + if ( vis_len < tw ) { + output.append( tw - vis_len, ' ' ); + } + output += " \033[90m|\033[0m "; + col_idx++; + } + output += "\n"; +} + +} // namespace + +void render_table( const parser::DomNode& node, RenderResult& result, const FormContext& ctx ) +{ + std::string& output = result.text; + const std::vector widths = calc_table_widths( node ); + + output += "\n\033[90m+" + std::string( 78, '-' ) + "+\033[0m\n"; + + for ( const auto& child : node.children ) { + const std::size_t child_space = child.tag_name.find( ' ' ); + const std::string child_tag + = ( child_space == std::string::npos ) ? child.tag_name : child.tag_name.substr( 0, child_space ); + if ( child_tag == "tr" ) { + render_table_row( child, result, ctx, widths ); + } else if ( child_tag == "tbody" || child_tag == "thead" ) { + for ( const auto& row : child.children ) { + if ( row.tag_name.starts_with( "tr" ) ) { + render_table_row( row, result, ctx, widths ); + } + } + } + } + output += "\033[90m+" + std::string( 78, '-' ) + "+\033[0m\n"; +} + +} // namespace ricochet::render -- cgit v1.2.3