ALib C++ Library
Library Version: 2402 R1
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 !defined(ALIB_DOX)
10#if !defined (HPP_ALIB_LANG_FORMAT_PARAGRAPHS)
12#endif
13# if !defined(HPP_ALIB_LANG_FORMAT_EXCEPTIONS)
15# endif
16
17#if !defined (HPP_ALIB_STRINGS_UTIL_TOKENIZER)
19#endif
20
21#if !defined (HPP_ALIB_CAMP_MESSAGE_EXCEPTION)
23#endif
24#endif // !defined(ALIB_DOX)
25
26using namespace alib::strings;
27namespace alib::lang::format {
28
29
30// #################################################################################################
31// Non-static methods (if used with instance).
32// #################################################################################################
34: Buffer(text)
35, Formatter( Formatter::GetDefault() )
36{
37 text.SetBuffer(2048);
38}
39
41: Buffer(externalBuffer)
42, Formatter( Formatter::GetDefault() )
43{}
44
45
46
47Paragraphs& Paragraphs::PushIndent( const String& indentFirstLine,
48 const String& pIndentOtherLines )
49{
50 String indentOtherLines= pIndentOtherLines.IsNull() ? indentFirstLine
51 : pIndentOtherLines;
52
53 IndentFirstLine ._( indentFirstLine );
54 IndentOtherLines._( indentOtherLines );
55 IndentSizesFirstLine .push( indentFirstLine.Length() );
56 IndentSizesOtherLines.push( indentOtherLines.Length() );
57 return *this;
58}
59
60
62{
63 IndentFirstLine .InsertChars( fillChar, static_cast<integer>( qty ) );
64 IndentOtherLines.InsertChars( fillChar, static_cast<integer>( qty ) );
65 IndentSizesFirstLine .push( static_cast<integer>( qty ) );
66 IndentSizesOtherLines.push( static_cast<integer>( qty ) );
67 return *this;
68}
69
70
72{
73 ALIB_ASSERT_ERROR( !IndentSizesFirstLine.empty(), "FMT", "Paragraphs: PopIndent without prior push." )
76
77 ALIB_ASSERT_ERROR( !IndentSizesOtherLines.empty(),"FMT", "Paragraphs: PopIndent without prior push." )
80 return *this;
81}
82
83
95
96
97
99{
100 integer startIdx= Buffer.Length();
102 try
103 {
104 Formatter->FormatArgs( Buffer, args );
105 }
106 catch(Exception& )
107 {
109 throw;
110 }
112
113 integer maxLineWidth;
115 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
116
117 if ( Buffer.IsNotEmpty() && !Buffer.EndsWith( NewLine() ) )
118 {
119 #if defined( _WIN32 )
120 if( Buffer.CharAtEnd() == '\n' )
121 Buffer.DeleteEnd(1);
122 #endif
123 Buffer << NewLine();
124 }
125}
126
127//! @cond NO_DOX
128namespace
129{
131 void throwMarkerException( FMTExceptions eType, String& markedBuffer, integer errPos )
132 {
133 String64 actText;
134 integer exceptPos= 25;
135 integer exceptStart= errPos - 25;
136 if( exceptStart <= 0 )
137 {
138 exceptPos+= exceptStart;
139 exceptStart= 0;
140 }
141 else
142 {
143 actText._( A_CHAR("[...]") );
144 exceptPos+= 5;
145 }
146
147
148 actText._( markedBuffer, exceptStart, 50 );
149 if( markedBuffer.Length() > exceptStart + 50 )
150 actText._( A_CHAR("[...]") );
151 actText.SearchAndReplace( A_CHAR("\r"), A_CHAR("\\r"), exceptPos );
152 actText.SearchAndReplace( A_CHAR("\n"), A_CHAR("\\n"), exceptPos );
153 exceptPos+= actText.SearchAndReplace( A_CHAR( "\r"), A_CHAR( "\\r") );
154 exceptPos+= actText.SearchAndReplace( A_CHAR( "\n"), A_CHAR( "\\n") );
155
156 throw Exception( ALIB_CALLER_NULLED, eType, errPos, actText, exceptPos );
157 }
158
159}
160//! @endcond
161
163{
165 character searchCharBuf[2];
166 searchCharBuf[0]= MarkerChar;
167 searchCharBuf[1]= '\n';
169 String searchChars(searchCharBuf, 2);
170
172 try
173 {
175 }
176 catch(Exception& )
177 {
179 throw;
180 }
182
183
184
185 Substring parser = markedBuffer;
186 integer lastTextStart= Buffer.Length();
187
188 while( parser.IsNotEmpty() )
189 {
190 integer pos= parser.template IndexOfAny<lang::Inclusion::Include, false>( searchChars );
191
192 // not found
193 if( pos < 0 )
194 {
195 Buffer << parser;
196 break;
197 }
198
199 // new line
200 if( parser.CharAt( pos ) == '\n' )
201 {
202 parser.template ConsumeChars<false, lang::CurrentData::Keep>( pos, Buffer, 1 );
203 parser.ConsumeChar( '\r' );
204 Buffer << NewLine();
205 integer maxLineWidth;
207 maxLineWidth, IndentFirstLine, IndentOtherLines );
208 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
209 lastTextStart= Buffer.Length();
210 continue;
211 }
212
213 parser.template ConsumeChars<false, lang::CurrentData::Keep>( pos, Buffer, 1 );
214
215 // double marker: insert one symbol
216 if( parser.ConsumeChar( MarkerChar ) )
218
219 // Indent
220 else if( parser.ConsumeString(A_CHAR( ">'" )) )
221 PushIndent( parser.ConsumeToken( '\'' ) );
222
223 else if( parser.ConsumeString(A_CHAR( ">>" )) )
224 PushIndent( A_CHAR( " " ) );
225
226 else if( parser.ConsumeString(A_CHAR( "<<" )) )
227 {
228 if( IndentSizesFirstLine.empty() )
230 markedBuffer.Length() - parser.Length() - 3 );
231 PopIndent();
232 }
233
234 // bullets
235 else if( parser.ConsumeString(A_CHAR( "*>" )) )
236 {
237 if( markerBulletLevel > 0 )
238 {
239 IndentFirstLine .DeleteEnd( 2 )._( A_CHAR( " " ) );
240 IndentOtherLines.DeleteEnd( 2 )._( A_CHAR( " " ) );
241 }
243 IndentOtherLines._( " " );
245 }
246 else if( parser.ConsumeString(A_CHAR( "<*" )) )
247 {
248 if( markerBulletLevel == 0 )
250 markedBuffer.Length() - parser.Length() - 3 );
251
252 int deIndentCnt= markerBulletLevel > 1 ? 4 : 2;
253 IndentFirstLine .DeleteEnd( deIndentCnt );
254 IndentOtherLines.DeleteEnd( deIndentCnt );
255 if( --markerBulletLevel > 0 )
256 {
258 IndentOtherLines._( A_CHAR( " " ) );
259 }
260 }
261
262 else if( parser.ConsumeChar('p' ) || parser.ConsumeChar('P') )
263 Buffer << NewLine();
264
265
266 // horizontal line
267 else if( parser.ConsumeString(A_CHAR( "HL" )) )
268 {
270 ._( NewLine() );
271 }
272
273 // not recognized
274 else
275 {
276 throwMarkerException( FMTExceptions::UnknownMarker, markedBuffer,
277 markedBuffer.Length() - parser.Length() - 1 );
278 }
279 }
280
281 if( lastTextStart < Buffer.Length() )
282 {
283 integer maxLineWidth;
284 Paragraphs::Format( Buffer, lastTextStart, LineWidth, JustifyChar, maxLineWidth,
286 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
287 }
288
289 if ( Buffer.IsNotEmpty() && !Buffer.EndsWith( NewLine() ) )
290 Buffer << NewLine();
291}
292
293
294// #################################################################################################
295// The static formatter method
296// #################################################################################################
298 integer startIdx, integer lineWidth,
299 character justifyChar, integer& maxLineWidth,
300 const String& pIndentFirstLine,
301 const String& pIndentOtherLines )
302{
303 maxLineWidth= 0;
304 String indentFirstLines= pIndentFirstLine .IsNotNull() ? pIndentFirstLine : EmptyString();
305 String indentOtherLines= pIndentOtherLines.IsNotNull() ? pIndentOtherLines : pIndentFirstLine;
306
307 bool isFirstLine= true;
308
309 String indent = nullptr;
310 bool indentAreJustSpaces= false;
311
312 // loop over lines
313 integer maxLineWidthDetectionStartIdx= startIdx;
314 bool hasNL= false;
315 for(;;)
316 {
317 maxLineWidth= (std::max)( maxLineWidth, startIdx - maxLineWidthDetectionStartIdx
318 - ( !hasNL ? 0 :
319 #if defined( _WIN32 )
320 2
321 #else
322 1
323 #endif
324 )
325 );
326 if ( startIdx == text.Length() )
327 break;
328 maxLineWidthDetectionStartIdx= startIdx;
329 hasNL= false;
330
331 // skip lines beginning with newline characters, unless indent has non-space characters
332 int isWinNL= text[ startIdx ] == '\r' ? 1 : 0;
333 if ( text[ startIdx + isWinNL ] == '\n' )
334 {
335 hasNL= true;
336
337 // set indent and check if its just spaces
338 if( indent.IsNull() )
339 {
340 indent = isFirstLine ? indentFirstLines : indentOtherLines;
341 indentAreJustSpaces= (indent.template IndexOfAny<lang::Inclusion::Exclude>( A_CHAR( " " ) ) < 0 );
342 }
343
344 // insert indent if not just spaces
345 if ( !indentAreJustSpaces )
346 {
348 text.InsertAt( indent, startIdx );
349 startIdx+= indent.Length();
351 }
352
353 #if defined( _WIN32 )
354 if( !isWinNL )
355 {
356 text.template InsertChars<false>('\r', 1, startIdx );
357 isWinNL= true;
358 }
359 #else
360 if( isWinNL )
361 {
362 text.template Delete<false>(startIdx, 1);
363 isWinNL= false;
364 }
365 #endif
366
367
368 startIdx+= 1 + isWinNL;
369 if( isFirstLine )
370 {
371 isFirstLine= false;
372 indent= nullptr;
373 }
374
375 continue;
376 }
377
378 // insert indent
379 if( indent.IsNull() )
380 {
381 indent = isFirstLine ? indentFirstLines : indentOtherLines;
382 indentAreJustSpaces= (indent.template IndexOfAny<lang::Inclusion::Exclude>( A_CHAR( " " ) ) < 0 );
383 }
384 text.InsertAt( indent, startIdx );
385
386 if( isFirstLine )
387 {
388 isFirstLine= false;
389 indent= nullptr;
390 }
391
392
393 // find next end of line. Remember last space in line
394 integer lastSpaceInLine = 0;
395 bool isLastLine = true;
396 bool exceeds = false;
397 integer idx = startIdx + indent.Length() - 1;
398 while (++idx < text.Length() )
399 {
400 character c= text[idx];
401 if ( c == '\n' )
402 {
403 hasNL= true;
404 ++idx;
405 break;
406 }
407 exceeds= lineWidth > 0 && idx - startIdx >= lineWidth;
408
409 if( c == ' ' )
410 {
411 if(idx - startIdx <= lineWidth )
412 lastSpaceInLine= idx;
413
414 if( exceeds )
415 {
416 isLastLine= false;
417 break;
418 }
419 }
420 }
421
422 // correct newline
423 #if defined( _WIN32 )
424 if( text[idx-1] == '\n' && text[idx-2] != '\r' )
425 {
426 text.template InsertChars<false>('\r', 1, idx-1 );
427 ++idx;
428 }
429 #else
430 if( text[idx-1] == '\n' && text[idx-2] == '\r' )
431 {
432 text.template Delete<false>((idx-2), 1);
433 --idx;
434 }
435 #endif
436
437 // wrap line.
438 if( exceeds && ( lastSpaceInLine || !isLastLine ) )
439 {
440 integer wrapPos= lastSpaceInLine > 0 ? lastSpaceInLine : idx;
441 text.template ReplaceSubstring<false>( NewLine(), wrapPos, 1 );
442 idx= wrapPos + NewLine().Length();
443 hasNL= true;
444
445 // block justification
446 if( justifyChar != '\0' )
447 {
448 integer qtyInserts= lineWidth - (wrapPos - startIdx );
449 if( qtyInserts > 0 )
450 {
451 // search first non-space after indent.
452 integer leftInsertBoundary= startIdx + indent.Length();
453 while ( leftInsertBoundary < idx && text[leftInsertBoundary] == ' ' )
454 ++leftInsertBoundary;
455
456 if( leftInsertBoundary < idx )
457 {
458 while( qtyInserts > 0 )
459 {
460 integer actPos= idx - 1;
461 bool foundOne= false;
462 while( qtyInserts > 0 )
463 {
464 actPos= text.LastIndexOf( ' ', actPos );
465 if( actPos < leftInsertBoundary )
466 break;
467 foundOne= true;
468 text.InsertChars( justifyChar, 1, actPos );
469 ++idx;
470 --qtyInserts;
471 while( --actPos > leftInsertBoundary && text[actPos] == ' ' )
472 ;
473 }
474
475 if( !foundOne )
476 break;
477 }
478 }
479 }
480 }
481 }
482
483 startIdx= idx;
484 }
485}
486
487} // namespace [alib::lang::format]
ALIB_API Formatter & FormatArgs(AString &target)
ALIB_API Boxes & Acquire(const NCString &dbgFile, int dbgLine, const NCString &dbgFunc)
ALIB_API void Release()
defined(ALIB_DOX)
Definition formatter.cpp:70
ALIB_API void AddMarked(Boxes &args)
std::stack< integer > IndentSizesOtherLines
ALIB_API Paragraphs & PushIndent(uinteger qty, character fillChar=' ')
std::vector< character > MarkerBullets
ALIB_API Paragraphs & Clear()
ALIB_API Paragraphs & PopIndent()
std::stack< integer > IndentSizesFirstLine
ALIB_API void Add(Boxes &args)
static ALIB_API void Format(AString &text, integer startIdx, integer lineWidth, character justifyChar, integer &maxLineWidth, const String &indentFirstLine=nullptr, const String &indentOtherLines=nullptr)
TAString & DeleteEnd(integer regionLength)
Definition astring.hpp:1589
TAString & InsertChars(TChar c, integer qty)
Definition astring.hpp:1405
TAString & _(const TString< TChar > &src, integer regionStart, integer regionLength=MAX_LEN)
Definition astring.hpp:1056
ALIB_API void SetBuffer(integer newCapacity)
Definition astring.cpp:107
ALIB_API integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0)
Definition astring.cpp:330
TAString & InsertAt(const TString< TChar > &src, integer pos)
Definition astring.hpp:1366
constexpr bool IsNull() const
Definition string.hpp:395
TChar CharAt(integer idx) const
Definition string.hpp:437
constexpr bool IsNotEmpty() const
Definition string.hpp:420
constexpr integer Length() const
Definition string.hpp:357
constexpr bool IsNotNull() const
Definition string.hpp:402
TChar CharAtEnd() const
Definition string.hpp:481
ALIB_WARNINGS_RESTORE integer LastIndexOf(TChar needle, integer startIndex=MAX_LEN) const
Definition string.hpp:1027
bool EndsWith(const TString &needle) const
Definition string.hpp:847
TString< TChar > ConsumeToken(TChar separator=',')
bool ConsumeString(const TString< TChar > &consumable)
#define ALIB_CALLER_NULLED
Definition alib.hpp:846
#define A_CHAR(STR)
#define ALIB_NO_RETURN
Definition alib.hpp:553
#define ALIB_WARNINGS_RESTORE
Definition alib.hpp:715
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:984
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
Definition alib.hpp:644
#define ALIB_CALLER_PRUNED
Definition alib.hpp:845
platform_specific integer
Definition integers.hpp:50
platform_specific uinteger
Definition integers.hpp:56
lang::Exception Exception
Type alias in namespace alib.
constexpr CString NewLine()
Definition cstring.hpp:528
constexpr CString EmptyString()
Definition cstring.hpp:502
characters::character character
Type alias in namespace alib.