Spaces:
Running
Running
File size: 6,372 Bytes
d12bc25 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that
# displays parameter information as you open parens.
import inspect
import string
import sys
import traceback
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_uppercase
+ string.ascii_lowercase
+ string.digits,
):
# 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 __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__
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:
if inspect.isclass(ob):
# Look for the highest __init__ in the class chain.
fob = _find_constructor(ob)
if fob is None:
fob = lambda: None
else:
fob = ob
if inspect.isfunction(fob) or inspect.ismethod(fob):
try:
argText = str(inspect.signature(fob))
except:
print("Failed to format the args")
traceback.print_exc()
# See if we can use the docstring
if hasattr(ob, "__doc__"):
doc = ob.__doc__
try:
doc = doc.strip()
pos = doc.find("\n")
except AttributeError:
## New style classes may have __doc__ slot without actually
## having a string assigned to it
pass
else:
if pos < 0 or pos > 70:
pos = 70
if argText:
argText = argText + "\n"
argText = 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, *args)"
def t4(*args):
"(*args)"
def t5(a, *args):
"(a, *args)"
def t6(a, b=None, *args, **kw):
"(a, b=None, *args, **kw)"
class TC:
"(self, a=None, *b)"
def __init__(self, a=None, *b):
"(self, a=None, *b)"
def t1(self):
"(self)"
def t2(self, a, b=None):
"(self, a, b=None)"
def t3(self, a, *args):
"(self, a, *args)"
def t4(self, *args):
"(self, *args)"
def t5(self, a, *args):
"(self, a, *args)"
def t6(self, a, b=None, *args, **kw):
"(self, a, b=None, *args, **kw)"
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, repr(expected), repr(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)
|