blob: a8a3abe6c6982a8a7db1c5f991cbe93f4a931da5 [file] [log] [blame]
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -04001"""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.
Thomas Wouterscf297e42007-02-23 15:07:44 +00006
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00007"""
Terry Jan Reedya0f1e222014-01-26 19:55:34 -05008import inspect
Thomas Wouterscf297e42007-02-23 15:07:44 +00009import re
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000010import sys
Terry Jan Reedya0f1e222014-01-26 19:55:34 -050011import textwrap
David Scherer7aced172000-08-15 01:13:23 +000012import types
13
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040014from idlelib import calltip_w
15from idlelib.hyperparser import HyperParser
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040016import __main__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000017
David Scherer7aced172000-08-15 01:13:23 +000018class CallTips:
19
20 menudefs = [
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000021 ('edit', [
22 ("Show call tip", "<<force-open-calltip>>"),
23 ])
David Scherer7aced172000-08-15 01:13:23 +000024 ]
25
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000026 def __init__(self, editwin=None):
Raymond Hettingerc5e378d2004-05-04 08:34:56 +000027 if editwin is None: # subprocess and test
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000028 self.editwin = None
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000029 else:
30 self.editwin = editwin
31 self.text = editwin.text
32 self.active_calltip = None
33 self._calltip_window = self._make_tk_calltip_window
David Scherer7aced172000-08-15 01:13:23 +000034
35 def close(self):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000036 self._calltip_window = None
David Scherer7aced172000-08-15 01:13:23 +000037
David Scherer7aced172000-08-15 01:13:23 +000038 def _make_tk_calltip_window(self):
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000039 # See __init__ for usage
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040040 return calltip_w.CallTip(self.text)
David Scherer7aced172000-08-15 01:13:23 +000041
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000042 def _remove_calltip_window(self, event=None):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000043 if self.active_calltip:
44 self.active_calltip.hidetip()
45 self.active_calltip = None
Kurt B. Kaiserae676472001-07-12 23:10:35 +000046
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000047 def force_open_calltip_event(self, event):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000048 "The user selected the menu entry or hotkey, open the tip."
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000049 self.open_calltip(True)
Serhiy Storchaka213ce122017-06-27 07:02:32 +030050 return "break"
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000051
52 def try_open_calltip_event(self, event):
53 """Happens when it would be nice to open a CallTip, but not really
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000054 necessary, for example after an opening bracket, so function calls
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000055 won't be made.
56 """
57 self.open_calltip(False)
58
59 def refresh_calltip_event(self, event):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000060 if self.active_calltip and self.active_calltip.is_active():
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000061 self.open_calltip(False)
62
63 def open_calltip(self, evalfuncs):
David Scherer7aced172000-08-15 01:13:23 +000064 self._remove_calltip_window()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000065
66 hp = HyperParser(self.editwin, "insert")
67 sur_paren = hp.get_surrounding_brackets('(')
68 if not sur_paren:
69 return
70 hp.set_index(sur_paren[0])
Terry Jan Reedye606e232012-06-03 00:27:54 -040071 expression = hp.get_expression()
72 if not expression:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000073 return
Terry Jan Reedye606e232012-06-03 00:27:54 -040074 if not evalfuncs and (expression.find('(') != -1):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000075 return
Terry Jan Reedye606e232012-06-03 00:27:54 -040076 argspec = self.fetch_tip(expression)
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000077 if not argspec:
78 return
79 self.active_calltip = self._calltip_window()
80 self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000081
Terry Jan Reedye606e232012-06-03 00:27:54 -040082 def fetch_tip(self, expression):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000083 """Return the argument list and docstring of a function or class.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000084
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000085 If there is a Python subprocess, get the calltip there. Otherwise,
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000086 either this fetch_tip() is running in the subprocess or it was
87 called in an IDLE running without the subprocess.
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000088
89 The subprocess environment is that of the most recently run script. If
90 two unrelated modules are being edited some calltips in the current
91 module may be inoperative if the module was not the last to run.
92
Thomas Wouterscf297e42007-02-23 15:07:44 +000093 To find methods, fetch_tip must be fed a fully qualified name.
94
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000095 """
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000096 try:
97 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
Terry Jan Reedye606e232012-06-03 00:27:54 -040098 except AttributeError:
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000099 rpcclt = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000100 if rpcclt:
101 return rpcclt.remotecall("exec", "get_the_calltip",
Terry Jan Reedye606e232012-06-03 00:27:54 -0400102 (expression,), {})
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000103 else:
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400104 return get_argspec(get_entity(expression))
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000105
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400106def get_entity(expression):
107 """Return the object corresponding to expression evaluated
108 in a namespace spanning sys.modules and __main.dict__.
109 """
110 if expression:
111 namespace = sys.modules.copy()
112 namespace.update(__main__.__dict__)
113 try:
114 return eval(expression, namespace)
115 except BaseException:
116 # An uncaught exception closes idle, and eval can raise any
117 # exception, especially if user classes are involved.
118 return None
David Scherer7aced172000-08-15 01:13:23 +0000119
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500120# The following are used in get_argspec and some in tests
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500121_MAX_COLS = 85
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500122_MAX_LINES = 5 # enough for bytes
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500123_INDENT = ' '*4 # for wrapped signatures
R David Murray44b548d2016-09-08 13:59:53 -0400124_first_param = re.compile(r'(?<=\()\w*\,?\s*')
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500125_default_callable_argspec = "See source or doc"
David Scherer7aced172000-08-15 01:13:23 +0000126
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500127
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000128def get_argspec(ob):
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500129 '''Return a string describing the signature of a callable object, or ''.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400130
131 For Python-coded functions and methods, the first line is introspected.
132 Delete 'self' parameter for classes (.__init__) and bound methods.
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500133 The next lines are the first lines of the doc string up to the first
134 empty line or _MAX_LINES. For builtins, this typically includes
135 the arguments in addition to the return value.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400136 '''
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000137 argspec = ""
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500138 try:
139 ob_call = ob.__call__
140 except BaseException:
141 return argspec
142 if isinstance(ob, type):
143 fob = ob.__init__
144 elif isinstance(ob_call, types.MethodType):
145 fob = ob_call
146 else:
147 fob = ob
148 if isinstance(fob, (types.FunctionType, types.MethodType)):
149 argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
150 if (isinstance(ob, (type, types.MethodType)) or
151 isinstance(ob_call, types.MethodType)):
152 argspec = _first_param.sub("", argspec)
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400153
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500154 lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
155 if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
156
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500157 if isinstance(ob_call, types.MethodType):
158 doc = ob_call.__doc__
159 else:
160 doc = getattr(ob, "__doc__", "")
161 if doc:
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500162 for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500163 line = line.strip()
164 if not line:
165 break
166 if len(line) > _MAX_COLS:
167 line = line[: _MAX_COLS - 3] + '...'
168 lines.append(line)
169 argspec = '\n'.join(lines)
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500170 if not argspec:
171 argspec = _default_callable_argspec
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000172 return argspec
David Scherer7aced172000-08-15 01:13:23 +0000173
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000174if __name__ == '__main__':
Terry Jan Reedydb4e5c52013-05-27 21:32:03 -0400175 from unittest import main
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500176 main('idlelib.idle_test.test_calltips', verbosity=2)