@@ -2560,9 +2560,7 @@ private static string ReplaceRegexMatchesWithSpaces(Regex regex, string input)
25602560
25612561 private static string PrepareLine ( string lang , string line )
25622562 {
2563- var result = lang == "python"
2564- ? MaskPythonSingleLineFStrings ( line )
2565- : line ;
2563+ var result = line ;
25662564 if ( lang == "rust" )
25672565 result = MaskRustLifetimeTokens ( result ) ;
25682566 if ( lang != "cobol" )
@@ -3188,6 +3186,211 @@ private static string MaskPythonSingleLineFStrings(string line)
31883186 return new string ( masked ) ;
31893187 }
31903188
3189+ private static string [ ] MaskPythonFStrings ( IReadOnlyList < string > lines )
3190+ {
3191+ var result = new string [ lines . Count ] ;
3192+ for ( var lineIndex = 0 ; lineIndex < lines . Count ; lineIndex ++ )
3193+ result [ lineIndex ] = lines [ lineIndex ] ;
3194+
3195+ for ( var lineIndex = 0 ; lineIndex < result . Length ; lineIndex ++ )
3196+ {
3197+ var line = result [ lineIndex ] ;
3198+ if ( line . IndexOf ( 'f' ) < 0 && line . IndexOf ( 'F' ) < 0 )
3199+ continue ;
3200+
3201+ var chars = line . ToCharArray ( ) ;
3202+ var changed = false ;
3203+ for ( var column = 0 ; column < line . Length ; column ++ )
3204+ {
3205+ if ( ! TryOpenPythonString ( line , column , out var prefixLength , out var quoteChar , out var isRaw , out var isFString , out var isTripleQuoted ) )
3206+ continue ;
3207+
3208+ if ( ! isFString )
3209+ {
3210+ column += prefixLength ;
3211+ continue ;
3212+ }
3213+
3214+ if ( ! isTripleQuoted )
3215+ {
3216+ result [ lineIndex ] = MaskPythonSingleLineFStrings ( line ) ;
3217+ chars = result [ lineIndex ] . ToCharArray ( ) ;
3218+ changed = true ;
3219+ break ;
3220+ }
3221+
3222+ MaskPythonTripleQuotedFString ( result , lineIndex , column , prefixLength , quoteChar , isRaw , out var endLineIndex , out var endColumn ) ;
3223+ lineIndex = endLineIndex ;
3224+ line = result [ lineIndex ] ;
3225+ chars = line . ToCharArray ( ) ;
3226+ column = endColumn ;
3227+ changed = true ;
3228+ }
3229+
3230+ if ( changed )
3231+ result [ lineIndex ] = new string ( chars ) ;
3232+ }
3233+
3234+ return result ;
3235+ }
3236+
3237+ private static void MaskPythonTripleQuotedFString (
3238+ string [ ] lines ,
3239+ int startLineIndex ,
3240+ int startColumn ,
3241+ int prefixLength ,
3242+ char quoteChar ,
3243+ bool isRaw ,
3244+ out int endLineIndex ,
3245+ out int endColumn )
3246+ {
3247+ var lineIndex = startLineIndex ;
3248+ var column = startColumn ;
3249+ var inExpression = false ;
3250+ var inExpressionString = false ;
3251+ var expressionDepth = 0 ;
3252+ var expressionStringQuote = '\0 ' ;
3253+ var expressionStringTripleQuoted = false ;
3254+
3255+ endLineIndex = startLineIndex ;
3256+ endColumn = startColumn ;
3257+
3258+ while ( lineIndex < lines . Length )
3259+ {
3260+ var line = lines [ lineIndex ] ;
3261+ var chars = line . ToCharArray ( ) ;
3262+ if ( lineIndex == startLineIndex )
3263+ {
3264+ ReplaceWithSpaces ( chars , startColumn , prefixLength + 3 ) ;
3265+ column = startColumn + prefixLength + 3 ;
3266+ }
3267+ else
3268+ {
3269+ column = 0 ;
3270+ }
3271+
3272+ while ( column < line . Length )
3273+ {
3274+ if ( ! inExpression )
3275+ {
3276+ if ( ! isRaw && line [ column ] == '\\ ' && column + 1 < line . Length )
3277+ {
3278+ ReplaceWithSpaces ( chars , column , 2 ) ;
3279+ column += 2 ;
3280+ continue ;
3281+ }
3282+
3283+ if ( line [ column ] == '{' && column + 1 < line . Length && line [ column + 1 ] == '{' )
3284+ {
3285+ ReplaceWithSpaces ( chars , column , 2 ) ;
3286+ column += 2 ;
3287+ continue ;
3288+ }
3289+
3290+ if ( line [ column ] == '}' && column + 1 < line . Length && line [ column + 1 ] == '}' )
3291+ {
3292+ ReplaceWithSpaces ( chars , column , 2 ) ;
3293+ column += 2 ;
3294+ continue ;
3295+ }
3296+
3297+ if ( line [ column ] == '{' )
3298+ {
3299+ chars [ column ++ ] = ' ' ;
3300+ inExpression = true ;
3301+ expressionDepth = 1 ;
3302+ continue ;
3303+ }
3304+
3305+ if ( column + 2 < line . Length
3306+ && line [ column ] == quoteChar
3307+ && line [ column + 1 ] == quoteChar
3308+ && line [ column + 2 ] == quoteChar )
3309+ {
3310+ ReplaceWithSpaces ( chars , column , 3 ) ;
3311+ lines [ lineIndex ] = new string ( chars ) ;
3312+ endLineIndex = lineIndex ;
3313+ endColumn = column + 2 ;
3314+ return ;
3315+ }
3316+
3317+ chars [ column ++ ] = ' ' ;
3318+ continue ;
3319+ }
3320+
3321+ if ( inExpressionString )
3322+ {
3323+ if ( line [ column ] == '\\ ' && column + 1 < line . Length )
3324+ {
3325+ column += 2 ;
3326+ continue ;
3327+ }
3328+
3329+ if ( expressionStringTripleQuoted )
3330+ {
3331+ if ( column + 2 < line . Length
3332+ && line [ column ] == expressionStringQuote
3333+ && line [ column + 1 ] == expressionStringQuote
3334+ && line [ column + 2 ] == expressionStringQuote )
3335+ {
3336+ column += 3 ;
3337+ inExpressionString = false ;
3338+ continue ;
3339+ }
3340+
3341+ column ++ ;
3342+ continue ;
3343+ }
3344+
3345+ if ( line [ column ] == expressionStringQuote )
3346+ {
3347+ column ++ ;
3348+ inExpressionString = false ;
3349+ continue ;
3350+ }
3351+
3352+ column ++ ;
3353+ continue ;
3354+ }
3355+
3356+ if ( line [ column ] == '\' ' || line [ column ] == '"' )
3357+ {
3358+ expressionStringQuote = line [ column ] ;
3359+ expressionStringTripleQuoted = column + 2 < line . Length
3360+ && line [ column + 1 ] == expressionStringQuote
3361+ && line [ column + 2 ] == expressionStringQuote ;
3362+ column += expressionStringTripleQuoted ? 3 : 1 ;
3363+ inExpressionString = true ;
3364+ continue ;
3365+ }
3366+
3367+ if ( line [ column ] == '{' )
3368+ {
3369+ expressionDepth ++ ;
3370+ column ++ ;
3371+ continue ;
3372+ }
3373+
3374+ if ( line [ column ] == '}' )
3375+ {
3376+ expressionDepth -- ;
3377+ chars [ column ++ ] = ' ' ;
3378+ if ( expressionDepth == 0 )
3379+ inExpression = false ;
3380+ continue ;
3381+ }
3382+
3383+ column ++ ;
3384+ }
3385+
3386+ lines [ lineIndex ] = new string ( chars ) ;
3387+ lineIndex ++ ;
3388+ }
3389+
3390+ endLineIndex = Math . Max ( startLineIndex , lines . Length - 1 ) ;
3391+ endColumn = 0 ;
3392+ }
3393+
31913394 private static void ReplaceWithSpaces ( char [ ] buffer , int start , int length )
31923395 {
31933396 for ( var i = start ; i < start + length && i < buffer . Length ; i ++ )
@@ -3235,6 +3438,63 @@ private static bool TryOpenPythonSingleLineString(
32353438 return true ;
32363439 }
32373440
3441+ private static bool TryOpenPythonString (
3442+ string line ,
3443+ int startIndex ,
3444+ out int prefixLength ,
3445+ out char quoteChar ,
3446+ out bool isRaw ,
3447+ out bool isFString ,
3448+ out bool isTripleQuoted )
3449+ {
3450+ isTripleQuoted = false ;
3451+ if ( ! TryOpenPythonSingleOrTripleString ( line , startIndex , out prefixLength , out quoteChar , out isRaw , out isFString , out isTripleQuoted ) )
3452+ return false ;
3453+ return true ;
3454+ }
3455+
3456+ private static bool TryOpenPythonSingleOrTripleString (
3457+ string line ,
3458+ int startIndex ,
3459+ out int prefixLength ,
3460+ out char quoteChar ,
3461+ out bool isRaw ,
3462+ out bool isFString ,
3463+ out bool isTripleQuoted )
3464+ {
3465+ prefixLength = 0 ;
3466+ quoteChar = '\0 ' ;
3467+ isRaw = false ;
3468+ isFString = false ;
3469+ isTripleQuoted = false ;
3470+
3471+ if ( startIndex < 0 || startIndex >= line . Length )
3472+ return false ;
3473+
3474+ if ( startIndex > 0 && IsIdentifierChar ( line [ startIndex - 1 ] ) )
3475+ return false ;
3476+
3477+ var p = startIndex ;
3478+ var prefixChars = 0 ;
3479+ while ( p < line . Length && prefixChars < 2 && IsPythonStringPrefixChar ( line [ p ] ) )
3480+ {
3481+ if ( line [ p ] is 'r' or 'R' )
3482+ isRaw = true ;
3483+ if ( line [ p ] is 'f' or 'F' )
3484+ isFString = true ;
3485+ p ++ ;
3486+ prefixChars ++ ;
3487+ }
3488+
3489+ if ( p >= line . Length || ( line [ p ] != '\' ' && line [ p ] != '"' ) )
3490+ return false ;
3491+
3492+ prefixLength = p - startIndex ;
3493+ quoteChar = line [ p ] ;
3494+ isTripleQuoted = p + 2 < line . Length && line [ p + 1 ] == quoteChar && line [ p + 2 ] == quoteChar ;
3495+ return true ;
3496+ }
3497+
32383498 private static bool IsIgnoredCallName ( string language , string name )
32393499 {
32403500 if ( LanguageSpecificCallNameKeeps . TryGetValue ( language , out var languageSpecificKeepNames )
0 commit comments