bpo-40511: Stop unwanted flashing of IDLE calltips (GH-20910)
They were occurring with both repeated 'force-calltip' invocations and by typing parentheses
in expressions, strings, and comments in the argument code.
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
diff --git a/Lib/idlelib/calltip.py b/Lib/idlelib/calltip.py
index b02f872..549e224 100644
--- a/Lib/idlelib/calltip.py
+++ b/Lib/idlelib/calltip.py
@@ -55,18 +55,50 @@ def refresh_calltip_event(self, event):
self.open_calltip(False)
def open_calltip(self, evalfuncs):
- self.remove_calltip_window()
+ """Maybe close an existing calltip and maybe open a new calltip.
+ Called from (force_open|try_open|refresh)_calltip_event functions.
+ """
hp = HyperParser(self.editwin, "insert")
sur_paren = hp.get_surrounding_brackets('(')
+
+ # If not inside parentheses, no calltip.
if not sur_paren:
+ self.remove_calltip_window()
return
+
+ # If a calltip is shown for the current parentheses, do
+ # nothing.
+ if self.active_calltip:
+ opener_line, opener_col = map(int, sur_paren[0].split('.'))
+ if (
+ (opener_line, opener_col) ==
+ (self.active_calltip.parenline, self.active_calltip.parencol)
+ ):
+ return
+
hp.set_index(sur_paren[0])
- expression = hp.get_expression()
+ try:
+ expression = hp.get_expression()
+ except ValueError:
+ expression = None
if not expression:
+ # No expression before the opening parenthesis, e.g.
+ # because it's in a string or the opener for a tuple:
+ # Do nothing.
return
+
+ # At this point, the current index is after an opening
+ # parenthesis, in a section of code, preceded by a valid
+ # expression. If there is a calltip shown, it's not for the
+ # same index and should be closed.
+ self.remove_calltip_window()
+
+ # Simple, fast heuristic: If the preceding expression includes
+ # an opening parenthesis, it likely includes a function call.
if not evalfuncs and (expression.find('(') != -1):
return
+
argspec = self.fetch_tip(expression)
if not argspec:
return