##// END OF EJS Templates
Fix a completion crasher (index error during completion)....
Gael Varoquaux -
Show More
@@ -1,294 +1,305 b''
1 """
1 """
2 Base front end class for all line-oriented frontends, rather than
2 Base front end class for all line-oriented frontends, rather than
3 block-oriented.
3 block-oriented.
4
4
5 Currently this focuses on synchronous frontends.
5 Currently this focuses on synchronous frontends.
6 """
6 """
7 __docformat__ = "restructuredtext en"
7 __docformat__ = "restructuredtext en"
8
8
9 #-------------------------------------------------------------------------------
9 #-------------------------------------------------------------------------------
10 # Copyright (C) 2008 The IPython Development Team
10 # Copyright (C) 2008 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15
15
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 import re
19 import re
20
20
21 import IPython
21 import IPython
22 import sys
22 import sys
23
23
24 from frontendbase import FrontEndBase
24 from frontendbase import FrontEndBase
25 from IPython.kernel.core.interpreter import Interpreter
25 from IPython.kernel.core.interpreter import Interpreter
26
26
27 def common_prefix(strings):
27 def common_prefix(strings):
28 """ Given a list of strings, return the common prefix between all
28 """ Given a list of strings, return the common prefix between all
29 these strings.
29 these strings.
30 """
30 """
31 ref = strings[0]
31 ref = strings[0]
32 prefix = ''
32 prefix = ''
33 for size in range(len(ref)):
33 for size in range(len(ref)):
34 test_prefix = ref[:size+1]
34 test_prefix = ref[:size+1]
35 for string in strings[1:]:
35 for string in strings[1:]:
36 if not string.startswith(test_prefix):
36 if not string.startswith(test_prefix):
37 return prefix
37 return prefix
38 prefix = test_prefix
38 prefix = test_prefix
39
39
40 return prefix
40 return prefix
41
41
42 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
43 # Base class for the line-oriented front ends
43 # Base class for the line-oriented front ends
44 #-------------------------------------------------------------------------------
44 #-------------------------------------------------------------------------------
45 class LineFrontEndBase(FrontEndBase):
45 class LineFrontEndBase(FrontEndBase):
46 """ Concrete implementation of the FrontEndBase class. This is meant
46 """ Concrete implementation of the FrontEndBase class. This is meant
47 to be the base class behind all the frontend that are line-oriented,
47 to be the base class behind all the frontend that are line-oriented,
48 rather than block-oriented.
48 rather than block-oriented.
49 """
49 """
50
50
51 # We need to keep the prompt number, to be able to increment
51 # We need to keep the prompt number, to be able to increment
52 # it when there is an exception.
52 # it when there is an exception.
53 prompt_number = 1
53 prompt_number = 1
54
54
55 # We keep a reference to the last result: it helps testing and
55 # We keep a reference to the last result: it helps testing and
56 # programatic control of the frontend.
56 # programatic control of the frontend.
57 last_result = dict(number=0)
57 last_result = dict(number=0)
58
58
59 # The input buffer being edited
59 # The input buffer being edited
60 input_buffer = ''
60 input_buffer = ''
61
61
62 # Set to true for debug output
62 # Set to true for debug output
63 debug = False
63 debug = False
64
64
65 # A banner to print at startup
65 # A banner to print at startup
66 banner = None
66 banner = None
67
67
68 #--------------------------------------------------------------------------
68 #--------------------------------------------------------------------------
69 # FrontEndBase interface
69 # FrontEndBase interface
70 #--------------------------------------------------------------------------
70 #--------------------------------------------------------------------------
71
71
72 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
72 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
73 if shell is None:
73 if shell is None:
74 shell = Interpreter()
74 shell = Interpreter()
75 FrontEndBase.__init__(self, shell=shell, history=history)
75 FrontEndBase.__init__(self, shell=shell, history=history)
76
76
77 if banner is not None:
77 if banner is not None:
78 self.banner = banner
78 self.banner = banner
79
80 def start(self):
81 """ Put the frontend in a state where it is ready for user
82 interaction.
83 """
79 if self.banner is not None:
84 if self.banner is not None:
80 self.write(self.banner, refresh=False)
85 self.write(self.banner, refresh=False)
81
86
82 self.new_prompt(self.input_prompt_template.substitute(number=1))
87 self.new_prompt(self.input_prompt_template.substitute(number=1))
83
88
84
89
85 def complete(self, line):
90 def complete(self, line):
86 """Complete line in engine's user_ns
91 """Complete line in engine's user_ns
87
92
88 Parameters
93 Parameters
89 ----------
94 ----------
90 line : string
95 line : string
91
96
92 Result
97 Result
93 ------
98 ------
94 The replacement for the line and the list of possible completions.
99 The replacement for the line and the list of possible completions.
95 """
100 """
96 completions = self.shell.complete(line)
101 completions = self.shell.complete(line)
97 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
102 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
98 if completions:
103 if completions:
99 prefix = common_prefix(completions)
104 prefix = common_prefix(completions)
100 residual = complete_sep.split(line)[:-1]
105 residual = complete_sep.split(line)[:-1]
101 line = line[:-len(residual)] + prefix
106 line = line[:-len(residual)] + prefix
102 return line, completions
107 return line, completions
103
108
104
109
105 def render_result(self, result):
110 def render_result(self, result):
106 """ Frontend-specific rendering of the result of a calculation
111 """ Frontend-specific rendering of the result of a calculation
107 that has been sent to an engine.
112 that has been sent to an engine.
108 """
113 """
109 if 'stdout' in result and result['stdout']:
114 if 'stdout' in result and result['stdout']:
110 self.write('\n' + result['stdout'])
115 self.write('\n' + result['stdout'])
111 if 'display' in result and result['display']:
116 if 'display' in result and result['display']:
112 self.write("%s%s\n" % (
117 self.write("%s%s\n" % (
113 self.output_prompt_template.substitute(
118 self.output_prompt_template.substitute(
114 number=result['number']),
119 number=result['number']),
115 result['display']['pprint']
120 result['display']['pprint']
116 ) )
121 ) )
117
122
118
123
119 def render_error(self, failure):
124 def render_error(self, failure):
120 """ Frontend-specific rendering of error.
125 """ Frontend-specific rendering of error.
121 """
126 """
122 self.write('\n\n'+str(failure)+'\n\n')
127 self.write('\n\n'+str(failure)+'\n\n')
123 return failure
128 return failure
124
129
125
130
126 def is_complete(self, string):
131 def is_complete(self, string):
127 """ Check if a string forms a complete, executable set of
132 """ Check if a string forms a complete, executable set of
128 commands.
133 commands.
129
134
130 For the line-oriented frontend, multi-line code is not executed
135 For the line-oriented frontend, multi-line code is not executed
131 as soon as it is complete: the users has to enter two line
136 as soon as it is complete: the users has to enter two line
132 returns.
137 returns.
133 """
138 """
134 if string in ('', '\n'):
139 if string in ('', '\n'):
135 # Prefiltering, eg through ipython0, may return an empty
140 # Prefiltering, eg through ipython0, may return an empty
136 # string although some operations have been accomplished. We
141 # string although some operations have been accomplished. We
137 # thus want to consider an empty string as a complete
142 # thus want to consider an empty string as a complete
138 # statement.
143 # statement.
139 return True
144 return True
140 elif ( len(self.input_buffer.split('\n'))>2
145 elif ( len(self.input_buffer.split('\n'))>2
141 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
146 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
142 return False
147 return False
143 else:
148 else:
144 # Add line returns here, to make sure that the statement is
149 # Add line returns here, to make sure that the statement is
145 # complete.
150 # complete.
146 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
151 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
147
152
148
153
149 def write(self, string, refresh=True):
154 def write(self, string, refresh=True):
150 """ Write some characters to the display.
155 """ Write some characters to the display.
151
156
152 Subclass should overide this method.
157 Subclass should overide this method.
153
158
154 The refresh keyword argument is used in frontends with an
159 The refresh keyword argument is used in frontends with an
155 event loop, to choose whether the write should trigget an UI
160 event loop, to choose whether the write should trigget an UI
156 refresh, and thus be syncrhonous, or not.
161 refresh, and thus be syncrhonous, or not.
157 """
162 """
158 print >>sys.__stderr__, string
163 print >>sys.__stderr__, string
159
164
160
165
161 def execute(self, python_string, raw_string=None):
166 def execute(self, python_string, raw_string=None):
162 """ Stores the raw_string in the history, and sends the
167 """ Stores the raw_string in the history, and sends the
163 python string to the interpreter.
168 python string to the interpreter.
164 """
169 """
165 if raw_string is None:
170 if raw_string is None:
166 raw_string = python_string
171 raw_string = python_string
167 # Create a false result, in case there is an exception
172 # Create a false result, in case there is an exception
168 self.last_result = dict(number=self.prompt_number)
173 self.last_result = dict(number=self.prompt_number)
169 try:
174 try:
170 self.history.input_cache[-1] = raw_string.rstrip()
175 self.history.input_cache[-1] = raw_string.rstrip()
171 result = self.shell.execute(python_string)
176 result = self.shell.execute(python_string)
172 self.last_result = result
177 self.last_result = result
173 self.render_result(result)
178 self.render_result(result)
174 except:
179 except:
175 self.show_traceback()
180 self.show_traceback()
176 finally:
181 finally:
177 self.after_execute()
182 self.after_execute()
178
183
179 #--------------------------------------------------------------------------
184 #--------------------------------------------------------------------------
180 # LineFrontEndBase interface
185 # LineFrontEndBase interface
181 #--------------------------------------------------------------------------
186 #--------------------------------------------------------------------------
182
187
183 def prefilter_input(self, string):
188 def prefilter_input(self, string):
184 """ Priflter the input to turn it in valid python.
189 """ Priflter the input to turn it in valid python.
185 """
190 """
186 string = string.replace('\r\n', '\n')
191 string = string.replace('\r\n', '\n')
187 string = string.replace('\t', 4*' ')
192 string = string.replace('\t', 4*' ')
188 # Clean the trailing whitespace
193 # Clean the trailing whitespace
189 string = '\n'.join(l.rstrip() for l in string.split('\n'))
194 string = '\n'.join(l.rstrip() for l in string.split('\n'))
190 return string
195 return string
191
196
192
197
193 def after_execute(self):
198 def after_execute(self):
194 """ All the operations required after an execution to put the
199 """ All the operations required after an execution to put the
195 terminal back in a shape where it is usable.
200 terminal back in a shape where it is usable.
196 """
201 """
197 self.prompt_number += 1
202 self.prompt_number += 1
198 self.new_prompt(self.input_prompt_template.substitute(
203 self.new_prompt(self.input_prompt_template.substitute(
199 number=(self.last_result['number'] + 1)))
204 number=(self.last_result['number'] + 1)))
200 # Start a new empty history entry
205 # Start a new empty history entry
201 self._add_history(None, '')
206 self._add_history(None, '')
202 self.history_cursor = len(self.history.input_cache) - 1
207 self.history_cursor = len(self.history.input_cache) - 1
203
208
204
209
205 def complete_current_input(self):
210 def complete_current_input(self):
206 """ Do code completion on current line.
211 """ Do code completion on current line.
207 """
212 """
208 if self.debug:
213 if self.debug:
209 print >>sys.__stdout__, "complete_current_input",
214 print >>sys.__stdout__, "complete_current_input",
210 line = self.input_buffer
215 line = self.input_buffer
211 new_line, completions = self.complete(line)
216 new_line, completions = self.complete(line)
212 if len(completions)>1:
217 if len(completions)>1:
213 self.write_completion(completions)
218 self.write_completion(completions, new_line=new_line)
214 self.input_buffer = new_line
215 if self.debug:
219 if self.debug:
220 print >>sys.__stdout__, 'line', line
221 print >>sys.__stdout__, 'new_line', new_line
216 print >>sys.__stdout__, completions
222 print >>sys.__stdout__, completions
217
223
218
224
219 def get_line_width(self):
225 def get_line_width(self):
220 """ Return the width of the line in characters.
226 """ Return the width of the line in characters.
221 """
227 """
222 return 80
228 return 80
223
229
224
230
225 def write_completion(self, possibilities):
231 def write_completion(self, possibilities, new_line=None):
226 """ Write the list of possible completions.
232 """ Write the list of possible completions.
233
234 new_line is the completed input line that should be displayed
235 after the completion are writen. If None, the input_buffer
236 before the completion is used.
227 """
237 """
228 current_buffer = self.input_buffer
238 if new_line is None:
239 new_line = self.input_buffer
229
240
230 self.write('\n')
241 self.write('\n')
231 max_len = len(max(possibilities, key=len)) + 1
242 max_len = len(max(possibilities, key=len)) + 1
232
243
233 # Now we check how much symbol we can put on a line...
244 # Now we check how much symbol we can put on a line...
234 chars_per_line = self.get_line_width()
245 chars_per_line = self.get_line_width()
235 symbols_per_line = max(1, chars_per_line/max_len)
246 symbols_per_line = max(1, chars_per_line/max_len)
236
247
237 pos = 1
248 pos = 1
238 buf = []
249 buf = []
239 for symbol in possibilities:
250 for symbol in possibilities:
240 if pos < symbols_per_line:
251 if pos < symbols_per_line:
241 buf.append(symbol.ljust(max_len))
252 buf.append(symbol.ljust(max_len))
242 pos += 1
253 pos += 1
243 else:
254 else:
244 buf.append(symbol.rstrip() + '\n')
255 buf.append(symbol.rstrip() + '\n')
245 pos = 1
256 pos = 1
246 self.write(''.join(buf))
257 self.write(''.join(buf))
247 self.new_prompt(self.input_prompt_template.substitute(
258 self.new_prompt(self.input_prompt_template.substitute(
248 number=self.last_result['number'] + 1))
259 number=self.last_result['number'] + 1))
249 self.input_buffer = current_buffer
260 self.input_buffer = new_line
250
261
251
262
252 def new_prompt(self, prompt):
263 def new_prompt(self, prompt):
253 """ Prints a prompt and starts a new editing buffer.
264 """ Prints a prompt and starts a new editing buffer.
254
265
255 Subclasses should use this method to make sure that the
266 Subclasses should use this method to make sure that the
256 terminal is put in a state favorable for a new line
267 terminal is put in a state favorable for a new line
257 input.
268 input.
258 """
269 """
259 self.input_buffer = ''
270 self.input_buffer = ''
260 self.write(prompt)
271 self.write(prompt)
261
272
262
273
263 #--------------------------------------------------------------------------
274 #--------------------------------------------------------------------------
264 # Private API
275 # Private API
265 #--------------------------------------------------------------------------
276 #--------------------------------------------------------------------------
266
277
267 def _on_enter(self):
278 def _on_enter(self):
268 """ Called when the return key is pressed in a line editing
279 """ Called when the return key is pressed in a line editing
269 buffer.
280 buffer.
270 """
281 """
271 current_buffer = self.input_buffer
282 current_buffer = self.input_buffer
272 cleaned_buffer = self.prefilter_input(current_buffer)
283 cleaned_buffer = self.prefilter_input(current_buffer)
273 if self.is_complete(cleaned_buffer):
284 if self.is_complete(cleaned_buffer):
274 self.execute(cleaned_buffer, raw_string=current_buffer)
285 self.execute(cleaned_buffer, raw_string=current_buffer)
275 else:
286 else:
276 self.input_buffer += self._get_indent_string(
287 self.input_buffer += self._get_indent_string(
277 current_buffer[:-1])
288 current_buffer[:-1])
278 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
289 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
279 self.input_buffer += '\t'
290 self.input_buffer += '\t'
280
291
281
292
282 def _get_indent_string(self, string):
293 def _get_indent_string(self, string):
283 """ Return the string of whitespace that prefixes a line. Used to
294 """ Return the string of whitespace that prefixes a line. Used to
284 add the right amount of indendation when creating a new line.
295 add the right amount of indendation when creating a new line.
285 """
296 """
286 string = string.replace('\t', ' '*4)
297 string = string.replace('\t', ' '*4)
287 string = string.split('\n')[-1]
298 string = string.split('\n')[-1]
288 indent_chars = len(string) - len(string.lstrip())
299 indent_chars = len(string) - len(string.lstrip())
289 indent_string = '\t'*(indent_chars // 4) + \
300 indent_string = '\t'*(indent_chars // 4) + \
290 ' '*(indent_chars % 4)
301 ' '*(indent_chars % 4)
291
302
292 return indent_string
303 return indent_string
293
304
294
305
@@ -1,223 +1,241 b''
1 """
1 """
2 Frontend class that uses IPython0 to prefilter the inputs.
2 Frontend class that uses IPython0 to prefilter the inputs.
3
3
4 Using the IPython0 mechanism gives us access to the magics.
4 Using the IPython0 mechanism gives us access to the magics.
5
5
6 This is a transitory class, used here to do the transition between
6 This is a transitory class, used here to do the transition between
7 ipython0 and ipython1. This class is meant to be short-lived as more
7 ipython0 and ipython1. This class is meant to be short-lived as more
8 functionnality is abstracted out of ipython0 in reusable functions and
8 functionnality is abstracted out of ipython0 in reusable functions and
9 is added on the interpreter. This class can be a used to guide this
9 is added on the interpreter. This class can be a used to guide this
10 refactoring.
10 refactoring.
11 """
11 """
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24 import sys
24 import sys
25
25
26 from linefrontendbase import LineFrontEndBase, common_prefix
26 from linefrontendbase import LineFrontEndBase, common_prefix
27 from frontendbase import FrontEndBase
27
28
28 from IPython.ipmaker import make_IPython
29 from IPython.ipmaker import make_IPython
29 from IPython.ipapi import IPApi
30 from IPython.ipapi import IPApi
30 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31
32
32 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
33
34
34 from IPython.genutils import Term
35 from IPython.genutils import Term
35 import pydoc
36 import pydoc
36 import os
37 import os
38 import sys
37
39
38
40
39 def mk_system_call(system_call_function, command):
41 def mk_system_call(system_call_function, command):
40 """ given a os.system replacement, and a leading string command,
42 """ given a os.system replacement, and a leading string command,
41 returns a function that will execute the command with the given
43 returns a function that will execute the command with the given
42 argument string.
44 argument string.
43 """
45 """
44 def my_system_call(args):
46 def my_system_call(args):
45 system_call_function("%s %s" % (command, args))
47 system_call_function("%s %s" % (command, args))
46 return my_system_call
48 return my_system_call
47
49
48 #-------------------------------------------------------------------------------
50 #-------------------------------------------------------------------------------
49 # Frontend class using ipython0 to do the prefiltering.
51 # Frontend class using ipython0 to do the prefiltering.
50 #-------------------------------------------------------------------------------
52 #-------------------------------------------------------------------------------
51 class PrefilterFrontEnd(LineFrontEndBase):
53 class PrefilterFrontEnd(LineFrontEndBase):
52 """ Class that uses ipython0 to do prefilter the input, do the
54 """ Class that uses ipython0 to do prefilter the input, do the
53 completion and the magics.
55 completion and the magics.
54
56
55 The core trick is to use an ipython0 instance to prefilter the
57 The core trick is to use an ipython0 instance to prefilter the
56 input, and share the namespace between the interpreter instance used
58 input, and share the namespace between the interpreter instance used
57 to execute the statements and the ipython0 used for code
59 to execute the statements and the ipython0 used for code
58 completion...
60 completion...
59 """
61 """
62
63 debug = False
60
64
61 def __init__(self, ipython0=None, *args, **kwargs):
65 def __init__(self, ipython0=None, *args, **kwargs):
62 """ Parameters:
66 """ Parameters:
63 -----------
67 -----------
64
68
65 ipython0: an optional ipython0 instance to use for command
69 ipython0: an optional ipython0 instance to use for command
66 prefiltering and completion.
70 prefiltering and completion.
67 """
71 """
72 LineFrontEndBase.__init__(self, *args, **kwargs)
73 self.shell.output_trap = RedirectorOutputTrap(
74 out_callback=self.write,
75 err_callback=self.write,
76 )
77 self.shell.traceback_trap = SyncTracebackTrap(
78 formatters=self.shell.traceback_trap.formatters,
79 )
80
81 # Start the ipython0 instance:
68 self.save_output_hooks()
82 self.save_output_hooks()
69 if ipython0 is None:
83 if ipython0 is None:
70 # Instanciate an IPython0 interpreter to be able to use the
84 # Instanciate an IPython0 interpreter to be able to use the
71 # prefiltering.
85 # prefiltering.
72 # XXX: argv=[] is a bit bold.
86 # XXX: argv=[] is a bit bold.
73 ipython0 = make_IPython(argv=[])
87 ipython0 = make_IPython(argv=[],
88 user_ns=self.shell.user_ns,
89 user_global_ns=self.shell.user_global_ns)
74 self.ipython0 = ipython0
90 self.ipython0 = ipython0
75 # Set the pager:
91 # Set the pager:
76 self.ipython0.set_hook('show_in_pager',
92 self.ipython0.set_hook('show_in_pager',
77 lambda s, string: self.write("\n" + string))
93 lambda s, string: self.write("\n" + string))
78 self.ipython0.write = self.write
94 self.ipython0.write = self.write
79 self._ip = _ip = IPApi(self.ipython0)
95 self._ip = _ip = IPApi(self.ipython0)
80 # Make sure the raw system call doesn't get called, as we don't
96 # Make sure the raw system call doesn't get called, as we don't
81 # have a stdin accessible.
97 # have a stdin accessible.
82 self._ip.system = self.system_call
98 self._ip.system = self.system_call
83 # XXX: Muck around with magics so that they work better
99 # XXX: Muck around with magics so that they work better
84 # in our environment
100 # in our environment
85 self.ipython0.magic_ls = mk_system_call(self.system_call,
101 self.ipython0.magic_ls = mk_system_call(self.system_call,
86 'ls -CF')
102 'ls -CF')
87 # And now clean up the mess created by ipython0
103 # And now clean up the mess created by ipython0
88 self.release_output()
104 self.release_output()
105
106
89 if not 'banner' in kwargs and self.banner is None:
107 if not 'banner' in kwargs and self.banner is None:
90 kwargs['banner'] = self.ipython0.BANNER + """
108 self.banner = self.ipython0.BANNER + """
91 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
92
110
93 LineFrontEndBase.__init__(self, *args, **kwargs)
94 # XXX: Hack: mix the two namespaces
111 # XXX: Hack: mix the two namespaces
95 self.shell.user_ns.update(self.ipython0.user_ns)
112 # self.shell.user_ns.update(self.ipython0.user_ns)
96 self.ipython0.user_ns = self.shell.user_ns
113 # self.ipython0.user_ns = self.shell.user_ns
97 self.shell.user_global_ns.update(self.ipython0.user_global_ns)
114 # self.shell.user_global_ns.update(self.ipython0.user_global_ns)
98 self.ipython0.user_global_ns = self.shell.user_global_ns
115 # self.ipython0.user_global_ns = self.shell.user_global_ns
116
117 # self.ipython0.user_ns.update(self.shell.user_ns)
118 # self.shell.user_ns = self.ipython0.user_ns
119 # self.ipython0.user_global_ns.update(self.shell.user_global_ns)
120 # self.shell.user_global_ns = self.ipython0.user_global_ns
99
121
100 self.shell.output_trap = RedirectorOutputTrap(
122 self.start()
101 out_callback=self.write,
102 err_callback=self.write,
103 )
104 self.shell.traceback_trap = SyncTracebackTrap(
105 formatters=self.shell.traceback_trap.formatters,
106 )
107
123
108 #--------------------------------------------------------------------------
124 #--------------------------------------------------------------------------
109 # FrontEndBase interface
125 # FrontEndBase interface
110 #--------------------------------------------------------------------------
126 #--------------------------------------------------------------------------
111
127
112 def show_traceback(self):
128 def show_traceback(self):
113 """ Use ipython0 to capture the last traceback and display it.
129 """ Use ipython0 to capture the last traceback and display it.
114 """
130 """
115 self.capture_output()
131 self.capture_output()
116 self.ipython0.showtraceback()
132 self.ipython0.showtraceback()
117 self.release_output()
133 self.release_output()
118
134
119
135
120 def execute(self, python_string, raw_string=None):
136 def execute(self, python_string, raw_string=None):
121 if self.debug:
137 if self.debug:
122 print 'Executing Python code:', repr(python_string)
138 print 'Executing Python code:', repr(python_string)
123 self.capture_output()
139 self.capture_output()
124 LineFrontEndBase.execute(self, python_string,
140 LineFrontEndBase.execute(self, python_string,
125 raw_string=raw_string)
141 raw_string=raw_string)
126 self.release_output()
142 self.release_output()
127
143
128
144
129 def save_output_hooks(self):
145 def save_output_hooks(self):
130 """ Store all the output hooks we can think of, to be able to
146 """ Store all the output hooks we can think of, to be able to
131 restore them.
147 restore them.
132
148
133 We need to do this early, as starting the ipython0 instance will
149 We need to do this early, as starting the ipython0 instance will
134 screw ouput hooks.
150 screw ouput hooks.
135 """
151 """
136 self.__old_cout_write = Term.cout.write
152 self.__old_cout_write = Term.cout.write
137 self.__old_cerr_write = Term.cerr.write
153 self.__old_cerr_write = Term.cerr.write
138 self.__old_stdout = sys.stdout
154 self.__old_stdout = sys.stdout
139 self.__old_stderr= sys.stderr
155 self.__old_stderr= sys.stderr
140 self.__old_help_output = pydoc.help.output
156 self.__old_help_output = pydoc.help.output
141 self.__old_display_hook = sys.displayhook
157 self.__old_display_hook = sys.displayhook
142
158
143
159
144 def capture_output(self):
160 def capture_output(self):
145 """ Capture all the output mechanisms we can think of.
161 """ Capture all the output mechanisms we can think of.
146 """
162 """
147 self.save_output_hooks()
163 self.save_output_hooks()
148 Term.cout.write = self.write
164 Term.cout.write = self.write
149 Term.cerr.write = self.write
165 Term.cerr.write = self.write
150 sys.stdout = Term.cout
166 sys.stdout = Term.cout
151 sys.stderr = Term.cerr
167 sys.stderr = Term.cerr
152 pydoc.help.output = self.shell.output_trap.out
168 pydoc.help.output = self.shell.output_trap.out
153
169
154
170
155 def release_output(self):
171 def release_output(self):
156 """ Release all the different captures we have made.
172 """ Release all the different captures we have made.
157 """
173 """
158 Term.cout.write = self.__old_cout_write
174 Term.cout.write = self.__old_cout_write
159 Term.cerr.write = self.__old_cerr_write
175 Term.cerr.write = self.__old_cerr_write
160 sys.stdout = self.__old_stdout
176 sys.stdout = self.__old_stdout
161 sys.stderr = self.__old_stderr
177 sys.stderr = self.__old_stderr
162 pydoc.help.output = self.__old_help_output
178 pydoc.help.output = self.__old_help_output
163 sys.displayhook = self.__old_display_hook
179 sys.displayhook = self.__old_display_hook
164
180
165
181
166 def complete(self, line):
182 def complete(self, line):
183 # FIXME: This should be factored out in the linefrontendbase
184 # method.
167 word = line.split('\n')[-1].split(' ')[-1]
185 word = line.split('\n')[-1].split(' ')[-1]
168 completions = self.ipython0.complete(word)
186 completions = self.ipython0.complete(word)
169 # FIXME: The proper sort should be done in the complete method.
187 # FIXME: The proper sort should be done in the complete method.
170 key = lambda x: x.replace('_', '')
188 key = lambda x: x.replace('_', '')
171 completions.sort(key=key)
189 completions.sort(key=key)
172 if completions:
190 if completions:
173 prefix = common_prefix(completions)
191 prefix = common_prefix(completions)
174 line = line[:-len(word)] + prefix
192 line = line[:-len(word)] + prefix
175 return line, completions
193 return line, completions
176
194
177
195
178 #--------------------------------------------------------------------------
196 #--------------------------------------------------------------------------
179 # LineFrontEndBase interface
197 # LineFrontEndBase interface
180 #--------------------------------------------------------------------------
198 #--------------------------------------------------------------------------
181
199
182 def prefilter_input(self, input_string):
200 def prefilter_input(self, input_string):
183 """ Using IPython0 to prefilter the commands to turn them
201 """ Using IPython0 to prefilter the commands to turn them
184 in executable statements that are valid Python strings.
202 in executable statements that are valid Python strings.
185 """
203 """
186 input_string = LineFrontEndBase.prefilter_input(self, input_string)
204 input_string = LineFrontEndBase.prefilter_input(self, input_string)
187 filtered_lines = []
205 filtered_lines = []
188 # The IPython0 prefilters sometime produce output. We need to
206 # The IPython0 prefilters sometime produce output. We need to
189 # capture it.
207 # capture it.
190 self.capture_output()
208 self.capture_output()
191 self.last_result = dict(number=self.prompt_number)
209 self.last_result = dict(number=self.prompt_number)
192 try:
210 try:
193 for line in input_string.split('\n'):
211 for line in input_string.split('\n'):
194 filtered_lines.append(
212 filtered_lines.append(
195 self.ipython0.prefilter(line, False).rstrip())
213 self.ipython0.prefilter(line, False).rstrip())
196 except:
214 except:
197 # XXX: probably not the right thing to do.
215 # XXX: probably not the right thing to do.
198 self.ipython0.showsyntaxerror()
216 self.ipython0.showsyntaxerror()
199 self.after_execute()
217 self.after_execute()
200 finally:
218 finally:
201 self.release_output()
219 self.release_output()
202
220
203 # Clean up the trailing whitespace, to avoid indentation errors
221 # Clean up the trailing whitespace, to avoid indentation errors
204 filtered_string = '\n'.join(filtered_lines)
222 filtered_string = '\n'.join(filtered_lines)
205 return filtered_string
223 return filtered_string
206
224
207
225
208 #--------------------------------------------------------------------------
226 #--------------------------------------------------------------------------
209 # PrefilterFrontEnd interface
227 # PrefilterFrontEnd interface
210 #--------------------------------------------------------------------------
228 #--------------------------------------------------------------------------
211
229
212 def system_call(self, command_string):
230 def system_call(self, command_string):
213 """ Allows for frontend to define their own system call, to be
231 """ Allows for frontend to define their own system call, to be
214 able capture output and redirect input.
232 able capture output and redirect input.
215 """
233 """
216 return os.system(command_string)
234 return os.system(command_string)
217
235
218
236
219 def do_exit(self):
237 def do_exit(self):
220 """ Exit the shell, cleanup and save the history.
238 """ Exit the shell, cleanup and save the history.
221 """
239 """
222 self.ipython0.atexit_operations()
240 self.ipython0.atexit_operations()
223
241
@@ -1,510 +1,511 b''
1 # encoding: utf-8 -*- test-case-name:
1 # encoding: utf-8 -*- test-case-name:
2 # FIXME: Need to add tests.
2 # FIXME: Need to add tests.
3 # ipython1.frontend.wx.tests.test_wx_frontend -*-
3 # ipython1.frontend.wx.tests.test_wx_frontend -*-
4
4
5 """Classes to provide a Wx frontend to the
5 """Classes to provide a Wx frontend to the
6 IPython.kernel.core.interpreter.
6 IPython.kernel.core.interpreter.
7
7
8 This class inherits from ConsoleWidget, that provides a console-like
8 This class inherits from ConsoleWidget, that provides a console-like
9 widget to provide a text-rendering widget suitable for a terminal.
9 widget to provide a text-rendering widget suitable for a terminal.
10 """
10 """
11
11
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24
24
25 # Major library imports
25 # Major library imports
26 import re
26 import re
27 import __builtin__
27 import __builtin__
28 from time import sleep
28 from time import sleep
29 import sys
29 import sys
30 from threading import Lock
30 from threading import Lock
31 import string
31 import string
32
32
33 import wx
33 import wx
34 from wx import stc
34 from wx import stc
35
35
36 # Ipython-specific imports.
36 # Ipython-specific imports.
37 from IPython.frontend._process import PipedProcess
37 from IPython.frontend._process import PipedProcess
38 from console_widget import ConsoleWidget
38 from console_widget import ConsoleWidget
39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
40
40
41 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
42 # Constants
42 # Constants
43 #-------------------------------------------------------------------------------
43 #-------------------------------------------------------------------------------
44
44
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47 _ERROR_BG = '#FFF1F1' # Nice red
47 _ERROR_BG = '#FFF1F1' # Nice red
48
48
49 _COMPLETE_BUFFER_MARKER = 31
49 _COMPLETE_BUFFER_MARKER = 31
50 _ERROR_MARKER = 30
50 _ERROR_MARKER = 30
51 _INPUT_MARKER = 29
51 _INPUT_MARKER = 29
52
52
53 prompt_in1 = \
53 prompt_in1 = \
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
55
55
56 prompt_out = \
56 prompt_out = \
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58
58
59 #-------------------------------------------------------------------------------
59 #-------------------------------------------------------------------------------
60 # Classes to implement the Wx frontend
60 # Classes to implement the Wx frontend
61 #-------------------------------------------------------------------------------
61 #-------------------------------------------------------------------------------
62 class WxController(ConsoleWidget, PrefilterFrontEnd):
62 class WxController(ConsoleWidget, PrefilterFrontEnd):
63 """Classes to provide a Wx frontend to the
63 """Classes to provide a Wx frontend to the
64 IPython.kernel.core.interpreter.
64 IPython.kernel.core.interpreter.
65
65
66 This class inherits from ConsoleWidget, that provides a console-like
66 This class inherits from ConsoleWidget, that provides a console-like
67 widget to provide a text-rendering widget suitable for a terminal.
67 widget to provide a text-rendering widget suitable for a terminal.
68 """
68 """
69
69
70 output_prompt_template = string.Template(prompt_out)
70 output_prompt_template = string.Template(prompt_out)
71
71
72 input_prompt_template = string.Template(prompt_in1)
72 input_prompt_template = string.Template(prompt_in1)
73
73
74 # Print debug info on what is happening to the console.
74 # Print debug info on what is happening to the console.
75 debug = False
75 debug = False
76
76
77 # The title of the terminal, as captured through the ANSI escape
77 # The title of the terminal, as captured through the ANSI escape
78 # sequences.
78 # sequences.
79 def _set_title(self, title):
79 def _set_title(self, title):
80 return self.Parent.SetTitle(title)
80 return self.Parent.SetTitle(title)
81
81
82 def _get_title(self):
82 def _get_title(self):
83 return self.Parent.GetTitle()
83 return self.Parent.GetTitle()
84
84
85 title = property(_get_title, _set_title)
85 title = property(_get_title, _set_title)
86
86
87
87
88 # The buffer being edited.
88 # The buffer being edited.
89 # We are duplicating the definition here because of multiple
89 # We are duplicating the definition here because of multiple
90 # inheritence
90 # inheritence
91 def _set_input_buffer(self, string):
91 def _set_input_buffer(self, string):
92 ConsoleWidget._set_input_buffer(self, string)
92 ConsoleWidget._set_input_buffer(self, string)
93 self._colorize_input_buffer()
93 self._colorize_input_buffer()
94
94
95 def _get_input_buffer(self):
95 def _get_input_buffer(self):
96 """ Returns the text in current edit buffer.
96 """ Returns the text in current edit buffer.
97 """
97 """
98 return ConsoleWidget._get_input_buffer(self)
98 return ConsoleWidget._get_input_buffer(self)
99
99
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
101
101
102
102
103 #--------------------------------------------------------------------------
103 #--------------------------------------------------------------------------
104 # Private Attributes
104 # Private Attributes
105 #--------------------------------------------------------------------------
105 #--------------------------------------------------------------------------
106
106
107 # A flag governing the behavior of the input. Can be:
107 # A flag governing the behavior of the input. Can be:
108 #
108 #
109 # 'readline' for readline-like behavior with a prompt
109 # 'readline' for readline-like behavior with a prompt
110 # and an edit buffer.
110 # and an edit buffer.
111 # 'raw_input' similar to readline, but triggered by a raw-input
111 # 'raw_input' similar to readline, but triggered by a raw-input
112 # call. Can be used by subclasses to act differently.
112 # call. Can be used by subclasses to act differently.
113 # 'subprocess' for sending the raw input directly to a
113 # 'subprocess' for sending the raw input directly to a
114 # subprocess.
114 # subprocess.
115 # 'buffering' for buffering of the input, that will be used
115 # 'buffering' for buffering of the input, that will be used
116 # when the input state switches back to another state.
116 # when the input state switches back to another state.
117 _input_state = 'readline'
117 _input_state = 'readline'
118
118
119 # Attribute to store reference to the pipes of a subprocess, if we
119 # Attribute to store reference to the pipes of a subprocess, if we
120 # are running any.
120 # are running any.
121 _running_process = False
121 _running_process = False
122
122
123 # A queue for writing fast streams to the screen without flooding the
123 # A queue for writing fast streams to the screen without flooding the
124 # event loop
124 # event loop
125 _out_buffer = []
125 _out_buffer = []
126
126
127 # A lock to lock the _out_buffer to make sure we don't empty it
127 # A lock to lock the _out_buffer to make sure we don't empty it
128 # while it is being swapped
128 # while it is being swapped
129 _out_buffer_lock = Lock()
129 _out_buffer_lock = Lock()
130
130
131 # The different line markers used to higlight the prompts.
131 _markers = dict()
132 _markers = dict()
132
133
133 #--------------------------------------------------------------------------
134 #--------------------------------------------------------------------------
134 # Public API
135 # Public API
135 #--------------------------------------------------------------------------
136 #--------------------------------------------------------------------------
136
137
137 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
139 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
139 *args, **kwds):
140 *args, **kwds):
140 """ Create Shell instance.
141 """ Create Shell instance.
141 """
142 """
142 ConsoleWidget.__init__(self, parent, id, pos, size, style)
143 ConsoleWidget.__init__(self, parent, id, pos, size, style)
143 PrefilterFrontEnd.__init__(self, **kwds)
144 PrefilterFrontEnd.__init__(self, **kwds)
144
145
145 # Marker for complete buffer.
146 # Marker for complete buffer.
146 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
147 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
147 background=_COMPLETE_BUFFER_BG)
148 background=_COMPLETE_BUFFER_BG)
148 # Marker for current input buffer.
149 # Marker for current input buffer.
149 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
150 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
150 background=_INPUT_BUFFER_BG)
151 background=_INPUT_BUFFER_BG)
151 # Marker for tracebacks.
152 # Marker for tracebacks.
152 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
153 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
153 background=_ERROR_BG)
154 background=_ERROR_BG)
154
155
155 # A time for flushing the write buffer
156 # A time for flushing the write buffer
156 BUFFER_FLUSH_TIMER_ID = 100
157 BUFFER_FLUSH_TIMER_ID = 100
157 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
158 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
158 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
159 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
159
160
160 if 'debug' in kwds:
161 if 'debug' in kwds:
161 self.debug = kwds['debug']
162 self.debug = kwds['debug']
162 kwds.pop('debug')
163 kwds.pop('debug')
163
164
164 # Inject self in namespace, for debug
165 # Inject self in namespace, for debug
165 if self.debug:
166 if self.debug:
166 self.shell.user_ns['self'] = self
167 self.shell.user_ns['self'] = self
167
168
168
169
169 def raw_input(self, prompt):
170 def raw_input(self, prompt):
170 """ A replacement from python's raw_input.
171 """ A replacement from python's raw_input.
171 """
172 """
172 self.new_prompt(prompt)
173 self.new_prompt(prompt)
173 self._input_state = 'raw_input'
174 self._input_state = 'raw_input'
174 if hasattr(self, '_cursor'):
175 if hasattr(self, '_cursor'):
175 del self._cursor
176 del self._cursor
176 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
177 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
177 self.waiting = True
178 self.waiting = True
178 self.__old_on_enter = self._on_enter
179 self.__old_on_enter = self._on_enter
179 def my_on_enter():
180 def my_on_enter():
180 self.waiting = False
181 self.waiting = False
181 self._on_enter = my_on_enter
182 self._on_enter = my_on_enter
182 # XXX: Busy waiting, ugly.
183 # XXX: Busy waiting, ugly.
183 while self.waiting:
184 while self.waiting:
184 wx.Yield()
185 wx.Yield()
185 sleep(0.1)
186 sleep(0.1)
186 self._on_enter = self.__old_on_enter
187 self._on_enter = self.__old_on_enter
187 self._input_state = 'buffering'
188 self._input_state = 'buffering'
188 self._cursor = wx.BusyCursor()
189 self._cursor = wx.BusyCursor()
189 return self.input_buffer.rstrip('\n')
190 return self.input_buffer.rstrip('\n')
190
191
191
192
192 def system_call(self, command_string):
193 def system_call(self, command_string):
193 self._input_state = 'subprocess'
194 self._input_state = 'subprocess'
194 self._running_process = PipedProcess(command_string,
195 self._running_process = PipedProcess(command_string,
195 out_callback=self.buffered_write,
196 out_callback=self.buffered_write,
196 end_callback = self._end_system_call)
197 end_callback = self._end_system_call)
197 self._running_process.start()
198 self._running_process.start()
198 # XXX: another one of these polling loops to have a blocking
199 # XXX: another one of these polling loops to have a blocking
199 # call
200 # call
200 wx.Yield()
201 wx.Yield()
201 while self._running_process:
202 while self._running_process:
202 wx.Yield()
203 wx.Yield()
203 sleep(0.1)
204 sleep(0.1)
204 # Be sure to flush the buffer.
205 # Be sure to flush the buffer.
205 self._buffer_flush(event=None)
206 self._buffer_flush(event=None)
206
207
207
208
208 def do_calltip(self):
209 def do_calltip(self):
209 """ Analyse current and displays useful calltip for it.
210 """ Analyse current and displays useful calltip for it.
210 """
211 """
211 if self.debug:
212 if self.debug:
212 print >>sys.__stdout__, "do_calltip"
213 print >>sys.__stdout__, "do_calltip"
213 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
214 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
214 symbol = self.input_buffer
215 symbol = self.input_buffer
215 symbol_string = separators.split(symbol)[-1]
216 symbol_string = separators.split(symbol)[-1]
216 base_symbol_string = symbol_string.split('.')[0]
217 base_symbol_string = symbol_string.split('.')[0]
217 if base_symbol_string in self.shell.user_ns:
218 if base_symbol_string in self.shell.user_ns:
218 symbol = self.shell.user_ns[base_symbol_string]
219 symbol = self.shell.user_ns[base_symbol_string]
219 elif base_symbol_string in self.shell.user_global_ns:
220 elif base_symbol_string in self.shell.user_global_ns:
220 symbol = self.shell.user_global_ns[base_symbol_string]
221 symbol = self.shell.user_global_ns[base_symbol_string]
221 elif base_symbol_string in __builtin__.__dict__:
222 elif base_symbol_string in __builtin__.__dict__:
222 symbol = __builtin__.__dict__[base_symbol_string]
223 symbol = __builtin__.__dict__[base_symbol_string]
223 else:
224 else:
224 return False
225 return False
225 try:
226 try:
226 for name in symbol_string.split('.')[1:] + ['__doc__']:
227 for name in symbol_string.split('.')[1:] + ['__doc__']:
227 symbol = getattr(symbol, name)
228 symbol = getattr(symbol, name)
228 self.AutoCompCancel()
229 self.AutoCompCancel()
229 wx.Yield()
230 wx.Yield()
230 self.CallTipShow(self.GetCurrentPos(), symbol)
231 self.CallTipShow(self.GetCurrentPos(), symbol)
231 except:
232 except:
232 # The retrieve symbol couldn't be converted to a string
233 # The retrieve symbol couldn't be converted to a string
233 pass
234 pass
234
235
235
236
236 def _popup_completion(self, create=False):
237 def _popup_completion(self, create=False):
237 """ Updates the popup completion menu if it exists. If create is
238 """ Updates the popup completion menu if it exists. If create is
238 true, open the menu.
239 true, open the menu.
239 """
240 """
240 if self.debug:
241 if self.debug:
241 print >>sys.__stdout__, "_popup_completion",
242 print >>sys.__stdout__, "_popup_completion"
242 line = self.input_buffer
243 line = self.input_buffer
243 if (self.AutoCompActive() and not line[-1] == '.') \
244 if (self.AutoCompActive() and line and not line[-1] == '.') \
244 or create==True:
245 or create==True:
245 suggestion, completions = self.complete(line)
246 suggestion, completions = self.complete(line)
246 offset=0
247 offset=0
247 if completions:
248 if completions:
248 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
249 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
249 residual = complete_sep.split(line)[-1]
250 residual = complete_sep.split(line)[-1]
250 offset = len(residual)
251 offset = len(residual)
251 self.pop_completion(completions, offset=offset)
252 self.pop_completion(completions, offset=offset)
252 if self.debug:
253 if self.debug:
253 print >>sys.__stdout__, completions
254 print >>sys.__stdout__, completions
254
255
255
256
256 def buffered_write(self, text):
257 def buffered_write(self, text):
257 """ A write method for streams, that caches the stream in order
258 """ A write method for streams, that caches the stream in order
258 to avoid flooding the event loop.
259 to avoid flooding the event loop.
259
260
260 This can be called outside of the main loop, in separate
261 This can be called outside of the main loop, in separate
261 threads.
262 threads.
262 """
263 """
263 self._out_buffer_lock.acquire()
264 self._out_buffer_lock.acquire()
264 self._out_buffer.append(text)
265 self._out_buffer.append(text)
265 self._out_buffer_lock.release()
266 self._out_buffer_lock.release()
266 if not self._buffer_flush_timer.IsRunning():
267 if not self._buffer_flush_timer.IsRunning():
267 wx.CallAfter(self._buffer_flush_timer.Start,
268 wx.CallAfter(self._buffer_flush_timer.Start,
268 milliseconds=100, oneShot=True)
269 milliseconds=100, oneShot=True)
269
270
270
271
271 #--------------------------------------------------------------------------
272 #--------------------------------------------------------------------------
272 # LineFrontEnd interface
273 # LineFrontEnd interface
273 #--------------------------------------------------------------------------
274 #--------------------------------------------------------------------------
274
275
275 def execute(self, python_string, raw_string=None):
276 def execute(self, python_string, raw_string=None):
276 self._input_state = 'buffering'
277 self._input_state = 'buffering'
277 self.CallTipCancel()
278 self.CallTipCancel()
278 self._cursor = wx.BusyCursor()
279 self._cursor = wx.BusyCursor()
279 if raw_string is None:
280 if raw_string is None:
280 raw_string = python_string
281 raw_string = python_string
281 end_line = self.current_prompt_line \
282 end_line = self.current_prompt_line \
282 + max(1, len(raw_string.split('\n'))-1)
283 + max(1, len(raw_string.split('\n'))-1)
283 for i in range(self.current_prompt_line, end_line):
284 for i in range(self.current_prompt_line, end_line):
284 if i in self._markers:
285 if i in self._markers:
285 self.MarkerDeleteHandle(self._markers[i])
286 self.MarkerDeleteHandle(self._markers[i])
286 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
287 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
287 # Update the display:
288 # Update the display:
288 wx.Yield()
289 wx.Yield()
289 self.GotoPos(self.GetLength())
290 self.GotoPos(self.GetLength())
290 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
291 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
291
292
292 def save_output_hooks(self):
293 def save_output_hooks(self):
293 self.__old_raw_input = __builtin__.raw_input
294 self.__old_raw_input = __builtin__.raw_input
294 PrefilterFrontEnd.save_output_hooks(self)
295 PrefilterFrontEnd.save_output_hooks(self)
295
296
296 def capture_output(self):
297 def capture_output(self):
297 __builtin__.raw_input = self.raw_input
298 __builtin__.raw_input = self.raw_input
298 self.SetLexer(stc.STC_LEX_NULL)
299 self.SetLexer(stc.STC_LEX_NULL)
299 PrefilterFrontEnd.capture_output(self)
300 PrefilterFrontEnd.capture_output(self)
300
301
301
302
302 def release_output(self):
303 def release_output(self):
303 __builtin__.raw_input = self.__old_raw_input
304 __builtin__.raw_input = self.__old_raw_input
304 PrefilterFrontEnd.release_output(self)
305 PrefilterFrontEnd.release_output(self)
305 self.SetLexer(stc.STC_LEX_PYTHON)
306 self.SetLexer(stc.STC_LEX_PYTHON)
306
307
307
308
308 def after_execute(self):
309 def after_execute(self):
309 PrefilterFrontEnd.after_execute(self)
310 PrefilterFrontEnd.after_execute(self)
310 # Clear the wait cursor
311 # Clear the wait cursor
311 if hasattr(self, '_cursor'):
312 if hasattr(self, '_cursor'):
312 del self._cursor
313 del self._cursor
313 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
314 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
314
315
315
316
316 def show_traceback(self):
317 def show_traceback(self):
317 start_line = self.GetCurrentLine()
318 start_line = self.GetCurrentLine()
318 PrefilterFrontEnd.show_traceback(self)
319 PrefilterFrontEnd.show_traceback(self)
319 wx.Yield()
320 wx.Yield()
320 for i in range(start_line, self.GetCurrentLine()):
321 for i in range(start_line, self.GetCurrentLine()):
321 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
322 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
322
323
323
324
324 #--------------------------------------------------------------------------
325 #--------------------------------------------------------------------------
325 # ConsoleWidget interface
326 # ConsoleWidget interface
326 #--------------------------------------------------------------------------
327 #--------------------------------------------------------------------------
327
328
328 def new_prompt(self, prompt):
329 def new_prompt(self, prompt):
329 """ Display a new prompt, and start a new input buffer.
330 """ Display a new prompt, and start a new input buffer.
330 """
331 """
331 self._input_state = 'readline'
332 self._input_state = 'readline'
332 ConsoleWidget.new_prompt(self, prompt)
333 ConsoleWidget.new_prompt(self, prompt)
333 i = self.current_prompt_line
334 i = self.current_prompt_line
334 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
335 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
335
336
336
337
337 def write(self, *args, **kwargs):
338 def write(self, *args, **kwargs):
338 # Avoid multiple inheritence, be explicit about which
339 # Avoid multiple inheritence, be explicit about which
339 # parent method class gets called
340 # parent method class gets called
340 ConsoleWidget.write(self, *args, **kwargs)
341 ConsoleWidget.write(self, *args, **kwargs)
341
342
342
343
343 def _on_key_down(self, event, skip=True):
344 def _on_key_down(self, event, skip=True):
344 """ Capture the character events, let the parent
345 """ Capture the character events, let the parent
345 widget handle them, and put our logic afterward.
346 widget handle them, and put our logic afterward.
346 """
347 """
347 # FIXME: This method needs to be broken down in smaller ones.
348 # FIXME: This method needs to be broken down in smaller ones.
348 current_line_number = self.GetCurrentLine()
349 current_line_number = self.GetCurrentLine()
349 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
350 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
350 # Capture Control-C
351 # Capture Control-C
351 if self._input_state == 'subprocess':
352 if self._input_state == 'subprocess':
352 if self.debug:
353 if self.debug:
353 print >>sys.__stderr__, 'Killing running process'
354 print >>sys.__stderr__, 'Killing running process'
354 self._running_process.process.kill()
355 self._running_process.process.kill()
355 elif self._input_state == 'buffering':
356 elif self._input_state == 'buffering':
356 if self.debug:
357 if self.debug:
357 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
358 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
358 raise KeyboardInterrupt
359 raise KeyboardInterrupt
359 # XXX: We need to make really sure we
360 # XXX: We need to make really sure we
360 # get back to a prompt.
361 # get back to a prompt.
361 elif self._input_state == 'subprocess' and (
362 elif self._input_state == 'subprocess' and (
362 ( event.KeyCode<256 and
363 ( event.KeyCode<256 and
363 not event.ControlDown() )
364 not event.ControlDown() )
364 or
365 or
365 ( event.KeyCode in (ord('d'), ord('D')) and
366 ( event.KeyCode in (ord('d'), ord('D')) and
366 event.ControlDown())):
367 event.ControlDown())):
367 # We are running a process, we redirect keys.
368 # We are running a process, we redirect keys.
368 ConsoleWidget._on_key_down(self, event, skip=skip)
369 ConsoleWidget._on_key_down(self, event, skip=skip)
369 char = chr(event.KeyCode)
370 char = chr(event.KeyCode)
370 # Deal with some inconsistency in wx keycodes:
371 # Deal with some inconsistency in wx keycodes:
371 if char == '\r':
372 if char == '\r':
372 char = '\n'
373 char = '\n'
373 elif not event.ShiftDown():
374 elif not event.ShiftDown():
374 char = char.lower()
375 char = char.lower()
375 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
376 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
376 char = '\04'
377 char = '\04'
377 self._running_process.process.stdin.write(char)
378 self._running_process.process.stdin.write(char)
378 self._running_process.process.stdin.flush()
379 self._running_process.process.stdin.flush()
379 elif event.KeyCode in (ord('('), 57):
380 elif event.KeyCode in (ord('('), 57):
380 # Calltips
381 # Calltips
381 event.Skip()
382 event.Skip()
382 self.do_calltip()
383 self.do_calltip()
383 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
384 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
384 event.Skip()
385 event.Skip()
385 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
386 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
386 wx.CallAfter(self._popup_completion, create=True)
387 wx.CallAfter(self._popup_completion, create=True)
387 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
388 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
388 wx.WXK_RIGHT, wx.WXK_ESCAPE):
389 wx.WXK_RIGHT, wx.WXK_ESCAPE):
389 wx.CallAfter(self._popup_completion)
390 wx.CallAfter(self._popup_completion)
390 else:
391 else:
391 # Up history
392 # Up history
392 if event.KeyCode == wx.WXK_UP and (
393 if event.KeyCode == wx.WXK_UP and (
393 ( current_line_number == self.current_prompt_line and
394 ( current_line_number == self.current_prompt_line and
394 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
395 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
395 or event.ControlDown() ):
396 or event.ControlDown() ):
396 new_buffer = self.get_history_previous(
397 new_buffer = self.get_history_previous(
397 self.input_buffer)
398 self.input_buffer)
398 if new_buffer is not None:
399 if new_buffer is not None:
399 self.input_buffer = new_buffer
400 self.input_buffer = new_buffer
400 if self.GetCurrentLine() > self.current_prompt_line:
401 if self.GetCurrentLine() > self.current_prompt_line:
401 # Go to first line, for seemless history up.
402 # Go to first line, for seemless history up.
402 self.GotoPos(self.current_prompt_pos)
403 self.GotoPos(self.current_prompt_pos)
403 # Down history
404 # Down history
404 elif event.KeyCode == wx.WXK_DOWN and (
405 elif event.KeyCode == wx.WXK_DOWN and (
405 ( current_line_number == self.LineCount -1 and
406 ( current_line_number == self.LineCount -1 and
406 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
407 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
407 or event.ControlDown() ):
408 or event.ControlDown() ):
408 new_buffer = self.get_history_next()
409 new_buffer = self.get_history_next()
409 if new_buffer is not None:
410 if new_buffer is not None:
410 self.input_buffer = new_buffer
411 self.input_buffer = new_buffer
411 # Tab-completion
412 # Tab-completion
412 elif event.KeyCode == ord('\t'):
413 elif event.KeyCode == ord('\t'):
413 last_line = self.input_buffer.split('\n')[-1]
414 last_line = self.input_buffer.split('\n')[-1]
414 if not re.match(r'^\s*$', last_line):
415 if not re.match(r'^\s*$', last_line):
415 self.complete_current_input()
416 self.complete_current_input()
416 if self.AutoCompActive():
417 if self.AutoCompActive():
417 wx.CallAfter(self._popup_completion, create=True)
418 wx.CallAfter(self._popup_completion, create=True)
418 else:
419 else:
419 event.Skip()
420 event.Skip()
420 else:
421 else:
421 ConsoleWidget._on_key_down(self, event, skip=skip)
422 ConsoleWidget._on_key_down(self, event, skip=skip)
422
423
423
424
424 def _on_key_up(self, event, skip=True):
425 def _on_key_up(self, event, skip=True):
425 """ Called when any key is released.
426 """ Called when any key is released.
426 """
427 """
427 if event.KeyCode in (59, ord('.')):
428 if event.KeyCode in (59, ord('.')):
428 # Intercepting '.'
429 # Intercepting '.'
429 event.Skip()
430 event.Skip()
430 self._popup_completion(create=True)
431 wx.CallAfter(self._popup_completion, create=True)
431 else:
432 else:
432 ConsoleWidget._on_key_up(self, event, skip=skip)
433 ConsoleWidget._on_key_up(self, event, skip=skip)
433
434
434
435
435 def _on_enter(self):
436 def _on_enter(self):
436 """ Called on return key down, in readline input_state.
437 """ Called on return key down, in readline input_state.
437 """
438 """
438 if self.debug:
439 if self.debug:
439 print >>sys.__stdout__, repr(self.input_buffer)
440 print >>sys.__stdout__, repr(self.input_buffer)
440 PrefilterFrontEnd._on_enter(self)
441 PrefilterFrontEnd._on_enter(self)
441
442
442
443
443 #--------------------------------------------------------------------------
444 #--------------------------------------------------------------------------
444 # EditWindow API
445 # EditWindow API
445 #--------------------------------------------------------------------------
446 #--------------------------------------------------------------------------
446
447
447 def OnUpdateUI(self, event):
448 def OnUpdateUI(self, event):
448 """ Override the OnUpdateUI of the EditWindow class, to prevent
449 """ Override the OnUpdateUI of the EditWindow class, to prevent
449 syntax highlighting both for faster redraw, and for more
450 syntax highlighting both for faster redraw, and for more
450 consistent look and feel.
451 consistent look and feel.
451 """
452 """
452 if not self._input_state == 'readline':
453 if not self._input_state == 'readline':
453 ConsoleWidget.OnUpdateUI(self, event)
454 ConsoleWidget.OnUpdateUI(self, event)
454
455
455 #--------------------------------------------------------------------------
456 #--------------------------------------------------------------------------
456 # Private API
457 # Private API
457 #--------------------------------------------------------------------------
458 #--------------------------------------------------------------------------
458
459
459 def _end_system_call(self):
460 def _end_system_call(self):
460 """ Called at the end of a system call.
461 """ Called at the end of a system call.
461 """
462 """
462 self._input_state = 'buffering'
463 self._input_state = 'buffering'
463 self._running_process = False
464 self._running_process = False
464
465
465
466
466 def _buffer_flush(self, event):
467 def _buffer_flush(self, event):
467 """ Called by the timer to flush the write buffer.
468 """ Called by the timer to flush the write buffer.
468
469
469 This is always called in the mainloop, by the wx timer.
470 This is always called in the mainloop, by the wx timer.
470 """
471 """
471 self._out_buffer_lock.acquire()
472 self._out_buffer_lock.acquire()
472 _out_buffer = self._out_buffer
473 _out_buffer = self._out_buffer
473 self._out_buffer = []
474 self._out_buffer = []
474 self._out_buffer_lock.release()
475 self._out_buffer_lock.release()
475 self.write(''.join(_out_buffer), refresh=False)
476 self.write(''.join(_out_buffer), refresh=False)
476
477
477
478
478 def _colorize_input_buffer(self):
479 def _colorize_input_buffer(self):
479 """ Keep the input buffer lines at a bright color.
480 """ Keep the input buffer lines at a bright color.
480 """
481 """
481 if not self._input_state in ('readline', 'raw_input'):
482 if not self._input_state in ('readline', 'raw_input'):
482 return
483 return
483 end_line = self.GetCurrentLine()
484 end_line = self.GetCurrentLine()
484 if not sys.platform == 'win32':
485 if not sys.platform == 'win32':
485 end_line += 1
486 end_line += 1
486 for i in range(self.current_prompt_line, end_line):
487 for i in range(self.current_prompt_line, end_line):
487 if i in self._markers:
488 if i in self._markers:
488 self.MarkerDeleteHandle(self._markers[i])
489 self.MarkerDeleteHandle(self._markers[i])
489 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
490 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
490
491
491
492
492 if __name__ == '__main__':
493 if __name__ == '__main__':
493 class MainWindow(wx.Frame):
494 class MainWindow(wx.Frame):
494 def __init__(self, parent, id, title):
495 def __init__(self, parent, id, title):
495 wx.Frame.__init__(self, parent, id, title, size=(300,250))
496 wx.Frame.__init__(self, parent, id, title, size=(300,250))
496 self._sizer = wx.BoxSizer(wx.VERTICAL)
497 self._sizer = wx.BoxSizer(wx.VERTICAL)
497 self.shell = WxController(self)
498 self.shell = WxController(self)
498 self._sizer.Add(self.shell, 1, wx.EXPAND)
499 self._sizer.Add(self.shell, 1, wx.EXPAND)
499 self.SetSizer(self._sizer)
500 self.SetSizer(self._sizer)
500 self.SetAutoLayout(1)
501 self.SetAutoLayout(1)
501 self.Show(True)
502 self.Show(True)
502
503
503 app = wx.PySimpleApp()
504 app = wx.PySimpleApp()
504 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
505 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
505 frame.shell.SetFocus()
506 frame.shell.SetFocus()
506 frame.SetSize((680, 460))
507 frame.SetSize((680, 460))
507 self = frame.shell
508 self = frame.shell
508
509
509 app.MainLoop()
510 app.MainLoop()
510
511
General Comments 0
You need to be logged in to leave comments. Login now