##// END OF EJS Templates
Capture errors in user's input lines and raises meaningfull exceptions.
gvaroquaux -
Show More
@@ -1,311 +1,320 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 import codeop
23 import codeop
24 import traceback
24
25
25 from frontendbase import FrontEndBase
26 from frontendbase import FrontEndBase
26 from IPython.kernel.core.interpreter import Interpreter
27 from IPython.kernel.core.interpreter import Interpreter
27
28
28 def common_prefix(strings):
29 def common_prefix(strings):
29 """ Given a list of strings, return the common prefix between all
30 """ Given a list of strings, return the common prefix between all
30 these strings.
31 these strings.
31 """
32 """
32 ref = strings[0]
33 ref = strings[0]
33 prefix = ''
34 prefix = ''
34 for size in range(len(ref)):
35 for size in range(len(ref)):
35 test_prefix = ref[:size+1]
36 test_prefix = ref[:size+1]
36 for string in strings[1:]:
37 for string in strings[1:]:
37 if not string.startswith(test_prefix):
38 if not string.startswith(test_prefix):
38 return prefix
39 return prefix
39 prefix = test_prefix
40 prefix = test_prefix
40
41
41 return prefix
42 return prefix
42
43
43 #-------------------------------------------------------------------------------
44 #-------------------------------------------------------------------------------
44 # Base class for the line-oriented front ends
45 # Base class for the line-oriented front ends
45 #-------------------------------------------------------------------------------
46 #-------------------------------------------------------------------------------
46 class LineFrontEndBase(FrontEndBase):
47 class LineFrontEndBase(FrontEndBase):
47 """ Concrete implementation of the FrontEndBase class. This is meant
48 """ Concrete implementation of the FrontEndBase class. This is meant
48 to be the base class behind all the frontend that are line-oriented,
49 to be the base class behind all the frontend that are line-oriented,
49 rather than block-oriented.
50 rather than block-oriented.
50 """
51 """
51
52
52 # We need to keep the prompt number, to be able to increment
53 # We need to keep the prompt number, to be able to increment
53 # it when there is an exception.
54 # it when there is an exception.
54 prompt_number = 1
55 prompt_number = 1
55
56
56 # We keep a reference to the last result: it helps testing and
57 # We keep a reference to the last result: it helps testing and
57 # programatic control of the frontend.
58 # programatic control of the frontend.
58 last_result = dict(number=0)
59 last_result = dict(number=0)
59
60
60 # The input buffer being edited
61 # The input buffer being edited
61 input_buffer = ''
62 input_buffer = ''
62
63
63 # Set to true for debug output
64 # Set to true for debug output
64 debug = False
65 debug = False
65
66
66 # A banner to print at startup
67 # A banner to print at startup
67 banner = None
68 banner = None
68
69
69 #--------------------------------------------------------------------------
70 #--------------------------------------------------------------------------
70 # FrontEndBase interface
71 # FrontEndBase interface
71 #--------------------------------------------------------------------------
72 #--------------------------------------------------------------------------
72
73
73 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
74 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
74 if shell is None:
75 if shell is None:
75 shell = Interpreter()
76 shell = Interpreter()
76 FrontEndBase.__init__(self, shell=shell, history=history)
77 FrontEndBase.__init__(self, shell=shell, history=history)
77
78
78 if banner is not None:
79 if banner is not None:
79 self.banner = banner
80 self.banner = banner
80
81
81 def start(self):
82 def start(self):
82 """ Put the frontend in a state where it is ready for user
83 """ Put the frontend in a state where it is ready for user
83 interaction.
84 interaction.
84 """
85 """
85 if self.banner is not None:
86 if self.banner is not None:
86 self.write(self.banner, refresh=False)
87 self.write(self.banner, refresh=False)
87
88
88 self.new_prompt(self.input_prompt_template.substitute(number=1))
89 self.new_prompt(self.input_prompt_template.substitute(number=1))
89
90
90
91
91 def complete(self, line):
92 def complete(self, line):
92 """Complete line in engine's user_ns
93 """Complete line in engine's user_ns
93
94
94 Parameters
95 Parameters
95 ----------
96 ----------
96 line : string
97 line : string
97
98
98 Result
99 Result
99 ------
100 ------
100 The replacement for the line and the list of possible completions.
101 The replacement for the line and the list of possible completions.
101 """
102 """
102 completions = self.shell.complete(line)
103 completions = self.shell.complete(line)
103 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
104 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
104 if completions:
105 if completions:
105 prefix = common_prefix(completions)
106 prefix = common_prefix(completions)
106 residual = complete_sep.split(line)[:-1]
107 residual = complete_sep.split(line)[:-1]
107 line = line[:-len(residual)] + prefix
108 line = line[:-len(residual)] + prefix
108 return line, completions
109 return line, completions
109
110
110
111
111 def render_result(self, result):
112 def render_result(self, result):
112 """ Frontend-specific rendering of the result of a calculation
113 """ Frontend-specific rendering of the result of a calculation
113 that has been sent to an engine.
114 that has been sent to an engine.
114 """
115 """
115 if 'stdout' in result and result['stdout']:
116 if 'stdout' in result and result['stdout']:
116 self.write('\n' + result['stdout'])
117 self.write('\n' + result['stdout'])
117 if 'display' in result and result['display']:
118 if 'display' in result and result['display']:
118 self.write("%s%s\n" % (
119 self.write("%s%s\n" % (
119 self.output_prompt_template.substitute(
120 self.output_prompt_template.substitute(
120 number=result['number']),
121 number=result['number']),
121 result['display']['pprint']
122 result['display']['pprint']
122 ) )
123 ) )
123
124
124
125
125 def render_error(self, failure):
126 def render_error(self, failure):
126 """ Frontend-specific rendering of error.
127 """ Frontend-specific rendering of error.
127 """
128 """
128 self.write('\n\n'+str(failure)+'\n\n')
129 self.write('\n\n'+str(failure)+'\n\n')
129 return failure
130 return failure
130
131
131
132
132 def is_complete(self, string):
133 def is_complete(self, string):
133 """ Check if a string forms a complete, executable set of
134 """ Check if a string forms a complete, executable set of
134 commands.
135 commands.
135
136
136 For the line-oriented frontend, multi-line code is not executed
137 For the line-oriented frontend, multi-line code is not executed
137 as soon as it is complete: the users has to enter two line
138 as soon as it is complete: the users has to enter two line
138 returns.
139 returns.
139 """
140 """
140 if string in ('', '\n'):
141 if string in ('', '\n'):
141 # Prefiltering, eg through ipython0, may return an empty
142 # Prefiltering, eg through ipython0, may return an empty
142 # string although some operations have been accomplished. We
143 # string although some operations have been accomplished. We
143 # thus want to consider an empty string as a complete
144 # thus want to consider an empty string as a complete
144 # statement.
145 # statement.
145 return True
146 return True
146 elif ( len(self.input_buffer.split('\n'))>2
147 elif ( len(self.input_buffer.split('\n'))>2
147 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
148 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
148 return False
149 return False
149 else:
150 else:
150 # Add line returns here, to make sure that the statement is
151 self.capture_output()
151 # complete.
152 try:
152 return codeop.compile_command(string.rstrip() + '\n\n',
153 # Add line returns here, to make sure that the statement is
153 "<string>", "exec")
154 # complete.
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
156 "<string>", "exec")
157 self.release_output()
158 except Exception, e:
159 # XXX: Hack: return True so that the
160 # code gets executed and the error captured.
161 is_complete = True
162 return is_complete
154
163
155
164
156 def write(self, string, refresh=True):
165 def write(self, string, refresh=True):
157 """ Write some characters to the display.
166 """ Write some characters to the display.
158
167
159 Subclass should overide this method.
168 Subclass should overide this method.
160
169
161 The refresh keyword argument is used in frontends with an
170 The refresh keyword argument is used in frontends with an
162 event loop, to choose whether the write should trigget an UI
171 event loop, to choose whether the write should trigget an UI
163 refresh, and thus be syncrhonous, or not.
172 refresh, and thus be syncrhonous, or not.
164 """
173 """
165 print >>sys.__stderr__, string
174 print >>sys.__stderr__, string
166
175
167
176
168 def execute(self, python_string, raw_string=None):
177 def execute(self, python_string, raw_string=None):
169 """ Stores the raw_string in the history, and sends the
178 """ Stores the raw_string in the history, and sends the
170 python string to the interpreter.
179 python string to the interpreter.
171 """
180 """
172 if raw_string is None:
181 if raw_string is None:
173 raw_string = python_string
182 raw_string = python_string
174 # Create a false result, in case there is an exception
183 # Create a false result, in case there is an exception
175 self.last_result = dict(number=self.prompt_number)
184 self.last_result = dict(number=self.prompt_number)
176 try:
185 try:
177 self.history.input_cache[-1] = raw_string.rstrip()
186 self.history.input_cache[-1] = raw_string.rstrip()
178 result = self.shell.execute(python_string)
187 result = self.shell.execute(python_string)
179 self.last_result = result
188 self.last_result = result
180 self.render_result(result)
189 self.render_result(result)
181 except:
190 except:
182 self.show_traceback()
191 self.show_traceback()
183 finally:
192 finally:
184 self.after_execute()
193 self.after_execute()
185
194
186 #--------------------------------------------------------------------------
195 #--------------------------------------------------------------------------
187 # LineFrontEndBase interface
196 # LineFrontEndBase interface
188 #--------------------------------------------------------------------------
197 #--------------------------------------------------------------------------
189
198
190 def prefilter_input(self, string):
199 def prefilter_input(self, string):
191 """ Prefilter the input to turn it in valid python.
200 """ Prefilter the input to turn it in valid python.
192 """
201 """
193 string = string.replace('\r\n', '\n')
202 string = string.replace('\r\n', '\n')
194 string = string.replace('\t', 4*' ')
203 string = string.replace('\t', 4*' ')
195 # Clean the trailing whitespace
204 # Clean the trailing whitespace
196 string = '\n'.join(l.rstrip() for l in string.split('\n'))
205 string = '\n'.join(l.rstrip() for l in string.split('\n'))
197 return string
206 return string
198
207
199
208
200 def after_execute(self):
209 def after_execute(self):
201 """ All the operations required after an execution to put the
210 """ All the operations required after an execution to put the
202 terminal back in a shape where it is usable.
211 terminal back in a shape where it is usable.
203 """
212 """
204 self.prompt_number += 1
213 self.prompt_number += 1
205 self.new_prompt(self.input_prompt_template.substitute(
214 self.new_prompt(self.input_prompt_template.substitute(
206 number=(self.last_result['number'] + 1)))
215 number=(self.last_result['number'] + 1)))
207 # Start a new empty history entry
216 # Start a new empty history entry
208 self._add_history(None, '')
217 self._add_history(None, '')
209 self.history_cursor = len(self.history.input_cache) - 1
218 self.history_cursor = len(self.history.input_cache) - 1
210
219
211
220
212 def complete_current_input(self):
221 def complete_current_input(self):
213 """ Do code completion on current line.
222 """ Do code completion on current line.
214 """
223 """
215 if self.debug:
224 if self.debug:
216 print >>sys.__stdout__, "complete_current_input",
225 print >>sys.__stdout__, "complete_current_input",
217 line = self.input_buffer
226 line = self.input_buffer
218 new_line, completions = self.complete(line)
227 new_line, completions = self.complete(line)
219 if len(completions)>1:
228 if len(completions)>1:
220 self.write_completion(completions, new_line=new_line)
229 self.write_completion(completions, new_line=new_line)
221 elif not line == new_line:
230 elif not line == new_line:
222 self.input_buffer = new_line
231 self.input_buffer = new_line
223 if self.debug:
232 if self.debug:
224 print >>sys.__stdout__, 'line', line
233 print >>sys.__stdout__, 'line', line
225 print >>sys.__stdout__, 'new_line', new_line
234 print >>sys.__stdout__, 'new_line', new_line
226 print >>sys.__stdout__, completions
235 print >>sys.__stdout__, completions
227
236
228
237
229 def get_line_width(self):
238 def get_line_width(self):
230 """ Return the width of the line in characters.
239 """ Return the width of the line in characters.
231 """
240 """
232 return 80
241 return 80
233
242
234
243
235 def write_completion(self, possibilities, new_line=None):
244 def write_completion(self, possibilities, new_line=None):
236 """ Write the list of possible completions.
245 """ Write the list of possible completions.
237
246
238 new_line is the completed input line that should be displayed
247 new_line is the completed input line that should be displayed
239 after the completion are writen. If None, the input_buffer
248 after the completion are writen. If None, the input_buffer
240 before the completion is used.
249 before the completion is used.
241 """
250 """
242 if new_line is None:
251 if new_line is None:
243 new_line = self.input_buffer
252 new_line = self.input_buffer
244
253
245 self.write('\n')
254 self.write('\n')
246 max_len = len(max(possibilities, key=len)) + 1
255 max_len = len(max(possibilities, key=len)) + 1
247
256
248 # Now we check how much symbol we can put on a line...
257 # Now we check how much symbol we can put on a line...
249 chars_per_line = self.get_line_width()
258 chars_per_line = self.get_line_width()
250 symbols_per_line = max(1, chars_per_line/max_len)
259 symbols_per_line = max(1, chars_per_line/max_len)
251
260
252 pos = 1
261 pos = 1
253 buf = []
262 buf = []
254 for symbol in possibilities:
263 for symbol in possibilities:
255 if pos < symbols_per_line:
264 if pos < symbols_per_line:
256 buf.append(symbol.ljust(max_len))
265 buf.append(symbol.ljust(max_len))
257 pos += 1
266 pos += 1
258 else:
267 else:
259 buf.append(symbol.rstrip() + '\n')
268 buf.append(symbol.rstrip() + '\n')
260 pos = 1
269 pos = 1
261 self.write(''.join(buf))
270 self.write(''.join(buf))
262 self.new_prompt(self.input_prompt_template.substitute(
271 self.new_prompt(self.input_prompt_template.substitute(
263 number=self.last_result['number'] + 1))
272 number=self.last_result['number'] + 1))
264 self.input_buffer = new_line
273 self.input_buffer = new_line
265
274
266
275
267 def new_prompt(self, prompt):
276 def new_prompt(self, prompt):
268 """ Prints a prompt and starts a new editing buffer.
277 """ Prints a prompt and starts a new editing buffer.
269
278
270 Subclasses should use this method to make sure that the
279 Subclasses should use this method to make sure that the
271 terminal is put in a state favorable for a new line
280 terminal is put in a state favorable for a new line
272 input.
281 input.
273 """
282 """
274 self.input_buffer = ''
283 self.input_buffer = ''
275 self.write(prompt)
284 self.write(prompt)
276
285
277
286
278 #--------------------------------------------------------------------------
287 #--------------------------------------------------------------------------
279 # Private API
288 # Private API
280 #--------------------------------------------------------------------------
289 #--------------------------------------------------------------------------
281
290
282 def _on_enter(self):
291 def _on_enter(self):
283 """ Called when the return key is pressed in a line editing
292 """ Called when the return key is pressed in a line editing
284 buffer.
293 buffer.
285 """
294 """
286 current_buffer = self.input_buffer
295 current_buffer = self.input_buffer
287 cleaned_buffer = self.prefilter_input(current_buffer)
296 cleaned_buffer = self.prefilter_input(current_buffer)
288 if self.is_complete(cleaned_buffer):
297 if self.is_complete(cleaned_buffer):
289 self.execute(cleaned_buffer, raw_string=current_buffer)
298 self.execute(cleaned_buffer, raw_string=current_buffer)
290 else:
299 else:
291 self.input_buffer += self._get_indent_string(
300 self.input_buffer += self._get_indent_string(
292 current_buffer[:-1])
301 current_buffer[:-1])
293 if len(current_buffer.split('\n')) == 2:
302 if len(current_buffer.split('\n')) == 2:
294 self.input_buffer += '\t\t'
303 self.input_buffer += '\t\t'
295 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
304 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
296 self.input_buffer += '\t'
305 self.input_buffer += '\t'
297
306
298
307
299 def _get_indent_string(self, string):
308 def _get_indent_string(self, string):
300 """ Return the string of whitespace that prefixes a line. Used to
309 """ Return the string of whitespace that prefixes a line. Used to
301 add the right amount of indendation when creating a new line.
310 add the right amount of indendation when creating a new line.
302 """
311 """
303 string = string.replace('\t', ' '*4)
312 string = string.replace('\t', ' '*4)
304 string = string.split('\n')[-1]
313 string = string.split('\n')[-1]
305 indent_chars = len(string) - len(string.lstrip())
314 indent_chars = len(string) - len(string.lstrip())
306 indent_string = '\t'*(indent_chars // 4) + \
315 indent_string = '\t'*(indent_chars // 4) + \
307 ' '*(indent_chars % 4)
316 ' '*(indent_chars % 4)
308
317
309 return indent_string
318 return indent_string
310
319
311
320
@@ -1,512 +1,523 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 # The different line markers used to higlight the prompts.
132 _markers = dict()
132 _markers = dict()
133
133
134 #--------------------------------------------------------------------------
134 #--------------------------------------------------------------------------
135 # Public API
135 # Public API
136 #--------------------------------------------------------------------------
136 #--------------------------------------------------------------------------
137
137
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139 size=wx.DefaultSize,
139 size=wx.DefaultSize,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
141 *args, **kwds):
141 *args, **kwds):
142 """ Create Shell instance.
142 """ Create Shell instance.
143 """
143 """
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
145 PrefilterFrontEnd.__init__(self, **kwds)
145 PrefilterFrontEnd.__init__(self, **kwds)
146
146
147 # Marker for complete buffer.
147 # Marker for complete buffer.
148 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
148 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
149 background=_COMPLETE_BUFFER_BG)
149 background=_COMPLETE_BUFFER_BG)
150 # Marker for current input buffer.
150 # Marker for current input buffer.
151 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
151 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
152 background=_INPUT_BUFFER_BG)
152 background=_INPUT_BUFFER_BG)
153 # Marker for tracebacks.
153 # Marker for tracebacks.
154 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
154 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
155 background=_ERROR_BG)
155 background=_ERROR_BG)
156
156
157 # A time for flushing the write buffer
157 # A time for flushing the write buffer
158 BUFFER_FLUSH_TIMER_ID = 100
158 BUFFER_FLUSH_TIMER_ID = 100
159 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
159 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
160 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
160 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
161
161
162 if 'debug' in kwds:
162 if 'debug' in kwds:
163 self.debug = kwds['debug']
163 self.debug = kwds['debug']
164 kwds.pop('debug')
164 kwds.pop('debug')
165
165
166 # Inject self in namespace, for debug
166 # Inject self in namespace, for debug
167 if self.debug:
167 if self.debug:
168 self.shell.user_ns['self'] = self
168 self.shell.user_ns['self'] = self
169
169
170
170
171 def raw_input(self, prompt):
171 def raw_input(self, prompt):
172 """ A replacement from python's raw_input.
172 """ A replacement from python's raw_input.
173 """
173 """
174 self.new_prompt(prompt)
174 self.new_prompt(prompt)
175 self._input_state = 'raw_input'
175 self._input_state = 'raw_input'
176 if hasattr(self, '_cursor'):
176 if hasattr(self, '_cursor'):
177 del self._cursor
177 del self._cursor
178 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
178 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
179 self.waiting = True
179 self.waiting = True
180 self.__old_on_enter = self._on_enter
180 self.__old_on_enter = self._on_enter
181 def my_on_enter():
181 def my_on_enter():
182 self.waiting = False
182 self.waiting = False
183 self._on_enter = my_on_enter
183 self._on_enter = my_on_enter
184 # XXX: Busy waiting, ugly.
184 # XXX: Busy waiting, ugly.
185 while self.waiting:
185 while self.waiting:
186 wx.Yield()
186 wx.Yield()
187 sleep(0.1)
187 sleep(0.1)
188 self._on_enter = self.__old_on_enter
188 self._on_enter = self.__old_on_enter
189 self._input_state = 'buffering'
189 self._input_state = 'buffering'
190 self._cursor = wx.BusyCursor()
190 self._cursor = wx.BusyCursor()
191 return self.input_buffer.rstrip('\n')
191 return self.input_buffer.rstrip('\n')
192
192
193
193
194 def system_call(self, command_string):
194 def system_call(self, command_string):
195 self._input_state = 'subprocess'
195 self._input_state = 'subprocess'
196 self._running_process = PipedProcess(command_string,
196 self._running_process = PipedProcess(command_string,
197 out_callback=self.buffered_write,
197 out_callback=self.buffered_write,
198 end_callback = self._end_system_call)
198 end_callback = self._end_system_call)
199 self._running_process.start()
199 self._running_process.start()
200 # XXX: another one of these polling loops to have a blocking
200 # XXX: another one of these polling loops to have a blocking
201 # call
201 # call
202 wx.Yield()
202 wx.Yield()
203 while self._running_process:
203 while self._running_process:
204 wx.Yield()
204 wx.Yield()
205 sleep(0.1)
205 sleep(0.1)
206 # Be sure to flush the buffer.
206 # Be sure to flush the buffer.
207 self._buffer_flush(event=None)
207 self._buffer_flush(event=None)
208
208
209
209
210 def do_calltip(self):
210 def do_calltip(self):
211 """ Analyse current and displays useful calltip for it.
211 """ Analyse current and displays useful calltip for it.
212 """
212 """
213 if self.debug:
213 if self.debug:
214 print >>sys.__stdout__, "do_calltip"
214 print >>sys.__stdout__, "do_calltip"
215 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
215 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
216 symbol = self.input_buffer
216 symbol = self.input_buffer
217 symbol_string = separators.split(symbol)[-1]
217 symbol_string = separators.split(symbol)[-1]
218 base_symbol_string = symbol_string.split('.')[0]
218 base_symbol_string = symbol_string.split('.')[0]
219 if base_symbol_string in self.shell.user_ns:
219 if base_symbol_string in self.shell.user_ns:
220 symbol = self.shell.user_ns[base_symbol_string]
220 symbol = self.shell.user_ns[base_symbol_string]
221 elif base_symbol_string in self.shell.user_global_ns:
221 elif base_symbol_string in self.shell.user_global_ns:
222 symbol = self.shell.user_global_ns[base_symbol_string]
222 symbol = self.shell.user_global_ns[base_symbol_string]
223 elif base_symbol_string in __builtin__.__dict__:
223 elif base_symbol_string in __builtin__.__dict__:
224 symbol = __builtin__.__dict__[base_symbol_string]
224 symbol = __builtin__.__dict__[base_symbol_string]
225 else:
225 else:
226 return False
226 return False
227 try:
227 try:
228 for name in symbol_string.split('.')[1:] + ['__doc__']:
228 for name in symbol_string.split('.')[1:] + ['__doc__']:
229 symbol = getattr(symbol, name)
229 symbol = getattr(symbol, name)
230 self.AutoCompCancel()
230 self.AutoCompCancel()
231 wx.Yield()
231 wx.Yield()
232 self.CallTipShow(self.GetCurrentPos(), symbol)
232 self.CallTipShow(self.GetCurrentPos(), symbol)
233 except:
233 except:
234 # The retrieve symbol couldn't be converted to a string
234 # The retrieve symbol couldn't be converted to a string
235 pass
235 pass
236
236
237
237
238 def _popup_completion(self, create=False):
238 def _popup_completion(self, create=False):
239 """ Updates the popup completion menu if it exists. If create is
239 """ Updates the popup completion menu if it exists. If create is
240 true, open the menu.
240 true, open the menu.
241 """
241 """
242 if self.debug:
242 if self.debug:
243 print >>sys.__stdout__, "_popup_completion"
243 print >>sys.__stdout__, "_popup_completion"
244 line = self.input_buffer
244 line = self.input_buffer
245 if (self.AutoCompActive() and line and not line[-1] == '.') \
245 if (self.AutoCompActive() and line and not line[-1] == '.') \
246 or create==True:
246 or create==True:
247 suggestion, completions = self.complete(line)
247 suggestion, completions = self.complete(line)
248 offset=0
248 offset=0
249 if completions:
249 if completions:
250 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
250 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
251 residual = complete_sep.split(line)[-1]
251 residual = complete_sep.split(line)[-1]
252 offset = len(residual)
252 offset = len(residual)
253 self.pop_completion(completions, offset=offset)
253 self.pop_completion(completions, offset=offset)
254 if self.debug:
254 if self.debug:
255 print >>sys.__stdout__, completions
255 print >>sys.__stdout__, completions
256
256
257
257
258 def buffered_write(self, text):
258 def buffered_write(self, text):
259 """ A write method for streams, that caches the stream in order
259 """ A write method for streams, that caches the stream in order
260 to avoid flooding the event loop.
260 to avoid flooding the event loop.
261
261
262 This can be called outside of the main loop, in separate
262 This can be called outside of the main loop, in separate
263 threads.
263 threads.
264 """
264 """
265 self._out_buffer_lock.acquire()
265 self._out_buffer_lock.acquire()
266 self._out_buffer.append(text)
266 self._out_buffer.append(text)
267 self._out_buffer_lock.release()
267 self._out_buffer_lock.release()
268 if not self._buffer_flush_timer.IsRunning():
268 if not self._buffer_flush_timer.IsRunning():
269 wx.CallAfter(self._buffer_flush_timer.Start,
269 wx.CallAfter(self._buffer_flush_timer.Start,
270 milliseconds=100, oneShot=True)
270 milliseconds=100, oneShot=True)
271
271
272
272
273 #--------------------------------------------------------------------------
273 #--------------------------------------------------------------------------
274 # LineFrontEnd interface
274 # LineFrontEnd interface
275 #--------------------------------------------------------------------------
275 #--------------------------------------------------------------------------
276
276
277 def execute(self, python_string, raw_string=None):
277 def execute(self, python_string, raw_string=None):
278 self._input_state = 'buffering'
278 self._input_state = 'buffering'
279 self.CallTipCancel()
279 self.CallTipCancel()
280 self._cursor = wx.BusyCursor()
280 self._cursor = wx.BusyCursor()
281 if raw_string is None:
281 if raw_string is None:
282 raw_string = python_string
282 raw_string = python_string
283 end_line = self.current_prompt_line \
283 end_line = self.current_prompt_line \
284 + max(1, len(raw_string.split('\n'))-1)
284 + max(1, len(raw_string.split('\n'))-1)
285 for i in range(self.current_prompt_line, end_line):
285 for i in range(self.current_prompt_line, end_line):
286 if i in self._markers:
286 if i in self._markers:
287 self.MarkerDeleteHandle(self._markers[i])
287 self.MarkerDeleteHandle(self._markers[i])
288 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
288 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
289 # Update the display:
289 # Update the display:
290 wx.Yield()
290 wx.Yield()
291 self.GotoPos(self.GetLength())
291 self.GotoPos(self.GetLength())
292 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
292 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
293
293
294 def save_output_hooks(self):
294 def save_output_hooks(self):
295 self.__old_raw_input = __builtin__.raw_input
295 self.__old_raw_input = __builtin__.raw_input
296 PrefilterFrontEnd.save_output_hooks(self)
296 PrefilterFrontEnd.save_output_hooks(self)
297
297
298 def capture_output(self):
298 def capture_output(self):
299 __builtin__.raw_input = self.raw_input
299 __builtin__.raw_input = self.raw_input
300 self.SetLexer(stc.STC_LEX_NULL)
300 self.SetLexer(stc.STC_LEX_NULL)
301 PrefilterFrontEnd.capture_output(self)
301 PrefilterFrontEnd.capture_output(self)
302
302
303
303
304 def release_output(self):
304 def release_output(self):
305 __builtin__.raw_input = self.__old_raw_input
305 __builtin__.raw_input = self.__old_raw_input
306 PrefilterFrontEnd.release_output(self)
306 PrefilterFrontEnd.release_output(self)
307 self.SetLexer(stc.STC_LEX_PYTHON)
307 self.SetLexer(stc.STC_LEX_PYTHON)
308
308
309
309
310 def after_execute(self):
310 def after_execute(self):
311 PrefilterFrontEnd.after_execute(self)
311 PrefilterFrontEnd.after_execute(self)
312 # Clear the wait cursor
312 # Clear the wait cursor
313 if hasattr(self, '_cursor'):
313 if hasattr(self, '_cursor'):
314 del self._cursor
314 del self._cursor
315 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
315 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
316
316
317
317
318 def show_traceback(self):
318 def show_traceback(self):
319 start_line = self.GetCurrentLine()
319 start_line = self.GetCurrentLine()
320 PrefilterFrontEnd.show_traceback(self)
320 PrefilterFrontEnd.show_traceback(self)
321 wx.Yield()
321 wx.Yield()
322 for i in range(start_line, self.GetCurrentLine()):
322 for i in range(start_line, self.GetCurrentLine()):
323 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
323 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
324
324
325
325
326 #--------------------------------------------------------------------------
326 #--------------------------------------------------------------------------
327 # FrontEndBase interface
328 #--------------------------------------------------------------------------
329
330 def render_error(self, e):
331 start_line = self.GetCurrentLine()
332 self.write('\n' + e + '\n')
333 for i in range(start_line, self.GetCurrentLine()):
334 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
335
336
337 #--------------------------------------------------------------------------
327 # ConsoleWidget interface
338 # ConsoleWidget interface
328 #--------------------------------------------------------------------------
339 #--------------------------------------------------------------------------
329
340
330 def new_prompt(self, prompt):
341 def new_prompt(self, prompt):
331 """ Display a new prompt, and start a new input buffer.
342 """ Display a new prompt, and start a new input buffer.
332 """
343 """
333 self._input_state = 'readline'
344 self._input_state = 'readline'
334 ConsoleWidget.new_prompt(self, prompt)
345 ConsoleWidget.new_prompt(self, prompt)
335 i = self.current_prompt_line
346 i = self.current_prompt_line
336 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
347 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
337
348
338
349
339 def write(self, *args, **kwargs):
350 def write(self, *args, **kwargs):
340 # Avoid multiple inheritence, be explicit about which
351 # Avoid multiple inheritence, be explicit about which
341 # parent method class gets called
352 # parent method class gets called
342 ConsoleWidget.write(self, *args, **kwargs)
353 ConsoleWidget.write(self, *args, **kwargs)
343
354
344
355
345 def _on_key_down(self, event, skip=True):
356 def _on_key_down(self, event, skip=True):
346 """ Capture the character events, let the parent
357 """ Capture the character events, let the parent
347 widget handle them, and put our logic afterward.
358 widget handle them, and put our logic afterward.
348 """
359 """
349 # FIXME: This method needs to be broken down in smaller ones.
360 # FIXME: This method needs to be broken down in smaller ones.
350 current_line_number = self.GetCurrentLine()
361 current_line_number = self.GetCurrentLine()
351 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
362 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
352 # Capture Control-C
363 # Capture Control-C
353 if self._input_state == 'subprocess':
364 if self._input_state == 'subprocess':
354 if self.debug:
365 if self.debug:
355 print >>sys.__stderr__, 'Killing running process'
366 print >>sys.__stderr__, 'Killing running process'
356 self._running_process.process.kill()
367 self._running_process.process.kill()
357 elif self._input_state == 'buffering':
368 elif self._input_state == 'buffering':
358 if self.debug:
369 if self.debug:
359 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
370 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
360 raise KeyboardInterrupt
371 raise KeyboardInterrupt
361 # XXX: We need to make really sure we
372 # XXX: We need to make really sure we
362 # get back to a prompt.
373 # get back to a prompt.
363 elif self._input_state == 'subprocess' and (
374 elif self._input_state == 'subprocess' and (
364 ( event.KeyCode<256 and
375 ( event.KeyCode<256 and
365 not event.ControlDown() )
376 not event.ControlDown() )
366 or
377 or
367 ( event.KeyCode in (ord('d'), ord('D')) and
378 ( event.KeyCode in (ord('d'), ord('D')) and
368 event.ControlDown())):
379 event.ControlDown())):
369 # We are running a process, we redirect keys.
380 # We are running a process, we redirect keys.
370 ConsoleWidget._on_key_down(self, event, skip=skip)
381 ConsoleWidget._on_key_down(self, event, skip=skip)
371 char = chr(event.KeyCode)
382 char = chr(event.KeyCode)
372 # Deal with some inconsistency in wx keycodes:
383 # Deal with some inconsistency in wx keycodes:
373 if char == '\r':
384 if char == '\r':
374 char = '\n'
385 char = '\n'
375 elif not event.ShiftDown():
386 elif not event.ShiftDown():
376 char = char.lower()
387 char = char.lower()
377 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
388 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
378 char = '\04'
389 char = '\04'
379 self._running_process.process.stdin.write(char)
390 self._running_process.process.stdin.write(char)
380 self._running_process.process.stdin.flush()
391 self._running_process.process.stdin.flush()
381 elif event.KeyCode in (ord('('), 57):
392 elif event.KeyCode in (ord('('), 57):
382 # Calltips
393 # Calltips
383 event.Skip()
394 event.Skip()
384 self.do_calltip()
395 self.do_calltip()
385 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
396 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
386 event.Skip()
397 event.Skip()
387 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
398 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
388 wx.CallAfter(self._popup_completion, create=True)
399 wx.CallAfter(self._popup_completion, create=True)
389 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
400 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
390 wx.WXK_RIGHT, wx.WXK_ESCAPE):
401 wx.WXK_RIGHT, wx.WXK_ESCAPE):
391 wx.CallAfter(self._popup_completion)
402 wx.CallAfter(self._popup_completion)
392 else:
403 else:
393 # Up history
404 # Up history
394 if event.KeyCode == wx.WXK_UP and (
405 if event.KeyCode == wx.WXK_UP and (
395 ( current_line_number == self.current_prompt_line and
406 ( current_line_number == self.current_prompt_line and
396 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
407 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
397 or event.ControlDown() ):
408 or event.ControlDown() ):
398 new_buffer = self.get_history_previous(
409 new_buffer = self.get_history_previous(
399 self.input_buffer)
410 self.input_buffer)
400 if new_buffer is not None:
411 if new_buffer is not None:
401 self.input_buffer = new_buffer
412 self.input_buffer = new_buffer
402 if self.GetCurrentLine() > self.current_prompt_line:
413 if self.GetCurrentLine() > self.current_prompt_line:
403 # Go to first line, for seemless history up.
414 # Go to first line, for seemless history up.
404 self.GotoPos(self.current_prompt_pos)
415 self.GotoPos(self.current_prompt_pos)
405 # Down history
416 # Down history
406 elif event.KeyCode == wx.WXK_DOWN and (
417 elif event.KeyCode == wx.WXK_DOWN and (
407 ( current_line_number == self.LineCount -1 and
418 ( current_line_number == self.LineCount -1 and
408 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
419 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
409 or event.ControlDown() ):
420 or event.ControlDown() ):
410 new_buffer = self.get_history_next()
421 new_buffer = self.get_history_next()
411 if new_buffer is not None:
422 if new_buffer is not None:
412 self.input_buffer = new_buffer
423 self.input_buffer = new_buffer
413 # Tab-completion
424 # Tab-completion
414 elif event.KeyCode == ord('\t'):
425 elif event.KeyCode == ord('\t'):
415 last_line = self.input_buffer.split('\n')[-1]
426 last_line = self.input_buffer.split('\n')[-1]
416 if not re.match(r'^\s*$', last_line):
427 if not re.match(r'^\s*$', last_line):
417 self.complete_current_input()
428 self.complete_current_input()
418 if self.AutoCompActive():
429 if self.AutoCompActive():
419 wx.CallAfter(self._popup_completion, create=True)
430 wx.CallAfter(self._popup_completion, create=True)
420 else:
431 else:
421 event.Skip()
432 event.Skip()
422 else:
433 else:
423 ConsoleWidget._on_key_down(self, event, skip=skip)
434 ConsoleWidget._on_key_down(self, event, skip=skip)
424
435
425
436
426 def _on_key_up(self, event, skip=True):
437 def _on_key_up(self, event, skip=True):
427 """ Called when any key is released.
438 """ Called when any key is released.
428 """
439 """
429 if event.KeyCode in (59, ord('.')):
440 if event.KeyCode in (59, ord('.')):
430 # Intercepting '.'
441 # Intercepting '.'
431 event.Skip()
442 event.Skip()
432 wx.CallAfter(self._popup_completion, create=True)
443 wx.CallAfter(self._popup_completion, create=True)
433 else:
444 else:
434 ConsoleWidget._on_key_up(self, event, skip=skip)
445 ConsoleWidget._on_key_up(self, event, skip=skip)
435
446
436
447
437 def _on_enter(self):
448 def _on_enter(self):
438 """ Called on return key down, in readline input_state.
449 """ Called on return key down, in readline input_state.
439 """
450 """
440 if self.debug:
451 if self.debug:
441 print >>sys.__stdout__, repr(self.input_buffer)
452 print >>sys.__stdout__, repr(self.input_buffer)
442 PrefilterFrontEnd._on_enter(self)
453 PrefilterFrontEnd._on_enter(self)
443
454
444
455
445 #--------------------------------------------------------------------------
456 #--------------------------------------------------------------------------
446 # EditWindow API
457 # EditWindow API
447 #--------------------------------------------------------------------------
458 #--------------------------------------------------------------------------
448
459
449 def OnUpdateUI(self, event):
460 def OnUpdateUI(self, event):
450 """ Override the OnUpdateUI of the EditWindow class, to prevent
461 """ Override the OnUpdateUI of the EditWindow class, to prevent
451 syntax highlighting both for faster redraw, and for more
462 syntax highlighting both for faster redraw, and for more
452 consistent look and feel.
463 consistent look and feel.
453 """
464 """
454 if not self._input_state == 'readline':
465 if not self._input_state == 'readline':
455 ConsoleWidget.OnUpdateUI(self, event)
466 ConsoleWidget.OnUpdateUI(self, event)
456
467
457 #--------------------------------------------------------------------------
468 #--------------------------------------------------------------------------
458 # Private API
469 # Private API
459 #--------------------------------------------------------------------------
470 #--------------------------------------------------------------------------
460
471
461 def _end_system_call(self):
472 def _end_system_call(self):
462 """ Called at the end of a system call.
473 """ Called at the end of a system call.
463 """
474 """
464 self._input_state = 'buffering'
475 self._input_state = 'buffering'
465 self._running_process = False
476 self._running_process = False
466
477
467
478
468 def _buffer_flush(self, event):
479 def _buffer_flush(self, event):
469 """ Called by the timer to flush the write buffer.
480 """ Called by the timer to flush the write buffer.
470
481
471 This is always called in the mainloop, by the wx timer.
482 This is always called in the mainloop, by the wx timer.
472 """
483 """
473 self._out_buffer_lock.acquire()
484 self._out_buffer_lock.acquire()
474 _out_buffer = self._out_buffer
485 _out_buffer = self._out_buffer
475 self._out_buffer = []
486 self._out_buffer = []
476 self._out_buffer_lock.release()
487 self._out_buffer_lock.release()
477 self.write(''.join(_out_buffer), refresh=False)
488 self.write(''.join(_out_buffer), refresh=False)
478
489
479
490
480 def _colorize_input_buffer(self):
491 def _colorize_input_buffer(self):
481 """ Keep the input buffer lines at a bright color.
492 """ Keep the input buffer lines at a bright color.
482 """
493 """
483 if not self._input_state in ('readline', 'raw_input'):
494 if not self._input_state in ('readline', 'raw_input'):
484 return
495 return
485 end_line = self.GetCurrentLine()
496 end_line = self.GetCurrentLine()
486 if not sys.platform == 'win32':
497 if not sys.platform == 'win32':
487 end_line += 1
498 end_line += 1
488 for i in range(self.current_prompt_line, end_line):
499 for i in range(self.current_prompt_line, end_line):
489 if i in self._markers:
500 if i in self._markers:
490 self.MarkerDeleteHandle(self._markers[i])
501 self.MarkerDeleteHandle(self._markers[i])
491 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
502 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
492
503
493
504
494 if __name__ == '__main__':
505 if __name__ == '__main__':
495 class MainWindow(wx.Frame):
506 class MainWindow(wx.Frame):
496 def __init__(self, parent, id, title):
507 def __init__(self, parent, id, title):
497 wx.Frame.__init__(self, parent, id, title, size=(300,250))
508 wx.Frame.__init__(self, parent, id, title, size=(300,250))
498 self._sizer = wx.BoxSizer(wx.VERTICAL)
509 self._sizer = wx.BoxSizer(wx.VERTICAL)
499 self.shell = WxController(self)
510 self.shell = WxController(self)
500 self._sizer.Add(self.shell, 1, wx.EXPAND)
511 self._sizer.Add(self.shell, 1, wx.EXPAND)
501 self.SetSizer(self._sizer)
512 self.SetSizer(self._sizer)
502 self.SetAutoLayout(1)
513 self.SetAutoLayout(1)
503 self.Show(True)
514 self.Show(True)
504
515
505 app = wx.PySimpleApp()
516 app = wx.PySimpleApp()
506 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
517 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
507 frame.shell.SetFocus()
518 frame.shell.SetFocus()
508 frame.SetSize((680, 460))
519 frame.SetSize((680, 460))
509 self = frame.shell
520 self = frame.shell
510
521
511 app.MainLoop()
522 app.MainLoop()
512
523
General Comments 0
You need to be logged in to leave comments. Login now