blob: 81bd5f1890e0e498c109c5ea6651a17f24ddb6dd [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.
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 __main__
9import inspect
Thomas Wouterscf297e42007-02-23 15:07:44 +000010import re
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000011import sys
Terry Jan Reedya0f1e222014-01-26 19:55:34 -050012import textwrap
David Scherer7aced172000-08-15 01:13:23 +000013import types
14
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000015from idlelib import CallTipWindow
16from idlelib.HyperParser import HyperParser
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
David Scherer7aced172000-08-15 01:13:23 +000040 return CallTipWindow.CallTip(self.text)
41
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)
50
51 def try_open_calltip_event(self, event):
52 """Happens when it would be nice to open a CallTip, but not really
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000053 necessary, for example after an opening bracket, so function calls
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000054 won't be made.
55 """
56 self.open_calltip(False)
57
58 def refresh_calltip_event(self, event):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000059 if self.active_calltip and self.active_calltip.is_active():
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000060 self.open_calltip(False)
61
62 def open_calltip(self, evalfuncs):
David Scherer7aced172000-08-15 01:13:23 +000063 self._remove_calltip_window()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000064
65 hp = HyperParser(self.editwin, "insert")
66 sur_paren = hp.get_surrounding_brackets('(')
67 if not sur_paren:
68 return
69 hp.set_index(sur_paren[0])
Terry Jan Reedye606e232012-06-03 00:27:54 -040070 expression = hp.get_expression()
71 if not expression:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000072 return
Terry Jan Reedye606e232012-06-03 00:27:54 -040073 if not evalfuncs and (expression.find('(') != -1):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000074 return
Terry Jan Reedye606e232012-06-03 00:27:54 -040075 argspec = self.fetch_tip(expression)
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000076 if not argspec:
77 return
78 self.active_calltip = self._calltip_window()
79 self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000080
Terry Jan Reedye606e232012-06-03 00:27:54 -040081 def fetch_tip(self, expression):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000082 """Return the argument list and docstring of a function or class.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000083
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000084 If there is a Python subprocess, get the calltip there. Otherwise,
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000085 either this fetch_tip() is running in the subprocess or it was
86 called in an IDLE running without the subprocess.
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000087
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
Thomas Wouterscf297e42007-02-23 15:07:44 +000092 To find methods, fetch_tip must be fed a fully qualified name.
93
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000094 """
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000095 try:
96 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
Terry Jan Reedye606e232012-06-03 00:27:54 -040097 except AttributeError:
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000098 rpcclt = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000099 if rpcclt:
100 return rpcclt.remotecall("exec", "get_the_calltip",
Terry Jan Reedye606e232012-06-03 00:27:54 -0400101 (expression,), {})
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000102 else:
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400103 return get_argspec(get_entity(expression))
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000104
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400105def get_entity(expression):
106 """Return the object corresponding to expression evaluated
107 in a namespace spanning sys.modules and __main.dict__.
108 """
109 if expression:
110 namespace = sys.modules.copy()
111 namespace.update(__main__.__dict__)
112 try:
113 return eval(expression, namespace)
114 except BaseException:
115 # An uncaught exception closes idle, and eval can raise any
116 # exception, especially if user classes are involved.
117 return None
David Scherer7aced172000-08-15 01:13:23 +0000118
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500119# The following are used in get_argspec and some in tests
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500120_MAX_COLS = 85
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500121_MAX_LINES = 5 # enough for bytes
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500122_INDENT = ' '*4 # for wrapped signatures
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400123_first_param = re.compile('(?<=\()\w*\,?\s*')
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500124_default_callable_argspec = "See source or doc"
David Scherer7aced172000-08-15 01:13:23 +0000125
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500126
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000127def get_argspec(ob):
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500128 '''Return a string describing the signature of a callable object, or ''.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400129
130 For Python-coded functions and methods, the first line is introspected.
131 Delete 'self' parameter for classes (.__init__) and bound methods.
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500132 The next lines are the first lines of the doc string up to the first
133 empty line or _MAX_LINES. For builtins, this typically includes
134 the arguments in addition to the return value.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400135 '''
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000136 argspec = ""
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500137 try:
138 ob_call = ob.__call__
139 except BaseException:
140 return argspec
141 if isinstance(ob, type):
142 fob = ob.__init__
143 elif isinstance(ob_call, types.MethodType):
144 fob = ob_call
145 else:
146 fob = ob
147 if isinstance(fob, (types.FunctionType, types.MethodType)):
148 argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
149 if (isinstance(ob, (type, types.MethodType)) or
150 isinstance(ob_call, types.MethodType)):
151 argspec = _first_param.sub("", argspec)
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400152
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500153 lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
154 if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
155
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500156 if isinstance(ob_call, types.MethodType):
157 doc = ob_call.__doc__
158 else:
159 doc = getattr(ob, "__doc__", "")
160 if doc:
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500161 for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500162 line = line.strip()
163 if not line:
164 break
165 if len(line) > _MAX_COLS:
166 line = line[: _MAX_COLS - 3] + '...'
167 lines.append(line)
168 argspec = '\n'.join(lines)
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500169 if not argspec:
170 argspec = _default_callable_argspec
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000171 return argspec
David Scherer7aced172000-08-15 01:13:23 +0000172
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000173if __name__ == '__main__':
Terry Jan Reedydb4e5c52013-05-27 21:32:03 -0400174 from unittest import main
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500175 main('idlelib.idle_test.test_calltips', verbosity=2)