diff --git a/common/common.cpp b/common/common.cpp index e3325d332f..15ca369681 100644 --- a/common/common.cpp +++ b/common/common.cpp @@ -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( "<< 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( "<< 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( "<< 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 << 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 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::functionLast(); + // 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 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: \@{...} → << (converts backslash to marker) - // Then unprotect: << → @{...} (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( "<<LinebreakText( text, colWidth, GetTextSize(), GetEffectiveTextPenWidth(), IsBold(), IsItalic() ); // Convert escape markers back to literals for final display - text = UnprotectEscapes( text ); + text.Replace( wxT( "<<* 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 (<< VAR */