mirror of
https://github.com/python/cpython.git
synced 2025-07-28 05:34:31 +00:00

(with one small bugfix in bgen/bgen/scantools.py) This replaces string module functions with string methods for the stuff in the Tools directory. Several uses of string.letters etc. are still remaining.
198 lines
6.6 KiB
Python
198 lines
6.6 KiB
Python
# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that
|
|
# displays parameter information as you open parens.
|
|
|
|
import string
|
|
import sys
|
|
import types
|
|
|
|
class CallTips:
|
|
|
|
menudefs = [
|
|
]
|
|
|
|
keydefs = {
|
|
'<<paren-open>>': ['<Key-parenleft>'],
|
|
'<<paren-close>>': ['<Key-parenright>'],
|
|
'<<check-calltip-cancel>>': ['<KeyRelease>'],
|
|
'<<calltip-cancel>>': ['<ButtonPress>', '<Key-Escape>'],
|
|
}
|
|
|
|
windows_keydefs = {
|
|
}
|
|
|
|
unix_keydefs = {
|
|
}
|
|
|
|
def __init__(self, editwin):
|
|
self.editwin = editwin
|
|
self.text = editwin.text
|
|
self.calltip = None
|
|
if hasattr(self.text, "make_calltip_window"):
|
|
self._make_calltip_window = self.text.make_calltip_window
|
|
else:
|
|
self._make_calltip_window = self._make_tk_calltip_window
|
|
|
|
def close(self):
|
|
self._make_calltip_window = None
|
|
|
|
# Makes a Tk based calltip window. Used by IDLE, but not Pythonwin.
|
|
# See __init__ above for how this is used.
|
|
def _make_tk_calltip_window(self):
|
|
import CallTipWindow
|
|
return CallTipWindow.CallTip(self.text)
|
|
|
|
def _remove_calltip_window(self):
|
|
if self.calltip:
|
|
self.calltip.hidetip()
|
|
self.calltip = None
|
|
|
|
def paren_open_event(self, event):
|
|
self._remove_calltip_window()
|
|
arg_text = get_arg_text(self.get_object_at_cursor())
|
|
if arg_text:
|
|
self.calltip_start = self.text.index("insert")
|
|
self.calltip = self._make_calltip_window()
|
|
self.calltip.showtip(arg_text)
|
|
return "" #so the event is handled normally.
|
|
|
|
def paren_close_event(self, event):
|
|
# Now just hides, but later we should check if other
|
|
# paren'd expressions remain open.
|
|
self._remove_calltip_window()
|
|
return "" #so the event is handled normally.
|
|
|
|
def check_calltip_cancel_event(self, event):
|
|
if self.calltip:
|
|
# If we have moved before the start of the calltip,
|
|
# or off the calltip line, then cancel the tip.
|
|
# (Later need to be smarter about multi-line, etc)
|
|
if self.text.compare("insert", "<=", self.calltip_start) or \
|
|
self.text.compare("insert", ">", self.calltip_start + " lineend"):
|
|
self._remove_calltip_window()
|
|
return "" #so the event is handled normally.
|
|
|
|
def calltip_cancel_event(self, event):
|
|
self._remove_calltip_window()
|
|
return "" #so the event is handled normally.
|
|
|
|
def get_object_at_cursor(self,
|
|
wordchars="._" + string.ascii_letters + string.digits):
|
|
# Usage of ascii_letters is necessary to avoid UnicodeErrors
|
|
# if chars contains non-ASCII.
|
|
|
|
# XXX - This needs to be moved to a better place
|
|
# so the "." attribute lookup code can also use it.
|
|
text = self.text
|
|
chars = text.get("insert linestart", "insert")
|
|
i = len(chars)
|
|
while i and chars[i-1] in wordchars:
|
|
i = i-1
|
|
word = chars[i:]
|
|
if word:
|
|
# How is this for a hack!
|
|
import sys, __main__
|
|
namespace = sys.modules.copy()
|
|
namespace.update(__main__.__dict__)
|
|
try:
|
|
return eval(word, namespace)
|
|
except:
|
|
pass
|
|
return None # Can't find an object.
|
|
|
|
def _find_constructor(class_ob):
|
|
# Given a class object, return a function object used for the
|
|
# constructor (ie, __init__() ) or None if we can't find one.
|
|
try:
|
|
return class_ob.__init__.im_func
|
|
except AttributeError:
|
|
for base in class_ob.__bases__:
|
|
rc = _find_constructor(base)
|
|
if rc is not None: return rc
|
|
return None
|
|
|
|
def get_arg_text(ob):
|
|
# Get a string describing the arguments for the given object.
|
|
argText = ""
|
|
if ob is not None:
|
|
argOffset = 0
|
|
if type(ob)==types.ClassType:
|
|
# Look for the highest __init__ in the class chain.
|
|
fob = _find_constructor(ob)
|
|
if fob is None:
|
|
fob = lambda: None
|
|
else:
|
|
argOffset = 1
|
|
elif type(ob)==types.MethodType:
|
|
# bit of a hack for methods - turn it into a function
|
|
# but we drop the "self" param.
|
|
fob = ob.im_func
|
|
argOffset = 1
|
|
else:
|
|
fob = ob
|
|
# Try and build one for Python defined functions
|
|
if type(fob) in [types.FunctionType, types.LambdaType]:
|
|
try:
|
|
realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
|
|
defaults = fob.func_defaults or []
|
|
defaults = list(map(lambda name: "=%s" % name, defaults))
|
|
defaults = [""] * (len(realArgs)-len(defaults)) + defaults
|
|
items = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
|
|
if fob.func_code.co_flags & 0x4:
|
|
items.append("...")
|
|
if fob.func_code.co_flags & 0x8:
|
|
items.append("***")
|
|
argText = ", ".join(items)
|
|
argText = "(%s)" % argText
|
|
except:
|
|
pass
|
|
# See if we can use the docstring
|
|
doc = getattr(ob, "__doc__", "")
|
|
if doc:
|
|
while doc[:1] in " \t\n":
|
|
doc = doc[1:]
|
|
pos = doc.find("\n")
|
|
if pos < 0 or pos > 70:
|
|
pos = 70
|
|
if argText:
|
|
argText += "\n"
|
|
argText += doc[:pos]
|
|
|
|
return argText
|
|
|
|
#################################################
|
|
#
|
|
# Test code
|
|
#
|
|
if __name__=='__main__':
|
|
|
|
def t1(): "()"
|
|
def t2(a, b=None): "(a, b=None)"
|
|
def t3(a, *args): "(a, ...)"
|
|
def t4(*args): "(...)"
|
|
def t5(a, *args): "(a, ...)"
|
|
def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
|
|
|
|
class TC:
|
|
"(a=None, ...)"
|
|
def __init__(self, a=None, *b): "(a=None, ...)"
|
|
def t1(self): "()"
|
|
def t2(self, a, b=None): "(a, b=None)"
|
|
def t3(self, a, *args): "(a, ...)"
|
|
def t4(self, *args): "(...)"
|
|
def t5(self, a, *args): "(a, ...)"
|
|
def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)"
|
|
|
|
def test( tests ):
|
|
failed=[]
|
|
for t in tests:
|
|
expected = t.__doc__ + "\n" + t.__doc__
|
|
if get_arg_text(t) != expected:
|
|
failed.append(t)
|
|
print "%s - expected %s, but got %s" % (t, `expected`, `get_arg_text(t)`)
|
|
print "%d of %d tests failed" % (len(failed), len(tests))
|
|
|
|
tc = TC()
|
|
tests = t1, t2, t3, t4, t5, t6, \
|
|
TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6
|
|
|
|
test(tests)
|