diff --git a/python/python-syntax/resources/intellij.python.syntax.xml b/python/python-syntax/resources/intellij.python.syntax.xml
index dc695ea19edd..a6da3c9d748c 100644
--- a/python/python-syntax/resources/intellij.python.syntax.xml
+++ b/python/python-syntax/resources/intellij.python.syntax.xml
@@ -17,6 +17,7 @@
+
diff --git a/python/python-syntax/src/com/jetbrains/python/editor/selectWord/PyFStringSelectionHandler.kt b/python/python-syntax/src/com/jetbrains/python/editor/selectWord/PyFStringSelectionHandler.kt
new file mode 100644
index 000000000000..86f9d256d25b
--- /dev/null
+++ b/python/python-syntax/src/com/jetbrains/python/editor/selectWord/PyFStringSelectionHandler.kt
@@ -0,0 +1,57 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.jetbrains.python.editor.selectWord
+
+import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElement
+import com.intellij.psi.util.PsiTreeUtil
+import com.jetbrains.python.ast.PyAstExpression
+import com.jetbrains.python.ast.PyAstFStringFragment
+import com.jetbrains.python.ast.PyAstFormattedStringElement
+
+
+class PyFStringSelectionHandler : ExtendWordSelectionHandlerBase() {
+
+ override fun canSelect(e: PsiElement): Boolean {
+ return PsiTreeUtil.getParentOfType(e, PyAstFStringFragment::class.java, false) != null
+ }
+
+ override fun select(
+ e: PsiElement,
+ editorText: CharSequence,
+ cursorOffset: Int,
+ editor: Editor
+ ): List? {
+ val fragment: PyAstFStringFragment =
+ PsiTreeUtil.getParentOfType(e, PyAstFStringFragment::class.java, false)
+ ?: return null
+
+ val expression: PyAstExpression = fragment.expression ?: return null
+ val exprRange: TextRange = expression.textRange
+
+ val result = mutableListOf()
+
+ result.add(exprRange)
+
+ val typeConversionPsi = fragment.typeConversion
+ if (typeConversionPsi != null) {
+ val typeConversionRange = typeConversionPsi.textRange
+ result.add(TextRange(exprRange.startOffset, typeConversionRange.endOffset))
+ }
+
+ val formatPartPsi = fragment.formatPart
+ if (formatPartPsi != null) {
+ val formatTextRange = formatPartPsi.textRange
+ result.add(TextRange(exprRange.startOffset, formatTextRange.endOffset))
+ }
+
+ result.add(fragment.textRange)
+
+ val parent = fragment.parent
+ if (parent is PyAstFormattedStringElement) {
+ result.add(parent.textRange)
+ }
+ return result
+ }
+}
\ No newline at end of file
diff --git a/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after1.py b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after1.py
new file mode 100644
index 000000000000..0f49d1318349
--- /dev/null
+++ b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after1.py
@@ -0,0 +1 @@
+s = f"pipeline:1:{ref!s:{width}}"
\ No newline at end of file
diff --git a/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after2.py b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after2.py
new file mode 100644
index 000000000000..c58f5a8860a8
--- /dev/null
+++ b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after2.py
@@ -0,0 +1 @@
+s = f"pipeline:1:{ref!s:{width}}"
\ No newline at end of file
diff --git a/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after3.py b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after3.py
new file mode 100644
index 000000000000..da3ec501776d
--- /dev/null
+++ b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after3.py
@@ -0,0 +1 @@
+s = f"pipeline:1:{ref!s:{width}}"
\ No newline at end of file
diff --git a/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after4.py b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after4.py
new file mode 100644
index 000000000000..52f865292a2b
--- /dev/null
+++ b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/after4.py
@@ -0,0 +1 @@
+s = f"pipeline:1:{ref!s:{width}}"
\ No newline at end of file
diff --git a/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/before.py b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/before.py
new file mode 100644
index 000000000000..918dde9b7caf
--- /dev/null
+++ b/python/testData/selectWord/fStringFragmentExpressionTypeConversionAndFormatSelection/before.py
@@ -0,0 +1 @@
+s = f"pipeline:1:{ref!s:{width}}"
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/PySelectWordTest.java b/python/testSrc/com/jetbrains/python/PySelectWordTest.java
index 39c443ddc861..11b03008f54a 100644
--- a/python/testSrc/com/jetbrains/python/PySelectWordTest.java
+++ b/python/testSrc/com/jetbrains/python/PySelectWordTest.java
@@ -38,6 +38,11 @@ public class PySelectWordTest extends PyTestCase {
doTest();
}
+ // PY-83580
+ public void testFStringFragmentExpressionTypeConversionAndFormatSelection() {
+ doTest();
+ }
+
private void doTest() {
CodeInsightTestUtil.doWordSelectionTestOnDirectory(myFixture, "selectWord/" + getTestName(true), "py");
}