InjectionResult (@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar, @NotNull String prefix, @NotNull String suffix, @NotNull Formatting formatting) { final String missingValue = "missing_value"; if (element instanceof PyStringLiteralExpression) { boolean injected = false; boolean strict = true; final PyStringLiteralExpression expr = (PyStringLiteralExpression)element; for (PyStringElement stringElem : expr.getStringElements()) { final int nodeOffsetInParent = stringElem.getTextOffset() - expr.getTextRange().getStartOffset(); final TextRange contentRange = stringElem.getContentRange(); final int contentStartOffset = contentRange.getStartOffset(); if (formatting != Formatting.NONE || stringElem.isFormatted()) { // Each range is relative to the start of the string element final List<TextRange> subsRanges; if (formatting != Formatting.NONE) { final String content = stringElem.getContent(); subsRanges = StreamEx.of(formatting == Formatting.NEW_STYLE ? parseNewStyleFormat(content) : parsePercentFormat(content)) .select(SubstitutionChunk.class) .map(chunk -> chunk.getTextRange().shiftRight(contentStartOffset)) .toList(); } else { subsRanges = ContainerUtil.map(((PyFormattedStringElement)stringElem).getFragments(), PsiElement::getTextRangeInParent); } if (!subsRanges.isEmpty()) { strict = false; } final TextRange sentinel = TextRange.from(contentRange.getEndOffset(), 0); final List<TextRange> withSentinel = ContainerUtil.append(subsRanges, sentinel); int literalChunkStart = contentStartOffset; int literalChunkEnd; for (int i = 0; i < withSentinel.size(); i++) { final TextRange subRange = withSentinel.get(i); literalChunkEnd = subRange.getStartOffset(); if (literalChunkEnd > literalChunkStart) { final String chunkPrefix; if (i == 0) { chunkPrefix = prefix; } else if (i == 1 && withSentinel.get(0).getStartOffset() == contentStartOffset) { chunkPrefix = missingValue; } else { chunkPrefix = ""; } final String chunkSuffix; if (i < withSentinel.size() - 1) { chunkSuffix = missingValue; } else if (i == withSentinel.size() - 1) { chunkSuffix = suffix; } else { chunkSuffix = ""; } final TextRange chunkRange = TextRange.create(literalChunkStart, literalChunkEnd); registrar.addPlace(chunkPrefix, chunkSuffix, expr, chunkRange.shiftRight(nodeOffsetInParent)); injected = true; } literalChunkStart = subRange.getEndOffset(); } } else { registrar.addPlace(prefix, suffix, expr, contentRange.shiftRight(nodeOffsetInParent)); injected = true; } } return new InjectionResult(injected, strict); } else if (element instanceof PyParenthesizedExpression) { final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression(); if (contained != null) { return processStringLiteral(contained, registrar, prefix, suffix, formatting); } } else if (element instanceof PyBinaryExpression expr) { final PyExpression left = expr.getLeftExpression(); final PyExpression right = expr.getRightExpression(); final boolean isLeftString = isStringLiteralPart(left, null); if (expr.isOperator("+")) { final boolean isRightString = right != null && isStringLiteralPart(right, null); InjectionResult result = InjectionResult.EMPTY; if (isLeftString) { result = result.append(processStringLiteral(left, registrar, prefix, isRightString ? "" : missingValue, formatting)); } if (isRightString) { result = result.append(processStringLiteral(right, registrar, isLeftString ? "" : missingValue, suffix, formatting)); } return result; } else if (expr.isOperator("%")) { return processStringLiteral(left, registrar, prefix, suffix, Formatting.PERCENT); } } else if (element instanceof PyCallExpression) { final PyExpression qualifier = getFormatCallQualifier((PyCallExpression)element); if (qualifier != null) { return processStringLiteral(qualifier, registrar, prefix, suffix, Formatting.NEW_STYLE); } } return InjectionResult.EMPTY; }