ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
paragraphs.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !DOXYGEN
15#endif // !DOXYGEN
16
17using namespace alib::strings;
18namespace alib::lang::format {
19
20
21// #################################################################################################
22// Non-static methods (if used with instance).
23// #################################################################################################
25: allocator (ALIB_DBG("Paragraphs",) 4)
26, Buffer (text)
28, MarkerBullets (allocator)
29, IndentFirstLine (allocator)
30, IndentOtherLines (allocator)
31, IndentSizesFirstLine (StdDequeMono<integer>(allocator))
32, IndentSizesOtherLines (StdDequeMono<integer>(allocator))
33, boxes (allocator)
34{
35 text.SetBuffer(2048);
36 MarkerBullets= {'*', '-', '*', '-', '*', '-' };
39}
40
42: allocator(ALIB_DBG("Paragraphs",) 2)
43, Buffer(externalBuffer)
45, MarkerBullets (allocator)
46, IndentFirstLine (allocator)
47, IndentOtherLines (allocator)
48, IndentSizesFirstLine (StdDequeMono<integer>(allocator))
49, IndentSizesOtherLines (StdDequeMono<integer>(allocator))
50, boxes(allocator)
51{
52 MarkerBullets= {'*', '-', '*', '-', '*', '-' };
55}
56
57
58
59Paragraphs& Paragraphs::PushIndent( const String& indentFirstLine,
60 const String& pIndentOtherLines )
61{
62 String indentOtherLines= pIndentOtherLines.IsNull() ? indentFirstLine
63 : pIndentOtherLines;
64
65 IndentFirstLine ._( indentFirstLine );
66 IndentOtherLines._( indentOtherLines );
67 IndentSizesFirstLine .push( indentFirstLine.Length() );
68 IndentSizesOtherLines.push( indentOtherLines.Length() );
69 return *this;
70}
71
72
74{
75 IndentFirstLine .InsertChars( fillChar, static_cast<integer>( qty ) );
76 IndentOtherLines.InsertChars( fillChar, static_cast<integer>( qty ) );
77 IndentSizesFirstLine .push( static_cast<integer>( qty ) );
78 IndentSizesOtherLines.push( static_cast<integer>( qty ) );
79 return *this;
80}
81
82
84{
85 ALIB_ASSERT_ERROR( !IndentSizesFirstLine.empty(), "FMT", "Paragraphs: PopIndent without prior push." )
88
89 ALIB_ASSERT_ERROR( !IndentSizesOtherLines.empty(),"FMT", "Paragraphs: PopIndent without prior push." )
92 return *this;
93}
94
95
97{
98 Buffer.Reset();
99 while( IndentSizesFirstLine .size() ) IndentSizesFirstLine .pop();
100 while( IndentSizesOtherLines.size() ) IndentSizesOtherLines.pop();
105 return *this;
106}
107
108
109//! @cond NO_DOX
110
111template<>
113{
114 integer startIdx= Buffer.Length();
115 Formatter->FormatArgs( Buffer, args ); // may throw!
116
117 integer maxLineWidth;
119 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
120
122 {
123 #if defined( _WIN32 )
124 if( Buffer.CharAtEnd() == '\n' )
125 Buffer.DeleteEnd(1);
126 #endif
127 Buffer.NewLine();
128 }
129}
130
132namespace {
134 void throwMarkerException( FMTExceptions eType, String& markedBuffer, integer errPos )
135 {
136 String64 actText;
137 integer exceptPos= 25;
138 integer exceptStart= errPos - 25;
139 if( exceptStart <= 0 )
140 {
141 exceptPos+= exceptStart;
142 exceptStart= 0;
143 }
144 else
145 {
146 actText._( A_CHAR("[...]") );
147 exceptPos+= 5;
148 }
149
150
151 actText._( markedBuffer, exceptStart, 50 );
152 if( markedBuffer.Length() > exceptStart + 50 )
153 actText._( A_CHAR("[...]") );
154 actText.SearchAndReplace( A_CHAR("\r"), A_CHAR("\\r"), exceptPos );
155 actText.SearchAndReplace( A_CHAR("\n"), A_CHAR("\\n"), exceptPos );
156 exceptPos+= actText.SearchAndReplace( A_CHAR( "\r"), A_CHAR( "\\r") );
157 exceptPos+= actText.SearchAndReplace( A_CHAR( "\n"), A_CHAR( "\\n") );
158
159 throw Exception( ALIB_CALLER_NULLED, eType, errPos, actText, exceptPos );
160 }
161}
163
165{
167 character searchCharBuf[2];
168 searchCharBuf[0]= MarkerChar;
169 searchCharBuf[1]= '\n';
171 String searchChars(searchCharBuf, 2);
172
173 Formatter->FormatArgs( markedBuffer.Reset(), args ); // may throw
174
175 Substring parser = markedBuffer;
176 integer lastTextStart= Buffer.Length();
177
178 while( parser.IsNotEmpty() )
179 {
180 integer pos= parser.template IndexOfAny<lang::Inclusion::Include, NC>( searchChars );
181
182 // not found
183 if( pos < 0 )
184 {
185 Buffer << parser;
186 break;
187 }
188
189 // new line
190 if( parser.CharAt( pos ) == '\n' )
191 {
192 parser.template ConsumeChars<NC, lang::CurrentData::Keep>( pos, Buffer, 1 );
193 if (Buffer.CharAtEnd<NC>() == '\r')
194 Buffer.DeleteEnd<NC>(1);
195 Buffer.NewLine();
196 integer maxLineWidth;
198 maxLineWidth, IndentFirstLine, IndentOtherLines );
199 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
200 lastTextStart= Buffer.Length();
201 continue;
202 }
203
204 parser.template ConsumeChars<NC, lang::CurrentData::Keep>( pos, Buffer, 1 );
205
206 // double marker: insert one symbol
207 if( parser.ConsumeChar( MarkerChar ) )
209
210 // Indent
211 else if( parser.ConsumeString(A_CHAR( ">'" )) )
212 PushIndent( parser.ConsumeToken( '\'' ) );
213
214 else if( parser.ConsumeString(A_CHAR( ">>" )) )
215 PushIndent( A_CHAR( " " ) );
216
217 else if( parser.ConsumeString(A_CHAR( "<<" )) )
218 {
219 if( IndentSizesFirstLine.empty() )
221 markedBuffer.Length() - parser.Length() - 3 );
222 PopIndent();
223 }
224
225 // bullets
226 else if( parser.ConsumeString(A_CHAR( "*>" )) )
227 {
228 if( markerBulletLevel > 0 )
229 {
230 IndentFirstLine .DeleteEnd( 2 )._( A_CHAR( " " ) );
231 IndentOtherLines.DeleteEnd( 2 )._( A_CHAR( " " ) );
232 }
234 IndentOtherLines._( " " );
236 }
237 else if( parser.ConsumeString(A_CHAR( "<*" )) )
238 {
239 if( markerBulletLevel == 0 )
241 markedBuffer.Length() - parser.Length() - 3 );
242
243 int deIndentCnt= markerBulletLevel > 1 ? 4 : 2;
244 IndentFirstLine .DeleteEnd( deIndentCnt );
245 IndentOtherLines.DeleteEnd( deIndentCnt );
246 if( --markerBulletLevel > 0 )
247 {
249 IndentOtherLines._( A_CHAR( " " ) );
250 }
251 }
252
253 else if( parser.ConsumeChar('p' ) || parser.ConsumeChar('P') )
254 Buffer.NewLine();
255
256
257 // horizontal line
258 else if( parser.ConsumeString(A_CHAR( "HL" )) )
259 {
261 .NewLine();
262 }
263
264 // not recognized
265 else
266 {
267 throwMarkerException( FMTExceptions::UnknownMarker, markedBuffer,
268 markedBuffer.Length() - parser.Length() - 1 );
269 }
270 }
271
272 if( lastTextStart < Buffer.Length() )
273 {
274 integer maxLineWidth;
275 Paragraphs::Format( Buffer, lastTextStart, LineWidth, JustifyChar, maxLineWidth,
277 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
278 }
279
281 Buffer.NewLine();
282}
283
284//! @endcond
285
286// #################################################################################################
287// The static formatter method
288// #################################################################################################
290 integer startIdx, integer lineWidth,
291 character justifyChar, integer& maxLineWidth,
292 const String& pIndentFirstLine,
293 const String& pIndentOtherLines )
294{
295 maxLineWidth= 0;
296 String indentFirstLines= pIndentFirstLine .IsNotNull() ? pIndentFirstLine : EMPTY_STRING;
297 String indentOtherLines= pIndentOtherLines.IsNotNull() ? pIndentOtherLines : pIndentFirstLine;
298
299 bool isFirstLine= true;
300
301 String indent = nullptr;
302 bool indentAreJustSpaces= false;
303
304 // loop over lines
305 integer maxLineWidthDetectionStartIdx= startIdx;
306 bool hasNL= false;
307 for(;;)
308 {
309 maxLineWidth= (std::max)( maxLineWidth, startIdx - maxLineWidthDetectionStartIdx
310 - ( !hasNL ? 0 :
311 #if defined( _WIN32 )
312 2
313 #else
314 1
315 #endif
316 )
317 );
318 if ( startIdx == text.Length() )
319 break;
320 maxLineWidthDetectionStartIdx= startIdx;
321 hasNL= false;
322
323 // skip lines beginning with newline characters, unless indent has non-space characters
324 int isWinNL= text[ startIdx ] == '\r' ? 1 : 0;
325 if ( text[ startIdx + isWinNL ] == '\n' )
326 {
327 hasNL= true;
328
329 // set indent and check if its just spaces
330 if( indent.IsNull() )
331 {
332 indent = isFirstLine ? indentFirstLines : indentOtherLines;
333 indentAreJustSpaces= (indent.template IndexOfAny<lang::Inclusion::Exclude>( A_CHAR( " " ) ) < 0 );
334 }
335
336 // insert indent if not just spaces
337 if ( !indentAreJustSpaces )
338 {
340 text.InsertAt( indent, startIdx );
341 startIdx+= indent.Length();
343 }
344
345 #if defined( _WIN32 )
346 if( !isWinNL )
347 {
348 text.template InsertChars<NC>('\r', 1, startIdx );
349 isWinNL= true;
350 }
351 #else
352 if( isWinNL )
353 {
354 text.template Delete<NC>(startIdx, 1);
355 isWinNL= false;
356 }
357 #endif
358
359
360 startIdx+= 1 + isWinNL;
361 if( isFirstLine )
362 {
363 isFirstLine= false;
364 indent= nullptr;
365 }
366
367 continue;
368 }
369
370 // insert indent
371 if( indent.IsNull() )
372 {
373 indent = isFirstLine ? indentFirstLines : indentOtherLines;
374 indentAreJustSpaces= (indent.template IndexOfAny<lang::Inclusion::Exclude>( A_CHAR( " " ) ) < 0 );
375 }
376 text.InsertAt( indent, startIdx );
377
378 integer idx = startIdx + indent.Length() - 1;
379
380 if( isFirstLine )
381 {
382 isFirstLine= false;
383 indent= nullptr;
384 }
385
386 // find next end of line. Remember last space in line
387 integer lastSpaceInLine = 0;
388 bool isLastLine = true;
389 bool exceeds = false;
390 while (++idx < text.Length() )
391 {
392 character c= text[idx];
393 if ( c == '\n' )
394 {
395 hasNL= true;
396 ++idx;
397 break;
398 }
399 exceeds= lineWidth > 0 && idx - startIdx >= lineWidth;
400
401 if( c == ' ' )
402 {
403 if(idx - startIdx <= lineWidth )
404 lastSpaceInLine= idx;
405
406 if( exceeds )
407 {
408 isLastLine= false;
409 break;
410 }
411 }
412 }
413
414 // correct newline
415 #if defined( _WIN32 )
416 if( text[idx-1] == '\n' && text[idx-2] != '\r' )
417 {
418 text.template InsertChars<NC>('\r', 1, idx-1 );
419 ++idx;
420 }
421 #else
422 if( text[idx-1] == '\n' && text[idx-2] == '\r' )
423 {
424 text.template Delete<NC>((idx-2), 1);
425 --idx;
426 }
427 #endif
428
429 // wrap line.
430 if( exceeds && ( lastSpaceInLine || !isLastLine ) )
431 {
432 integer wrapPos= lastSpaceInLine > 0 ? lastSpaceInLine : idx;
433 text.template ReplaceSubstring<NC>( NEW_LINE, wrapPos, 1 );
434 idx= wrapPos + NEW_LINE.Length();
435 hasNL= true;
436
437 // block justification
438 if( justifyChar != '\0' )
439 {
440 integer qtyInserts= lineWidth - (wrapPos - startIdx );
441 if( qtyInserts > 0 )
442 {
443 // search first non-space after indent.
444 integer leftInsertBoundary= startIdx + indent.Length();
445 while ( leftInsertBoundary < idx && text[leftInsertBoundary] == ' ' )
446 ++leftInsertBoundary;
447
448 if( leftInsertBoundary < idx )
449 {
450 while( qtyInserts > 0 )
451 {
452 integer actPos= idx - 1;
453 bool foundOne= false;
454 while( qtyInserts > 0 )
455 {
456 actPos= text.LastIndexOf( ' ', actPos );
457 if( actPos < leftInsertBoundary )
458 break;
459 foundOne= true;
460 text.InsertChars( justifyChar, 1, actPos );
461 ++idx;
462 --qtyInserts;
463 while( --actPos > leftInsertBoundary && text[actPos] == ' ' )
464 ;
465 }
466
467 if( !foundOne )
468 break;
469 }
470 }
471 }
472 }
473 }
474
475 startIdx= idx;
476 }
477}
478
479
480#if !DOXYGEN
482{
483 boxes.clear();
484 boxes.Add(args);
485 Add(boxes);
486}
487
488template<> void Paragraphs::Add( boxing::TBoxes<PoolAllocator>& args )
489{
490 boxes.clear();
491 boxes.Add(args);
492 Add(boxes);
493}
495{
496 boxes.clear();
497 boxes.Add(args);
499}
500
502{
503 boxes.clear();
504 boxes.Add(args);
506}
507#endif // !DOXYGEN
508
509
510} // namespace [alib::lang::format]
TBoxes & Add()
Definition boxes.inl:74
Formatter & FormatArgs(AString &target)
std::stack< integer, StdDequeMono< integer > > IndentSizesFirstLine
ALIB_API Paragraphs & PushIndent(uinteger qty, character fillChar=' ')
std::stack< integer, StdDequeMono< integer > > IndentSizesOtherLines
AString markedBuffer
Buffer for processing marked text.
BoxesMA boxes
Internally reused list of boxes.
void Add(boxing::TBoxes< TAllocatorArgs > &args)
void AddMarked(boxing::TBoxes< TAllocatorArgs > &args)
size_t markerBulletLevel
Buffer for processing marked text.
ALIB_API Paragraphs & Clear()
ALIB_API Paragraphs & PopIndent()
StdVectorMono< character > MarkerBullets
static ALIB_API void Format(AString &text, integer startIdx, integer lineWidth, character justifyChar, integer &maxLineWidth, const String &indentFirstLine=nullptr, const String &indentOtherLines=nullptr)
AString text
Internal buffer, used for field Paragraphs, if no external string object was given.
integer LineWidth
Used as parameter lineWidth of static method invocations.
ALIB_API integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0, integer endIdx=strings::MAX_LEN)
TAString & DeleteEnd(integer regionLength)
TAString & InsertChars(TChar c, integer qty)
TAString & _(const TString< TChar > &src, integer regionStart, integer regionLength=MAX_LEN)
ALIB_API void SetBuffer(integer newCapacity)
TAString & InsertAt(const TString< TChar > &src, integer pos)
constexpr bool IsNull() const
Definition string.hpp:364
TChar CharAt(integer idx) const
Definition string.hpp:444
constexpr bool IsNotEmpty() const
Definition string.hpp:389
constexpr integer Length() const
Definition string.hpp:326
constexpr bool IsNotNull() const
Definition string.hpp:371
TChar CharAtEnd() const
Definition string.hpp:488
ALIB_WARNINGS_RESTORE integer LastIndexOf(TChar needle, integer startIndex=MAX_LEN) const
Definition string.hpp:1034
bool EndsWith(const TString &needle) const
Definition string.hpp:854
bool ConsumeString(const TString< TChar > &consumable)
TString< TChar > ConsumeToken(TChar separator=',', lang::Inclusion includeSeparator=lang::Inclusion::Include)
#define ALIB_CALLER_NULLED
Definition alib.hpp:1173
#define A_CHAR(STR)
#define ALIB_NO_RETURN
Definition alib.hpp:654
#define ALIB_WARNINGS_RESTORE
Definition alib.hpp:849
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
Definition alib.hpp:760
#define ALIB_DBG(...)
Definition alib.hpp:390
platform_specific integer
Definition integers.hpp:43
platform_specific uinteger
Definition integers.hpp:47
lang::Exception Exception
Type alias in namespace alib.
std::deque< T, SCAMono< T > > StdDequeMono
Type alias in namespace alib.
Definition stddeque.hpp:21
constexpr const String EMPTY_STRING
An empty string of the default character type.
Definition string.hpp:2529
constexpr CString NEW_LINE
A zero-terminated string containing the new-line character sequence.
Definition cstring.hpp:580
characters::character character
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273