ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
paragraphs.cpp
1using namespace alib::strings;
2namespace alib::format {
3
4
5//##################################################################################################
6// Non-static methods (if used with instance).
7//##################################################################################################
9: allocator (ALIB_DBG("Paragraphs",) 16)
10, Buffer (text)
11, Formatter ( Formatter::DEFAULT )
17, boxes (allocator) {
18 text.SetBuffer(2048);
19 MarkerBullets= {'*', '-', '*', '-', '*', '-' };
20 IndentFirstLine .SetBuffer(20);
21 IndentOtherLines.SetBuffer(20);
22}
23
25: allocator(ALIB_DBG("Paragraphs",) 16)
26, Buffer (externalBuffer)
27, Formatter ( Formatter::DEFAULT )
34 MarkerBullets= {'*', '-', '*', '-', '*', '-' };
35 IndentFirstLine .SetBuffer(20);
36 IndentOtherLines.SetBuffer(20);
37}
38
39
40
41Paragraphs& Paragraphs::PushIndent( const String& indentFirstLine,
42 const String& pIndentOtherLines ) {
43 String indentOtherLines= pIndentOtherLines.IsNull() ? indentFirstLine
44 : pIndentOtherLines;
45
46 IndentFirstLine ._( indentFirstLine );
47 IndentOtherLines._( indentOtherLines );
48 IndentSizesFirstLine .push( indentFirstLine.Length() );
49
50 IndentSizesOtherLines.push( indentOtherLines.Length() );
51 return *this;
52}
53
54
56 IndentFirstLine .InsertChars( fillChar, integer( qty ) );
57 IndentOtherLines.InsertChars( fillChar, integer( qty ) );
58 IndentSizesFirstLine .push( integer( qty ) );
59 IndentSizesOtherLines.push( integer( qty ) );
60 return *this;
61}
62
63
65 ALIB_ASSERT_ERROR( !IndentSizesFirstLine.empty(), "FORMAT",
66 "Paragraphs: PopIndent without prior push." )
67 IndentFirstLine.DeleteEnd( integer( IndentSizesFirstLine.top() ) );
69
70 ALIB_ASSERT_ERROR( !IndentSizesOtherLines.empty(), "FORMAT",
71 "Paragraphs: PopIndent without prior push." )
74 return *this;
75}
76
77
79 Buffer.Reset();
80 while( IndentSizesFirstLine .size() ) IndentSizesFirstLine .pop();
81 while( IndentSizesOtherLines.size() ) IndentSizesOtherLines.pop();
82 IndentFirstLine .Reset();
83 IndentOtherLines.Reset();
86 return *this;
87}
88
89
90//! @cond NO_DOX
91
92template<>
94 integer startIdx= Buffer.Length();
95 #if !ALIB_SINGLE_THREADED
98 : nullptr
100 #endif
101 Formatter->FormatArgs( Buffer, args ); // may throw!
102
103 integer maxLineWidth;
105 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
106
107 if ( Buffer.IsNotEmpty() && !Buffer.EndsWith( NEW_LINE ) ) {
108 #if defined( _WIN32 )
109 if( Buffer.CharAtEnd() == '\n' )
110 Buffer.DeleteEnd(1);
111 #endif
112 Buffer.NewLine();
113} }
114
115# include "ALib.Lang.CIFunctions.H"
116namespace {
117[[ noreturn ]]
118void throwMarkerException( FMTExceptions eType, String& markedBuffer, integer errPos ) {
119 String64 actText;
120 integer exceptPos= 25;
121 integer exceptStart= errPos - 25;
122 if( exceptStart <= 0 ) {
123 exceptPos+= exceptStart;
124 exceptStart= 0;
125 } else {
126 actText._( A_CHAR("[...]") );
127 exceptPos+= 5;
128 }
129
130
131 actText._( markedBuffer, exceptStart, 50 );
132 if( markedBuffer.Length() > exceptStart + 50 )
133 actText._( A_CHAR("[...]") );
134 actText.SearchAndReplace( A_CHAR("\r"), A_CHAR("\\r"), exceptPos );
135 actText.SearchAndReplace( A_CHAR("\n"), A_CHAR("\\n"), exceptPos );
136 exceptPos+= actText.SearchAndReplace( A_CHAR( "\r"), A_CHAR( "\\r") );
137 exceptPos+= actText.SearchAndReplace( A_CHAR( "\n"), A_CHAR( "\\n") );
138
139 throw Exception( ALIB_CALLER_NULLED, eType, errPos, actText, exceptPos );
140}
141}
142# include "ALib.Lang.CIMethods.H"
143
144template<> void Paragraphs::AddMarked( boxing::TBoxes<MonoAllocator>& args ) {
145 character searchCharBuf[2];
146 searchCharBuf[0]= MarkerChar;
147 searchCharBuf[1]= '\n';
148 String searchChars(searchCharBuf, 2);
149
150 #if !ALIB_SINGLE_THREADED
151 lang::OwnerRecursive<RecursiveLock, true> lock(
153 : nullptr
155 #endif
156 Formatter->FormatArgs( markedBuffer.Reset(), args ); // may throw
157
158 Substring parser = markedBuffer;
159 integer lastTextStart= Buffer.Length();
160
161 while( parser.IsNotEmpty() ) {
162 integer pos= parser.template IndexOfAny<lang::Inclusion::Include, NC>( searchChars );
163
164 // not found
165 if( pos < 0 ) {
166 Buffer << parser;
167 break;
168 }
169
170 // new line
171 if( parser.CharAt( pos ) == '\n' ) {
172 parser.template ConsumeChars<NC, lang::CurrentData::Keep>( pos, Buffer, 1 );
173 if (Buffer.CharAtEnd() == '\r')
174 Buffer.DeleteEnd<NC>(1);
175 Buffer.NewLine();
176 integer maxLineWidth;
178 maxLineWidth, IndentFirstLine, IndentOtherLines );
179 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
180 lastTextStart= Buffer.Length();
181 continue;
182 }
183
184 parser.template ConsumeChars<NC, lang::CurrentData::Keep>( pos, Buffer, 1 );
185
186 // double marker: insert one symbol
187 if( parser.ConsumeChar( MarkerChar ) )
189
190 // Indent
191 else if( parser.ConsumeString(A_CHAR( ">'" )) )
192 PushIndent( parser.ConsumeToken( '\'' ) );
193
194 else if( parser.ConsumeString(A_CHAR( ">>" )) )
195 PushIndent( A_CHAR( " " ) );
196
197 else if( parser.ConsumeString(A_CHAR( "<<" )) ) {
198 if( IndentSizesFirstLine.empty() )
200 markedBuffer.Length() - parser.Length() - 3 );
201 PopIndent();
202 }
203
204 // bullets
205 else if( parser.ConsumeString(A_CHAR( "*>" )) ) {
206 if( markerBulletLevel > 0 ) {
207 IndentFirstLine .DeleteEnd( 2 )._( A_CHAR( " " ) );
208 IndentOtherLines.DeleteEnd( 2 )._( A_CHAR( " " ) );
209 }
211 IndentOtherLines._( " " );
213 }
214 else if( parser.ConsumeString(A_CHAR( "<*" )) ) {
215 if( markerBulletLevel == 0 )
217 markedBuffer.Length() - parser.Length() - 3 );
218
219 int deIndentCnt= markerBulletLevel > 1 ? 4 : 2;
220 IndentFirstLine .DeleteEnd( deIndentCnt );
221 IndentOtherLines.DeleteEnd( deIndentCnt );
222 if( --markerBulletLevel > 0 ) {
224 IndentOtherLines._( A_CHAR( " " ) );
225 } }
226
227 else if( parser.ConsumeChar('p' ) || parser.ConsumeChar('P') )
228 Buffer.NewLine();
229
230
231 // horizontal line
232 else if( parser.ConsumeString(A_CHAR( "HL" )) ) {
233 Buffer.InsertChars( parser.ConsumeChar() , LineWidth - IndentFirstLine.Length() )
234 .NewLine();
235 }
236
237 // not recognized
238 else {
239 throwMarkerException( FMTExceptions::UnknownMarker, markedBuffer,
240 markedBuffer.Length() - parser.Length() - 1 );
241 } }
242
243 if( lastTextStart < Buffer.Length() ) {
244 integer maxLineWidth;
245 Paragraphs::Format( Buffer, lastTextStart, LineWidth, JustifyChar, maxLineWidth,
247 DetectedMaxLineWidth= (std::max)(DetectedMaxLineWidth, maxLineWidth );
248 }
249
250 if ( Buffer.IsNotEmpty() && !Buffer.EndsWith( NEW_LINE ) )
251 Buffer.NewLine();
252}
253
254//! @endcond
255
256//##################################################################################################
257// The static formatter method
258//##################################################################################################
260 integer startIdx, integer lineWidth,
261 character justifyChar, integer& maxLineWidth,
262 const String& pIndentFirstLine,
263 const String& pIndentOtherLines ) {
264 maxLineWidth= 0;
265 String indentFirstLines= pIndentFirstLine .IsNotNull() ? pIndentFirstLine : EMPTY_STRING;
266 String indentOtherLines= pIndentOtherLines.IsNotNull() ? pIndentOtherLines : pIndentFirstLine;
267
268 bool isFirstLine= true;
269
270 String indent = nullptr;
271 bool indentAreJustSpaces= false;
272
273 // loop over lines
274 integer maxLineWidthDetectionStartIdx= startIdx;
275 bool hasNL= false;
276 for(;;) {
277 maxLineWidth= (std::max)( maxLineWidth, startIdx - maxLineWidthDetectionStartIdx
278 - ( !hasNL ? 0 :
279 #if defined( _WIN32 )
280 2
281 #else
282 1
283 #endif
284 )
285 );
286 if ( startIdx == text.Length() )
287 break;
288 maxLineWidthDetectionStartIdx= startIdx;
289 hasNL= false;
290
291 // skip lines beginning with newline characters, unless indent has non-space characters
292 int isWinNL= text[ startIdx ] == '\r' ? 1 : 0;
293 if ( text[ startIdx + isWinNL ] == '\n' ) {
294 hasNL= true;
295
296 // set indent and check if its just spaces
297 if( indent.IsNull() ) {
298 indent = isFirstLine ? indentFirstLines : indentOtherLines;
299 indentAreJustSpaces= (indent.template IndexOfAny<lang::Inclusion::Exclude>( A_CHAR( " " ) ) < 0 );
300 }
301
302 // insert indent if not just spaces
303 if ( !indentAreJustSpaces ) {
304 text.InsertAt( indent, startIdx );
305 startIdx+= indent.Length();
306 }
307
308 #if defined( _WIN32 )
309 if( !isWinNL ) {
310 text.template InsertChars<NC>('\r', 1, startIdx );
311 isWinNL= true;
312 }
313 #else
314 if( isWinNL ) {
315 text.template Delete<NC>(startIdx, 1);
316 isWinNL= false;
317 }
318 #endif
319
320
321 startIdx+= 1 + isWinNL;
322 if( isFirstLine ) {
323 isFirstLine= false;
324 indent= nullptr;
325 }
326
327 continue;
328 }
329
330 // insert indent
331 if( indent.IsNull() ) {
332 indent = isFirstLine ? indentFirstLines : indentOtherLines;
333 indentAreJustSpaces= (indent.template IndexOfAny<lang::Inclusion::Exclude>( A_CHAR( " " ) ) < 0 );
334 }
335 text.InsertAt( indent, startIdx );
336
337 integer idx = startIdx + indent.Length() - 1;
338
339 if( isFirstLine ) {
340 isFirstLine= false;
341 indent= nullptr;
342 }
343
344 // find next end of line. Remember last space in line
345 integer lastSpaceInLine = 0;
346 bool isLastLine = true;
347 bool exceeds = false;
348 while (++idx < text.Length() ) {
349 character c= text[idx];
350 if ( c == '\n' ) {
351 hasNL= true;
352 ++idx;
353 break;
354 }
355 exceeds= lineWidth > 0 && idx - startIdx >= lineWidth;
356
357 if( c == ' ' ) {
358 if(idx - startIdx <= lineWidth )
359 lastSpaceInLine= idx;
360
361 if( exceeds ) {
362 isLastLine= false;
363 break;
364 } } }
365
366 // correct newline
367 #if defined( _WIN32 )
368 if( text[idx-1] == '\n' && text[idx-2] != '\r' ) {
369 text.template InsertChars<NC>('\r', 1, idx-1 );
370 ++idx;
371 }
372 #else
373 if( text[idx-1] == '\n' && text[idx-2] == '\r' ) {
374 text.template Delete<NC>((idx-2), 1);
375 --idx;
376 }
377 #endif
378
379 // wrap line.
380 if( exceeds && ( lastSpaceInLine || !isLastLine ) ) {
381 integer wrapPos= lastSpaceInLine > 0 ? lastSpaceInLine : idx;
382 text.template ReplaceSubstring<NC>( NEW_LINE, wrapPos, 1 );
383 idx= wrapPos + NEW_LINE.Length();
384 hasNL= true;
385
386 // block justification
387 if( justifyChar != '\0' ) {
388 integer qtyInserts= lineWidth - (wrapPos - startIdx );
389 if( qtyInserts > 0 ) {
390 // search first non-space after indent.
391 integer leftInsertBoundary= startIdx + indent.Length();
392 while ( leftInsertBoundary < idx && text[leftInsertBoundary] == ' ' )
393 ++leftInsertBoundary;
394
395 if( leftInsertBoundary < idx ) {
396 while( qtyInserts > 0 ) {
397 integer actPos= idx - 1;
398 bool foundOne= false;
399 while( qtyInserts > 0 ) {
400 actPos= text.LastIndexOf( ' ', actPos );
401 if( actPos < leftInsertBoundary )
402 break;
403 foundOne= true;
404 text.InsertChars( justifyChar, 1, actPos );
405 ++idx;
406 --qtyInserts;
407 while( --actPos > leftInsertBoundary && text[actPos] == ' ' )
408 ;
409 }
410
411 if( !foundOne )
412 break;
413 } } } } }
414
415 startIdx= idx;
416} }
417
418
419#if !DOXYGEN
421 boxes.clear();
422 boxes.Add(args);
423 Add(boxes);
424}
425
426template<> void Paragraphs::Add( boxing::TBoxes<PoolAllocator>& args ) {
427 boxes.clear();
428 boxes.Add(args);
429 Add(boxes);
430}
432 boxes.clear();
433 boxes.Add(args);
435}
436
437template<> void Paragraphs::AddMarked( boxing::TBoxes<PoolAllocator>& args ) {
438 boxes.clear();
439 boxes.Add(args);
441}
442#endif // !DOXYGEN
443
444
445} // namespace [alib::format]
#define ALIB_COMMA_CALLER_PRUNED
#define ALIB_CALLER_NULLED
#define A_CHAR(STR)
#define ALIB_DBG(...)
#define ALIB_ASSERT_ERROR(cond, domain,...)
TBoxes & Add()
Definition boxes.hpp:54
static threads::RecursiveLock DEFAULT_LOCK
Formatter & FormatArgs(AString &target)
static SPFormatter DEFAULT
AString markedBuffer
Buffer for processing marked text.
StdVectorMA< character > MarkerBullets
Paragraphs & PushIndent(uinteger qty, character fillChar=' ')
size_t markerBulletLevel
Buffer for processing marked text.
std::stack< integer, StdDequeMA< integer > > IndentSizesFirstLine
void Add(boxing::TBoxes< TAllocatorArgs > &args)
std::stack< integer, StdDequeMA< integer > > IndentSizesOtherLines
void AddMarked(boxing::TBoxes< TAllocatorArgs > &args)
static void Format(AString &text, integer startIdx, integer lineWidth, character justifyChar, integer &maxLineWidth, const String &indentFirstLine=nullptr, const String &indentOtherLines=nullptr)
MonoAllocator allocator
Allocator used for internal container types.
integer LineWidth
Used as parameter lineWidth of static method invocations.
BoxesMA boxes
Internally reused list of boxes.
AString text
Internal buffer, used for field #"Paragraphs", if no external string object was given.
Paragraphs & PopIndent()
integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0, integer endIdx=strings::MAX_LEN)
TAString & DeleteEnd(integer regionLength)
TAString & _(const TAppendable &src)
constexpr integer Length() const
Definition string.hpp:300
bool EndsWith(const TString &needle) const
Definition string.hpp:764
constexpr bool IsNotNull() const
Definition string.hpp:339
TChar CharAt(integer idx) const
Definition string.hpp:399
constexpr bool IsNotEmpty() const
Definition string.hpp:353
TChar CharAtEnd() const
Definition string.hpp:436
constexpr bool IsNull() const
Definition string.hpp:334
bool ConsumeString(const TString< TChar > &consumable)
TString< TChar > ConsumeToken(TChar separator=',', lang::Inclusion includeSeparator=lang::Inclusion::Include)
std::deque< T, StdMA< T > > StdDequeMA
Type alias in namespace #"%alib".
LocalString< 64 > String64
Type alias name for #"TLocalString;TLocalString<character,64>".
constexpr CString NEW_LINE
A zero-terminated string containing the new-line character sequence.
Definition cstring.hpp:536
constexpr const String EMPTY_STRING
An empty string of the default character type.
Definition string.hpp:2227
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
strings::TString< character > String
Type alias in namespace #"%alib".
Definition string.hpp:2165
strings::TSubstring< character > Substring
Type alias in namespace #"%alib".
exceptions::Exception Exception
Type alias in namespace #"%alib".
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace #"%alib".
characters::character character
Type alias in namespace #"%alib".
lang::uinteger uinteger
Type alias in namespace #"%alib".
Definition integers.hpp:152