From c3d9df11efeb42e643cb9cf09234199a0044c66a Mon Sep 17 00:00:00 2001 From: Egor Eliseev Date: Thu, 12 Feb 2026 19:07:06 +0000 Subject: [PATCH] [python] PY-87389 [debugpy] Support "value tooltip" Merge-request: IJ-MR-191406 Merged-by: Egor Eliseev GitOrigin-RevId: f2fa583abddf285ccb92804f55b7286b70a4bdac --- .../python/debugger/PyDebuggerEvaluator.kt | 75 +++++++++++++------ 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/python/src/com/jetbrains/python/debugger/PyDebuggerEvaluator.kt b/python/src/com/jetbrains/python/debugger/PyDebuggerEvaluator.kt index 021e2898c13f..88fad440cecc 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebuggerEvaluator.kt +++ b/python/src/com/jetbrains/python/debugger/PyDebuggerEvaluator.kt @@ -12,11 +12,13 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiReference import com.intellij.xdebugger.XSourcePosition import com.intellij.xdebugger.evaluation.XDebuggerEvaluator +import com.intellij.xdebugger.evaluation.XDebuggerEvaluator.XEvaluationCallback import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerDocumentOffsetEvaluator import com.intellij.xdebugger.impl.evaluate.quick.common.ValueHintType import com.jetbrains.python.console.PyConsoleIndentUtil import com.jetbrains.python.psi.PyCallExpression import com.jetbrains.python.psi.PyExpression +import org.jetbrains.annotations.ApiStatus internal class PyDebuggerEvaluator(private val myProject: Project, private val myDebugProcess: PyFrameAccessor) : XDebuggerEvaluator(), XDebuggerDocumentOffsetEvaluator { override fun evaluate(expression: String, callback: XEvaluationCallback, expressionPosition: XSourcePosition?): Unit = @@ -51,35 +53,60 @@ internal class PyDebuggerEvaluator(private val myProject: Project, private val m }) } - override fun getExpressionRangeAtOffset(project: Project?, document: Document?, offset: Int, sideEffectsAllowed: Boolean): TextRange? = + override fun getExpressionRangeAtOffset(project: Project?, document: Document?, offset: Int, sideEffectsAllowed: Boolean): TextRange? = PyDebugSupportUtils.getExpressionRangeAtOffset(project, document, offset) override fun formatTextForEvaluation(text: String): String = PyConsoleIndentUtil.normalize(text) override fun evaluate(document: Document, offset: Int, hintType: ValueHintType, callback: XEvaluationCallback) { - if (!Registry.`is`("python.debugger.show.value.tooltip")) { - callback.errorOccurred("") - return - } - - val ref = ReadAction.compute(ThrowableComputable { - PsiDocumentManager.getInstance(myProject).getPsiFile(document)?.findReferenceAt(offset) - }) - if (ref == null) { - callback.errorOccurred("") - return - } - - val element = ref.getElement() - when(element) { - is PyExpression -> { - if (element.getParent() is PyCallExpression) { - callback.errorOccurred("") - } else { - doEvaluate(element.getText(), callback, false) - } - } - else -> callback.errorOccurred("") + evaluateWithOffset(myProject, document, offset, hintType, callback) { expression -> + doEvaluate(expression, callback, false) } } +} + +/** + * Common implementation for evaluating expressions at a given document offset (for value tooltips). + * + * @param project the project + * @param document the document + * @param offset the offset in the document + * @param hintType the hint type + * @param callback the evaluation callback + * @param evaluateExpression the function to evaluate the expression text + */ +@ApiStatus.Internal +fun evaluateWithOffset( + project: Project, + document: Document, + offset: Int, + hintType: ValueHintType, + callback: XEvaluationCallback, + evaluateExpression: (String) -> Unit, +) { + if (!Registry.`is`("python.debugger.show.value.tooltip")) { + callback.errorOccurred("") + return + } + + val ref = ReadAction.compute(ThrowableComputable { + PsiDocumentManager.getInstance(project).getPsiFile(document)?.findReferenceAt(offset) + }) + if (ref == null) { + callback.errorOccurred("") + return + } + + val element = ref.element + when (element) { + is PyExpression -> { + if (element.parent is PyCallExpression) { + callback.errorOccurred("") + } + else { + evaluateExpression(element.text) + } + } + else -> callback.errorOccurred("") + } } \ No newline at end of file