2024-02-26 21:21:44 +01:00
|
|
|
#include "PrettifyLineBuilder.h"
|
|
|
|
|
|
|
|
|
|
#include "bscript/compilercfg.h"
|
|
|
|
|
#include "clib/filecont.h"
|
|
|
|
|
#include "clib/logfacility.h"
|
|
|
|
|
#include <EscriptGrammar/EscriptLexer.h>
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
// #define DEBUG_FORMAT_BREAK
|
|
|
|
|
|
|
|
|
|
namespace Pol::Bscript::Compiler
|
|
|
|
|
{
|
|
|
|
|
using namespace EscriptGrammar;
|
|
|
|
|
|
|
|
|
|
const std::vector<std::string>& PrettifyLineBuilder::formattedLines() const
|
|
|
|
|
{
|
|
|
|
|
return _lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::setRawLines( std::vector<std::string> rawlines )
|
|
|
|
|
{
|
|
|
|
|
_rawlines = std::move( rawlines );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::setComments( std::vector<FmtToken> comments )
|
|
|
|
|
{
|
|
|
|
|
_comments = std::move( comments );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::setSkipLines( std::vector<Range> skiplines )
|
|
|
|
|
{
|
|
|
|
|
_skiplines = std::move( skiplines );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::addPart( FmtToken part )
|
|
|
|
|
{
|
|
|
|
|
for ( const auto& skip : _skiplines )
|
|
|
|
|
if ( skip.contains( part.pos ) )
|
|
|
|
|
return;
|
|
|
|
|
_line_parts.emplace_back( std::move( part ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PrettifyLineBuilder::finalize()
|
|
|
|
|
{
|
|
|
|
|
mergeEOFNonTokens();
|
|
|
|
|
return _line_parts.empty() && _comments.empty() && _skiplines.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<FmtToken>& PrettifyLineBuilder::currentTokens() const
|
|
|
|
|
{
|
|
|
|
|
return _line_parts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::mergeRawContent( size_t nextlineno )
|
|
|
|
|
{
|
|
|
|
|
for ( auto itr = _skiplines.begin(); itr != _skiplines.end(); )
|
|
|
|
|
{
|
|
|
|
|
if ( itr->start.line_number < nextlineno )
|
|
|
|
|
{
|
|
|
|
|
mergeCommentsBefore( itr->start.line_number );
|
|
|
|
|
addEmptyLines( itr->start.line_number );
|
|
|
|
|
for ( size_t i = itr->start.line_number - 1; i < itr->end.line_number && i < _rawlines.size();
|
|
|
|
|
++i )
|
|
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
_packableline_allowed = false;
|
2024-02-26 21:21:44 +01:00
|
|
|
_lines.emplace_back( _rawlines[i] );
|
|
|
|
|
_last_line = i + 1;
|
|
|
|
|
}
|
|
|
|
|
itr = _skiplines.erase( itr );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
++itr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::mergeCommentsBefore( size_t nextlineno )
|
|
|
|
|
{
|
|
|
|
|
// add comments before current line
|
|
|
|
|
while ( !_comments.empty() )
|
|
|
|
|
{
|
|
|
|
|
if ( _comments.front().pos.line_number < nextlineno )
|
|
|
|
|
{
|
|
|
|
|
addEmptyLines( _comments.front().pos.line_number );
|
2024-09-13 06:49:00 +02:00
|
|
|
_lines.push_back( indentSpacing() + _comments.front().text );
|
|
|
|
|
if ( _packableline_allowed &&
|
|
|
|
|
_packablelinestart ==
|
|
|
|
|
_lines.size() - 1 ) // potential packed line not yet build, so safe to add a comment
|
|
|
|
|
++_packablelinestart;
|
|
|
|
|
else if ( _comments.front().style & FmtToken::FORCED_BREAK )
|
|
|
|
|
_packableline_allowed = false;
|
|
|
|
|
|
2024-02-26 21:21:44 +01:00
|
|
|
_last_line = _comments.front().pos_end.line_number;
|
|
|
|
|
_comments.erase( _comments.begin() );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::mergeComments()
|
|
|
|
|
{
|
|
|
|
|
if ( _line_parts.empty() )
|
|
|
|
|
return;
|
|
|
|
|
mergeRawContent( _line_parts.front().pos.line_number );
|
|
|
|
|
mergeCommentsBefore( _line_parts.front().pos.line_number );
|
|
|
|
|
// add comments inbetween
|
|
|
|
|
for ( size_t i = 0; i < _line_parts.size(); )
|
|
|
|
|
{
|
|
|
|
|
if ( _comments.empty() )
|
|
|
|
|
break;
|
|
|
|
|
if ( _line_parts[i].pos.token_index > _comments.front().pos.token_index )
|
|
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
_packableline_allowed = false;
|
2024-02-26 21:21:44 +01:00
|
|
|
auto info = _comments.front();
|
2024-09-13 06:49:00 +02:00
|
|
|
info.group = i ? _line_parts[i - 1].group : _currentgroup;
|
2024-02-26 21:21:44 +01:00
|
|
|
_line_parts.insert( _line_parts.begin() + i, std::move( info ) );
|
|
|
|
|
_comments.erase( _comments.begin() );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
// add comments at the end of the current line
|
|
|
|
|
if ( !_comments.empty() )
|
|
|
|
|
{
|
|
|
|
|
if ( _comments.front().pos.line_number == _line_parts.back().pos.line_number &&
|
|
|
|
|
_comments.front().pos.token_index <= _line_parts.back().pos.token_index + 1 )
|
|
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
// no disable of packableline since its at the end
|
2024-02-26 21:21:44 +01:00
|
|
|
addPart( _comments.front() );
|
|
|
|
|
_comments.erase( _comments.begin() );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
std::vector<FmtToken> PrettifyLineBuilder::buildLineSplits()
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
std::vector<FmtToken> lines;
|
2024-08-11 22:09:30 +02:00
|
|
|
bool has_varcomma = false; // only if a "var," exists split based on these, otherwise var
|
|
|
|
|
// statement would be splitted
|
2026-01-31 09:14:02 +01:00
|
|
|
for ( auto& _line_part : _line_parts )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2026-01-31 09:14:02 +01:00
|
|
|
if ( _line_part.context == FmtContext::VAR_COMMA )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
has_varcomma = true;
|
|
|
|
|
break;
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
FmtToken part;
|
2024-08-11 22:09:30 +02:00
|
|
|
for ( size_t i = 0; i < _line_parts.size(); ++i )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
2024-10-03 15:10:21 +02:00
|
|
|
INFO_PRINTLN( "\"{}\" {} {}", _line_parts[i].text, _line_parts[i].group, _line_parts[i].style );
|
2024-09-13 06:49:00 +02:00
|
|
|
#endif
|
|
|
|
|
if ( part.text.empty() )
|
|
|
|
|
{
|
|
|
|
|
part.firstgroup = _line_parts[i].group;
|
|
|
|
|
part.pos = _line_parts[i].pos;
|
|
|
|
|
part.scope = _line_parts[i].scope;
|
|
|
|
|
part.context = _line_parts[i].context;
|
|
|
|
|
}
|
|
|
|
|
part.text += _line_parts[i].text;
|
|
|
|
|
if ( part.context == FmtContext::NONE )
|
|
|
|
|
part.context = _line_parts[i].context;
|
2024-08-11 22:09:30 +02:00
|
|
|
// add space if set, but not if the following part is attached
|
|
|
|
|
if ( _line_parts[i].style & FmtToken::SPACE )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( i + 1 < _line_parts.size() )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( !( _line_parts[i + 1].style & FmtToken::ATTACHED ) )
|
2024-09-13 06:49:00 +02:00
|
|
|
part.text += ' ';
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
else
|
2024-09-13 06:49:00 +02:00
|
|
|
part.text += ' ';
|
|
|
|
|
// if something is left and not attached
|
|
|
|
|
if ( i + 1 < _line_parts.size() )
|
|
|
|
|
{
|
|
|
|
|
if ( !( _line_parts[i + 1].style & FmtToken::ATTACHED ) )
|
|
|
|
|
{
|
|
|
|
|
// if its already quite long split it
|
|
|
|
|
if ( part.text.size() > compilercfg.FormatterLineWidth * 0.5 )
|
|
|
|
|
{
|
|
|
|
|
part.style = _line_parts[i].style;
|
|
|
|
|
part.group = _line_parts[i].group;
|
|
|
|
|
part.pos_end = _line_parts[i].pos_end;
|
|
|
|
|
lines.emplace_back( std::move( part ) );
|
|
|
|
|
part = FmtToken{};
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
// if a forced break does not appear at the end disallow merging the lines
|
|
|
|
|
if ( _line_parts[i].style & FmtToken::FORCED_BREAK && i < _line_parts.size() - 1 )
|
|
|
|
|
_packableline_allowed = false;
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( _line_parts[i].style & FmtToken::FORCED_BREAK ||
|
|
|
|
|
( has_varcomma && ( _line_parts[i].context == FmtContext::VAR_STATEMENT ||
|
|
|
|
|
_line_parts[i].context == FmtContext::VAR_COMMA ) ) )
|
|
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
// need to break directly after "var" to align multiple variables
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( _line_parts[i].context == FmtContext::VAR_STATEMENT ||
|
|
|
|
|
_line_parts[i].context == FmtContext::VAR_COMMA )
|
|
|
|
|
_line_parts[i].style |= FmtToken::PREFERRED_BREAK_VAR;
|
2024-09-13 06:49:00 +02:00
|
|
|
part.style = _line_parts[i].style;
|
|
|
|
|
part.group = _line_parts[i].group;
|
|
|
|
|
part.pos_end = _line_parts[i].pos_end;
|
|
|
|
|
lines.emplace_back( std::move( part ) );
|
|
|
|
|
part = FmtToken{};
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
// start a new line if breakpoint
|
|
|
|
|
else if ( _line_parts[i].style & FmtToken::BREAKPOINT )
|
|
|
|
|
{
|
|
|
|
|
// if the next part is attached, dont break now and instead later
|
|
|
|
|
// eg dont split blubb[1]()
|
|
|
|
|
bool skip = false;
|
|
|
|
|
if ( i + 1 < _line_parts.size() )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
// next one is attached
|
|
|
|
|
if ( _line_parts[i + 1].style & FmtToken::ATTACHED )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
// search for space or breakpoint
|
|
|
|
|
for ( size_t j = i + 1; j < _line_parts.size(); ++j )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( _line_parts[j].style & FmtToken::ATTACHED )
|
|
|
|
|
skip = true;
|
|
|
|
|
if ( _line_parts[j].style & FmtToken::SPACE ||
|
|
|
|
|
_line_parts[j].style & FmtToken::BREAKPOINT )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
_line_parts[j].style |= FmtToken::BREAKPOINT;
|
|
|
|
|
skip = true;
|
|
|
|
|
break;
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( skip )
|
|
|
|
|
continue;
|
2024-09-13 06:49:00 +02:00
|
|
|
part.style = _line_parts[i].style;
|
|
|
|
|
part.group = _line_parts[i].group;
|
|
|
|
|
part.pos_end = _line_parts[i].pos_end;
|
|
|
|
|
lines.emplace_back( std::move( part ) );
|
|
|
|
|
part = FmtToken{};
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
// add remainings
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( !part.text.empty() )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
part.style = _line_parts.back().style;
|
|
|
|
|
part.group = _line_parts.back().group;
|
|
|
|
|
part.pos_end = _line_parts.back().pos_end;
|
|
|
|
|
lines.emplace_back( std::move( part ) );
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
bool PrettifyLineBuilder::binPack( const FmtToken& part, std::string line, size_t index,
|
|
|
|
|
size_t upto, const std::vector<FmtToken>& lines,
|
|
|
|
|
bool only_single_line, std::vector<std::string>* finallines,
|
|
|
|
|
std::map<size_t, size_t>* alignmentspace, size_t* skipindex,
|
2024-10-03 15:10:21 +02:00
|
|
|
const std::map<size_t, size_t>& initial_alignmentspace,
|
|
|
|
|
std::string& newpart ) const
|
2024-09-13 06:49:00 +02:00
|
|
|
{
|
|
|
|
|
auto packTaktic =
|
|
|
|
|
[]( size_t index, size_t end_group, size_t max_len, const std::vector<FmtToken>& lines )
|
|
|
|
|
{
|
|
|
|
|
// fill up lines with given max_len
|
|
|
|
|
std::string currline;
|
|
|
|
|
std::vector<std::string> lineparts;
|
|
|
|
|
for ( size_t j = index; j < end_group; ++j )
|
|
|
|
|
{
|
|
|
|
|
const auto& lpart = lines[j].text;
|
|
|
|
|
if ( lines[j].style & FmtToken::FORCED_BREAK )
|
|
|
|
|
{
|
|
|
|
|
currline += lpart;
|
|
|
|
|
lineparts.push_back( std::move( currline ) );
|
|
|
|
|
currline.clear();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// new part still fits, add to line
|
|
|
|
|
if ( currline.size() + lpart.size() <= max_len )
|
|
|
|
|
{
|
|
|
|
|
currline += lpart;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// we are already at end, start a new line
|
|
|
|
|
if ( currline.size() >= max_len )
|
|
|
|
|
{
|
|
|
|
|
lineparts.push_back( std::move( currline ) );
|
|
|
|
|
currline = lpart;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// we were smaller, add it anyway and start a new line
|
|
|
|
|
// the line will be bigger then the available room
|
|
|
|
|
// but I think looks better
|
|
|
|
|
currline += lpart;
|
|
|
|
|
lineparts.push_back( std::move( currline ) );
|
|
|
|
|
currline.clear();
|
|
|
|
|
}
|
|
|
|
|
if ( !currline.empty() )
|
|
|
|
|
lineparts.push_back( currline );
|
|
|
|
|
return lineparts;
|
|
|
|
|
};
|
|
|
|
|
auto indent = indentSpacing();
|
|
|
|
|
size_t room = compilercfg.FormatterLineWidth - line.size();
|
|
|
|
|
size_t end_group = 0;
|
|
|
|
|
bool prefferesbreak = false;
|
|
|
|
|
// where does the group end?
|
|
|
|
|
for ( size_t j = index + 1; j <= upto; ++j )
|
|
|
|
|
{
|
|
|
|
|
if ( lines[j].style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
{
|
|
|
|
|
prefferesbreak = true;
|
|
|
|
|
}
|
|
|
|
|
if ( lines[j].group == part.group || lines[j].firstgroup == part.group )
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
end_group = j;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if ( end_group == 0 )
|
|
|
|
|
end_group = upto + 1;
|
|
|
|
|
*skipindex = end_group; // independent of the result we will skip till end of group
|
|
|
|
|
std::string total;
|
|
|
|
|
bool total_valid{ true };
|
|
|
|
|
for ( size_t j = index; j < end_group; ++j )
|
|
|
|
|
{
|
|
|
|
|
total += lines[j].text;
|
|
|
|
|
if ( lines[j].style & FmtToken::FORCED_BREAK )
|
|
|
|
|
{
|
|
|
|
|
total_valid = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// it fits directly into the line
|
|
|
|
|
if ( total_valid && total.size() + line.size() < compilercfg.FormatterLineWidth )
|
|
|
|
|
{
|
|
|
|
|
if ( !alignmentspace->count( part.group ) )
|
|
|
|
|
( *alignmentspace )[part.group] = line.size() - indent.size();
|
|
|
|
|
line += total;
|
2024-10-03 15:10:21 +02:00
|
|
|
newpart = total;
|
2024-09-13 06:49:00 +02:00
|
|
|
stripline( line );
|
|
|
|
|
finallines->emplace_back( std::move( line ) );
|
|
|
|
|
line.clear();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// if its part of an active groupformatting only allow a single line
|
|
|
|
|
if ( only_single_line )
|
|
|
|
|
return false;
|
|
|
|
|
// TODO: if only a few chars room are left start a newline before
|
|
|
|
|
// option 1: try to equally split the line into two, for structs and dicts half the available
|
|
|
|
|
// room
|
|
|
|
|
size_t lenOption2 = std::min( room, total.size() / 2 );
|
|
|
|
|
if ( ( part.scope & FmtToken::Scope::STRUCT ) == FmtToken::Scope::STRUCT ||
|
|
|
|
|
( part.scope & FmtToken::Scope::DICT ) == FmtToken::Scope::DICT )
|
|
|
|
|
{
|
|
|
|
|
lenOption2 = room / 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto lineparts = packTaktic( index, end_group, lenOption2, lines );
|
|
|
|
|
|
|
|
|
|
// option1 failed, for arrays its allowed to use 3 lines
|
|
|
|
|
// no array/dict/struct scope means any length is allowed
|
|
|
|
|
if ( lineparts.size() != 2 && part.scope != FmtToken::Scope::NONE &&
|
|
|
|
|
part.scope != FmtToken::Scope::FUNCTION && part.scope != FmtToken::Scope::VAR )
|
|
|
|
|
{
|
|
|
|
|
if ( ( part.scope & FmtToken::Scope::STRUCT ) == FmtToken::Scope::STRUCT ||
|
|
|
|
|
( part.scope & FmtToken::Scope::DICT ) == FmtToken::Scope::DICT )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
size_t lenOption3 = std::min( room, total.size() / 3 );
|
|
|
|
|
lineparts = packTaktic( index, end_group, lenOption3, lines );
|
|
|
|
|
if ( lineparts.size() != 3 )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// dont keep spacing over var,
|
|
|
|
|
if ( prefferesbreak )
|
|
|
|
|
*alignmentspace = initial_alignmentspace;
|
|
|
|
|
// we found a solution
|
|
|
|
|
if ( !alignmentspace->count( part.group ) )
|
|
|
|
|
( *alignmentspace )[part.group] = line.size() - indent.size();
|
2024-10-03 15:10:21 +02:00
|
|
|
newpart = "";
|
2024-09-13 06:49:00 +02:00
|
|
|
for ( const auto& lpart : lineparts )
|
|
|
|
|
{
|
|
|
|
|
if ( line.empty() )
|
|
|
|
|
{
|
|
|
|
|
line = alignmentSpacing( ( *alignmentspace )[part.firstgroup] );
|
|
|
|
|
line += indent;
|
|
|
|
|
}
|
|
|
|
|
line += lpart;
|
2024-10-03 15:10:21 +02:00
|
|
|
newpart += lpart;
|
2024-09-13 06:49:00 +02:00
|
|
|
stripline( line );
|
|
|
|
|
finallines->emplace_back( std::move( line ) );
|
|
|
|
|
line.clear();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
std::vector<std::string> PrettifyLineBuilder::createBasedOnGroups(
|
2024-10-03 15:10:21 +02:00
|
|
|
std::vector<FmtToken>& lines ) const
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
// TODO align closing brackets better
|
2024-08-11 22:09:30 +02:00
|
|
|
std::vector<std::string> finallines;
|
2024-02-26 21:21:44 +01:00
|
|
|
std::string line;
|
2024-08-11 22:09:30 +02:00
|
|
|
// store for each group the alignment
|
|
|
|
|
std::map<size_t, size_t> alignmentspace = {};
|
2024-09-13 06:49:00 +02:00
|
|
|
std::map<size_t, size_t> initial_alignmentspace = {};
|
2024-08-11 22:09:30 +02:00
|
|
|
size_t lastgroup = 0xffffFFFF;
|
|
|
|
|
bool newline = false;
|
2024-09-13 06:49:00 +02:00
|
|
|
auto indent = indentSpacing();
|
|
|
|
|
line = indent;
|
2024-08-11 22:09:30 +02:00
|
|
|
bool groupdiffered = false;
|
2024-09-13 06:49:00 +02:00
|
|
|
size_t i = 0, skipuntil = 0, tried_binpack_until = 0;
|
2024-10-03 15:10:21 +02:00
|
|
|
for ( auto& part : lines )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( skipuntil > i ) // already handled during binpack
|
|
|
|
|
{
|
|
|
|
|
++i;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// it was a binpack before
|
|
|
|
|
if ( line.empty() && !finallines.empty() )
|
|
|
|
|
{
|
|
|
|
|
// add a following comment to the line if it fits
|
|
|
|
|
if ( part.context == FmtContext::LINE_COMMENT || part.context == FmtContext::COMMENT )
|
|
|
|
|
{
|
|
|
|
|
if ( part.text.size() + finallines.back().size() < compilercfg.FormatterLineWidth )
|
|
|
|
|
{
|
|
|
|
|
// if the last one was forced (comment) let the default behaviour later do it
|
|
|
|
|
if ( !( lines[i - 1].style & FmtToken::FORCED_BREAK ) )
|
|
|
|
|
{
|
|
|
|
|
if ( lines[i - 1].style & FmtToken::SPACE )
|
|
|
|
|
finallines.back() += ' '; // needed since already stripped
|
|
|
|
|
finallines.back() += part.text;
|
|
|
|
|
stripline( finallines.back() );
|
|
|
|
|
line = indent;
|
|
|
|
|
++i;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// last element still fits
|
|
|
|
|
if ( i + 1 == lines.size() &&
|
|
|
|
|
( part.text.size() + finallines.back().size() <= compilercfg.FormatterLineWidth ) )
|
|
|
|
|
{
|
|
|
|
|
if ( !( lines[i - 1].style & FmtToken::FORCED_BREAK ) )
|
|
|
|
|
{
|
|
|
|
|
if ( lines[i - 1].style & FmtToken::SPACE )
|
|
|
|
|
finallines.back() += ' '; // needed since already stripped
|
|
|
|
|
finallines.back() += part.text;
|
|
|
|
|
stripline( finallines.back() );
|
|
|
|
|
++i;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( line.empty() && !alignmentspace.empty() )
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( alignmentspace.find( part.firstgroup ) == alignmentspace.end() )
|
|
|
|
|
alignmentspace[part.firstgroup] = alignmentspace[part.firstgroup - 1];
|
|
|
|
|
line = alignmentSpacing( alignmentspace[part.firstgroup] );
|
|
|
|
|
line += indent;
|
|
|
|
|
}
|
|
|
|
|
if ( part.style & FmtToken::FORCED_BREAK )
|
|
|
|
|
{
|
|
|
|
|
auto nonspace = line.find_first_not_of( " \t" );
|
|
|
|
|
line += part.text;
|
|
|
|
|
// do not indent comments if they start at the beginning
|
|
|
|
|
if ( part.context == FmtContext::LINE_COMMENT || part.context == FmtContext::COMMENT )
|
|
|
|
|
{
|
|
|
|
|
if ( part.pos.character_column < 4 &&
|
|
|
|
|
nonspace ==
|
|
|
|
|
std::string::npos ) // TODO startpos does not include original space indent
|
|
|
|
|
{
|
|
|
|
|
auto nonspace_part = part.text.find_first_not_of( " \t" );
|
|
|
|
|
// is the comment really at the beginning
|
|
|
|
|
if ( nonspace_part != std::string::npos && part.text[nonspace_part] == '/' )
|
|
|
|
|
line = part.text;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
stripline( line );
|
|
|
|
|
finallines.emplace_back( std::move( line ) );
|
2024-09-13 06:49:00 +02:00
|
|
|
line.clear();
|
|
|
|
|
line = alignmentSpacing( alignmentspace[part.firstgroup] );
|
|
|
|
|
line += indent;
|
|
|
|
|
++i;
|
|
|
|
|
// dont keep spacing over var,
|
|
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
alignmentspace = initial_alignmentspace;
|
2024-08-11 22:09:30 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ( lastgroup == 0xffffFFFF )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
line += part.text; // first part
|
|
|
|
|
alignmentspace[part.firstgroup] = line.size() - indent.size();
|
|
|
|
|
alignmentspace[part.group] = line.size() - indent.size();
|
|
|
|
|
initial_alignmentspace = alignmentspace;
|
2024-08-11 22:09:30 +02:00
|
|
|
newline = true;
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
2024-09-13 06:49:00 +02:00
|
|
|
INFO_PRINTLN( "first {} {} a{}", line, part.firstgroup, alignmentspace );
|
|
|
|
|
#endif
|
|
|
|
|
++i;
|
|
|
|
|
lastgroup = part.firstgroup;
|
|
|
|
|
// dont keep spacing over var,
|
|
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
alignmentspace = initial_alignmentspace;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-10-03 15:10:21 +02:00
|
|
|
if ( tried_binpack_until <= i )
|
2024-09-13 06:49:00 +02:00
|
|
|
{
|
|
|
|
|
size_t upto = lines.size() - 1;
|
|
|
|
|
// check for nested groups, binpack is not allowed for these eg array of struct
|
|
|
|
|
for ( size_t j = i + 1; j < lines.size(); ++j )
|
|
|
|
|
{
|
|
|
|
|
if ( lines[j].group > part.group )
|
|
|
|
|
{
|
|
|
|
|
upto = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool invarpack = false;
|
|
|
|
|
if ( upto == 0 && lines[i ? i - 1 : 0].style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
{
|
|
|
|
|
// multiple variables in a single statement, find matching groups and pack these
|
|
|
|
|
for ( size_t j = i + 1; j < lines.size(); ++j )
|
|
|
|
|
{
|
|
|
|
|
// we are not inside of some group and eg array starts
|
|
|
|
|
if ( lastgroup == 0 && part.group == 1 )
|
|
|
|
|
{
|
|
|
|
|
if ( lines[j].group > 2 ) // entries are 2
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if ( lines[j].group > part.group )
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if ( lines[j].group != part.group )
|
|
|
|
|
if ( lines[j].style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
break; // stop at var, when another group starts
|
|
|
|
|
if ( lines[j].style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
upto = j;
|
|
|
|
|
}
|
|
|
|
|
if ( upto > 0 )
|
|
|
|
|
{
|
|
|
|
|
invarpack = true; // retry with newline
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( upto > 0 && i != upto )
|
|
|
|
|
{
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "trybinpack {}->{} {}->{}", i, upto, lines[i].text, lines[upto].text );
|
|
|
|
|
#endif
|
|
|
|
|
size_t skip;
|
2024-10-03 15:10:21 +02:00
|
|
|
std::string newpart;
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( binPack( part, line, i, upto, lines, tried_binpack_until > 0, &finallines,
|
2024-10-03 15:10:21 +02:00
|
|
|
&alignmentspace, &skip, initial_alignmentspace, newpart ) )
|
2024-09-13 06:49:00 +02:00
|
|
|
{
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "binpack {}->{} {}->{}", i, upto, lines[i].text, lines[skip - 1].text );
|
2024-08-11 22:09:30 +02:00
|
|
|
#endif
|
2024-09-13 06:49:00 +02:00
|
|
|
skipuntil = skip;
|
|
|
|
|
line.clear();
|
|
|
|
|
++i;
|
|
|
|
|
// dont keep spacing over var,
|
|
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
alignmentspace = initial_alignmentspace;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
if ( invarpack && !line.empty() )
|
2024-09-13 06:49:00 +02:00
|
|
|
{
|
|
|
|
|
// try it again when starting a new line
|
|
|
|
|
std::string oldline = line;
|
|
|
|
|
stripline( line );
|
|
|
|
|
if ( !line.empty() )
|
|
|
|
|
finallines.emplace_back( std::move( line ) );
|
|
|
|
|
line.clear();
|
|
|
|
|
line = alignmentSpacing( alignmentspace[part.firstgroup] );
|
|
|
|
|
line += indent;
|
2024-10-03 15:10:21 +02:00
|
|
|
std::string tmp;
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( binPack( part, line, i, upto, lines, tried_binpack_until > 0, &finallines,
|
2024-10-03 15:10:21 +02:00
|
|
|
&alignmentspace, &skip, initial_alignmentspace, tmp ) )
|
2024-09-13 06:49:00 +02:00
|
|
|
{
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "binpack2 {}->{} {}->{}", i, upto, lines[i].text, lines[skip - 1].text );
|
|
|
|
|
#endif
|
|
|
|
|
skipuntil = skip;
|
|
|
|
|
line.clear();
|
|
|
|
|
++i;
|
|
|
|
|
// dont keep spacing over var,
|
|
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
alignmentspace = initial_alignmentspace;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
|
|
|
|
|
line = oldline;
|
|
|
|
|
finallines.pop_back();
|
2024-09-13 06:49:00 +02:00
|
|
|
}
|
|
|
|
|
tried_binpack_until = skip;
|
|
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-10-03 15:10:21 +02:00
|
|
|
if ( lines[i].context == FmtContext::PREFERRED_BREAK_START )
|
|
|
|
|
{
|
|
|
|
|
// try to pack function calls, by modifying the current part
|
|
|
|
|
size_t upto = lines.size() - 1;
|
|
|
|
|
for ( size_t j = i + 1; j < lines.size(); ++j )
|
|
|
|
|
{
|
|
|
|
|
if ( lines[j].context == FmtContext::PREFERRED_BREAK_END )
|
|
|
|
|
{
|
|
|
|
|
upto = j;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "trybinpackfunc {}->{} {}->{}", i, upto, lines[i].text, lines[upto].text );
|
|
|
|
|
#endif
|
|
|
|
|
size_t skip;
|
|
|
|
|
std::string oldline = line;
|
|
|
|
|
std::string newpart;
|
|
|
|
|
if ( binPack( part, line, i, upto, lines, true, &finallines, &alignmentspace, &skip,
|
|
|
|
|
initial_alignmentspace, newpart ) )
|
|
|
|
|
{
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "binpackfunc {}->{} {}->{}", i, upto, lines[i].text, lines[skip - 1].text );
|
|
|
|
|
#endif
|
|
|
|
|
skipuntil = skip;
|
|
|
|
|
// dont keep spacing over var,
|
|
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
alignmentspace = initial_alignmentspace;
|
|
|
|
|
// use the packed text as current part
|
|
|
|
|
part.text = newpart;
|
|
|
|
|
finallines.pop_back();
|
|
|
|
|
line = oldline;
|
|
|
|
|
part.group = lines[skip].group;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( lastgroup < part.firstgroup ) // new group
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
|
|
|
|
groupdiffered = true;
|
|
|
|
|
bool newgroup = false;
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( !alignmentspace.count( part.firstgroup ) )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
newgroup = true;
|
2024-09-13 06:49:00 +02:00
|
|
|
alignmentspace[part.firstgroup] = line.size() - indent.size();
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
// if its not a new group and not a fresh line start a new one
|
|
|
|
|
// nested structs eg should start at the same line
|
|
|
|
|
if ( !newgroup && !newline )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
stripline( line );
|
|
|
|
|
if ( !line.empty() )
|
|
|
|
|
finallines.emplace_back( std::move( line ) );
|
2024-09-13 06:49:00 +02:00
|
|
|
line = alignmentSpacing( alignmentspace[part.firstgroup] );
|
|
|
|
|
line += indent;
|
|
|
|
|
line += part.text;
|
2024-02-26 21:21:44 +01:00
|
|
|
newline = true;
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
2024-08-11 22:09:30 +02:00
|
|
|
INFO_PRINTLN( "!new {} - {}", line, alignmentspace );
|
2024-02-26 21:21:44 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
else
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
line += part.text;
|
2024-02-26 21:21:44 +01:00
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
2024-08-11 22:09:30 +02:00
|
|
|
INFO_PRINTLN( "new {} - {}", line, alignmentspace );
|
2024-02-26 21:21:44 +01:00
|
|
|
#endif
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( !alignmentspace.count( part.group ) )
|
|
|
|
|
alignmentspace[part.group] = line.size() - indent.size();
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
else if ( lastgroup > part.firstgroup ) // descending
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
|
|
|
|
newline = true;
|
|
|
|
|
stripline( line );
|
|
|
|
|
if ( !line.empty() )
|
|
|
|
|
finallines.emplace_back( std::move( line ) );
|
2024-09-13 06:49:00 +02:00
|
|
|
line = alignmentSpacing( alignmentspace[part.firstgroup] );
|
|
|
|
|
line += indent;
|
|
|
|
|
line += part.text;
|
2024-02-26 21:21:44 +01:00
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
2024-08-11 22:09:30 +02:00
|
|
|
INFO_PRINTLN( "desc {} - {}", line, alignmentspace );
|
2024-02-26 21:21:44 +01:00
|
|
|
#endif
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
else // same group
|
|
|
|
|
{
|
|
|
|
|
// only if we already had a different group split the line
|
2024-10-03 15:10:21 +02:00
|
|
|
bool closing = false;
|
|
|
|
|
if ( !part.text.empty() )
|
|
|
|
|
closing = part.text[0] == ')'; // shouldnt force a new line
|
|
|
|
|
if ( groupdiffered && !closing )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
newline = false;
|
2024-02-26 21:21:44 +01:00
|
|
|
stripline( line );
|
|
|
|
|
if ( !line.empty() )
|
2024-08-11 22:09:30 +02:00
|
|
|
finallines.emplace_back( std::move( line ) );
|
2024-09-13 06:49:00 +02:00
|
|
|
line = alignmentSpacing( alignmentspace[part.firstgroup] );
|
|
|
|
|
line += indent;
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
line += part.text;
|
2024-02-26 21:21:44 +01:00
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
2024-10-03 15:10:21 +02:00
|
|
|
INFO_PRINTLN( "same {} - {} {} {}", line, alignmentspace, groupdiffered, part.text );
|
2024-02-26 21:21:44 +01:00
|
|
|
#endif
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( line.size() > compilercfg.FormatterLineWidth )
|
|
|
|
|
{
|
|
|
|
|
stripline( line );
|
|
|
|
|
if ( !line.empty() )
|
|
|
|
|
finallines.emplace_back( std::move( line ) );
|
2024-09-13 06:49:00 +02:00
|
|
|
line = alignmentSpacing( alignmentspace[part.firstgroup] );
|
|
|
|
|
line += indent;
|
|
|
|
|
// dont keep spacing over var,
|
|
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
alignmentspace = initial_alignmentspace;
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-02-26 21:21:44 +01:00
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
lastgroup = part.firstgroup;
|
|
|
|
|
++i;
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
if ( !line.empty() )
|
|
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "remaining {}", line );
|
|
|
|
|
#endif
|
|
|
|
|
// look for something like ); and add it to the last line
|
|
|
|
|
if ( auto nonspace = line.find_first_not_of( " \t" );
|
|
|
|
|
nonspace != std::string::npos && line.size() - nonspace < 4 )
|
|
|
|
|
{
|
|
|
|
|
if ( lines.size() >= 2 )
|
|
|
|
|
{
|
|
|
|
|
if ( !( lines[lines.size() - 2].style & FmtToken::FORCED_BREAK ) )
|
|
|
|
|
{
|
|
|
|
|
if ( lines[lines.size() - 2].style & FmtToken::SPACE )
|
|
|
|
|
finallines.back() += ' '; // needed since already stripped
|
|
|
|
|
finallines.back() += line.erase( 0, nonspace );
|
|
|
|
|
stripline( finallines.back() );
|
|
|
|
|
return finallines;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
stripline( line );
|
|
|
|
|
finallines.emplace_back( std::move( line ) );
|
|
|
|
|
}
|
|
|
|
|
return finallines;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
// helper to find the alignment of the last open parenthesis and use this as alignment for the
|
|
|
|
|
// next line
|
|
|
|
|
bool PrettifyLineBuilder::parenthesisAlign( const std::vector<std::string>& finallines,
|
|
|
|
|
std::string& line ) const
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
|
|
|
|
std::vector<size_t> parenthesisalign;
|
|
|
|
|
for ( const auto& finalline : finallines )
|
|
|
|
|
{
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
for ( auto c : finalline )
|
|
|
|
|
{
|
|
|
|
|
if ( c == '(' )
|
|
|
|
|
parenthesisalign.push_back( i );
|
|
|
|
|
else if ( c == ')' && !parenthesisalign.empty() )
|
|
|
|
|
parenthesisalign.pop_back();
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( c == '\t' )
|
|
|
|
|
i += compilercfg.FormatterTabWidth;
|
|
|
|
|
else
|
|
|
|
|
++i;
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( parenthesisalign.empty() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// if its at the end of last line start at the beginning
|
|
|
|
|
if ( parenthesisalign.back() >= ( finallines.back().size() - 1 ) )
|
|
|
|
|
return true;
|
|
|
|
|
size_t alignment = line.find_first_not_of( " \t" );
|
|
|
|
|
if ( alignment == std::string::npos )
|
|
|
|
|
alignment = line.size();
|
|
|
|
|
if ( compilercfg.FormatterUseTabs )
|
|
|
|
|
alignment *= compilercfg.FormatterTabWidth;
|
|
|
|
|
if ( parenthesisalign.back() + ( compilercfg.FormatterBracketSpacing ? 2 : 1 ) > alignment )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
auto newspace = alignmentSpacing( parenthesisalign.back() +
|
|
|
|
|
( compilercfg.FormatterBracketSpacing ? 2 : 1 ) );
|
|
|
|
|
auto space = line.find_first_not_of( " \t" );
|
|
|
|
|
line.erase( 0, space );
|
|
|
|
|
line = newspace + line;
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
// if its a operator +-*/<> align the actual "data" so subtract 2
|
|
|
|
|
auto space = line.find_first_not_of( " \t" );
|
|
|
|
|
if ( space != std::string::npos && space > 1 && space + 1 < line.size() &&
|
|
|
|
|
line[space + 1] == ' ' &&
|
|
|
|
|
( line[space] == '+' || line[space] == '-' || line[space] == '*' || line[space] == '/' ||
|
|
|
|
|
line[space] == '<' || line[space] == '>' ) )
|
|
|
|
|
{
|
|
|
|
|
// TODO tabs...
|
|
|
|
|
if ( !compilercfg.FormatterUseTabs )
|
|
|
|
|
line.erase( 0, 2 );
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> PrettifyLineBuilder::createBasedOnPreferredBreaks(
|
2024-09-13 06:49:00 +02:00
|
|
|
const std::vector<FmtToken>& lines, bool logical ) const
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
|
|
|
|
std::vector<std::string> finallines;
|
|
|
|
|
std::string line;
|
|
|
|
|
// first join parts until a forced/preferred break
|
|
|
|
|
size_t alignmentspace = 0;
|
2024-09-13 06:49:00 +02:00
|
|
|
size_t assignpos = std::string::npos;
|
|
|
|
|
std::vector<std::tuple<std::string, int, FmtContext>> parts;
|
2024-08-11 22:09:30 +02:00
|
|
|
std::string tmp;
|
|
|
|
|
// if breaks exists from logical "and/or" we only split based on them
|
|
|
|
|
// in a long if-statement we want to break line on the logical points and not mixed with
|
|
|
|
|
// commas from functions
|
|
|
|
|
int breakflag = logical ? FmtToken::PREFERRED_BREAK_LOGICAL : FmtToken::PREFERRED_BREAK;
|
2024-09-13 06:49:00 +02:00
|
|
|
std::vector<size_t> parenthesisalign;
|
|
|
|
|
FmtContext tmpcontext = FmtContext::NONE;
|
|
|
|
|
for ( const auto& part : lines )
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
|
|
|
|
if ( !alignmentspace )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
auto indent = indentSpacing();
|
|
|
|
|
alignmentspace = part.text.size() + indent.size();
|
|
|
|
|
line += indent;
|
2026-01-16 13:22:43 +01:00
|
|
|
parts.emplace_back( part.text, FmtToken::NONE, part.context );
|
2024-09-13 06:49:00 +02:00
|
|
|
assignpos = part.text.find( ":=" );
|
|
|
|
|
if ( assignpos != std::string::npos )
|
|
|
|
|
assignpos += indent.size() + 2;
|
2024-08-11 22:09:30 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
// if it gets to long we still have to split the line
|
|
|
|
|
if ( tmp.size() + part.text.size() > compilercfg.FormatterLineWidth )
|
|
|
|
|
{
|
2026-01-16 13:22:43 +01:00
|
|
|
parts.emplace_back( std::move( tmp ), FmtToken::NONE, tmpcontext );
|
2024-09-13 06:49:00 +02:00
|
|
|
tmp.clear();
|
|
|
|
|
tmpcontext = part.context;
|
|
|
|
|
}
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
for ( auto c : tmp )
|
|
|
|
|
{
|
|
|
|
|
if ( c == '(' )
|
|
|
|
|
parenthesisalign.push_back( i );
|
|
|
|
|
else if ( c == ')' && !parenthesisalign.empty() )
|
|
|
|
|
parenthesisalign.pop_back();
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
// something closed split it
|
|
|
|
|
if ( parenthesisalign.empty() )
|
|
|
|
|
{
|
2026-01-16 13:22:43 +01:00
|
|
|
parts.emplace_back( std::move( tmp ), FmtToken::NONE, tmpcontext );
|
2024-09-13 06:49:00 +02:00
|
|
|
tmp.clear();
|
|
|
|
|
tmpcontext = part.context;
|
|
|
|
|
}
|
|
|
|
|
tmp += part.text;
|
|
|
|
|
if ( tmpcontext == FmtContext::NONE )
|
|
|
|
|
tmpcontext = part.context;
|
|
|
|
|
else if ( tmpcontext == FmtContext::PREFERRED_BREAK_START &&
|
|
|
|
|
part.context == FmtContext::PREFERRED_BREAK_END )
|
|
|
|
|
tmpcontext = FmtContext::NONE; // start and stop so clear
|
2024-08-11 22:09:30 +02:00
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( ( part.style & FmtToken::FORCED_BREAK ) ||
|
|
|
|
|
( part.style & FmtToken::PREFERRED_BREAK_VAR ) || ( part.style & breakflag ) )
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
2026-01-16 13:22:43 +01:00
|
|
|
parts.emplace_back( std::move( tmp ), part.style, tmpcontext );
|
2024-08-11 22:09:30 +02:00
|
|
|
tmp.clear();
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( part.context != FmtContext::PREFERRED_BREAK_END )
|
|
|
|
|
tmpcontext = part.context;
|
|
|
|
|
else
|
|
|
|
|
tmpcontext = FmtContext::NONE;
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( !tmp.empty() )
|
2026-01-16 13:22:43 +01:00
|
|
|
parts.emplace_back( tmp, FmtToken::NONE, tmpcontext );
|
2024-08-11 22:09:30 +02:00
|
|
|
// now build the actual line(s)
|
|
|
|
|
bool alignpart = false;
|
2024-09-13 06:49:00 +02:00
|
|
|
size_t parti = 0;
|
|
|
|
|
for ( auto& [l, style, context] : parts )
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
2024-08-01 08:00:35 +02:00
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
2024-09-13 06:49:00 +02:00
|
|
|
INFO_PRINTLN( "'{}'{} -{} {} FILTERED", l, l.size(), style, (int)context );
|
2024-08-01 08:00:35 +02:00
|
|
|
#endif
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( line.empty() && alignmentspace && alignpart )
|
|
|
|
|
line = alignmentSpacing( alignmentspace );
|
2024-09-13 06:49:00 +02:00
|
|
|
|
|
|
|
|
if ( !logical ) // align line directly to have the correct linelen
|
|
|
|
|
parenthesisAlign( finallines, line );
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
alignpart = true; // otherwise first part would get spacing
|
2024-09-13 06:49:00 +02:00
|
|
|
bool newcontext_longer = false;
|
|
|
|
|
// if we start a new parenthesis group check if it fit completely
|
|
|
|
|
auto currlinespace = line.find_first_not_of( " \t" );
|
|
|
|
|
if ( context == FmtContext::PREFERRED_BREAK_START && currlinespace != std::string::npos )
|
|
|
|
|
{
|
|
|
|
|
size_t prefferedsize = l.size();
|
|
|
|
|
for ( size_t j = parti + 1; j < parts.size(); ++j )
|
|
|
|
|
{
|
|
|
|
|
prefferedsize += std::get<0>( parts[j] ).size();
|
|
|
|
|
if ( std::get<2>( parts[j] ) == FmtContext::PREFERRED_BREAK_END )
|
|
|
|
|
{
|
|
|
|
|
// does not fit into current line, start a new one
|
|
|
|
|
if ( prefferedsize + line.size() > compilercfg.FormatterLineWidth )
|
|
|
|
|
newcontext_longer = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// with a margin of 75% start a new line before
|
|
|
|
|
if ( ( ( line.size() + ( l.size() * 0.75 ) ) > compilercfg.FormatterLineWidth ) ||
|
|
|
|
|
newcontext_longer )
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
|
|
|
|
// TODO if next is linecomment dont split now, but split comment
|
2024-09-13 06:49:00 +02:00
|
|
|
std::string origline = line; // parenthesisAlign modifies
|
|
|
|
|
auto space = line.find_first_not_of( " \t" );
|
|
|
|
|
if ( space != std::string::npos ) // line contained only alignment
|
|
|
|
|
{
|
|
|
|
|
if ( !logical )
|
|
|
|
|
{
|
|
|
|
|
if ( !parenthesisAlign( finallines, line ) )
|
|
|
|
|
{
|
|
|
|
|
if ( assignpos != std::string::npos )
|
|
|
|
|
{
|
|
|
|
|
space = line.find_first_not_of( " \t" );
|
|
|
|
|
if ( space > assignpos ) // TODO operator shift of 2
|
|
|
|
|
line.erase( 0, space - assignpos );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// line shrinks due to align, check again
|
|
|
|
|
if ( newcontext_longer ||
|
|
|
|
|
( line.size() + ( l.size() * 0.75 ) ) > compilercfg.FormatterLineWidth )
|
|
|
|
|
{
|
|
|
|
|
bool skip = false;
|
|
|
|
|
if ( parti == parts.size() - 1 ) // last
|
|
|
|
|
{
|
|
|
|
|
if ( l.size() < 4 ||
|
|
|
|
|
line.size() + l.size() <= compilercfg.FormatterLineWidth ) // small
|
|
|
|
|
skip = true;
|
|
|
|
|
else if ( !l.empty() && l[0] == '/' ) // comment
|
|
|
|
|
skip = true;
|
|
|
|
|
}
|
|
|
|
|
else if ( auto c = line.find_last_not_of( " \t" );
|
|
|
|
|
c != std::string::npos &&
|
|
|
|
|
( line[c] == '(' || ( finallines.empty() &&
|
|
|
|
|
line.size() < compilercfg.FormatterLineWidth * 0.3 ) ) )
|
|
|
|
|
{
|
|
|
|
|
// a bracket just started, dont directly start a newline
|
|
|
|
|
// or no completed line (prevents eg empty "var" line)
|
|
|
|
|
skip = true;
|
|
|
|
|
}
|
|
|
|
|
if ( !skip )
|
|
|
|
|
{
|
|
|
|
|
stripline( line );
|
|
|
|
|
finallines.emplace_back( std::move( line ) );
|
|
|
|
|
line = alignmentSpacing( alignmentspace );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
line = origline;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
line = origline;
|
|
|
|
|
}
|
2024-08-01 08:00:35 +02:00
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
line += l;
|
|
|
|
|
// linewidth reached add current line, start a new one
|
|
|
|
|
if ( line.size() > compilercfg.FormatterLineWidth || style & FmtToken::FORCED_BREAK ||
|
|
|
|
|
style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
{
|
|
|
|
|
// TODO if next is linecomment dont split now, but split comment
|
2024-09-13 06:49:00 +02:00
|
|
|
std::string origline = line; // parenthesisAlign modifies
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( !logical )
|
2024-09-13 06:49:00 +02:00
|
|
|
{
|
|
|
|
|
if ( !parenthesisAlign( finallines, line ) )
|
|
|
|
|
{
|
|
|
|
|
if ( assignpos != std::string::npos )
|
|
|
|
|
{
|
|
|
|
|
auto space = line.find_first_not_of( " \t" );
|
|
|
|
|
if ( space > assignpos ) // TODO operator shift of 2
|
|
|
|
|
line.erase( 0, space - assignpos );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool startnew = true;
|
|
|
|
|
if ( style & FmtToken::FORCED_BREAK || style & FmtToken::PREFERRED_BREAK_VAR )
|
|
|
|
|
startnew = true;
|
|
|
|
|
else if ( line.size() < compilercfg.FormatterLineWidth )
|
|
|
|
|
startnew = false;
|
|
|
|
|
else if ( parti + 1 == parts.size() - 1 && std::get<0>( parts[parti + 1] ).size() < 4 )
|
|
|
|
|
startnew = false; // next is last and small so add it
|
|
|
|
|
else if ( parti + 1 == parts.size() - 1 && !std::get<0>( parts[parti + 1] ).empty() &&
|
|
|
|
|
std::get<0>( parts[parti + 1] )[0] == '/' )
|
|
|
|
|
startnew = false; // next is last and comment so add it
|
|
|
|
|
if ( startnew )
|
|
|
|
|
{
|
|
|
|
|
stripline( line );
|
|
|
|
|
finallines.emplace_back( std::move( line ) );
|
|
|
|
|
line.clear();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
line = origline;
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
++parti;
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
if ( !line.empty() )
|
|
|
|
|
{
|
|
|
|
|
stripline( line );
|
|
|
|
|
if ( !logical )
|
2024-09-13 06:49:00 +02:00
|
|
|
parenthesisAlign( finallines, line );
|
2024-08-11 22:09:30 +02:00
|
|
|
finallines.emplace_back( std::move( line ) );
|
|
|
|
|
}
|
|
|
|
|
return finallines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> PrettifyLineBuilder::createSimple(
|
2024-09-13 06:49:00 +02:00
|
|
|
const std::vector<FmtToken>& lines ) const
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
|
|
|
|
std::vector<std::string> finallines;
|
|
|
|
|
std::string line;
|
|
|
|
|
size_t alignmentspace = 0;
|
|
|
|
|
size_t i = 0;
|
2024-09-13 06:49:00 +02:00
|
|
|
for ( const auto& part : lines )
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
|
|
|
|
// following lines need to be aligned
|
|
|
|
|
if ( line.empty() && alignmentspace )
|
|
|
|
|
line = alignmentSpacing( alignmentspace );
|
2024-09-13 06:49:00 +02:00
|
|
|
// first breakpoint defines the alignment and add initial indent level
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( !alignmentspace )
|
|
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
auto indent = indentSpacing();
|
|
|
|
|
alignmentspace = part.text.size() + indent.size();
|
|
|
|
|
line += indent;
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
line += part.text;
|
2024-08-11 22:09:30 +02:00
|
|
|
bool forcebreakVarToLong{ false };
|
|
|
|
|
// if its a var definition, check if the next var would fit in the line,
|
|
|
|
|
// otherwise start directly a new line
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK_VAR && i > 0 )
|
2024-08-01 08:00:35 +02:00
|
|
|
{
|
2024-08-11 22:09:30 +02:00
|
|
|
std::string next_var;
|
|
|
|
|
for ( size_t j = i + 1; j < lines.size(); ++j )
|
2024-08-01 08:00:35 +02:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
next_var += lines[j].text;
|
|
|
|
|
if ( lines[j].style & FmtToken::PREFERRED_BREAK_VAR )
|
2024-08-11 22:09:30 +02:00
|
|
|
break;
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( ( line.size() + next_var.size() + alignmentSpacing( alignmentspace ).size() ) >
|
|
|
|
|
compilercfg.FormatterLineWidth )
|
|
|
|
|
forcebreakVarToLong = true;
|
|
|
|
|
}
|
|
|
|
|
// linewidth reached add current line, start a new one
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( line.size() > compilercfg.FormatterLineWidth || part.style & FmtToken::FORCED_BREAK ||
|
2024-08-11 22:09:30 +02:00
|
|
|
forcebreakVarToLong )
|
|
|
|
|
{
|
|
|
|
|
// TODO if next is linecomment dont split now, but split comment
|
2024-09-13 06:49:00 +02:00
|
|
|
// be extra generous if we only have a few chars left
|
|
|
|
|
if ( ( part.style & FmtToken::FORCED_BREAK ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
if ( i + 1 == lines.size() - 1 && lines[i + 1].text.size() < 4 )
|
|
|
|
|
{
|
|
|
|
|
++i;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
stripline( line );
|
2024-09-13 06:49:00 +02:00
|
|
|
parenthesisAlign( finallines, line );
|
2024-08-11 22:09:30 +02:00
|
|
|
finallines.emplace_back( std::move( line ) );
|
|
|
|
|
line.clear();
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
++i;
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
|
|
|
|
if ( !line.empty() )
|
|
|
|
|
{
|
|
|
|
|
stripline( line );
|
2024-09-13 06:49:00 +02:00
|
|
|
parenthesisAlign( finallines, line );
|
2024-08-11 22:09:30 +02:00
|
|
|
finallines.emplace_back( std::move( line ) );
|
|
|
|
|
}
|
|
|
|
|
return finallines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::stripline( std::string& line ) const
|
|
|
|
|
{
|
|
|
|
|
if ( line.empty() )
|
|
|
|
|
return;
|
|
|
|
|
auto lastchar = line.find_last_not_of( ' ' );
|
|
|
|
|
line.erase( line.begin() + lastchar + 1, line.end() );
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
void PrettifyLineBuilder::buildLine( size_t current_indent )
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
_currindent = current_indent;
|
2024-08-11 22:09:30 +02:00
|
|
|
if ( _line_parts.empty() )
|
2024-09-13 06:49:00 +02:00
|
|
|
{
|
|
|
|
|
packLines();
|
2024-08-11 22:09:30 +02:00
|
|
|
return;
|
2024-09-13 06:49:00 +02:00
|
|
|
}
|
2024-08-11 22:09:30 +02:00
|
|
|
mergeComments();
|
|
|
|
|
|
|
|
|
|
// fill lines with final strings splitted at breakpoints
|
|
|
|
|
auto lines = buildLineSplits();
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
2024-09-13 06:49:00 +02:00
|
|
|
INFO_PRINTLN( "BREAK" );
|
|
|
|
|
for ( const auto& part : lines )
|
|
|
|
|
INFO_PRINTLN( "\"{}\" {}-{} ->{} :{}-{}", part.text, part.group, part.firstgroup, part.style,
|
|
|
|
|
(int)part.scope, (int)part.context );
|
2024-08-11 22:09:30 +02:00
|
|
|
#endif
|
|
|
|
|
// add newline from original sourcecode
|
|
|
|
|
addEmptyLines( _line_parts.front().pos.line_number );
|
|
|
|
|
|
|
|
|
|
// sum up linelength, are groups inside or preferred breaks
|
|
|
|
|
bool groups = false;
|
|
|
|
|
bool has_preferred = false;
|
|
|
|
|
bool has_preferred_logical = false;
|
|
|
|
|
size_t linelength = 0;
|
2024-09-13 06:49:00 +02:00
|
|
|
for ( const auto& part : lines )
|
2024-08-11 22:09:30 +02:00
|
|
|
{
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( part.group != 0 )
|
2024-08-11 22:09:30 +02:00
|
|
|
groups = true;
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK )
|
2024-08-11 22:09:30 +02:00
|
|
|
has_preferred = true;
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( part.style & FmtToken::PREFERRED_BREAK_LOGICAL )
|
2024-08-11 22:09:30 +02:00
|
|
|
has_preferred_logical = true;
|
2024-09-13 06:49:00 +02:00
|
|
|
linelength += part.text.size();
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
std::vector<std::string> finallines;
|
2024-08-11 22:09:30 +02:00
|
|
|
// split based on groups
|
|
|
|
|
if ( groups && linelength > compilercfg.FormatterLineWidth )
|
|
|
|
|
{
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "split groupbased" );
|
|
|
|
|
#endif
|
2024-09-13 06:49:00 +02:00
|
|
|
finallines = createBasedOnGroups( lines );
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
else // split based on parts
|
|
|
|
|
{
|
|
|
|
|
// with preferred breaks
|
|
|
|
|
if ( has_preferred || has_preferred_logical )
|
|
|
|
|
{
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "split preferred" );
|
|
|
|
|
#endif
|
2024-09-13 06:49:00 +02:00
|
|
|
finallines = createBasedOnPreferredBreaks( lines, has_preferred_logical );
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
|
|
|
|
else // simple splitting
|
|
|
|
|
{
|
|
|
|
|
#ifdef DEBUG_FORMAT_BREAK
|
|
|
|
|
INFO_PRINTLN( "split simple" );
|
|
|
|
|
#endif
|
2024-09-13 06:49:00 +02:00
|
|
|
finallines = createSimple( lines );
|
2024-08-11 22:09:30 +02:00
|
|
|
}
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
2024-09-13 06:49:00 +02:00
|
|
|
alignComments( finallines );
|
|
|
|
|
_lines.insert( _lines.end(), std::make_move_iterator( finallines.begin() ),
|
|
|
|
|
std::make_move_iterator( finallines.end() ) );
|
2024-02-26 21:21:44 +01:00
|
|
|
_last_line = _line_parts.back().pos.line_number;
|
|
|
|
|
_line_parts.clear();
|
2024-09-13 06:49:00 +02:00
|
|
|
|
|
|
|
|
packLines();
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
|
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
void PrettifyLineBuilder::packLines()
|
|
|
|
|
{
|
|
|
|
|
if ( !_packableline_allowed || !_packablelineend )
|
|
|
|
|
return;
|
|
|
|
|
_packableline_allowed = false;
|
|
|
|
|
_packablelineend = false;
|
|
|
|
|
|
|
|
|
|
// 3 lines max
|
|
|
|
|
// for case its "label: blubb; break;
|
|
|
|
|
// for funcrefs its @(){ blubb; };
|
|
|
|
|
if ( _lines.size() - _packablelinestart >= 4 )
|
|
|
|
|
return;
|
|
|
|
|
std::string packedline;
|
|
|
|
|
for ( size_t i = _packablelinestart; i < _lines.size(); ++i )
|
|
|
|
|
{
|
|
|
|
|
auto l = _lines[i];
|
|
|
|
|
if ( l.find( "//" ) != std::string::npos )
|
|
|
|
|
{
|
|
|
|
|
// if the line comment is not at the end we cannot pack
|
|
|
|
|
if ( i < _lines.size() - 1 )
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
stripline( l );
|
|
|
|
|
if ( packedline.empty() )
|
|
|
|
|
packedline += l;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
auto begin = _lines[i].find_first_not_of( " \t" );
|
|
|
|
|
if ( begin > 0 && begin != std::string::npos )
|
|
|
|
|
l.erase( 0, begin );
|
|
|
|
|
packedline += " " + l;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( packedline.empty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ( packedline.size() <= compilercfg.FormatterLineWidth )
|
|
|
|
|
{
|
|
|
|
|
_lines.resize( _packablelinestart );
|
|
|
|
|
_lines.push_back( std::move( packedline ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::alignComments( std::vector<std::string>& finallines )
|
|
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterAlignTrailingComments )
|
|
|
|
|
return;
|
|
|
|
|
std::vector<size_t> commentstart;
|
|
|
|
|
// collect comment columns
|
2026-01-31 09:14:02 +01:00
|
|
|
for ( const auto& finalline : finallines )
|
2024-09-13 06:49:00 +02:00
|
|
|
{
|
2026-01-31 09:14:02 +01:00
|
|
|
auto linecomment = finalline.find( "//" );
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( linecomment == std::string::npos )
|
|
|
|
|
commentstart.push_back( 0 );
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// if the line only contains a comment dont align it
|
2026-01-31 09:14:02 +01:00
|
|
|
auto other = finalline.find_first_not_of( " \t" );
|
2024-09-13 06:49:00 +02:00
|
|
|
if ( other == linecomment )
|
|
|
|
|
commentstart.push_back( 0 );
|
|
|
|
|
else
|
|
|
|
|
commentstart.push_back( linecomment );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( commentstart.empty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// add spaces at comment start foreach comment other then max
|
|
|
|
|
auto max = *std::max_element( commentstart.begin(), commentstart.end() );
|
|
|
|
|
if ( max == 0 )
|
|
|
|
|
return;
|
|
|
|
|
for ( size_t i = 0; i < finallines.size(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if ( !commentstart[i] || max == commentstart[i] )
|
|
|
|
|
continue;
|
|
|
|
|
finallines[i].insert( commentstart[i], max - commentstart[i], ' ' );
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-26 21:21:44 +01:00
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::addEmptyLines( size_t line_number )
|
|
|
|
|
{
|
|
|
|
|
if ( compilercfg.FormatterMergeEmptyLines )
|
|
|
|
|
{
|
|
|
|
|
if ( line_number > _last_line + 1 )
|
|
|
|
|
_lines.emplace_back( "" );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while ( line_number > _last_line + 1 )
|
|
|
|
|
{
|
|
|
|
|
_lines.emplace_back( "" );
|
|
|
|
|
++_last_line;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-03 15:10:21 +02:00
|
|
|
int PrettifyLineBuilder::closingParenthesisStyle( bool args )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
auto style = FmtToken::SPACE | FmtToken::BREAKPOINT;
|
2024-10-03 15:10:21 +02:00
|
|
|
if ( !args )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterEmptyParenthesisSpacing )
|
|
|
|
|
style |= FmtToken::ATTACHED;
|
|
|
|
|
else if ( !_line_parts.empty() )
|
|
|
|
|
_line_parts.back().style |= FmtToken::SPACE;
|
|
|
|
|
}
|
|
|
|
|
else if ( !compilercfg.FormatterParenthesisSpacing )
|
|
|
|
|
style |= FmtToken::ATTACHED;
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int PrettifyLineBuilder::closingBracketStyle( size_t begin_size )
|
|
|
|
|
{
|
|
|
|
|
auto style = FmtToken::SPACE | FmtToken::BREAKPOINT;
|
|
|
|
|
if ( _line_parts.size() == begin_size )
|
|
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterEmptyBracketSpacing )
|
|
|
|
|
style |= FmtToken::ATTACHED;
|
|
|
|
|
else if ( !_line_parts.empty() )
|
|
|
|
|
_line_parts.back().style |= FmtToken::SPACE;
|
|
|
|
|
}
|
|
|
|
|
else if ( !compilercfg.FormatterBracketSpacing )
|
|
|
|
|
style |= FmtToken::ATTACHED;
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
int PrettifyLineBuilder::openingParenthesisStyle() const
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
return FmtToken::ATTACHED | FmtToken::BREAKPOINT |
|
|
|
|
|
( compilercfg.FormatterParenthesisSpacing ? FmtToken::SPACE : FmtToken::NONE );
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-17 21:59:42 +01:00
|
|
|
int PrettifyLineBuilder::openingBracketStyle( bool typeinit, bool force_unattached )
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
2025-02-17 21:59:42 +01:00
|
|
|
if ( ( typeinit && !compilercfg.FormatterBracketAttachToType ) || force_unattached )
|
2024-09-13 06:49:00 +02:00
|
|
|
return FmtToken::BREAKPOINT |
|
|
|
|
|
( compilercfg.FormatterBracketSpacing ? FmtToken::SPACE : FmtToken::NONE );
|
2024-02-26 21:21:44 +01:00
|
|
|
return FmtToken::ATTACHED | FmtToken::BREAKPOINT |
|
|
|
|
|
( compilercfg.FormatterBracketSpacing ? FmtToken::SPACE : FmtToken::NONE );
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
int PrettifyLineBuilder::delimiterStyle() const
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
return FmtToken::ATTACHED | FmtToken::BREAKPOINT |
|
|
|
|
|
( compilercfg.FormatterDelimiterSpacing ? FmtToken::SPACE : FmtToken::NONE );
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
int PrettifyLineBuilder::terminatorStyle() const
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
return FmtToken::SPACE | FmtToken::ATTACHED | FmtToken::BREAKPOINT;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
int PrettifyLineBuilder::assignmentStyle() const
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterAssignmentSpacing )
|
|
|
|
|
return FmtToken::ATTACHED;
|
|
|
|
|
return FmtToken::SPACE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
int PrettifyLineBuilder::comparisonStyle() const
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterComparisonSpacing )
|
|
|
|
|
return FmtToken::ATTACHED;
|
|
|
|
|
return FmtToken::SPACE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-19 18:33:56 +01:00
|
|
|
int PrettifyLineBuilder::ellipsisStyle() const
|
|
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterEllipsisSpacing )
|
|
|
|
|
return FmtToken::ATTACHED | FmtToken::SPACE;
|
|
|
|
|
return FmtToken::SPACE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
int PrettifyLineBuilder::operatorStyle() const
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterOperatorSpacing )
|
|
|
|
|
return FmtToken::ATTACHED;
|
|
|
|
|
return FmtToken::SPACE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
std::string PrettifyLineBuilder::indentSpacing() const
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterUseTabs )
|
2024-09-13 06:49:00 +02:00
|
|
|
return std::string( _currindent * compilercfg.FormatterIndentLevel, ' ' );
|
|
|
|
|
size_t total = _currindent * compilercfg.FormatterIndentLevel;
|
2024-02-26 21:21:44 +01:00
|
|
|
size_t tabs = total / compilercfg.FormatterTabWidth;
|
|
|
|
|
size_t remaining = total % compilercfg.FormatterTabWidth;
|
|
|
|
|
return std::string( tabs, '\t' ) + std::string( remaining, ' ' );
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:09:30 +02:00
|
|
|
std::string PrettifyLineBuilder::alignmentSpacing( size_t count ) const
|
2024-02-26 21:21:44 +01:00
|
|
|
{
|
|
|
|
|
if ( !count )
|
|
|
|
|
return {};
|
|
|
|
|
if ( !compilercfg.FormatterUseTabs )
|
|
|
|
|
return std::string( count, ' ' );
|
|
|
|
|
size_t tabs = count / compilercfg.FormatterTabWidth;
|
|
|
|
|
size_t remaining = count % compilercfg.FormatterTabWidth;
|
|
|
|
|
return std::string( tabs, '\t' ) + std::string( remaining, ' ' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::mergeEOFNonTokens()
|
|
|
|
|
{
|
|
|
|
|
mergeRawContent( _last_line );
|
|
|
|
|
while ( !_comments.empty() )
|
|
|
|
|
{
|
|
|
|
|
addEmptyLines( _comments.front().pos.line_number );
|
2024-09-13 06:49:00 +02:00
|
|
|
_lines.push_back( indentSpacing() + _comments.front().text );
|
2024-02-26 21:21:44 +01:00
|
|
|
_last_line = _comments.front().pos_end.line_number;
|
|
|
|
|
mergeRawContent( _last_line );
|
|
|
|
|
_comments.erase( _comments.begin() );
|
|
|
|
|
}
|
|
|
|
|
// purge raw content
|
|
|
|
|
mergeRawContent( std::numeric_limits<size_t>::max() );
|
|
|
|
|
// if the original file ends with a newline, make sure to also end
|
|
|
|
|
if ( !_lines.empty() && !_lines.back().empty() && !_rawlines.empty() && _rawlines.back().empty() )
|
2026-01-16 13:22:43 +01:00
|
|
|
_lines.emplace_back( "" );
|
2024-02-26 21:21:44 +01:00
|
|
|
}
|
|
|
|
|
|
2024-09-13 06:49:00 +02:00
|
|
|
void PrettifyLineBuilder::markPackableLineStart()
|
|
|
|
|
{
|
|
|
|
|
_packablelinestart = _lines.size();
|
|
|
|
|
_packableline_allowed = true;
|
|
|
|
|
_packablelineend = false;
|
|
|
|
|
}
|
|
|
|
|
void PrettifyLineBuilder::markPackableLineEnd()
|
|
|
|
|
{
|
|
|
|
|
_packablelineend = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrettifyLineBuilder::markLastTokensAsSwitchLabel()
|
|
|
|
|
{
|
|
|
|
|
if ( _line_parts.size() < 2 )
|
|
|
|
|
return;
|
|
|
|
|
_packable_switch_labels.push_back( _line_parts[_line_parts.size() - 2].text +
|
|
|
|
|
_line_parts[_line_parts.size() - 1].text );
|
|
|
|
|
}
|
|
|
|
|
void PrettifyLineBuilder::alignSingleLineSwitchStatements( size_t start )
|
|
|
|
|
{
|
|
|
|
|
if ( !compilercfg.FormatterAlignConsecutiveShortCaseStatements ||
|
|
|
|
|
!compilercfg.FormatterAllowShortCaseLabelsOnASingleLine )
|
|
|
|
|
return;
|
|
|
|
|
size_t l_index = 0;
|
|
|
|
|
std::vector<std::pair<size_t, size_t>> statementstart;
|
|
|
|
|
std::optional<std::pair<size_t, size_t>> default_start;
|
|
|
|
|
// collect label starts based on stored labels
|
|
|
|
|
// default: is collected extra
|
|
|
|
|
for ( size_t i = start; i < _lines.size(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if ( _packable_switch_labels.size() <= l_index )
|
|
|
|
|
break;
|
|
|
|
|
const auto& label = _packable_switch_labels[l_index];
|
|
|
|
|
auto labelend = _lines[i].find( label );
|
|
|
|
|
if ( labelend == std::string::npos )
|
|
|
|
|
continue;
|
|
|
|
|
// there should be only whitespace before (could be in a comment)
|
|
|
|
|
bool empty{ true };
|
|
|
|
|
for ( size_t w = 0; w < labelend; ++w )
|
|
|
|
|
{
|
|
|
|
|
if ( _lines[i][w] != ' ' && _lines[i][w] != '\t' )
|
|
|
|
|
{
|
|
|
|
|
empty = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( !empty )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
++l_index; // ~valid match, so next one
|
|
|
|
|
// nothing following no need to align or consider as max
|
|
|
|
|
auto next = _lines[i].find_first_not_of( " \t", labelend + label.size() );
|
|
|
|
|
if ( next == std::string::npos )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if ( label == "default:" )
|
|
|
|
|
default_start = std::make_pair( i, labelend + label.size() );
|
|
|
|
|
else
|
2026-01-16 13:22:43 +01:00
|
|
|
statementstart.emplace_back( i, labelend + label.size() );
|
2024-09-13 06:49:00 +02:00
|
|
|
}
|
|
|
|
|
_packable_switch_labels.clear();
|
|
|
|
|
if ( statementstart.empty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto max = std::max_element( statementstart.begin(), statementstart.end(),
|
|
|
|
|
[]( auto& a, auto& b ) { return a.second < b.second; } )
|
|
|
|
|
->second;
|
|
|
|
|
if ( max == 0 )
|
|
|
|
|
return;
|
|
|
|
|
for ( auto& [line, pos] : statementstart )
|
|
|
|
|
{
|
|
|
|
|
if ( pos == max )
|
|
|
|
|
continue;
|
|
|
|
|
_lines[line].insert( pos, max - pos, ' ' );
|
|
|
|
|
}
|
|
|
|
|
// we have a default, but only align it if its not the "biggest"
|
|
|
|
|
if ( default_start )
|
|
|
|
|
{
|
|
|
|
|
if ( max > default_start->second )
|
|
|
|
|
_lines[default_start->first].insert( default_start->second, max - default_start->second,
|
|
|
|
|
' ' );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 21:21:44 +01:00
|
|
|
} // namespace Pol::Bscript::Compiler
|
|
|
|
|
|
|
|
|
|
fmt::format_context::iterator fmt::formatter<Pol::Bscript::Compiler::FmtToken>::format(
|
|
|
|
|
const Pol::Bscript::Compiler::FmtToken& t, fmt::format_context& ctx ) const
|
|
|
|
|
{
|
|
|
|
|
return fmt::formatter<std::string>::format(
|
|
|
|
|
fmt::format( "{} ({}:{}:{})", t.text, t.style, t.pos.line_number, t.pos.token_index ), ctx );
|
|
|
|
|
}
|