// TextFlowCpp
//
// A single-header library for wrapping and laying out basic text, by Phil Nash
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This project is hosted at https://github.com/philsquared/textflowcpp
#ifndef TEXTFLOW_HPP_INCLUDED
#define TEXTFLOW_HPP_INCLUDED
#include <cassert>
#include <ostream>
#include <sstream>
#include <vector>
#ifndef TEXTFLOW_CONFIG_CONSOLE_WIDTH
#define TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
#endif
namespace TextFlow {
inline auto isWhitespace( char c ) -> bool {
static std::string chars = " \t\n\r" ;
return chars.find( c ) != std::string::npos;
}
inline auto isBreakableBefore( char c ) -> bool {
static std::string chars = "[({<|" ;
return chars.find( c ) != std::string::npos;
}
inline auto isBreakableAfter( char c ) -> bool {
static std::string chars = "])}>.,:;*+-=&/\\" ;
return chars.find( c ) != std::string::npos;
}
class Columns;
class Column {
std::vector<std::string> m_strings;
size_t m_width = TEXTFLOW_CONFIG_CONSOLE_WIDTH;
size_t m_indent = 0;
size_t m_initialIndent = std::string::npos;
public :
class iterator {
friend Column;
Column const & m_column;
size_t m_stringIndex = 0;
size_t m_pos = 0;
size_t m_len = 0;
size_t m_end = 0;
bool m_suffix = false ;
iterator( Column const & column, size_t stringIndex )
: m_column( column ),
m_stringIndex( stringIndex )
{}
auto line() const -> std::string const & { return m_column.m_strings[m_stringIndex]; }
auto isBoundary( size_t at ) const -> bool {
assert( at > 0 );
assert( at <= line().size() );
return at == line().size() ||
( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) ||
isBreakableBefore( line()[at] ) ||
isBreakableAfter( line()[at-1] );
}
void calcLength() {
assert( m_stringIndex < m_column.m_strings.size() );
m_suffix = false ;
auto width = m_column.m_width-indent();
m_end = m_pos;
if (!line().empty() && line()[m_pos] == '\n' )
++m_end;
while ( m_end < line().size() && line()[m_end] != '\n' )
++m_end;
if ( m_end < m_pos + width ) {
m_len = m_end - m_pos;
}
else {
size_t len = width;
while (len > 0 && !isBoundary(m_pos + len))
--len;
while (len > 0 && isWhitespace( line()[m_pos + len - 1] ))
--len;
if (len > 0) {
m_len = len;
} else {
m_suffix = true ;
m_len = width - 1;
}
}
}
auto indent() const -> size_t {
auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
return initial == std::string::npos ? m_column.m_indent : initial;
}
auto addIndentAndSuffix(std::string const &plain) const -> std::string {
return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain);
}
public :
using difference_type = std::ptrdiff_t;
using value_type = std::string;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::forward_iterator_tag;
explicit iterator( Column const & column ) : m_column( column ) {
assert( m_column.m_width > m_column.m_indent );
assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent );
calcLength();
if ( m_len == 0 )
m_stringIndex++; // Empty string
}
auto operator *() const -> std::string {
assert( m_stringIndex < m_column.m_strings.size() );
assert( m_pos <= m_end );
return addIndentAndSuffix(line().substr(m_pos, m_len));
}
auto operator ++() -> iterator& {
m_pos += m_len;
if ( m_pos < line().size() && line()[m_pos] == '\n' )
m_pos += 1;
else
while ( m_pos < line().size() && isWhitespace( line()[m_pos] ) )
++m_pos;
if ( m_pos == line().size() ) {
m_pos = 0;
++m_stringIndex;
}
if ( m_stringIndex < m_column.m_strings.size() )
calcLength();
return *this ;
}
auto operator ++(int ) -> iterator {
iterator prev( *this );
operator ++();
return prev;
}
auto operator ==( iterator const & other ) const -> bool {
return
m_pos == other.m_pos &&
m_stringIndex == other.m_stringIndex &&
&m_column == &other.m_column;
}
auto operator !=( iterator const & other ) const -> bool {
return !operator ==( other );
}
};
using const_iterator = iterator;
explicit Column( std::string const & text ) { m_strings.push_back( text ); }
auto width( size_t newWidth ) -> Column& {
assert( newWidth > 0 );
m_width = newWidth;
return *this ;
}
auto indent( size_t newIndent ) -> Column& {
m_indent = newIndent;
return *this ;
}
auto initialIndent( size_t newIndent ) -> Column& {
m_initialIndent = newIndent;
return *this ;
}
auto width() const -> size_t { return m_width; }
auto begin() const -> iterator { return iterator( *this ); }
auto end() const -> iterator { return { *this , m_strings.size() }; }
inline friend std::ostream& operator << ( std::ostream& os, Column const & col ) {
bool first = true ;
for ( auto line : col ) {
if ( first )
first = false ;
else
os << "\n" ;
os << line;
}
return os;
}
auto operator + ( Column const & other ) -> Columns;
auto toString() const -> std::string {
std::ostringstream oss;
oss << *this ;
return oss.str();
}
};
class Spacer : public Column {
public :
explicit Spacer( size_t spaceWidth ) : Column( "" ) {
width( spaceWidth );
}
};
class Columns {
std::vector<Column> m_columns;
public :
class iterator {
friend Columns;
struct EndTag {};
std::vector<Column> const & m_columns;
std::vector<Column::iterator> m_iterators;
size_t m_activeIterators;
iterator( Columns const & columns, EndTag )
: m_columns( columns.m_columns ),
m_activeIterators( 0 )
{
m_iterators.reserve( m_columns.size() );
for ( auto const & col : m_columns )
m_iterators.push_back( col.end() );
}
public :
using difference_type = std::ptrdiff_t;
using value_type = std::string;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::forward_iterator_tag;
explicit iterator( Columns const & columns )
: m_columns( columns.m_columns ),
m_activeIterators( m_columns.size() )
{
m_iterators.reserve( m_columns.size() );
for ( auto const & col : m_columns )
m_iterators.push_back( col.begin() );
}
auto operator ==( iterator const & other ) const -> bool {
return m_iterators == other.m_iterators;
}
auto operator !=( iterator const & other ) const -> bool {
return m_iterators != other.m_iterators;
}
auto operator *() const -> std::string {
std::string row, padding;
for ( size_t i = 0; i < m_columns.size(); ++i ) {
auto width = m_columns[i].width();
if ( m_iterators[i] != m_columns[i].end() ) {
std::string col = *m_iterators[i];
row += padding + col;
if ( col.size() < width )
padding = std::string( width - col.size(), ' ' );
else
padding = "" ;
}
else {
padding += std::string( width, ' ' );
}
}
return row;
}
auto operator ++() -> iterator& {
for ( size_t i = 0; i < m_columns.size(); ++i ) {
if (m_iterators[i] != m_columns[i].end())
++m_iterators[i];
}
return *this ;
}
auto operator ++(int ) -> iterator {
iterator prev( *this );
operator ++();
return prev;
}
};
using const_iterator = iterator;
auto begin() const -> iterator { return iterator( *this ); }
auto end() const -> iterator { return { *this , iterator::EndTag() }; }
auto operator += ( Column const & col ) -> Columns& {
m_columns.push_back( col );
return *this ;
}
auto operator + ( Column const & col ) -> Columns {
Columns combined = *this ;
combined += col;
return combined;
}
inline friend std::ostream& operator << ( std::ostream& os, Columns const & cols ) {
bool first = true ;
for ( auto line : cols ) {
if ( first )
first = false ;
else
os << "\n" ;
os << line;
}
return os;
}
auto toString() const -> std::string {
std::ostringstream oss;
oss << *this ;
return oss.str();
}
};
inline auto Column::operator + ( Column const & other ) -> Columns {
Columns cols;
cols += *this ;
cols += other;
return cols;
}
}
#endif // TEXTFLOW_HPP_INCLUDED
quality 90%
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland