From 046cf6d43f7773e055fc43880c34210922f59286 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 13 Oct 2023 05:26:17 -0400 Subject: [PATCH] Progress towards a word-wrap stream helper. --- word_wrap.cpp | 69 +++++++++++++++++++++++++++++++++++++++++---- word_wrap.h | 37 ++++++++++++++++++++++++ word_wrap.test/0.cc | 26 ++++++++--------- 3 files changed, 114 insertions(+), 18 deletions(-) diff --git a/word_wrap.cpp b/word_wrap.cpp index 654d58e..027df52 100644 --- a/word_wrap.cpp +++ b/word_wrap.cpp @@ -6,6 +6,8 @@ static_assert( __cplusplus > 2020 ); #include #include +#include +#include #include "evaluation_helpers.h" @@ -15,7 +17,7 @@ namespace Alepha::Cavorite ::detail:: word_wrap { // Returns the number of chars in the line just written to. std::size_t - applyWordToLine( const std::size_t maximumWidth, const std::size_t nextLineOffset, const std::size_t currentLineWidth, std::string &&word, std::string &result ) + applyWordToLine( const std::size_t maximumWidth, const std::size_t nextLineOffset, const std::size_t currentLineWidth, std::string &&word, std::ostream &result ) { if( word.empty() ) return currentLineWidth; @@ -23,25 +25,67 @@ namespace Alepha::Cavorite ::detail:: word_wrap { if( currentLineWidth + word.size() > maximumWidth ) { - result+= '\n'; - std::fill_n( back_inserter( result ), nextLineOffset, ' ' ); + result << "\n"; + std::fill_n( std::ostream_iterator< char >( result ), nextLineOffset, ' ' ); return nextLineOffset; } else return currentLineWidth; }; const auto rv= lineWidth + word.size(); - result+= std::move( word ); + result << std::move( word ); return rv; } } + void + WordWrapStreambuf::writeChar( const char ch ) + { + std::ostream outWrap{ underlying }; + if( ch == '\n' ) + { + const auto prev= currentLineLength; + const auto size= currentWord.size(); + currentLineLength= applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), outWrap ); + currentWord.clear(); + outWrap << '\n'; + if( currentLineLength == prev + size ) + { + std::fill_n( std::ostream_iterator< char >{ outWrap }, nextLineOffset, ' ' ); + currentLineLength= nextLineOffset; + } + else currentLineLength= 0; + } + else if( ch == ' ' ) + { + currentLineLength= applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), outWrap ); + currentWord.clear(); + if( currentLineLength < maximumWidth ) + { + outWrap << ' '; + ++currentLineLength; + } + } + else currentWord+= ch; + } + + void + WordWrapStreambuf::drain() + { + if( currentWord.empty() ) return; + std::ostream outWrap{ underlying }; + applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), outWrap ); + } + std::string exports::wordWrap( const std::string &text, const std::size_t width, const std::size_t nextLineOffset ) { + #if NEVER auto putWord= [width, nextLineOffset]( std::string &&word, std::string &line, const std::size_t lineLength ) { - const auto rv= applyWordToLine( width, nextLineOffset, lineLength, std::move( word ), line ); + std::ostringstream out; + const auto rv= applyWordToLine( width, nextLineOffset, lineLength, std::move( word ), out ); + line+= std::move( out ).str(); return rv; }; @@ -78,5 +122,20 @@ namespace Alepha::Cavorite ::detail:: word_wrap } if( not word.empty() ) std::ignore= putWord( std::move( word ), result, lineLength ); return result; + #else + std::ostringstream oss; + + WordWrapStreambuf buf; + buf.maximumWidth= width; + buf.nextLineOffset= nextLineOffset; + buf.underlying= static_cast< std::ostream & >( oss ).rdbuf(); + static_cast< std::ostream & >( oss ).rdbuf( &buf ); + + oss << text; + buf.drain(); + + auto rv= std::move( oss ).str(); + return rv; + #endif } } diff --git a/word_wrap.h b/word_wrap.h index 5f0ed97..204f395 100644 --- a/word_wrap.h +++ b/word_wrap.h @@ -5,13 +5,50 @@ static_assert( __cplusplus > 2020'00 ); #include #include +#include namespace Alepha::inline Cavorite ::detail:: word_wrap { inline namespace exports { std::string wordWrap( const std::string &text, std::size_t width, std::size_t nextLineOffset= 0 ); + + class WordWrapStreambuf; } + + class exports::WordWrapStreambuf + : public std::streambuf + { + public: + std::streambuf *underlying= nullptr; + + std::size_t maximumWidth= 0; + std::size_t nextLineOffset= 0; + std::size_t currentLineLength= 0; + + std::string currentWord; + + void writeChar( const char ch ); + + void drain(); + + public: + int + overflow( const int ch ) override + { + if( ch == EOF ) throw std::logic_error( "EOF!" ); + writeChar( ch ); + + return 1; + } + + std::streamsize + xsputn( const char *const data, const std::streamsize amt ) override + { + for( std::streamsize i= 0; i< amt; ++i ) overflow( data[ i ] ); + return amt; + } + }; } namespace Alepha::Cavorite::inline exports::inline word_wrap diff --git a/word_wrap.test/0.cc b/word_wrap.test/0.cc index 0a6c03c..ab429c7 100644 --- a/word_wrap.test/0.cc +++ b/word_wrap.test/0.cc @@ -91,19 +91,19 @@ static auto init= Alepha::Utility::enroll <=[] }, { "wider terminal, complex text: Hamlet's \"To Be or Not To Be\"", { - "To be, or not to be: " - "that is the question: " - "Whether 'tis nobler in the mind to suffer " - "The slings and arrows of outrageous fortune, " - "Or to take arms against a sea of troubles, " - "And by opposing end them? " - "To die: to sleep; " - "No more; and by a sleep to say we end " - "The heart-ache and the thousand natural shocks " - "That flesh is heir to, 'tis a consummation " - "Devoutly to be wish'd. To die, to sleep; " - "To sleep: perchance to dream: ay, there's the rub;", - 80, 0 + "To be, or not to be: " + "that is the question: " + "Whether 'tis nobler in the mind to suffer " + "The slings and arrows of outrageous fortune, " + "Or to take arms against a sea of troubles, " + "And by opposing end them? " + "To die: to sleep; " + "No more; and by a sleep to say we end " + "The heart-ache and the thousand natural shocks " + "That flesh is heir to, 'tis a consummation " + "Devoutly to be wish'd. To die, to sleep; " + "To sleep: perchance to dream: ay, there's the rub;", + 80, 0 }, "To be, or not to be: that is the question: Whether 'tis nobler in the mind to \n" "suffer The slings and arrows of outrageous fortune, Or to take arms against a \n"