blob: 47a1d55d3b05c7f6681670165dc0947ad235f0c0 [file] [log] [blame]
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00001"""CallTips.py - An IDLE Extension to Jog Your Memory
David Scherer7aced172000-08-15 01:13:23 +00002
Kurt B. Kaisere54710b2002-12-12 19:15:39 +00003Call Tips are floating windows which display function, class, and method
4parameter and docstring information when you type an opening parenthesis, and
5which disappear when you type a closing parenthesis.
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00006"""
7import sys
David Scherer7aced172000-08-15 01:13:23 +00008import types
9
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000010import CallTipWindow
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000011from HyperParser import HyperParser
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000012
13import __main__
14
David Scherer7aced172000-08-15 01:13:23 +000015class CallTips:
16
17 menudefs = [
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000018 ('edit', [
19 ("Show call tip", "<<force-open-calltip>>"),
20 ])
David Scherer7aced172000-08-15 01:13:23 +000021 ]
22
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000023 def __init__(self, editwin=None):
Raymond Hettingerc5e378d2004-05-04 08:34:56 +000024 if editwin is None: # subprocess and test
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000025 self.editwin = None
26 return
David Scherer7aced172000-08-15 01:13:23 +000027 self.editwin = editwin
28 self.text = editwin.text
29 self.calltip = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000030 self._make_calltip_window = self._make_tk_calltip_window
David Scherer7aced172000-08-15 01:13:23 +000031
32 def close(self):
33 self._make_calltip_window = None
34
David Scherer7aced172000-08-15 01:13:23 +000035 def _make_tk_calltip_window(self):
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000036 # See __init__ for usage
David Scherer7aced172000-08-15 01:13:23 +000037 return CallTipWindow.CallTip(self.text)
38
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000039 def _remove_calltip_window(self, event=None):
David Scherer7aced172000-08-15 01:13:23 +000040 if self.calltip:
41 self.calltip.hidetip()
42 self.calltip = None
Kurt B. Kaiserae676472001-07-12 23:10:35 +000043
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000044 def force_open_calltip_event(self, event):
45 """Happens when the user really wants to open a CallTip, even if a
46 function call is needed.
47 """
48 self.open_calltip(True)
49
50 def try_open_calltip_event(self, event):
51 """Happens when it would be nice to open a CallTip, but not really
52 neccesary, for example after an opening bracket, so function calls
53 won't be made.
54 """
55 self.open_calltip(False)
56
57 def refresh_calltip_event(self, event):
58 """If there is already a calltip window, check if it is still needed,
59 and if so, reload it.
60 """
61 if self.calltip and self.calltip.is_active():
62 self.open_calltip(False)
63
64 def open_calltip(self, evalfuncs):
David Scherer7aced172000-08-15 01:13:23 +000065 self._remove_calltip_window()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000066
67 hp = HyperParser(self.editwin, "insert")
68 sur_paren = hp.get_surrounding_brackets('(')
69 if not sur_paren:
70 return
71 hp.set_index(sur_paren[0])
72 name = hp.get_expression()
73 if not name or (not evalfuncs and name.find('(') != -1):
74 return
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000075 arg_text = self.fetch_tip(name)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000076 if not arg_text:
77 return
78 self.calltip = self._make_calltip_window()
79 self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000080
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000081 def fetch_tip(self, name):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000082 """Return the argument list and docstring of a function or class
83
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000084 If there is a Python subprocess, get the calltip there. Otherwise,
85 either fetch_tip() is running in the subprocess itself or it was called
86 in an IDLE EditorWindow before any script had been run.
87
88 The subprocess environment is that of the most recently run script. If
89 two unrelated modules are being edited some calltips in the current
90 module may be inoperative if the module was not the last to run.
91
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000092 """
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000093 try:
94 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
95 except:
96 rpcclt = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000097 if rpcclt:
98 return rpcclt.remotecall("exec", "get_the_calltip",
99 (name,), {})
100 else:
101 entity = self.get_entity(name)
102 return get_arg_text(entity)
103
104 def get_entity(self, name):
Kurt B. Kaisere54710b2002-12-12 19:15:39 +0000105 "Lookup name in a namespace spanning sys.modules and __main.dict__"
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000106 if name:
David Scherer7aced172000-08-15 01:13:23 +0000107 namespace = sys.modules.copy()
108 namespace.update(__main__.__dict__)
109 try:
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000110 return eval(name, namespace)
David Scherer7aced172000-08-15 01:13:23 +0000111 except:
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000112 return None
David Scherer7aced172000-08-15 01:13:23 +0000113
114def _find_constructor(class_ob):
115 # Given a class object, return a function object used for the
116 # constructor (ie, __init__() ) or None if we can't find one.
117 try:
118 return class_ob.__init__.im_func
119 except AttributeError:
120 for base in class_ob.__bases__:
121 rc = _find_constructor(base)
122 if rc is not None: return rc
123 return None
124
125def get_arg_text(ob):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000126 """Get a string describing the arguments for the given object"""
David Scherer7aced172000-08-15 01:13:23 +0000127 argText = ""
128 if ob is not None:
129 argOffset = 0
130 if type(ob)==types.ClassType:
131 # Look for the highest __init__ in the class chain.
132 fob = _find_constructor(ob)
133 if fob is None:
134 fob = lambda: None
135 else:
136 argOffset = 1
137 elif type(ob)==types.MethodType:
138 # bit of a hack for methods - turn it into a function
139 # but we drop the "self" param.
140 fob = ob.im_func
141 argOffset = 1
142 else:
143 fob = ob
144 # Try and build one for Python defined functions
145 if type(fob) in [types.FunctionType, types.LambdaType]:
146 try:
147 realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
148 defaults = fob.func_defaults or []
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000149 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
David Scherer7aced172000-08-15 01:13:23 +0000150 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
151 items = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
152 if fob.func_code.co_flags & 0x4:
153 items.append("...")
154 if fob.func_code.co_flags & 0x8:
155 items.append("***")
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000156 argText = ", ".join(items)
David Scherer7aced172000-08-15 01:13:23 +0000157 argText = "(%s)" % argText
158 except:
159 pass
160 # See if we can use the docstring
Kurt B. Kaiser6145a622003-07-23 15:42:14 +0000161 doc = getattr(ob, "__doc__", "")
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000162 if doc:
Kurt B. Kaiser6145a622003-07-23 15:42:14 +0000163 doc = doc.lstrip()
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000164 pos = doc.find("\n")
165 if pos < 0 or pos > 70:
166 pos = 70
167 if argText:
168 argText += "\n"
169 argText += doc[:pos]
David Scherer7aced172000-08-15 01:13:23 +0000170 return argText
171
172#################################################
173#
174# Test code
175#
176if __name__=='__main__':
177
178 def t1(): "()"
179 def t2(a, b=None): "(a, b=None)"
180 def t3(a, *args): "(a, ...)"
181 def t4(*args): "(...)"
182 def t5(a, *args): "(a, ...)"
183 def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
184
185 class TC:
186 "(a=None, ...)"
187 def __init__(self, a=None, *b): "(a=None, ...)"
188 def t1(self): "()"
189 def t2(self, a, b=None): "(a, b=None)"
190 def t3(self, a, *args): "(a, ...)"
191 def t4(self, *args): "(...)"
192 def t5(self, a, *args): "(a, ...)"
193 def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)"
194
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000195 def test(tests):
196 ct = CallTips()
David Scherer7aced172000-08-15 01:13:23 +0000197 failed=[]
198 for t in tests:
199 expected = t.__doc__ + "\n" + t.__doc__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000200 name = t.__name__
201 arg_text = ct.fetch_tip(name)
202 if arg_text != expected:
David Scherer7aced172000-08-15 01:13:23 +0000203 failed.append(t)
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000204 print "%s - expected %s, but got %s" % (t, expected,
205 get_arg_text(entity))
David Scherer7aced172000-08-15 01:13:23 +0000206 print "%d of %d tests failed" % (len(failed), len(tests))
207
208 tc = TC()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000209 tests = (t1, t2, t3, t4, t5, t6,
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000210 TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6)
David Scherer7aced172000-08-15 01:13:23 +0000211
212 test(tests)