• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 21100551564

17 Jan 2026 08:40PM UTC coverage: 60.504% (+0.01%) from 60.492%
21100551564

Pull #857

github

turleypol
fixed scope
Pull Request #857: ClangTidy readability-else-after-return

837 of 1874 new or added lines in 151 files covered. (44.66%)

48 existing lines in 26 files now uncovered.

44445 of 73458 relevant lines covered (60.5%)

515341.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.76
/pol-core/bscript/compiler/file/PrettifyLineBuilder.cpp
1
#include "PrettifyLineBuilder.h"
2

3
#include "bscript/compilercfg.h"
4
#include "clib/filecont.h"
5
#include "clib/logfacility.h"
6
#include <EscriptGrammar/EscriptLexer.h>
7

8
#include <algorithm>
9
#include <iostream>
10
#include <utility>
11

12
// #define DEBUG_FORMAT_BREAK
13

14
namespace Pol::Bscript::Compiler
15
{
16
using namespace EscriptGrammar;
17

18
const std::vector<std::string>& PrettifyLineBuilder::formattedLines() const
688✔
19
{
20
  return _lines;
688✔
21
}
22

23
void PrettifyLineBuilder::setRawLines( std::vector<std::string> rawlines )
660✔
24
{
25
  _rawlines = std::move( rawlines );
660✔
26
}
660✔
27

28
void PrettifyLineBuilder::setComments( std::vector<FmtToken> comments )
660✔
29
{
30
  _comments = std::move( comments );
660✔
31
}
660✔
32

33
void PrettifyLineBuilder::setSkipLines( std::vector<Range> skiplines )
660✔
34
{
35
  _skiplines = std::move( skiplines );
660✔
36
}
660✔
37

38
void PrettifyLineBuilder::addPart( FmtToken part )
51,604✔
39
{
40
  for ( const auto& skip : _skiplines )
51,824✔
41
    if ( skip.contains( part.pos ) )
1,674✔
42
      return;
1,454✔
43
  _line_parts.emplace_back( std::move( part ) );
50,150✔
44
}
45

46
bool PrettifyLineBuilder::finalize()
660✔
47
{
48
  mergeEOFNonTokens();
660✔
49
  return _line_parts.empty() && _comments.empty() && _skiplines.empty();
660✔
50
}
51

52
const std::vector<FmtToken>& PrettifyLineBuilder::currentTokens() const
666✔
53
{
54
  return _line_parts;
666✔
55
}
56

57
void PrettifyLineBuilder::mergeRawContent( size_t nextlineno )
9,678✔
58
{
59
  for ( auto itr = _skiplines.begin(); itr != _skiplines.end(); )
9,728✔
60
  {
61
    if ( itr->start.line_number < nextlineno )
50✔
62
    {
63
      mergeCommentsBefore( itr->start.line_number );
6✔
64
      addEmptyLines( itr->start.line_number );
6✔
65
      for ( size_t i = itr->start.line_number - 1; i < itr->end.line_number && i < _rawlines.size();
104✔
66
            ++i )
67
      {
68
        _packableline_allowed = false;
98✔
69
        _lines.emplace_back( _rawlines[i] );
98✔
70
        _last_line = i + 1;
98✔
71
      }
72
      itr = _skiplines.erase( itr );
6✔
73
      continue;
6✔
74
    }
6✔
75
    ++itr;
44✔
76
  }
77
}
9,678✔
78

79
void PrettifyLineBuilder::mergeCommentsBefore( size_t nextlineno )
8,311✔
80
{
81
  // add comments before current line
82
  while ( !_comments.empty() )
8,898✔
83
  {
84
    if ( _comments.front().pos.line_number < nextlineno )
3,599✔
85
    {
86
      addEmptyLines( _comments.front().pos.line_number );
587✔
87
      _lines.push_back( indentSpacing() + _comments.front().text );
587✔
88
      if ( _packableline_allowed &&
607✔
89
           _packablelinestart ==
20✔
90
               _lines.size() - 1 )  // potential packed line not yet build, so safe to add a comment
20✔
91
        ++_packablelinestart;
10✔
92
      else if ( _comments.front().style & FmtToken::FORCED_BREAK )
577✔
93
        _packableline_allowed = false;
569✔
94

95
      _last_line = _comments.front().pos_end.line_number;
587✔
96
      _comments.erase( _comments.begin() );
587✔
97
    }
98
    else
99
      break;
3,012✔
100
  }
101
}
8,311✔
102

103
void PrettifyLineBuilder::mergeComments()
8,305✔
104
{
105
  if ( _line_parts.empty() )
8,305✔
106
    return;
×
107
  mergeRawContent( _line_parts.front().pos.line_number );
8,305✔
108
  mergeCommentsBefore( _line_parts.front().pos.line_number );
8,305✔
109
  // add comments inbetween
110
  for ( size_t i = 0; i < _line_parts.size(); )
28,956✔
111
  {
112
    if ( _comments.empty() )
25,945✔
113
      break;
5,294✔
114
    if ( _line_parts[i].pos.token_index > _comments.front().pos.token_index )
20,651✔
115
    {
116
      _packableline_allowed = false;
7✔
117
      auto info = _comments.front();
7✔
118
      info.group = i ? _line_parts[i - 1].group : _currentgroup;
7✔
119
      _line_parts.insert( _line_parts.begin() + i, std::move( info ) );
7✔
120
      _comments.erase( _comments.begin() );
7✔
121
    }
7✔
122
    else
123
      ++i;
20,644✔
124
  }
125
  // add comments at the end of the current line
126
  if ( !_comments.empty() )
8,305✔
127
  {
128
    if ( _comments.front().pos.line_number == _line_parts.back().pos.line_number &&
3,326✔
129
         _comments.front().pos.token_index <= _line_parts.back().pos.token_index + 1 )
315✔
130
    {
131
      // no disable of packableline since its at the end
132
      addPart( _comments.front() );
300✔
133
      _comments.erase( _comments.begin() );
300✔
134
    }
135
  }
136
}
137

138
std::vector<FmtToken> PrettifyLineBuilder::buildLineSplits()
8,305✔
139
{
140
  std::vector<FmtToken> lines;
8,305✔
141
  bool has_varcomma = false;  // only if a "var," exists split based on these, otherwise var
8,305✔
142
                              // statement would be splitted
143
  for ( size_t i = 0; i < _line_parts.size(); ++i )
58,205✔
144
  {
145
    if ( _line_parts[i].context == FmtContext::VAR_COMMA )
49,937✔
146
    {
147
      has_varcomma = true;
37✔
148
      break;
37✔
149
    }
150
  }
151
  FmtToken part;
8,305✔
152
  for ( size_t i = 0; i < _line_parts.size(); ++i )
58,462✔
153
  {
154
#ifdef DEBUG_FORMAT_BREAK
155
    INFO_PRINTLN( "\"{}\" {} {}", _line_parts[i].text, _line_parts[i].group, _line_parts[i].style );
156
#endif
157
    if ( part.text.empty() )
50,157✔
158
    {
159
      part.firstgroup = _line_parts[i].group;
17,119✔
160
      part.pos = _line_parts[i].pos;
17,119✔
161
      part.scope = _line_parts[i].scope;
17,119✔
162
      part.context = _line_parts[i].context;
17,119✔
163
    }
164
    part.text += _line_parts[i].text;
50,157✔
165
    if ( part.context == FmtContext::NONE )
50,157✔
166
      part.context = _line_parts[i].context;
40,980✔
167
    // add space if set, but not if the following part is attached
168
    if ( _line_parts[i].style & FmtToken::SPACE )
50,157✔
169
    {
170
      if ( i + 1 < _line_parts.size() )
46,953✔
171
      {
172
        if ( !( _line_parts[i + 1].style & FmtToken::ATTACHED ) )
38,648✔
173
          part.text += ' ';
22,527✔
174
      }
175
      else
176
        part.text += ' ';
8,305✔
177
      // if something is left and not attached
178
      if ( i + 1 < _line_parts.size() )
46,953✔
179
      {
180
        if ( !( _line_parts[i + 1].style & FmtToken::ATTACHED ) )
38,648✔
181
        {
182
          // if its already quite long split it
183
          if ( part.text.size() > compilercfg.FormatterLineWidth * 0.5 )
22,527✔
184
          {
185
            part.style = _line_parts[i].style;
42✔
186
            part.group = _line_parts[i].group;
42✔
187
            part.pos_end = _line_parts[i].pos_end;
42✔
188
            lines.emplace_back( std::move( part ) );
42✔
189
            part = FmtToken{};
42✔
190
            continue;
42✔
191
          }
192
        }
193
      }
194
    }
195
    // if a forced break does not appear at the end disallow merging the lines
196
    if ( _line_parts[i].style & FmtToken::FORCED_BREAK && i < _line_parts.size() - 1 )
50,115✔
197
      _packableline_allowed = false;
1✔
198
    if ( _line_parts[i].style & FmtToken::FORCED_BREAK ||
50,492✔
199
         ( has_varcomma && ( _line_parts[i].context == FmtContext::VAR_STATEMENT ||
377✔
200
                             _line_parts[i].context == FmtContext::VAR_COMMA ) ) )
340✔
201
    {
202
      // need to break directly after "var" to align multiple variables
203
      if ( _line_parts[i].context == FmtContext::VAR_STATEMENT ||
785✔
204
           _line_parts[i].context == FmtContext::VAR_COMMA )
374✔
205
        _line_parts[i].style |= FmtToken::PREFERRED_BREAK_VAR;
110✔
206
      part.style = _line_parts[i].style;
411✔
207
      part.group = _line_parts[i].group;
411✔
208
      part.pos_end = _line_parts[i].pos_end;
411✔
209
      lines.emplace_back( std::move( part ) );
411✔
210
      part = FmtToken{};
411✔
211
    }
212
    // start a new line if breakpoint
213
    else if ( _line_parts[i].style & FmtToken::BREAKPOINT )
49,704✔
214
    {
215
      // if the next part is attached, dont break now and instead later
216
      // eg dont split blubb[1]()
217
      bool skip = false;
20,142✔
218
      if ( i + 1 < _line_parts.size() )
20,142✔
219
      {
220
        // next one is attached
221
        if ( _line_parts[i + 1].style & FmtToken::ATTACHED )
13,435✔
222
        {
223
          // search for space or breakpoint
224
          for ( size_t j = i + 1; j < _line_parts.size(); ++j )
4,966✔
225
          {
226
            if ( _line_parts[j].style & FmtToken::ATTACHED )
4,966✔
227
              skip = true;
4,828✔
228
            if ( _line_parts[j].style & FmtToken::SPACE ||
5,158✔
229
                 _line_parts[j].style & FmtToken::BREAKPOINT )
192✔
230
            {
231
              _line_parts[j].style |= FmtToken::BREAKPOINT;
4,774✔
232
              skip = true;
4,774✔
233
              break;
4,774✔
234
            }
235
          }
236
        }
237
      }
238
      if ( skip )
20,142✔
239
        continue;
4,774✔
240
      part.style = _line_parts[i].style;
15,368✔
241
      part.group = _line_parts[i].group;
15,368✔
242
      part.pos_end = _line_parts[i].pos_end;
15,368✔
243
      lines.emplace_back( std::move( part ) );
15,368✔
244
      part = FmtToken{};
15,368✔
245
    }
246
  }
247
  // add remainings
248
  if ( !part.text.empty() )
8,305✔
249
  {
250
    part.style = _line_parts.back().style;
1,298✔
251
    part.group = _line_parts.back().group;
1,298✔
252
    part.pos_end = _line_parts.back().pos_end;
1,298✔
253
    lines.emplace_back( std::move( part ) );
1,298✔
254
  }
255

256
  return lines;
16,610✔
257
}
8,305✔
258

259
bool PrettifyLineBuilder::binPack( const FmtToken& part, std::string line, size_t index,
28✔
260
                                   size_t upto, const std::vector<FmtToken>& lines,
261
                                   bool only_single_line, std::vector<std::string>* finallines,
262
                                   std::map<size_t, size_t>* alignmentspace, size_t* skipindex,
263
                                   const std::map<size_t, size_t>& initial_alignmentspace,
264
                                   std::string& newpart ) const
265
{
266
  auto packTaktic =
267
      []( size_t index, size_t end_group, size_t max_len, const std::vector<FmtToken>& lines )
8✔
268
  {
269
    // fill up lines with given max_len
270
    std::string currline;
8✔
271
    std::vector<std::string> lineparts;
8✔
272
    for ( size_t j = index; j < end_group; ++j )
48✔
273
    {
274
      const auto& lpart = lines[j].text;
40✔
275
      if ( lines[j].style & FmtToken::FORCED_BREAK )
40✔
276
      {
277
        currline += lpart;
×
278
        lineparts.push_back( std::move( currline ) );
×
279
        currline.clear();
×
280
        continue;
×
281
      }
282
      // new part still fits, add to line
283
      if ( currline.size() + lpart.size() <= max_len )
40✔
284
      {
285
        currline += lpart;
26✔
286
        continue;
26✔
287
      }
288
      // we are already at end, start a new line
289
      if ( currline.size() >= max_len )
14✔
290
      {
291
        lineparts.push_back( std::move( currline ) );
2✔
292
        currline = lpart;
2✔
293
        continue;
2✔
294
      }
295
      // we were smaller, add it anyway and start a new line
296
      // the line will be bigger then the available room
297
      // but I think looks better
298
      currline += lpart;
12✔
299
      lineparts.push_back( std::move( currline ) );
12✔
300
      currline.clear();
12✔
301
    }
302
    if ( !currline.empty() )
8✔
303
      lineparts.push_back( currline );
4✔
304
    return lineparts;
16✔
305
  };
8✔
306
  auto indent = indentSpacing();
28✔
307
  size_t room = compilercfg.FormatterLineWidth - line.size();
28✔
308
  size_t end_group = 0;
28✔
309
  bool prefferesbreak = false;
28✔
310
  // where does the group end?
311
  for ( size_t j = index + 1; j <= upto; ++j )
93✔
312
  {
313
    if ( lines[j].style & FmtToken::PREFERRED_BREAK_VAR )
83✔
314
    {
315
      prefferesbreak = true;
×
316
    }
317
    if ( lines[j].group == part.group || lines[j].firstgroup == part.group )
83✔
318
    {
319
      continue;
65✔
320
    }
321
    end_group = j;
18✔
322
    break;
18✔
323
  }
324
  if ( end_group == 0 )
28✔
325
    end_group = upto + 1;
10✔
326
  *skipindex = end_group;  // independent of the result we will skip till end of group
28✔
327
  std::string total;
28✔
328
  bool total_valid{ true };
28✔
329
  for ( size_t j = index; j < end_group; ++j )
121✔
330
  {
331
    total += lines[j].text;
93✔
332
    if ( lines[j].style & FmtToken::FORCED_BREAK )
93✔
333
    {
334
      total_valid = false;
×
335
      break;
×
336
    }
337
  }
338
  // it fits directly into the line
339
  if ( total_valid && total.size() + line.size() < compilercfg.FormatterLineWidth )
28✔
340
  {
341
    if ( !alignmentspace->count( part.group ) )
20✔
342
      ( *alignmentspace )[part.group] = line.size() - indent.size();
9✔
343
    line += total;
20✔
344
    newpart = total;
20✔
345
    stripline( line );
20✔
346
    finallines->emplace_back( std::move( line ) );
20✔
347
    line.clear();
20✔
348
    return true;
20✔
349
  }
350
  // if its part of an active groupformatting only allow a single line
351
  if ( only_single_line )
8✔
352
    return false;
×
353
  // TODO: if only a few chars room are left start a newline before
354
  //  option 1: try to equally split the line into two, for structs and dicts half the available
355
  //  room
356
  size_t lenOption2 = std::min( room, total.size() / 2 );
8✔
357
  if ( ( part.scope & FmtToken::Scope::STRUCT ) == FmtToken::Scope::STRUCT ||
10✔
358
       ( part.scope & FmtToken::Scope::DICT ) == FmtToken::Scope::DICT )
2✔
359
  {
360
    lenOption2 = room / 2;
6✔
361
  }
362

363
  auto lineparts = packTaktic( index, end_group, lenOption2, lines );
8✔
364

365
  // option1 failed, for arrays its allowed to use 3 lines
366
  // no array/dict/struct scope means any length is allowed
367
  if ( lineparts.size() != 2 && part.scope != FmtToken::Scope::NONE &&
10✔
368
       part.scope != FmtToken::Scope::FUNCTION && part.scope != FmtToken::Scope::VAR )
10✔
369
  {
370
    if ( ( part.scope & FmtToken::Scope::STRUCT ) == FmtToken::Scope::STRUCT ||
2✔
371
         ( part.scope & FmtToken::Scope::DICT ) == FmtToken::Scope::DICT )
×
372
    {
373
      return false;
2✔
374
    }
375
    size_t lenOption3 = std::min( room, total.size() / 3 );
×
376
    lineparts = packTaktic( index, end_group, lenOption3, lines );
×
377
    if ( lineparts.size() != 3 )
×
378
    {
379
      return false;
×
380
    }
381
  }
382
  // dont keep spacing over var,
383
  if ( prefferesbreak )
6✔
384
    *alignmentspace = initial_alignmentspace;
×
385
  // we found a solution
386
  if ( !alignmentspace->count( part.group ) )
6✔
387
    ( *alignmentspace )[part.group] = line.size() - indent.size();
4✔
388
  newpart = "";
6✔
389
  for ( const auto& lpart : lineparts )
18✔
390
  {
391
    if ( line.empty() )
12✔
392
    {
393
      line = alignmentSpacing( ( *alignmentspace )[part.firstgroup] );
6✔
394
      line += indent;
6✔
395
    }
396
    line += lpart;
12✔
397
    newpart += lpart;
12✔
398
    stripline( line );
12✔
399
    finallines->emplace_back( std::move( line ) );
12✔
400
    line.clear();
12✔
401
  }
402
  return true;
6✔
403
}
28✔
404

405
std::vector<std::string> PrettifyLineBuilder::createBasedOnGroups(
15✔
406
    std::vector<FmtToken>& lines ) const
407
{
408
  // TODO align closing brackets better
409
  std::vector<std::string> finallines;
15✔
410
  std::string line;
15✔
411
  // store for each group the alignment
412
  std::map<size_t, size_t> alignmentspace = {};
15✔
413
  std::map<size_t, size_t> initial_alignmentspace = {};
15✔
414
  size_t lastgroup = 0xffffFFFF;
15✔
415
  bool newline = false;
15✔
416
  auto indent = indentSpacing();
15✔
417
  line = indent;
15✔
418
  bool groupdiffered = false;
15✔
419
  size_t i = 0, skipuntil = 0, tried_binpack_until = 0;
15✔
420
  for ( auto& part : lines )
227✔
421
  {
422
    if ( skipuntil > i )  // already handled during binpack
212✔
423
    {
424
      ++i;
56✔
425
      continue;
56✔
426
    }
427
    // it was a binpack before
428
    if ( line.empty() && !finallines.empty() )
156✔
429
    {
430
      // add a following comment to the line if it fits
431
      if ( part.context == FmtContext::LINE_COMMENT || part.context == FmtContext::COMMENT )
18✔
432
      {
433
        if ( part.text.size() + finallines.back().size() < compilercfg.FormatterLineWidth )
×
434
        {
435
          // if the last one was forced (comment) let the default behaviour later do it
436
          if ( !( lines[i - 1].style & FmtToken::FORCED_BREAK ) )
×
437
          {
438
            if ( lines[i - 1].style & FmtToken::SPACE )
×
439
              finallines.back() += ' ';  // needed since already stripped
×
440
            finallines.back() += part.text;
×
441
            stripline( finallines.back() );
×
442
            line = indent;
×
443
            ++i;
×
444
            continue;
×
445
          }
446
        }
447
      }
448
      // last element still fits
449
      if ( i + 1 == lines.size() &&
24✔
450
           ( part.text.size() + finallines.back().size() <= compilercfg.FormatterLineWidth ) )
6✔
451
      {
452
        if ( !( lines[i - 1].style & FmtToken::FORCED_BREAK ) )
6✔
453
        {
454
          if ( lines[i - 1].style & FmtToken::SPACE )
6✔
455
            finallines.back() += ' ';  // needed since already stripped
6✔
456
          finallines.back() += part.text;
6✔
457
          stripline( finallines.back() );
6✔
458
          ++i;
6✔
459
          continue;
6✔
460
        }
461
      }
462
    }
463
    if ( line.empty() && !alignmentspace.empty() )
150✔
464
    {
465
      if ( alignmentspace.find( part.firstgroup ) == alignmentspace.end() )
12✔
466
        alignmentspace[part.firstgroup] = alignmentspace[part.firstgroup - 1];
×
467
      line = alignmentSpacing( alignmentspace[part.firstgroup] );
12✔
468
      line += indent;
12✔
469
    }
470
    if ( part.style & FmtToken::FORCED_BREAK )
150✔
471
    {
472
      auto nonspace = line.find_first_not_of( " \t" );
×
473
      line += part.text;
×
474
      // do not indent comments if they start at the beginning
475
      if ( part.context == FmtContext::LINE_COMMENT || part.context == FmtContext::COMMENT )
×
476
      {
477
        if ( part.pos.character_column < 4 &&
×
478
             nonspace ==
479
                 std::string::npos )  // TODO startpos does not include original space indent
480
        {
481
          auto nonspace_part = part.text.find_first_not_of( " \t" );
×
482
          // is the comment really at the beginning
483
          if ( nonspace_part != std::string::npos && part.text[nonspace_part] == '/' )
×
484
            line = part.text;
×
485
        }
486
      }
487
      stripline( line );
×
488
      finallines.emplace_back( std::move( line ) );
×
489
      line.clear();
×
490
      line = alignmentSpacing( alignmentspace[part.firstgroup] );
×
491
      line += indent;
×
492
      ++i;
×
493
      // dont keep spacing over var,
494
      if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
×
495
        alignmentspace = initial_alignmentspace;
×
496
      continue;
×
497
    }
×
498
    if ( lastgroup == 0xffffFFFF )
150✔
499
    {
500
      line += part.text;  // first part
15✔
501
      alignmentspace[part.firstgroup] = line.size() - indent.size();
15✔
502
      alignmentspace[part.group] = line.size() - indent.size();
15✔
503
      initial_alignmentspace = alignmentspace;
15✔
504
      newline = true;
15✔
505
#ifdef DEBUG_FORMAT_BREAK
506
      INFO_PRINTLN( "first {} {} a{}", line, part.firstgroup, alignmentspace );
507
#endif
508
      ++i;
15✔
509
      lastgroup = part.firstgroup;
15✔
510
      // dont keep spacing over var,
511
      if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
15✔
512
        alignmentspace = initial_alignmentspace;
×
513
      continue;
15✔
514
    }
515
    if ( tried_binpack_until <= i )
135✔
516
    {
517
      size_t upto = lines.size() - 1;
126✔
518
      // check for nested groups, binpack is not allowed for these eg array of struct
519
      for ( size_t j = i + 1; j < lines.size(); ++j )
475✔
520
      {
521
        if ( lines[j].group > part.group )
448✔
522
        {
523
          upto = 0;
99✔
524
          break;
99✔
525
        }
526
      }
527
      bool invarpack = false;
126✔
528
      if ( upto == 0 && lines[i ? i - 1 : 0].style & FmtToken::PREFERRED_BREAK_VAR )
126✔
529
      {
530
        // multiple variables in a single statement, find matching groups and pack these
531
        for ( size_t j = i + 1; j < lines.size(); ++j )
×
532
        {
533
          // we are not inside of some group and eg array starts
534
          if ( lastgroup == 0 && part.group == 1 )
×
535
          {
536
            if ( lines[j].group > 2 )  // entries are 2
×
537
              break;
×
538
          }
539
          else if ( lines[j].group > part.group )
×
540
          {
541
            break;
×
542
          }
543
          if ( lines[j].group != part.group )
×
544
            if ( lines[j].style & FmtToken::PREFERRED_BREAK_VAR )
×
545
              break;  // stop at var, when another group starts
×
546
          if ( lines[j].style & FmtToken::PREFERRED_BREAK_VAR )
×
547
            upto = j;
×
548
        }
549
        if ( upto > 0 )
×
550
        {
551
          invarpack = true;  // retry with newline
×
552
        }
553
      }
554
      if ( upto > 0 && i != upto )
126✔
555
      {
556
#ifdef DEBUG_FORMAT_BREAK
557
        INFO_PRINTLN( "trybinpack {}->{} {}->{}", i, upto, lines[i].text, lines[upto].text );
558
#endif
559
        size_t skip;
560
        std::string newpart;
27✔
561
        if ( binPack( part, line, i, upto, lines, tried_binpack_until > 0, &finallines,
27✔
562
                      &alignmentspace, &skip, initial_alignmentspace, newpart ) )
563
        {
564
#ifdef DEBUG_FORMAT_BREAK
565
          INFO_PRINTLN( "binpack {}->{} {}->{}", i, upto, lines[i].text, lines[skip - 1].text );
566
#endif
567
          skipuntil = skip;
25✔
568
          line.clear();
25✔
569
          ++i;
25✔
570
          // dont keep spacing over var,
571
          if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
25✔
572
            alignmentspace = initial_alignmentspace;
×
573
          continue;
25✔
574
        }
575
        if ( invarpack && !line.empty() )
2✔
576
        {
577
          // try it again when starting a new line
578
          std::string oldline = line;
×
579
          stripline( line );
×
580
          if ( !line.empty() )
×
581
            finallines.emplace_back( std::move( line ) );
×
582
          line.clear();
×
583
          line = alignmentSpacing( alignmentspace[part.firstgroup] );
×
584
          line += indent;
×
585
          std::string tmp;
×
586
          if ( binPack( part, line, i, upto, lines, tried_binpack_until > 0, &finallines,
×
587
                        &alignmentspace, &skip, initial_alignmentspace, tmp ) )
588
          {
589
#ifdef DEBUG_FORMAT_BREAK
590
            INFO_PRINTLN( "binpack2 {}->{} {}->{}", i, upto, lines[i].text, lines[skip - 1].text );
591
#endif
592
            skipuntil = skip;
×
593
            line.clear();
×
594
            ++i;
×
595
            // dont keep spacing over var,
596
            if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
×
597
              alignmentspace = initial_alignmentspace;
×
598
            continue;
×
599
          }
600

NEW
601
          line = oldline;
×
NEW
602
          finallines.pop_back();
×
UNCOV
603
        }
×
604
        tried_binpack_until = skip;
2✔
605
      }
27✔
606
    }
607
    if ( lines[i].context == FmtContext::PREFERRED_BREAK_START )
110✔
608
    {
609
      // try to pack function calls, by modifying the current part
610
      size_t upto = lines.size() - 1;
1✔
611
      for ( size_t j = i + 1; j < lines.size(); ++j )
1✔
612
      {
613
        if ( lines[j].context == FmtContext::PREFERRED_BREAK_END )
1✔
614
        {
615
          upto = j;
1✔
616
          break;
1✔
617
        }
618
      }
619
#ifdef DEBUG_FORMAT_BREAK
620
      INFO_PRINTLN( "trybinpackfunc {}->{} {}->{}", i, upto, lines[i].text, lines[upto].text );
621
#endif
622
      size_t skip;
623
      std::string oldline = line;
1✔
624
      std::string newpart;
1✔
625
      if ( binPack( part, line, i, upto, lines, true, &finallines, &alignmentspace, &skip,
1✔
626
                    initial_alignmentspace, newpart ) )
627
      {
628
#ifdef DEBUG_FORMAT_BREAK
629
        INFO_PRINTLN( "binpackfunc {}->{} {}->{}", i, upto, lines[i].text, lines[skip - 1].text );
630
#endif
631
        skipuntil = skip;
1✔
632
        // dont keep spacing over var,
633
        if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
1✔
634
          alignmentspace = initial_alignmentspace;
×
635
        // use the packed text as current part
636
        part.text = newpart;
1✔
637
        finallines.pop_back();
1✔
638
        line = oldline;
1✔
639
        part.group = lines[skip].group;
1✔
640
      }
641
    }
1✔
642
    if ( lastgroup < part.firstgroup )  // new group
110✔
643
    {
644
      groupdiffered = true;
29✔
645
      bool newgroup = false;
29✔
646
      if ( !alignmentspace.count( part.firstgroup ) )
29✔
647
      {
648
        newgroup = true;
22✔
649
        alignmentspace[part.firstgroup] = line.size() - indent.size();
22✔
650
      }
651
      // if its not a new group and not a fresh line start a new one
652
      // nested structs eg should start at the same line
653
      if ( !newgroup && !newline )
29✔
654
      {
655
        stripline( line );
3✔
656
        if ( !line.empty() )
3✔
657
          finallines.emplace_back( std::move( line ) );
3✔
658
        line = alignmentSpacing( alignmentspace[part.firstgroup] );
3✔
659
        line += indent;
3✔
660
        line += part.text;
3✔
661
        newline = true;
3✔
662
#ifdef DEBUG_FORMAT_BREAK
663
        INFO_PRINTLN( "!new {} - {}", line, alignmentspace );
664
#endif
665
      }
666
      else
667
      {
668
        line += part.text;
26✔
669
#ifdef DEBUG_FORMAT_BREAK
670
        INFO_PRINTLN( "new {} - {}", line, alignmentspace );
671
#endif
672
      }
673
      if ( !alignmentspace.count( part.group ) )
29✔
674
        alignmentspace[part.group] = line.size() - indent.size();
×
675
    }
676
    else if ( lastgroup > part.firstgroup )  // descending
81✔
677
    {
678
      newline = true;
9✔
679
      stripline( line );
9✔
680
      if ( !line.empty() )
9✔
681
        finallines.emplace_back( std::move( line ) );
9✔
682
      line = alignmentSpacing( alignmentspace[part.firstgroup] );
9✔
683
      line += indent;
9✔
684
      line += part.text;
9✔
685
#ifdef DEBUG_FORMAT_BREAK
686
      INFO_PRINTLN( "desc {} - {}", line, alignmentspace );
687
#endif
688
    }
689
    else  // same group
690
    {
691
      // only if we already had a different group split the line
692
      bool closing = false;
72✔
693
      if ( !part.text.empty() )
72✔
694
        closing = part.text[0] == ')';  // shouldnt force a new line
72✔
695
      if ( groupdiffered && !closing )
72✔
696
      {
697
        newline = false;
66✔
698
        stripline( line );
66✔
699
        if ( !line.empty() )
66✔
700
          finallines.emplace_back( std::move( line ) );
61✔
701
        line = alignmentSpacing( alignmentspace[part.firstgroup] );
66✔
702
        line += indent;
66✔
703
      }
704
      line += part.text;
72✔
705
#ifdef DEBUG_FORMAT_BREAK
706
      INFO_PRINTLN( "same {} - {} {} {}", line, alignmentspace, groupdiffered, part.text );
707
#endif
708
      if ( line.size() > compilercfg.FormatterLineWidth )
72✔
709
      {
710
        stripline( line );
×
711
        if ( !line.empty() )
×
712
          finallines.emplace_back( std::move( line ) );
×
713
        line = alignmentSpacing( alignmentspace[part.firstgroup] );
×
714
        line += indent;
×
715
        // dont keep spacing over var,
716
        if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
×
717
          alignmentspace = initial_alignmentspace;
×
718
      }
719
    }
720

721
    lastgroup = part.firstgroup;
110✔
722
    ++i;
110✔
723
  }
724
  if ( !line.empty() )
15✔
725
  {
726
#ifdef DEBUG_FORMAT_BREAK
727
    INFO_PRINTLN( "remaining {}", line );
728
#endif
729
    // look for something like ); and add it to the last line
730
    if ( auto nonspace = line.find_first_not_of( " \t" );
2✔
731
         nonspace != std::string::npos && line.size() - nonspace < 4 )
2✔
732
    {
733
      if ( lines.size() >= 2 )
×
734
      {
735
        if ( !( lines[lines.size() - 2].style & FmtToken::FORCED_BREAK ) )
×
736
        {
737
          if ( lines[lines.size() - 2].style & FmtToken::SPACE )
×
738
            finallines.back() += ' ';  // needed since already stripped
×
739
          finallines.back() += line.erase( 0, nonspace );
×
740
          stripline( finallines.back() );
×
741
          return finallines;
×
742
        }
743
      }
744
    }
745
    stripline( line );
2✔
746
    finallines.emplace_back( std::move( line ) );
2✔
747
  }
748
  return finallines;
15✔
749
}
15✔
750

751
// helper to find the alignment of the last open parenthesis and use this as alignment for the
752
// next line
753
bool PrettifyLineBuilder::parenthesisAlign( const std::vector<std::string>& finallines,
13,447✔
754
                                            std::string& line ) const
755
{
756
  std::vector<size_t> parenthesisalign;
13,447✔
757
  for ( const auto& finalline : finallines )
13,467✔
758
  {
759
    size_t i = 0;
20✔
760
    for ( auto c : finalline )
1,895✔
761
    {
762
      if ( c == '(' )
1,875✔
763
        parenthesisalign.push_back( i );
35✔
764
      else if ( c == ')' && !parenthesisalign.empty() )
1,840✔
765
        parenthesisalign.pop_back();
14✔
766
      if ( c == '\t' )
1,875✔
767
        i += compilercfg.FormatterTabWidth;
×
768
      else
769
        ++i;
1,875✔
770
    }
771
  }
772
  if ( parenthesisalign.empty() )
13,447✔
773
    return false;
13,433✔
774

775
  // if its at the end of last line start at the beginning
776
  if ( parenthesisalign.back() >= ( finallines.back().size() - 1 ) )
14✔
777
    return true;
×
778
  size_t alignment = line.find_first_not_of( " \t" );
14✔
779
  if ( alignment == std::string::npos )
14✔
780
    alignment = line.size();
2✔
781
  if ( compilercfg.FormatterUseTabs )
14✔
782
    alignment *= compilercfg.FormatterTabWidth;
×
783
  if ( parenthesisalign.back() + ( compilercfg.FormatterBracketSpacing ? 2 : 1 ) > alignment )
14✔
784
  {
785
    auto newspace = alignmentSpacing( parenthesisalign.back() +
5✔
786
                                      ( compilercfg.FormatterBracketSpacing ? 2 : 1 ) );
5✔
787
    auto space = line.find_first_not_of( " \t" );
5✔
788
    line.erase( 0, space );
5✔
789
    line = newspace + line;
5✔
790
  }
5✔
791
  // if its a operator +-*/<> align the actual "data" so subtract 2
792
  auto space = line.find_first_not_of( " \t" );
14✔
793
  if ( space != std::string::npos && space > 1 && space + 1 < line.size() &&
12✔
794
       line[space + 1] == ' ' &&
29✔
795
       ( line[space] == '+' || line[space] == '-' || line[space] == '*' || line[space] == '/' ||
3✔
796
         line[space] == '<' || line[space] == '>' ) )
×
797
  {
798
    // TODO tabs...
799
    if ( !compilercfg.FormatterUseTabs )
3✔
800
      line.erase( 0, 2 );
3✔
801
  }
802
  return true;
14✔
803
}
13,447✔
804

805
std::vector<std::string> PrettifyLineBuilder::createBasedOnPreferredBreaks(
846✔
806
    const std::vector<FmtToken>& lines, bool logical ) const
807
{
808
  std::vector<std::string> finallines;
846✔
809
  std::string line;
846✔
810
  // first join parts until a forced/preferred break
811
  size_t alignmentspace = 0;
846✔
812
  size_t assignpos = std::string::npos;
846✔
813
  std::vector<std::tuple<std::string, int, FmtContext>> parts;
846✔
814
  std::string tmp;
846✔
815
  // if breaks exists from logical "and/or" we only split based on them
816
  // in a long if-statement we want to break line on the logical points and not mixed with
817
  // commas from functions
818
  int breakflag = logical ? FmtToken::PREFERRED_BREAK_LOGICAL : FmtToken::PREFERRED_BREAK;
846✔
819
  std::vector<size_t> parenthesisalign;
846✔
820
  FmtContext tmpcontext = FmtContext::NONE;
846✔
821
  for ( const auto& part : lines )
4,916✔
822
  {
823
    if ( !alignmentspace )
4,070✔
824
    {
825
      auto indent = indentSpacing();
846✔
826
      alignmentspace = part.text.size() + indent.size();
846✔
827
      line += indent;
846✔
828
      parts.emplace_back( part.text, FmtToken::NONE, part.context );
846✔
829
      assignpos = part.text.find( ":=" );
846✔
830
      if ( assignpos != std::string::npos )
846✔
831
        assignpos += indent.size() + 2;
120✔
832
      continue;
846✔
833
    }
846✔
834
    // if it gets to long we still have to split the line
835
    if ( tmp.size() + part.text.size() > compilercfg.FormatterLineWidth )
3,224✔
836
    {
837
      parts.emplace_back( std::move( tmp ), FmtToken::NONE, tmpcontext );
×
838
      tmp.clear();
×
839
      tmpcontext = part.context;
×
840
    }
841
    size_t i = 0;
3,224✔
842
    for ( auto c : tmp )
12,742✔
843
    {
844
      if ( c == '(' )
9,518✔
845
        parenthesisalign.push_back( i );
392✔
846
      else if ( c == ')' && !parenthesisalign.empty() )
9,126✔
847
        parenthesisalign.pop_back();
346✔
848
      ++i;
9,518✔
849
    }
850
    // something closed split it
851
    if ( parenthesisalign.empty() )
3,224✔
852
    {
853
      parts.emplace_back( std::move( tmp ), FmtToken::NONE, tmpcontext );
2,422✔
854
      tmp.clear();
2,422✔
855
      tmpcontext = part.context;
2,422✔
856
    }
857
    tmp += part.text;
3,224✔
858
    if ( tmpcontext == FmtContext::NONE )
3,224✔
859
      tmpcontext = part.context;
2,288✔
860
    else if ( tmpcontext == FmtContext::PREFERRED_BREAK_START &&
936✔
861
              part.context == FmtContext::PREFERRED_BREAK_END )
274✔
862
      tmpcontext = FmtContext::NONE;  // start and stop so clear
14✔
863

864
    if ( ( part.style & FmtToken::FORCED_BREAK ) ||
3,224✔
865
         ( part.style & FmtToken::PREFERRED_BREAK_VAR ) || ( part.style & breakflag ) )
3,159✔
866
    {
867
      parts.emplace_back( std::move( tmp ), part.style, tmpcontext );
1,558✔
868
      tmp.clear();
1,558✔
869
      if ( part.context != FmtContext::PREFERRED_BREAK_END )
1,558✔
870
        tmpcontext = part.context;
1,548✔
871
      else
872
        tmpcontext = FmtContext::NONE;
10✔
873
    }
874
  }
875
  if ( !tmp.empty() )
846✔
876
    parts.emplace_back( tmp, FmtToken::NONE, tmpcontext );
781✔
877
  // now build the actual line(s)
878
  bool alignpart = false;
846✔
879
  size_t parti = 0;
846✔
880
  for ( auto& [l, style, context] : parts )
6,453✔
881
  {
882
#ifdef DEBUG_FORMAT_BREAK
883
    INFO_PRINTLN( "'{}'{} -{} {} FILTERED", l, l.size(), style, (int)context );
884
#endif
885
    if ( line.empty() && alignmentspace && alignpart )
5,607✔
886
      line = alignmentSpacing( alignmentspace );
2✔
887

888
    if ( !logical )  // align line directly to have the correct linelen
5,607✔
889
      parenthesisAlign( finallines, line );
5,218✔
890

891
    alignpart = true;  // otherwise first part would get spacing
5,607✔
892
    bool newcontext_longer = false;
5,607✔
893
    // if we start a new parenthesis group check if it fit completely
894
    auto currlinespace = line.find_first_not_of( " \t" );
5,607✔
895
    if ( context == FmtContext::PREFERRED_BREAK_START && currlinespace != std::string::npos )
5,607✔
896
    {
897
      size_t prefferedsize = l.size();
131✔
898
      for ( size_t j = parti + 1; j < parts.size(); ++j )
228✔
899
      {
900
        prefferedsize += std::get<0>( parts[j] ).size();
225✔
901
        if ( std::get<2>( parts[j] ) == FmtContext::PREFERRED_BREAK_END )
225✔
902
        {
903
          // does not fit into current line, start a new one
904
          if ( prefferedsize + line.size() > compilercfg.FormatterLineWidth )
128✔
905
            newcontext_longer = true;
2✔
906
          break;
128✔
907
        }
908
      }
909
    }
910
    // with a margin of 75% start a new line before
911
    if ( ( ( line.size() + ( l.size() * 0.75 ) ) > compilercfg.FormatterLineWidth ) ||
5,607✔
912
         newcontext_longer )
913
    {
914
      // TODO if next is linecomment dont split now, but split comment
915
      std::string origline = line;  // parenthesisAlign modifies
13✔
916
      auto space = line.find_first_not_of( " \t" );
13✔
917
      if ( space != std::string::npos )  // line contained only alignment
13✔
918
      {
919
        if ( !logical )
11✔
920
        {
921
          if ( !parenthesisAlign( finallines, line ) )
10✔
922
          {
923
            if ( assignpos != std::string::npos )
10✔
924
            {
925
              space = line.find_first_not_of( " \t" );
×
926
              if ( space > assignpos )  // TODO operator shift of 2
×
927
                line.erase( 0, space - assignpos );
×
928
            }
929
          }
930
        }
931
        // line shrinks due to align, check again
932
        if ( newcontext_longer ||
20✔
933
             ( line.size() + ( l.size() * 0.75 ) ) > compilercfg.FormatterLineWidth )
9✔
934
        {
935
          bool skip = false;
11✔
936
          if ( parti == parts.size() - 1 )  // last
11✔
937
          {
938
            if ( l.size() < 4 ||
13✔
939
                 line.size() + l.size() <= compilercfg.FormatterLineWidth )  // small
6✔
940
              skip = true;
1✔
941
            else if ( !l.empty() && l[0] == '/' )  // comment
6✔
942
              skip = true;
3✔
943
          }
944
          else if ( auto c = line.find_last_not_of( " \t" );
4✔
945
                    c != std::string::npos &&
8✔
946
                    ( line[c] == '(' || ( finallines.empty() &&
4✔
947
                                          line.size() < compilercfg.FormatterLineWidth * 0.3 ) ) )
3✔
948
          {
949
            // a bracket just started, dont directly start a newline
950
            // or no completed line (prevents eg empty "var" line)
951
            skip = true;
1✔
952
          }
953
          if ( !skip )
11✔
954
          {
955
            stripline( line );
6✔
956
            finallines.emplace_back( std::move( line ) );
6✔
957
            line = alignmentSpacing( alignmentspace );
6✔
958
          }
959
          else
960
            line = origline;
5✔
961
        }
962
        else
963
          line = origline;
×
964
      }
965
    }
13✔
966
    line += l;
5,607✔
967
    // linewidth reached add current line, start a new one
968
    if ( line.size() > compilercfg.FormatterLineWidth || style & FmtToken::FORCED_BREAK ||
11,145✔
969
         style & FmtToken::PREFERRED_BREAK_VAR )
5,538✔
970
    {
971
      // TODO if next is linecomment dont split now, but split comment
972
      std::string origline = line;  // parenthesisAlign modifies
69✔
973
      if ( !logical )
69✔
974
      {
975
        if ( !parenthesisAlign( finallines, line ) )
43✔
976
        {
977
          if ( assignpos != std::string::npos )
41✔
978
          {
979
            auto space = line.find_first_not_of( " \t" );
×
980
            if ( space > assignpos )  // TODO operator shift of 2
×
981
              line.erase( 0, space - assignpos );
×
982
          }
983
        }
984
      }
985
      bool startnew = true;
69✔
986
      if ( style & FmtToken::FORCED_BREAK || style & FmtToken::PREFERRED_BREAK_VAR )
69✔
987
        startnew = true;
65✔
988
      else if ( line.size() < compilercfg.FormatterLineWidth )
4✔
989
        startnew = false;
×
990
      else if ( parti + 1 == parts.size() - 1 && std::get<0>( parts[parti + 1] ).size() < 4 )
4✔
991
        startnew = false;  // next is last and small so add it
×
992
      else if ( parti + 1 == parts.size() - 1 && !std::get<0>( parts[parti + 1] ).empty() &&
5✔
993
                std::get<0>( parts[parti + 1] )[0] == '/' )
1✔
994
        startnew = false;  // next is last and comment so add it
×
995
      if ( startnew )
69✔
996
      {
997
        stripline( line );
69✔
998
        finallines.emplace_back( std::move( line ) );
69✔
999
        line.clear();
69✔
1000
      }
1001
      else
1002
        line = origline;
×
1003
    }
69✔
1004
    ++parti;
5,607✔
1005
  }
1006
  if ( !line.empty() )
846✔
1007
  {
1008
    stripline( line );
779✔
1009
    if ( !logical )
779✔
1010
      parenthesisAlign( finallines, line );
728✔
1011
    finallines.emplace_back( std::move( line ) );
779✔
1012
  }
1013
  return finallines;
1,692✔
1014
}
846✔
1015

1016
std::vector<std::string> PrettifyLineBuilder::createSimple(
7,444✔
1017
    const std::vector<FmtToken>& lines ) const
1018
{
1019
  std::vector<std::string> finallines;
7,444✔
1020
  std::string line;
7,444✔
1021
  size_t alignmentspace = 0;
7,444✔
1022
  size_t i = 0;
7,444✔
1023
  for ( const auto& part : lines )
20,281✔
1024
  {
1025
    // following lines need to be aligned
1026
    if ( line.empty() && alignmentspace )
12,837✔
1027
      line = alignmentSpacing( alignmentspace );
4✔
1028
    // first breakpoint defines the alignment and add initial indent level
1029
    if ( !alignmentspace )
12,837✔
1030
    {
1031
      auto indent = indentSpacing();
7,444✔
1032
      alignmentspace = part.text.size() + indent.size();
7,444✔
1033
      line += indent;
7,444✔
1034
    }
7,444✔
1035
    line += part.text;
12,837✔
1036
    bool forcebreakVarToLong{ false };
12,837✔
1037
    // if its a var definition, check if the next var would fit in the line,
1038
    // otherwise start directly a new line
1039
    if ( part.style & FmtToken::PREFERRED_BREAK_VAR && i > 0 )
12,837✔
1040
    {
1041
      std::string next_var;
73✔
1042
      for ( size_t j = i + 1; j < lines.size(); ++j )
110✔
1043
      {
1044
        next_var += lines[j].text;
73✔
1045
        if ( lines[j].style & FmtToken::PREFERRED_BREAK_VAR )
73✔
1046
          break;
36✔
1047
      }
1048
      if ( ( line.size() + next_var.size() + alignmentSpacing( alignmentspace ).size() ) >
146✔
1049
           compilercfg.FormatterLineWidth )
73✔
1050
        forcebreakVarToLong = true;
×
1051
    }
73✔
1052
    // linewidth reached add current line, start a new one
1053
    if ( line.size() > compilercfg.FormatterLineWidth || part.style & FmtToken::FORCED_BREAK ||
12,837✔
1054
         forcebreakVarToLong )
1055
    {
1056
      // TODO if next is linecomment dont split now, but split comment
1057
      // be extra generous if we only have a few chars left
1058
      if ( ( part.style & FmtToken::FORCED_BREAK ) == 0 )
250✔
1059
      {
1060
        if ( i + 1 == lines.size() - 1 && lines[i + 1].text.size() < 4 )
14✔
1061
        {
1062
          ++i;
3✔
1063
          continue;
3✔
1064
        }
1065
      }
1066
      stripline( line );
247✔
1067
      parenthesisAlign( finallines, line );
247✔
1068
      finallines.emplace_back( std::move( line ) );
247✔
1069
      line.clear();
247✔
1070
    }
1071
    ++i;
12,834✔
1072
  }
1073
  if ( !line.empty() )
7,444✔
1074
  {
1075
    stripline( line );
7,201✔
1076
    parenthesisAlign( finallines, line );
7,201✔
1077
    finallines.emplace_back( std::move( line ) );
7,201✔
1078
  }
1079
  return finallines;
14,888✔
1080
}
7,444✔
1081

1082
void PrettifyLineBuilder::stripline( std::string& line ) const
8,655✔
1083
{
1084
  if ( line.empty() )
8,655✔
1085
    return;
×
1086
  auto lastchar = line.find_last_not_of( ' ' );
8,655✔
1087
  line.erase( line.begin() + lastchar + 1, line.end() );
8,655✔
1088
}
1089

1090
void PrettifyLineBuilder::buildLine( size_t current_indent )
8,441✔
1091
{
1092
  _currindent = current_indent;
8,441✔
1093
  if ( _line_parts.empty() )
8,441✔
1094
  {
1095
    packLines();
136✔
1096
    return;
136✔
1097
  }
1098
  mergeComments();
8,305✔
1099

1100
  // fill lines with final strings splitted at breakpoints
1101
  auto lines = buildLineSplits();
8,305✔
1102
#ifdef DEBUG_FORMAT_BREAK
1103
  INFO_PRINTLN( "BREAK" );
1104
  for ( const auto& part : lines )
1105
    INFO_PRINTLN( "\"{}\" {}-{} ->{} :{}-{}", part.text, part.group, part.firstgroup, part.style,
1106
                  (int)part.scope, (int)part.context );
1107
#endif
1108
  // add newline from original sourcecode
1109
  addEmptyLines( _line_parts.front().pos.line_number );
8,305✔
1110

1111
  // sum up linelength, are groups inside or preferred breaks
1112
  bool groups = false;
8,305✔
1113
  bool has_preferred = false;
8,305✔
1114
  bool has_preferred_logical = false;
8,305✔
1115
  size_t linelength = 0;
8,305✔
1116
  for ( const auto& part : lines )
25,424✔
1117
  {
1118
    if ( part.group != 0 )
17,119✔
1119
      groups = true;
1,527✔
1120
    if ( part.style & FmtToken::PREFERRED_BREAK )
17,119✔
1121
      has_preferred = true;
1,489✔
1122
    if ( part.style & FmtToken::PREFERRED_BREAK_LOGICAL )
17,119✔
1123
      has_preferred_logical = true;
94✔
1124
    linelength += part.text.size();
17,119✔
1125
  }
1126

1127
  std::vector<std::string> finallines;
8,305✔
1128
  // split based on groups
1129
  if ( groups && linelength > compilercfg.FormatterLineWidth )
8,305✔
1130
  {
1131
#ifdef DEBUG_FORMAT_BREAK
1132
    INFO_PRINTLN( "split groupbased" );
1133
#endif
1134
    finallines = createBasedOnGroups( lines );
15✔
1135
  }
1136
  else  // split based on parts
1137
  {
1138
    // with preferred breaks
1139
    if ( has_preferred || has_preferred_logical )
8,290✔
1140
    {
1141
#ifdef DEBUG_FORMAT_BREAK
1142
      INFO_PRINTLN( "split preferred" );
1143
#endif
1144
      finallines = createBasedOnPreferredBreaks( lines, has_preferred_logical );
846✔
1145
    }
1146
    else  // simple splitting
1147
    {
1148
#ifdef DEBUG_FORMAT_BREAK
1149
      INFO_PRINTLN( "split simple" );
1150
#endif
1151
      finallines = createSimple( lines );
7,444✔
1152
    }
1153
  }
1154
  alignComments( finallines );
8,305✔
1155
  _lines.insert( _lines.end(), std::make_move_iterator( finallines.begin() ),
8,305✔
1156
                 std::make_move_iterator( finallines.end() ) );
1157
  _last_line = _line_parts.back().pos.line_number;
8,305✔
1158
  _line_parts.clear();
8,305✔
1159

1160
  packLines();
8,305✔
1161
}
8,305✔
1162

1163
void PrettifyLineBuilder::packLines()
8,441✔
1164
{
1165
  if ( !_packableline_allowed || !_packablelineend )
8,441✔
1166
    return;
8,333✔
1167
  _packableline_allowed = false;
140✔
1168
  _packablelineend = false;
140✔
1169

1170
  // 3 lines max
1171
  // for case its "label: blubb; break;
1172
  // for funcrefs its @(){ blubb; };
1173
  if ( _lines.size() - _packablelinestart >= 4 )
140✔
1174
    return;
32✔
1175
  std::string packedline;
108✔
1176
  for ( size_t i = _packablelinestart; i < _lines.size(); ++i )
343✔
1177
  {
1178
    auto l = _lines[i];
235✔
1179
    if ( l.find( "//" ) != std::string::npos )
235✔
1180
    {
1181
      // if the line comment is not at the end we cannot pack
1182
      if ( i < _lines.size() - 1 )
13✔
1183
        return;
×
1184
    }
1185
    stripline( l );
235✔
1186
    if ( packedline.empty() )
235✔
1187
      packedline += l;
108✔
1188
    else
1189
    {
1190
      auto begin = _lines[i].find_first_not_of( " \t" );
127✔
1191
      if ( begin > 0 && begin != std::string::npos )
127✔
1192
        l.erase( 0, begin );
114✔
1193
      packedline += " " + l;
127✔
1194
    }
1195
  }
235✔
1196
  if ( packedline.empty() )
108✔
1197
    return;
×
1198

1199
  if ( packedline.size() <= compilercfg.FormatterLineWidth )
108✔
1200
  {
1201
    _lines.resize( _packablelinestart );
105✔
1202
    _lines.push_back( std::move( packedline ) );
105✔
1203
  }
1204
}
108✔
1205

1206
void PrettifyLineBuilder::alignComments( std::vector<std::string>& finallines )
8,305✔
1207
{
1208
  if ( !compilercfg.FormatterAlignTrailingComments )
8,305✔
1209
    return;
8,005✔
1210
  std::vector<size_t> commentstart;
8,305✔
1211
  // collect comment columns
1212
  for ( size_t i = 0; i < finallines.size(); ++i )
16,713✔
1213
  {
1214
    auto linecomment = finallines[i].find( "//" );
8,408✔
1215
    if ( linecomment == std::string::npos )
8,408✔
1216
      commentstart.push_back( 0 );
8,107✔
1217
    else
1218
    {
1219
      // if the line only contains a comment dont align it
1220
      auto other = finallines[i].find_first_not_of( " \t" );
301✔
1221
      if ( other == linecomment )
301✔
1222
        commentstart.push_back( 0 );
×
1223
      else
1224
        commentstart.push_back( linecomment );
301✔
1225
    }
1226
  }
1227
  if ( commentstart.empty() )
8,305✔
1228
    return;
×
1229

1230
  // add spaces at comment start foreach comment other then max
1231
  auto max = *std::max_element( commentstart.begin(), commentstart.end() );
8,305✔
1232
  if ( max == 0 )
8,305✔
1233
    return;
8,005✔
1234
  for ( size_t i = 0; i < finallines.size(); ++i )
601✔
1235
  {
1236
    if ( !commentstart[i] || max == commentstart[i] )
301✔
1237
      continue;
300✔
1238
    finallines[i].insert( commentstart[i], max - commentstart[i], ' ' );
1✔
1239
  }
1240
}
8,305✔
1241

1242
void PrettifyLineBuilder::addEmptyLines( size_t line_number )
8,951✔
1243
{
1244
  if ( compilercfg.FormatterMergeEmptyLines )
8,951✔
1245
  {
1246
    if ( line_number > _last_line + 1 )
8,951✔
1247
      _lines.emplace_back( "" );
1,834✔
1248
  }
1249
  else
1250
  {
1251
    while ( line_number > _last_line + 1 )
×
1252
    {
1253
      _lines.emplace_back( "" );
×
1254
      ++_last_line;
×
1255
    }
1256
  }
1257
}
8,951✔
1258

1259
int PrettifyLineBuilder::closingParenthesisStyle( bool args )
5,728✔
1260
{
1261
  auto style = FmtToken::SPACE | FmtToken::BREAKPOINT;
5,728✔
1262
  if ( !args )
5,728✔
1263
  {
1264
    if ( !compilercfg.FormatterEmptyParenthesisSpacing )
550✔
1265
      style |= FmtToken::ATTACHED;
550✔
1266
    else if ( !_line_parts.empty() )
×
1267
      _line_parts.back().style |= FmtToken::SPACE;
×
1268
  }
1269
  else if ( !compilercfg.FormatterParenthesisSpacing )
5,178✔
1270
    style |= FmtToken::ATTACHED;
×
1271
  return style;
5,728✔
1272
}
1273

1274
int PrettifyLineBuilder::closingBracketStyle( size_t begin_size )
603✔
1275
{
1276
  auto style = FmtToken::SPACE | FmtToken::BREAKPOINT;
603✔
1277
  if ( _line_parts.size() == begin_size )
603✔
1278
  {
1279
    if ( !compilercfg.FormatterEmptyBracketSpacing )
212✔
1280
      style |= FmtToken::ATTACHED;
212✔
1281
    else if ( !_line_parts.empty() )
×
1282
      _line_parts.back().style |= FmtToken::SPACE;
×
1283
  }
1284
  else if ( !compilercfg.FormatterBracketSpacing )
391✔
1285
    style |= FmtToken::ATTACHED;
×
1286
  return style;
603✔
1287
}
1288

1289
int PrettifyLineBuilder::openingParenthesisStyle() const
5,728✔
1290
{
1291
  return FmtToken::ATTACHED | FmtToken::BREAKPOINT |
5,728✔
1292
         ( compilercfg.FormatterParenthesisSpacing ? FmtToken::SPACE : FmtToken::NONE );
11,456✔
1293
}
1294

1295
int PrettifyLineBuilder::openingBracketStyle( bool typeinit, bool force_unattached )
603✔
1296
{
1297
  if ( ( typeinit && !compilercfg.FormatterBracketAttachToType ) || force_unattached )
603✔
1298
    return FmtToken::BREAKPOINT |
59✔
1299
           ( compilercfg.FormatterBracketSpacing ? FmtToken::SPACE : FmtToken::NONE );
118✔
1300
  return FmtToken::ATTACHED | FmtToken::BREAKPOINT |
544✔
1301
         ( compilercfg.FormatterBracketSpacing ? FmtToken::SPACE : FmtToken::NONE );
1,088✔
1302
}
1303

1304
int PrettifyLineBuilder::delimiterStyle() const
2,079✔
1305
{
1306
  return FmtToken::ATTACHED | FmtToken::BREAKPOINT |
2,079✔
1307
         ( compilercfg.FormatterDelimiterSpacing ? FmtToken::SPACE : FmtToken::NONE );
4,158✔
1308
}
1309

1310
int PrettifyLineBuilder::terminatorStyle() const
5,730✔
1311
{
1312
  return FmtToken::SPACE | FmtToken::ATTACHED | FmtToken::BREAKPOINT;
5,730✔
1313
}
1314

1315
int PrettifyLineBuilder::assignmentStyle() const
1,805✔
1316
{
1317
  if ( !compilercfg.FormatterAssignmentSpacing )
1,805✔
1318
    return FmtToken::ATTACHED;
×
1319
  return FmtToken::SPACE;
1,805✔
1320
}
1321

1322
int PrettifyLineBuilder::comparisonStyle() const
458✔
1323
{
1324
  if ( !compilercfg.FormatterComparisonSpacing )
458✔
1325
    return FmtToken::ATTACHED;
×
1326
  return FmtToken::SPACE;
458✔
1327
}
1328

1329
int PrettifyLineBuilder::ellipsisStyle() const
283✔
1330
{
1331
  if ( !compilercfg.FormatterEllipsisSpacing )
283✔
1332
    return FmtToken::ATTACHED | FmtToken::SPACE;
283✔
1333
  return FmtToken::SPACE;
×
1334
}
1335

1336
int PrettifyLineBuilder::operatorStyle() const
881✔
1337
{
1338
  if ( !compilercfg.FormatterOperatorSpacing )
881✔
1339
    return FmtToken::ATTACHED;
×
1340
  return FmtToken::SPACE;
881✔
1341
}
1342

1343
std::string PrettifyLineBuilder::indentSpacing() const
8,973✔
1344
{
1345
  if ( !compilercfg.FormatterUseTabs )
8,973✔
1346
    return std::string( _currindent * compilercfg.FormatterIndentLevel, ' ' );
8,973✔
1347
  size_t total = _currindent * compilercfg.FormatterIndentLevel;
×
1348
  size_t tabs = total / compilercfg.FormatterTabWidth;
×
1349
  size_t remaining = total % compilercfg.FormatterTabWidth;
×
1350
  return std::string( tabs, '\t' ) + std::string( remaining, ' ' );
×
1351
}
1352

1353
std::string PrettifyLineBuilder::alignmentSpacing( size_t count ) const
186✔
1354
{
1355
  if ( !count )
186✔
1356
    return {};
×
1357
  if ( !compilercfg.FormatterUseTabs )
186✔
1358
    return std::string( count, ' ' );
186✔
1359
  size_t tabs = count / compilercfg.FormatterTabWidth;
×
1360
  size_t remaining = count % compilercfg.FormatterTabWidth;
×
1361
  return std::string( tabs, '\t' ) + std::string( remaining, ' ' );
×
1362
}
1363

1364
void PrettifyLineBuilder::mergeEOFNonTokens()
660✔
1365
{
1366
  mergeRawContent( _last_line );
660✔
1367
  while ( !_comments.empty() )
713✔
1368
  {
1369
    addEmptyLines( _comments.front().pos.line_number );
53✔
1370
    _lines.push_back( indentSpacing() + _comments.front().text );
53✔
1371
    _last_line = _comments.front().pos_end.line_number;
53✔
1372
    mergeRawContent( _last_line );
53✔
1373
    _comments.erase( _comments.begin() );
53✔
1374
  }
1375
  // purge raw content
1376
  mergeRawContent( std::numeric_limits<size_t>::max() );
660✔
1377
  // if the original file ends with a newline, make sure to also end
1378
  if ( !_lines.empty() && !_lines.back().empty() && !_rawlines.empty() && _rawlines.back().empty() )
660✔
1379
    _lines.emplace_back( "" );
5✔
1380
}
660✔
1381

1382
void PrettifyLineBuilder::markPackableLineStart()
186✔
1383
{
1384
  _packablelinestart = _lines.size();
186✔
1385
  _packableline_allowed = true;
186✔
1386
  _packablelineend = false;
186✔
1387
}
186✔
1388
void PrettifyLineBuilder::markPackableLineEnd()
187✔
1389
{
1390
  _packablelineend = true;
187✔
1391
}
187✔
1392

1393
void PrettifyLineBuilder::markLastTokensAsSwitchLabel()
93✔
1394
{
1395
  if ( _line_parts.size() < 2 )
93✔
1396
    return;
×
1397
  _packable_switch_labels.push_back( _line_parts[_line_parts.size() - 2].text +
93✔
1398
                                     _line_parts[_line_parts.size() - 1].text );
93✔
1399
}
1400
void PrettifyLineBuilder::alignSingleLineSwitchStatements( size_t start )
28✔
1401
{
1402
  if ( !compilercfg.FormatterAlignConsecutiveShortCaseStatements ||
28✔
1403
       !compilercfg.FormatterAllowShortCaseLabelsOnASingleLine )
28✔
1404
    return;
3✔
1405
  size_t l_index = 0;
28✔
1406
  std::vector<std::pair<size_t, size_t>> statementstart;
28✔
1407
  std::optional<std::pair<size_t, size_t>> default_start;
28✔
1408
  // collect label starts based on stored labels
1409
  // default: is collected extra
1410
  for ( size_t i = start; i < _lines.size(); ++i )
139✔
1411
  {
1412
    if ( _packable_switch_labels.size() <= l_index )
114✔
1413
      break;
3✔
1414
    const auto& label = _packable_switch_labels[l_index];
111✔
1415
    auto labelend = _lines[i].find( label );
111✔
1416
    if ( labelend == std::string::npos )
111✔
1417
      continue;
23✔
1418
    // there should be only whitespace before (could be in a comment)
1419
    bool empty{ true };
88✔
1420
    for ( size_t w = 0; w < labelend; ++w )
410✔
1421
    {
1422
      if ( _lines[i][w] != ' ' && _lines[i][w] != '\t' )
323✔
1423
      {
1424
        empty = false;
1✔
1425
        break;
1✔
1426
      }
1427
    }
1428
    if ( !empty )
88✔
1429
      continue;
1✔
1430

1431
    ++l_index;  // ~valid match, so next one
87✔
1432
    // nothing following no need to align or consider as max
1433
    auto next = _lines[i].find_first_not_of( " \t", labelend + label.size() );
87✔
1434
    if ( next == std::string::npos )
87✔
1435
      continue;
8✔
1436

1437
    if ( label == "default:" )
79✔
1438
      default_start = std::make_pair( i, labelend + label.size() );
13✔
1439
    else
1440
      statementstart.emplace_back( i, labelend + label.size() );
66✔
1441
  }
1442
  _packable_switch_labels.clear();
28✔
1443
  if ( statementstart.empty() )
28✔
1444
    return;
3✔
1445

1446
  auto max = std::max_element( statementstart.begin(), statementstart.end(),
25✔
1447
                               []( auto& a, auto& b ) { return a.second < b.second; } )
41✔
1448
                 ->second;
25✔
1449
  if ( max == 0 )
25✔
1450
    return;
×
1451
  for ( auto& [line, pos] : statementstart )
91✔
1452
  {
1453
    if ( pos == max )
66✔
1454
      continue;
40✔
1455
    _lines[line].insert( pos, max - pos, ' ' );
26✔
1456
  }
1457
  // we have a default, but only align it if its not the "biggest"
1458
  if ( default_start )
25✔
1459
  {
1460
    if ( max > default_start->second )
13✔
1461
      _lines[default_start->first].insert( default_start->second, max - default_start->second,
6✔
1462
                                           ' ' );
1463
  }
1464
}
28✔
1465

1466
}  // namespace Pol::Bscript::Compiler
1467

1468
fmt::format_context::iterator fmt::formatter<Pol::Bscript::Compiler::FmtToken>::format(
×
1469
    const Pol::Bscript::Compiler::FmtToken& t, fmt::format_context& ctx ) const
1470
{
1471
  return fmt::formatter<std::string>::format(
×
1472
      fmt::format( "{} ({}:{}:{})", t.text, t.style, t.pos.line_number, t.pos.token_index ), ctx );
×
1473
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc