@Nullable
  private static PsiElement replaceSubstringInStringLiteral(@NotNull PyStringLiteralExpression oldExpression,
                                                            @NotNull PsiElement newExpression,
                                                            @NotNull TextRange textRange) {
    final String fullText = oldExpression.getText();
    final Pair<String, String> detectedQuotes = PyStringLiteralCoreUtil.getQuotes(fullText);
    final Pair<String, String> quotes = detectedQuotes != null ? detectedQuotes : Pair.create("'", "'");
    final String prefix = fullText.substring(0, textRange.getStartOffset());
    final String suffix = fullText.substring(textRange.getEndOffset(), oldExpression.getTextLength());
    final PyExpression formatValue = getFormatValueExpression(oldExpression);
    final PyArgumentList newStyleFormatValue = getNewStyleFormatValueExpression(oldExpression);
    final String newText = newExpression.getText();

    final List<SubstitutionChunk> substitutions;
    if (newStyleFormatValue != null) {
      substitutions = filterSubstitutions(parseNewStyleFormat(fullText));
    }
    else {
      substitutions = filterSubstitutions(parsePercentFormat(fullText));
    }
    final boolean hasSubstitutions = !substitutions.isEmpty();

    if (formatValue != null && !containsStringFormatting(substitutions, textRange)) {
      if (formatValue instanceof PyTupleExpression) {
        return replaceSubstringWithTupleFormatting(oldExpression, newExpression, textRange, prefix, suffix,
                                                   (PyTupleExpression)formatValue, substitutions);
      }
      else if (formatValue instanceof PyDictLiteralExpression) {
        return replaceSubstringWithDictFormatting(oldExpression, quotes, prefix, suffix, formatValue, newText);
      }
      else {
        final TypeEvalContext context = TypeEvalContext.userInitiated(oldExpression.getProject(), oldExpression.getContainingFile());
        final PyType valueType = context.getType(formatValue);
        final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(oldExpression);
        final PyType tupleType = builtinCache.getTupleType();
        final PyType mappingType = PyTypeParser.getTypeByName(oldExpression, "collections.Mapping", context);
        if (!PyTypeChecker.match(tupleType, valueType, context) &&
            !(mappingType != null && PyTypeChecker.match(mappingType, valueType, context))) {
          return replaceSubstringWithSingleValueFormatting(oldExpression, textRange, prefix, suffix, formatValue, newText, substitutions);
        }
      }
    }

    if (newStyleFormatValue != null && hasSubstitutions && !containsStringFormatting(substitutions, textRange)) {
      final PyExpression[] arguments = newStyleFormatValue.getArguments();
      boolean hasStarArguments = false;
      for (PyExpression argument : arguments) {
        if (argument instanceof PyStarArgument) {
          hasStarArguments = true;
          break;
        }
      }
      if (!hasStarArguments) {
        return replaceSubstringWithNewStyleFormatting(oldExpression, textRange, prefix, suffix, newStyleFormatValue, newText,
                                                      substitutions);
      }
    }

    if (isConcatFormatting(oldExpression) || hasSubstitutions) {
      return replaceSubstringWithConcatFormatting(oldExpression, quotes, prefix, suffix, newText, hasSubstitutions);
    }

    return replaceSubstringWithoutFormatting(oldExpression, prefix, suffix, newText);
  }
@Nullable
  private static PsiElement replaceSubstringInStringLiteral(@NotNull PyStringLiteralExpression oldExpression,
                                                            @NotNull PsiElement newExpression,
                                                            @NotNull TextRange textRange)