simplify escape handling
This commit is contained in:
committed by
Jeff Young
parent
09ca9d4728
commit
8ebf7367bf
+31
-239
@@ -57,236 +57,6 @@ enum Bracket
|
||||
#endif
|
||||
Bracket_Max
|
||||
};
|
||||
// Forward declaration
|
||||
static wxString ProtectAllPatterns( const wxString& aSource );
|
||||
|
||||
// Recursively protect escaped expressions (\${...} and \@{...}) by converting them to escape markers
|
||||
// This must be done BEFORE any variable expansion to prevent variables inside escaped expressions from being expanded
|
||||
wxString ProtectEscapes( const wxString& aSource )
|
||||
{
|
||||
wxString newbuf;
|
||||
size_t sourceLen = aSource.length();
|
||||
|
||||
newbuf.Alloc( sourceLen );
|
||||
|
||||
for( size_t i = 0; i < sourceLen; ++i )
|
||||
{
|
||||
// Check for \${ or \@{
|
||||
if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
|
||||
( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
|
||||
{
|
||||
wxChar escapeChar = aSource[i + 1];
|
||||
|
||||
// Find the matching closing brace
|
||||
int braceDepth = 1;
|
||||
size_t contentStart = i + 3;
|
||||
size_t j = contentStart;
|
||||
|
||||
while( j < sourceLen && braceDepth > 0 )
|
||||
{
|
||||
if( aSource[j] == '{' )
|
||||
braceDepth++;
|
||||
else if( aSource[j] == '}' )
|
||||
braceDepth--;
|
||||
j++;
|
||||
}
|
||||
|
||||
// Extract the contents and protect ALL patterns inside (not just escaped ones)
|
||||
// This ensures that ${VAR} inside \@{...} won't be expanded
|
||||
wxString content = aSource.Mid( contentStart, j - contentStart - 1 );
|
||||
wxString protectedContent = ProtectAllPatterns( content ); // Protect ALL patterns
|
||||
|
||||
// Add the escape marker
|
||||
if( escapeChar == '$' )
|
||||
newbuf.append( wxT( "<<<ESC_DOLLAR:" ) );
|
||||
else
|
||||
newbuf.append( wxT( "<<<ESC_AT:" ) );
|
||||
|
||||
newbuf.append( protectedContent );
|
||||
newbuf.append( '}' );
|
||||
|
||||
i = j - 1; // Skip past the entire escaped expression
|
||||
}
|
||||
else
|
||||
{
|
||||
newbuf.append( aSource[i] );
|
||||
}
|
||||
}
|
||||
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
|
||||
// Recursively protect ALL ${...} and @{...} patterns (both escaped and unescaped)
|
||||
// Used when processing the contents of escaped expressions to prevent any expansion
|
||||
static wxString ProtectAllPatterns( const wxString& aSource )
|
||||
{
|
||||
wxString newbuf;
|
||||
size_t sourceLen = aSource.length();
|
||||
|
||||
newbuf.Alloc( sourceLen );
|
||||
|
||||
for( size_t i = 0; i < sourceLen; ++i )
|
||||
{
|
||||
// Check for \${ or \@{ (escaped patterns)
|
||||
if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
|
||||
( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
|
||||
{
|
||||
wxChar escapeChar = aSource[i + 1];
|
||||
|
||||
// Find the matching closing brace
|
||||
int braceDepth = 1;
|
||||
size_t contentStart = i + 3;
|
||||
size_t j = contentStart;
|
||||
|
||||
while( j < sourceLen && braceDepth > 0 )
|
||||
{
|
||||
if( aSource[j] == '{' )
|
||||
braceDepth++;
|
||||
else if( aSource[j] == '}' )
|
||||
braceDepth--;
|
||||
j++;
|
||||
}
|
||||
|
||||
// Extract and recursively protect the contents
|
||||
wxString content = aSource.Mid( contentStart, j - contentStart - 1 );
|
||||
wxString protectedContent = ProtectAllPatterns( content );
|
||||
|
||||
// Add the escape marker
|
||||
if( escapeChar == '$' )
|
||||
newbuf.append( wxT( "<<<ESC_DOLLAR:" ) );
|
||||
else
|
||||
newbuf.append( wxT( "<<<ESC_AT:" ) );
|
||||
|
||||
newbuf.append( protectedContent );
|
||||
newbuf.append( '}' );
|
||||
|
||||
i = j - 1;
|
||||
}
|
||||
// Check for ${ or @{ (unescaped patterns - also protect these)
|
||||
else if( ( aSource[i] == '$' || aSource[i] == '@' ) && i + 1 < sourceLen && aSource[i + 1] == '{' )
|
||||
{
|
||||
wxChar marker = aSource[i];
|
||||
|
||||
// Find the matching closing brace
|
||||
int braceDepth = 1;
|
||||
size_t contentStart = i + 2;
|
||||
size_t j = contentStart;
|
||||
|
||||
while( j < sourceLen && braceDepth > 0 )
|
||||
{
|
||||
if( aSource[j] == '{' )
|
||||
braceDepth++;
|
||||
else if( aSource[j] == '}' )
|
||||
braceDepth--;
|
||||
j++;
|
||||
}
|
||||
|
||||
// Extract and recursively protect the contents
|
||||
wxString content = aSource.Mid( contentStart, j - contentStart - 1 );
|
||||
wxString protectedContent = ProtectAllPatterns( content );
|
||||
|
||||
// Add the escape marker
|
||||
if( marker == '$' )
|
||||
newbuf.append( wxT( "<<<ESC_DOLLAR:" ) );
|
||||
else
|
||||
newbuf.append( wxT( "<<<ESC_AT:" ) );
|
||||
|
||||
newbuf.append( protectedContent );
|
||||
newbuf.append( '}' );
|
||||
|
||||
i = j - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
newbuf.append( aSource[i] );
|
||||
}
|
||||
}
|
||||
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
|
||||
// Recursively convert escape markers back to ${...} and @{...}
|
||||
// This should only be done at the very end, after all variable expansion and expression evaluation
|
||||
wxString UnprotectEscapes( const wxString& aSource )
|
||||
{
|
||||
wxString newbuf;
|
||||
size_t sourceLen = aSource.length();
|
||||
|
||||
newbuf.Alloc( sourceLen );
|
||||
|
||||
for( size_t i = 0; i < sourceLen; ++i )
|
||||
{
|
||||
// Check for <<<ESC_DOLLAR:
|
||||
if( i + 14 <= sourceLen && aSource.Mid( i, 14 ) == wxT( "<<<ESC_DOLLAR:" ) )
|
||||
{
|
||||
newbuf.append( wxT( "${" ) );
|
||||
i += 14;
|
||||
|
||||
// Find the matching closing brace and recursively unescape the contents
|
||||
int braceCount = 1;
|
||||
size_t contentStart = i;
|
||||
size_t j = i;
|
||||
|
||||
while( j < sourceLen && braceCount > 0 )
|
||||
{
|
||||
if( aSource[j] == '{' )
|
||||
braceCount++;
|
||||
else if( aSource[j] == '}' )
|
||||
braceCount--;
|
||||
if( braceCount > 0 )
|
||||
j++;
|
||||
}
|
||||
|
||||
// Extract and recursively unescape the contents
|
||||
wxString content = aSource.Mid( contentStart, j - contentStart );
|
||||
wxString unprotectedContent = UnprotectEscapes( content ); // Recursive call
|
||||
|
||||
newbuf.append( unprotectedContent );
|
||||
newbuf.append( '}' );
|
||||
|
||||
i = j; // Skip past the closing brace
|
||||
}
|
||||
// Check for <<<ESC_AT:
|
||||
else if( i + 10 <= sourceLen && aSource.Mid( i, 10 ) == wxT( "<<<ESC_AT:" ) )
|
||||
{
|
||||
newbuf.append( wxT( "@{" ) );
|
||||
i += 10;
|
||||
|
||||
// Find the matching closing brace and recursively unescape the contents
|
||||
int braceCount = 1;
|
||||
size_t contentStart = i;
|
||||
size_t j = i;
|
||||
|
||||
while( j < sourceLen && braceCount > 0 )
|
||||
{
|
||||
if( aSource[j] == '{' )
|
||||
braceCount++;
|
||||
else if( aSource[j] == '}' )
|
||||
braceCount--;
|
||||
if( braceCount > 0 )
|
||||
j++;
|
||||
}
|
||||
|
||||
// Extract and recursively unescape the contents
|
||||
wxString content = aSource.Mid( contentStart, j - contentStart );
|
||||
wxString unprotectedContent = UnprotectEscapes( content ); // Recursive call
|
||||
|
||||
newbuf.append( unprotectedContent );
|
||||
newbuf.append( '}' );
|
||||
|
||||
i = j; // Skip past the closing brace
|
||||
}
|
||||
else
|
||||
{
|
||||
newbuf.append( aSource[i] );
|
||||
}
|
||||
}
|
||||
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
|
||||
wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject, int aFlags )
|
||||
{
|
||||
@@ -357,8 +127,36 @@ wxString ExpandTextVars( const wxString& aSource, const std::function<bool( wxSt
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: Escape handling (\${...} and \@{...}) is now done by ProtectEscapes()
|
||||
// which is called at the start of ResolveTextVars(), before ExpandTextVars()
|
||||
// Handle escaped variable references: \${...} or \@{...}
|
||||
// Replace with escape markers that won't be expanded by multi-pass loops
|
||||
// The markers will be converted back to ${...} or @{...} only at the final display stage
|
||||
if( aSource[i] == '\\' && i + 1 < sourceLen )
|
||||
{
|
||||
if( ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) && i + 2 < sourceLen && aSource[i + 2] == '{' )
|
||||
{
|
||||
// Replace \${ with <<<ESC_DOLLAR: and \@{ with <<<ESC_AT:
|
||||
// Using unique delimiters without braces to avoid confusing the expression evaluator
|
||||
if( aSource[i + 1] == '$' )
|
||||
newbuf.append( wxT( "<<<ESC_DOLLAR:" ) );
|
||||
else
|
||||
newbuf.append( wxT( "<<<ESC_AT:" ) );
|
||||
i += 2;
|
||||
|
||||
// Copy everything until the matching closing brace, including the brace
|
||||
int braceDepth = 1;
|
||||
for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
|
||||
{
|
||||
if( aSource[i] == '{' )
|
||||
braceDepth++;
|
||||
else if( aSource[i] == '}' )
|
||||
braceDepth--;
|
||||
|
||||
newbuf.append( aSource[i] );
|
||||
}
|
||||
i--; // Adjust because loop will increment
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if( ( aSource[i] == '$' || aSource[i] == '@' ) && i + 1 < sourceLen && aSource[i + 1] == '{' )
|
||||
{
|
||||
@@ -450,10 +248,7 @@ wxString ResolveTextVars( const wxString& aSource, const std::function<bool( wxS
|
||||
{
|
||||
// Multi-pass resolution to handle nested variables like ${J601:UNIT(${ROW})}
|
||||
// and math expressions like @{${ROW}-1}
|
||||
|
||||
// FIRST: Protect any escaped expressions (\${...} or \@{...}) BEFORE expansion
|
||||
// This prevents variables inside escaped expressions from being expanded
|
||||
wxString text = ProtectEscapes( aSource );
|
||||
wxString text = aSource;
|
||||
const int maxDepth = ADVANCED_CFG::GetCfg().m_ResolveTextRecursionDepth;
|
||||
|
||||
static EXPRESSION_EVALUATOR evaluator;
|
||||
@@ -471,9 +266,6 @@ wxString ResolveTextVars( const wxString& aSource, const std::function<bool( wxS
|
||||
text = evaluator.Evaluate( text ); // Evaluate math expressions
|
||||
}
|
||||
|
||||
// NOTE: We do NOT unescape here because this function can be called recursively
|
||||
// (e.g., in nested CELL() references). Unescaping will be done in GetShownText() for final display.
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
@@ -286,7 +286,8 @@ wxString SCH_FIELD::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraT
|
||||
text = _( "File:" ) + wxS( " " ) + text;
|
||||
|
||||
// Convert escape markers back to literals for final display
|
||||
text = UnprotectEscapes( text );
|
||||
text.Replace( wxT( "<<<ESC_DOLLAR:" ), wxT( "${" ) );
|
||||
text.Replace( wxT( "<<<ESC_AT:" ), wxT( "@{" ) );
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -978,7 +978,8 @@ wxString SCH_LABEL_BASE::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowE
|
||||
text = ResolveTextVars( text, &textResolver, depth );
|
||||
|
||||
// Convert escape markers back to literals for final display
|
||||
text = UnprotectEscapes( text );
|
||||
text.Replace( wxT( "<<<ESC_DOLLAR:" ), wxT( "${" ) );
|
||||
text.Replace( wxT( "<<<ESC_AT:" ), wxT( "@{" ) );
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
+17
-12
@@ -152,7 +152,7 @@ static bool parseCellAddress( const wxString& aAddr, int& aRow, int& aCol )
|
||||
wxString SCH_TABLECELL::GetShownText( const RENDER_SETTINGS* aSettings, const SCH_SHEET_PATH* aPath,
|
||||
bool aAllowExtraText, int aDepth ) const
|
||||
{
|
||||
// Use local depth counter so each text element starts fresh
|
||||
// Local depth counter for ResolveTextVars iteration tracking (separate from cross-cell aDepth)
|
||||
int depth = 0;
|
||||
|
||||
SCH_SHEET* sheet = nullptr;
|
||||
@@ -160,6 +160,11 @@ wxString SCH_TABLECELL::GetShownText( const RENDER_SETTINGS* aSettings, const SC
|
||||
if( aPath )
|
||||
sheet = aPath->Last();
|
||||
|
||||
// Text variable resolver supporting:
|
||||
// - ${ROW}, ${COL}, ${ADDR} - cell position variables
|
||||
// - @{expression} - math expression evaluation
|
||||
// - ${CELL("A1")} or ${CELL(row,col)} - reference to another cell's evaluated text
|
||||
// - \${...} and \@{...} - escape sequences for literal display
|
||||
std::function<bool( wxString* )> tableCellResolver = [&]( wxString* token ) -> bool
|
||||
{
|
||||
if( token->IsSameAs( wxT( "ROW" ) ) )
|
||||
@@ -248,14 +253,11 @@ wxString SCH_TABLECELL::GetShownText( const RENDER_SETTINGS* aSettings, const SC
|
||||
SCH_TABLECELL* targetCell = table->GetCell( targetRow, targetCol );
|
||||
if( targetCell )
|
||||
{
|
||||
// Get the RAW text from the target cell (unevaluated)
|
||||
wxString rawText = targetCell->GetText();
|
||||
|
||||
// Consume one level of escaping: \@{${ROW}-10} becomes @{${ROW}-10}
|
||||
// First protect: \@{...} → <<<ESC_AT:...> (converts backslash to marker)
|
||||
// Then unprotect: <<<ESC_AT:...> → @{...} (removes marker, consuming escape)
|
||||
// Return the unescaped text and let the outer ResolveTextVars handle evaluation
|
||||
*token = UnprotectEscapes( ProtectEscapes( rawText ) );
|
||||
// Return the fully evaluated/displayed text from the target cell
|
||||
// Variables and expressions are evaluated in the target cell's context
|
||||
// (e.g., ${ROW} in the target cell refers to the target's row, not the referencing cell's row)
|
||||
// Increment aDepth to prevent infinite recursion in circular references
|
||||
*token = targetCell->GetShownText( aSettings, aPath, aAllowExtraText, aDepth + 1 );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -291,10 +293,13 @@ wxString SCH_TABLECELL::GetShownText( const RENDER_SETTINGS* aSettings, const SC
|
||||
GetDrawFont( aSettings )
|
||||
->LinebreakText( text, colWidth, GetTextSize(), GetEffectiveTextPenWidth(), IsBold(), IsItalic() );
|
||||
|
||||
// Convert escape markers back to literals for final display
|
||||
// Only unescape at the top level to avoid premature unescaping in nested CELL() calls
|
||||
// Convert escape markers back to literal ${} and @{} for final display
|
||||
// Only do this at the top level (aDepth == 0) to avoid premature unescaping in nested CELL() calls
|
||||
if( aDepth == 0 )
|
||||
text = UnprotectEscapes( text );
|
||||
{
|
||||
text.Replace( wxT( "<<<ESC_DOLLAR:" ), wxT( "${" ) );
|
||||
text.Replace( wxT( "<<<ESC_AT:" ), wxT( "@{" ) );
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -351,7 +351,8 @@ wxString SCH_TEXT::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraTe
|
||||
text = ResolveTextVars( text, &textResolver, depth );
|
||||
|
||||
// Convert escape markers back to literals for final display
|
||||
text = UnprotectEscapes( text );
|
||||
text.Replace( wxT( "<<<ESC_DOLLAR:" ), wxT( "${" ) );
|
||||
text.Replace( wxT( "<<<ESC_AT:" ), wxT( "@{" ) );
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -276,7 +276,8 @@ wxString SCH_TEXTBOX::GetShownText( const RENDER_SETTINGS* aSettings, const SCH_
|
||||
->LinebreakText( text, colWidth, GetTextSize(), GetEffectiveTextPenWidth(), IsBold(), IsItalic() );
|
||||
|
||||
// Convert escape markers back to literals for final display
|
||||
text = UnprotectEscapes( text );
|
||||
text.Replace( wxT( "<<<ESC_DOLLAR:" ), wxT( "${" ) );
|
||||
text.Replace( wxT( "<<<ESC_AT:" ), wxT( "@{" ) );
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -120,29 +120,6 @@ KICOMMON_API wxString ExpandTextVars( const wxString& aSource, const PROJECT* aP
|
||||
KICOMMON_API wxString ResolveTextVars( const wxString& aSource, const std::function<bool( wxString* )>* aResolver,
|
||||
int& aDepth );
|
||||
|
||||
/**
|
||||
* Recursively protect escaped expressions (\${...} and \@{...}) by converting them to escape markers.
|
||||
*
|
||||
* This must be done BEFORE any variable expansion to prevent variables inside escaped expressions
|
||||
* from being expanded. The function recursively protects all patterns inside escaped expressions.
|
||||
*
|
||||
* @param aSource The text containing escaped expressions (\${...} and \@{...})
|
||||
* @return Text with escaped expressions converted to markers (<<<ESC_DOLLAR:...} and <<<ESC_AT:...})
|
||||
*/
|
||||
KICOMMON_API wxString ProtectEscapes( const wxString& aSource );
|
||||
|
||||
/**
|
||||
* Recursively convert escape markers back to ${...} and @{...} for final display.
|
||||
*
|
||||
* This function unescapes protected expressions that were marked during ProtectEscapes().
|
||||
* It should only be called at the very end, in GetShownText() functions, after all
|
||||
* variable expansion and expression evaluation is complete.
|
||||
*
|
||||
* @param aSource The text containing escape markers (<<<ESC_DOLLAR:...} and <<<ESC_AT:...})
|
||||
* @return Text with escape markers converted back to ${...} and @{...}
|
||||
*/
|
||||
KICOMMON_API wxString UnprotectEscapes( const wxString& aSource );
|
||||
|
||||
/**
|
||||
* Returns any variables unexpanded, e.g. ${VAR} -> VAR
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user