##// END OF EJS Templates
Make code execution more robust.
Gael Varoquaux -
Show More
@@ -1,149 +1,158 b''
1 """
1 """
2 Base front end class for all line-oriented frontends.
2 Base front end class for all line-oriented frontends.
3
3
4 Currently this focuses on synchronous frontends.
4 Currently this focuses on synchronous frontends.
5 """
5 """
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18 import re
18 import re
19
19
20 import IPython
20 import IPython
21
21
22
22
23 from frontendbase import FrontEndBase
23 from frontendbase import FrontEndBase
24 from IPython.kernel.core.interpreter import Interpreter
24 from IPython.kernel.core.interpreter import Interpreter
25
25
26 #-------------------------------------------------------------------------------
26 #-------------------------------------------------------------------------------
27 # Base class for the line-oriented front ends
27 # Base class for the line-oriented front ends
28 #-------------------------------------------------------------------------------
28 #-------------------------------------------------------------------------------
29 class LineFrontEndBase(FrontEndBase):
29 class LineFrontEndBase(FrontEndBase):
30
30
31 # We need to keep the prompt number, to be able to increment
31 # We need to keep the prompt number, to be able to increment
32 # it when there is an exception.
32 # it when there is an exception.
33 prompt_number = 1
33 prompt_number = 1
34
34
35
35
36 #--------------------------------------------------------------------------
36 #--------------------------------------------------------------------------
37 # Public API
37 # Public API
38 #--------------------------------------------------------------------------
38 #--------------------------------------------------------------------------
39
39
40 def __init__(self, shell=None, history=None):
40 def __init__(self, shell=None, history=None):
41 if shell is None:
41 if shell is None:
42 shell = Interpreter()
42 shell = Interpreter()
43 FrontEndBase.__init__(self, shell=shell, history=history)
43 FrontEndBase.__init__(self, shell=shell, history=history)
44
44
45 #FIXME: print banner.
45 #FIXME: print banner.
46 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
46 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
47 % IPython.__version__
47 % IPython.__version__
48
48
49
49
50 def complete(self, token):
50 def complete(self, token):
51 """Complete token in engine's user_ns
51 """Complete token in engine's user_ns
52
52
53 Parameters
53 Parameters
54 ----------
54 ----------
55 token : string
55 token : string
56
56
57 Result
57 Result
58 ------
58 ------
59 Deferred result of
59 Deferred result of
60 IPython.kernel.engineservice.IEngineBase.complete
60 IPython.kernel.engineservice.IEngineBase.complete
61 """
61 """
62
62
63 return self.shell.complete(token)
63 return self.shell.complete(token)
64
64
65
65
66 def render_result(self, result):
66 def render_result(self, result):
67 if 'stdout' in result and result['stdout']:
67 if 'stdout' in result and result['stdout']:
68 self.write('\n' + result['stdout'])
68 self.write('\n' + result['stdout'])
69 if 'display' in result and result['display']:
69 if 'display' in result and result['display']:
70 self.write("%s%s\n" % (
70 self.write("%s%s\n" % (
71 self.output_prompt % result['number'],
71 self.output_prompt % result['number'],
72 result['display']['pprint']
72 result['display']['pprint']
73 ) )
73 ) )
74
74
75
75
76 def render_error(self, failure):
76 def render_error(self, failure):
77 self.insert_text('\n\n'+str(failure)+'\n\n')
77 self.insert_text('\n\n'+str(failure)+'\n\n')
78 return failure
78 return failure
79
79
80
80
81 def prefilter_input(self, string):
81 def prefilter_input(self, string):
82 string = string.replace('\r\n', '\n')
82 string = string.replace('\r\n', '\n')
83 string = string.replace('\t', 4*' ')
83 string = string.replace('\t', 4*' ')
84 # Clean the trailing whitespace
84 # Clean the trailing whitespace
85 string = '\n'.join(l.rstrip() for l in string.split('\n'))
85 string = '\n'.join(l.rstrip() for l in string.split('\n'))
86 return string
86 return string
87
87
88
88
89 def is_complete(self, string):
89 def is_complete(self, string):
90 if ( len(self.get_current_edit_buffer().split('\n'))>1
90 if ( len(self.get_current_edit_buffer().split('\n'))>1
91 and not re.findall(r"\n[\t ]*$", string)):
91 and not re.findall(r"\n[\t ]*$", string)):
92 return False
92 return False
93 else:
93 else:
94 return FrontEndBase.is_complete(self, string)
94 return FrontEndBase.is_complete(self, string)
95
95
96
96
97 def execute(self, python_string, raw_string=None):
97 def execute(self, python_string, raw_string=None):
98 """ Send the python_string to the interpreter, stores the
98 """ Send the python_string to the interpreter, stores the
99 raw_string in the history and starts a new prompt.
99 raw_string in the history and starts a new prompt.
100 """
100 """
101 if raw_string is None:
101 if raw_string is None:
102 raw_string = string
102 raw_string = string
103 # Create a false result, in case there is an exception
103 # Create a false result, in case there is an exception
104 result = dict(number=self.prompt_number)
104 self.last_result = dict(number=self.prompt_number)
105 try:
105 try:
106 self.history.input_cache[-1] = raw_string
106 self.history.input_cache[-1] = raw_string
107 result = self.shell.execute(python_string)
107 result = self.shell.execute(python_string)
108 self.last_result = result
108 self.render_result(result)
109 self.render_result(result)
109 except Exception, e:
110 except:
110 self.show_traceback()
111 self.show_traceback()
111 finally:
112 finally:
112 self.prompt_number += 1
113 self.after_execute()
113 self.new_prompt(self.prompt % (result['number'] + 1))
114
114 # Start a new empty history entry
115
115 self._add_history(None, '')
116 def after_execute(self):
116 # The result contains useful information that can be used
117 """ All the operations required after an execution to put the
117 # elsewhere.
118 terminal back in a shape where it is usable.
118 self.last_result = result
119 """
120 self.prompt_number += 1
121 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
122 # Start a new empty history entry
123 self._add_history(None, '')
124 # The result contains useful information that can be used
125 # elsewhere.
119
126
120
127
121 def _on_enter(self):
128 def _on_enter(self):
122 """ Called when the return key is pressed in a line editing
129 """ Called when the return key is pressed in a line editing
123 buffer.
130 buffer.
124 """
131 """
125 current_buffer = self.get_current_edit_buffer()
132 current_buffer = self.get_current_edit_buffer()
126 cleaned_buffer = self.prefilter_input(current_buffer)
133 cleaned_buffer = self.prefilter_input(current_buffer)
127 if self.is_complete(cleaned_buffer):
134 if self.is_complete(cleaned_buffer + '\n'):
135 # The '\n' is important in case prefiltering empties the
136 # line, to get a new prompt.
128 self.execute(cleaned_buffer, raw_string=current_buffer)
137 self.execute(cleaned_buffer, raw_string=current_buffer)
129 else:
138 else:
130 if len(current_buffer.split('\n'))>1:
139 if len(current_buffer.split('\n'))>1:
131 self.write('\n' + self._get_indent_string(current_buffer))
140 self.write(self._get_indent_string(current_buffer))
132 else:
141 else:
133 self.write('\n\t')
142 self.write('\t')
134
143
135
144
136 #--------------------------------------------------------------------------
145 #--------------------------------------------------------------------------
137 # Private API
146 # Private API
138 #--------------------------------------------------------------------------
147 #--------------------------------------------------------------------------
139
148
140 def _get_indent_string(self, string):
149 def _get_indent_string(self, string):
141 print >>sys.__stderr__, string.split('\n')
150 string = string.replace('\t', ' '*4)
142 string = string.split('\n')[-1]
151 string = string.split('\n')[-1]
143 indent_chars = len(string) - len(string.lstrip())
152 indent_chars = len(string) - len(string.lstrip())
144 indent_string = '\t'*(indent_chars // 4) + \
153 indent_string = '\t'*(indent_chars // 4) + \
145 ' '*(indent_chars % 4)
154 ' '*(indent_chars % 4)
146
155
147 return indent_string
156 return indent_string
148
157
149
158
@@ -1,410 +1,411 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A Wx widget to act as a console and input commands.
3 A Wx widget to act as a console and input commands.
4
4
5 This widget deals with prompts and provides an edit buffer
5 This widget deals with prompts and provides an edit buffer
6 restricted to after the last prompt.
6 restricted to after the last prompt.
7 """
7 """
8
8
9 __docformat__ = "restructuredtext en"
9 __docformat__ = "restructuredtext en"
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 The IPython Development Team
12 # Copyright (C) 2008 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is
14 # Distributed under the terms of the BSD License. The full license is
15 # in the file COPYING, distributed as part of this software.
15 # in the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 import wx
22 import wx
23 import wx.stc as stc
23 import wx.stc as stc
24
24
25 from wx.py import editwindow
25 from wx.py import editwindow
26
26
27 import re
27 import re
28
28
29 # FIXME: Need to provide an API for non user-generated display on the
29 # FIXME: Need to provide an API for non user-generated display on the
30 # screen: this should not be editable by the user.
30 # screen: this should not be editable by the user.
31
31
32 if wx.Platform == '__WXMSW__':
32 if wx.Platform == '__WXMSW__':
33 _DEFAULT_SIZE = 80
33 _DEFAULT_SIZE = 80
34 else:
34 else:
35 _DEFAULT_SIZE = 10
35 _DEFAULT_SIZE = 10
36
36
37 _DEFAULT_STYLE = {
37 _DEFAULT_STYLE = {
38 'stdout' : 'fore:#0000FF',
38 'stdout' : 'fore:#0000FF',
39 'stderr' : 'fore:#007f00',
39 'stderr' : 'fore:#007f00',
40 'trace' : 'fore:#FF0000',
40 'trace' : 'fore:#FF0000',
41
41
42 'default' : 'size:%d' % _DEFAULT_SIZE,
42 'default' : 'size:%d' % _DEFAULT_SIZE,
43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45
45
46 # properties for the various Python lexer styles
46 # properties for the various Python lexer styles
47 'comment' : 'fore:#007F00',
47 'comment' : 'fore:#007F00',
48 'number' : 'fore:#007F7F',
48 'number' : 'fore:#007F7F',
49 'string' : 'fore:#7F007F,italic',
49 'string' : 'fore:#7F007F,italic',
50 'char' : 'fore:#7F007F,italic',
50 'char' : 'fore:#7F007F,italic',
51 'keyword' : 'fore:#00007F,bold',
51 'keyword' : 'fore:#00007F,bold',
52 'triple' : 'fore:#7F0000',
52 'triple' : 'fore:#7F0000',
53 'tripledouble': 'fore:#7F0000',
53 'tripledouble': 'fore:#7F0000',
54 'class' : 'fore:#0000FF,bold,underline',
54 'class' : 'fore:#0000FF,bold,underline',
55 'def' : 'fore:#007F7F,bold',
55 'def' : 'fore:#007F7F,bold',
56 'operator' : 'bold',
56 'operator' : 'bold',
57
57
58 }
58 }
59
59
60 # new style numbers
60 # new style numbers
61 _STDOUT_STYLE = 15
61 _STDOUT_STYLE = 15
62 _STDERR_STYLE = 16
62 _STDERR_STYLE = 16
63 _TRACE_STYLE = 17
63 _TRACE_STYLE = 17
64
64
65
65
66 #-------------------------------------------------------------------------------
66 #-------------------------------------------------------------------------------
67 # The console widget class
67 # The console widget class
68 #-------------------------------------------------------------------------------
68 #-------------------------------------------------------------------------------
69 class ConsoleWidget(editwindow.EditWindow):
69 class ConsoleWidget(editwindow.EditWindow):
70 """ Specialized styled text control view for console-like workflow.
70 """ Specialized styled text control view for console-like workflow.
71
71
72 This widget is mainly interested in dealing with the prompt and
72 This widget is mainly interested in dealing with the prompt and
73 keeping the cursor inside the editing line.
73 keeping the cursor inside the editing line.
74 """
74 """
75
75
76 title = 'Console'
76 title = 'Console'
77
77
78 style = _DEFAULT_STYLE.copy()
78 style = _DEFAULT_STYLE.copy()
79
79
80 # Translation table from ANSI escape sequences to color. Override
80 # Translation table from ANSI escape sequences to color. Override
81 # this to specify your colors.
81 # this to specify your colors.
82 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
82 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
83 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
83 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
84 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
84 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
85 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
85 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
86 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
86 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
87 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
87 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
88 '1;34': [12, 'LIGHT BLUE'], '1;35':
88 '1;34': [12, 'LIGHT BLUE'], '1;35':
89 [13, 'MEDIUM VIOLET RED'],
89 [13, 'MEDIUM VIOLET RED'],
90 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
90 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
91
91
92 # The color of the carret (call _apply_style() after setting)
92 # The color of the carret (call _apply_style() after setting)
93 carret_color = 'BLACK'
93 carret_color = 'BLACK'
94
94
95
95
96 #--------------------------------------------------------------------------
96 #--------------------------------------------------------------------------
97 # Public API
97 # Public API
98 #--------------------------------------------------------------------------
98 #--------------------------------------------------------------------------
99
99
100 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
100 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
101 size=wx.DefaultSize, style=0,
101 size=wx.DefaultSize, style=0,
102 autocomplete_mode='IPYTHON'):
102 autocomplete_mode='IPYTHON'):
103 """ Autocomplete_mode: Can be 'IPYTHON' or 'STC'
103 """ Autocomplete_mode: Can be 'IPYTHON' or 'STC'
104 'IPYTHON' show autocompletion the ipython way
104 'IPYTHON' show autocompletion the ipython way
105 'STC" show it scintilla text control way
105 'STC" show it scintilla text control way
106 """
106 """
107 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
107 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
108 self.configure_scintilla()
108 self.configure_scintilla()
109
109
110 # FIXME: we need to retrieve this from the interpreter.
110 # FIXME: we need to retrieve this from the interpreter.
111 self.prompt = \
111 self.prompt = \
112 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
112 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
113 self.new_prompt(self.prompt % 1)
113 self.new_prompt(self.prompt % 1)
114
114
115 self.autocomplete_mode = autocomplete_mode
115 self.autocomplete_mode = autocomplete_mode
116
116
117 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
117 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
118 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
118 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
119
119
120
120
121 def configure_scintilla(self):
121 def configure_scintilla(self):
122 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
122 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
123 # the widget
123 # the widget
124 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
124 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
125 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
125 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
126 # Also allow Ctrl Shift "=" for poor non US keyboard users.
126 # Also allow Ctrl Shift "=" for poor non US keyboard users.
127 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
127 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
128 stc.STC_CMD_ZOOMIN)
128 stc.STC_CMD_ZOOMIN)
129
129
130 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
130 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
131 # stc.STC_CMD_PAGEUP)
131 # stc.STC_CMD_PAGEUP)
132
132
133 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
133 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
134 # stc.STC_CMD_PAGEDOWN)
134 # stc.STC_CMD_PAGEDOWN)
135
135
136 # Keys: we need to clear some of the keys the that don't play
136 # Keys: we need to clear some of the keys the that don't play
137 # well with a console.
137 # well with a console.
138 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
138 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
139 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
139 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
140 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
140 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
141
141
142
142
143 self.SetEOLMode(stc.STC_EOL_CRLF)
143 self.SetEOLMode(stc.STC_EOL_CRLF)
144 self.SetWrapMode(stc.STC_WRAP_CHAR)
144 self.SetWrapMode(stc.STC_WRAP_CHAR)
145 self.SetWrapMode(stc.STC_WRAP_WORD)
145 self.SetWrapMode(stc.STC_WRAP_WORD)
146 self.SetBufferedDraw(True)
146 self.SetBufferedDraw(True)
147 self.SetUseAntiAliasing(True)
147 self.SetUseAntiAliasing(True)
148 self.SetLayoutCache(stc.STC_CACHE_PAGE)
148 self.SetLayoutCache(stc.STC_CACHE_PAGE)
149 self.SetUndoCollection(False)
149 self.SetUndoCollection(False)
150 self.SetUseTabs(True)
150 self.SetUseTabs(True)
151 self.SetIndent(4)
151 self.SetIndent(4)
152 self.SetTabWidth(4)
152 self.SetTabWidth(4)
153
153
154 self.EnsureCaretVisible()
154 self.EnsureCaretVisible()
155
155
156 self.SetMargins(3, 3) #text is moved away from border with 3px
156 self.SetMargins(3, 3) #text is moved away from border with 3px
157 # Suppressing Scintilla margins
157 # Suppressing Scintilla margins
158 self.SetMarginWidth(0, 0)
158 self.SetMarginWidth(0, 0)
159 self.SetMarginWidth(1, 0)
159 self.SetMarginWidth(1, 0)
160 self.SetMarginWidth(2, 0)
160 self.SetMarginWidth(2, 0)
161
161
162 self._apply_style()
162 self._apply_style()
163
163
164 # Xterm escape sequences
164 # Xterm escape sequences
165 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
165 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
166 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
166 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
167
167
168 #self.SetEdgeMode(stc.STC_EDGE_LINE)
168 #self.SetEdgeMode(stc.STC_EDGE_LINE)
169 #self.SetEdgeColumn(80)
169 #self.SetEdgeColumn(80)
170
170
171 # styles
171 # styles
172 p = self.style
172 p = self.style
173 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
173 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
174 self.StyleClearAll()
174 self.StyleClearAll()
175 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
175 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
176 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
176 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
177 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
177 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
178
178
179 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
179 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
180 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
180 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
181 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
181 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
182 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
182 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
183 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
183 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
184 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
184 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
185 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
185 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
186 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
186 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
187 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
187 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
188 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
188 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
189 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
189 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
190 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
190 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
191 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
191 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
192
192
193
193
194 def write(self, text):
194 def write(self, text):
195 """ Write given text to buffer, while translating the ansi escape
195 """ Write given text to buffer, while translating the ansi escape
196 sequences.
196 sequences.
197 """
197 """
198 title = self.title_pat.split(text)
198 title = self.title_pat.split(text)
199 if len(title)>0:
199 if len(title)>0:
200 self.title = title[-1]
200 self.title = title[-1]
201
201
202 text = self.title_pat.sub('', text)
202 text = self.title_pat.sub('', text)
203 segments = self.color_pat.split(text)
203 segments = self.color_pat.split(text)
204 segment = segments.pop(0)
204 segment = segments.pop(0)
205 self.StartStyling(self.GetLength(), 0xFF)
205 self.StartStyling(self.GetLength(), 0xFF)
206 self.AppendText(segment)
206 self.AppendText(segment)
207
207
208 if segments:
208 if segments:
209 ansi_tags = self.color_pat.findall(text)
209 ansi_tags = self.color_pat.findall(text)
210
210
211 for tag in ansi_tags:
211 for tag in ansi_tags:
212 i = segments.index(tag)
212 i = segments.index(tag)
213 self.StartStyling(self.GetLength(), 0xFF)
213 self.StartStyling(self.GetLength(), 0xFF)
214 self.AppendText(segments[i+1])
214 self.AppendText(segments[i+1])
215
215
216 if tag != '0':
216 if tag != '0':
217 self.SetStyling(len(segments[i+1]),
217 self.SetStyling(len(segments[i+1]),
218 self.ANSI_STYLES[tag][0])
218 self.ANSI_STYLES[tag][0])
219
219
220 segments.pop(i)
220 segments.pop(i)
221
221
222 self.GotoPos(self.GetLength())
222 self.GotoPos(self.GetLength())
223
223
224
224
225 def new_prompt(self, prompt):
225 def new_prompt(self, prompt):
226 """ Prints a prompt at start of line, and move the start of the
226 """ Prints a prompt at start of line, and move the start of the
227 current block there.
227 current block there.
228
228
229 The prompt can be give with ascii escape sequences.
229 The prompt can be give with ascii escape sequences.
230 """
230 """
231 self.write(prompt)
231 self.write(prompt)
232 # now we update our cursor giving end of prompt
232 # now we update our cursor giving end of prompt
233 self.current_prompt_pos = self.GetLength()
233 self.current_prompt_pos = self.GetLength()
234 self.current_prompt_line = self.GetCurrentLine()
234 self.current_prompt_line = self.GetCurrentLine()
235 wx.Yield()
235 wx.Yield()
236 self.EnsureCaretVisible()
236 self.EnsureCaretVisible()
237
237
238
238
239 def replace_current_edit_buffer(self, text):
239 def replace_current_edit_buffer(self, text):
240 """ Replace currently entered command line with given text.
240 """ Replace currently entered command line with given text.
241 """
241 """
242 self.SetSelection(self.current_prompt_pos, self.GetLength())
242 self.SetSelection(self.current_prompt_pos, self.GetLength())
243 self.ReplaceSelection(text)
243 self.ReplaceSelection(text)
244 self.GotoPos(self.GetLength())
244 self.GotoPos(self.GetLength())
245
245
246
246
247 def get_current_edit_buffer(self):
247 def get_current_edit_buffer(self):
248 """ Returns the text in current edit buffer.
248 """ Returns the text in current edit buffer.
249 """
249 """
250 return self.GetTextRange(self.current_prompt_pos,
250 return self.GetTextRange(self.current_prompt_pos,
251 self.GetLength())
251 self.GetLength())
252
252
253
253
254 #--------------------------------------------------------------------------
254 #--------------------------------------------------------------------------
255 # Private API
255 # Private API
256 #--------------------------------------------------------------------------
256 #--------------------------------------------------------------------------
257
257
258 def _apply_style(self):
258 def _apply_style(self):
259 """ Applies the colors for the different text elements and the
259 """ Applies the colors for the different text elements and the
260 carret.
260 carret.
261 """
261 """
262 self.SetCaretForeground(self.carret_color)
262 self.SetCaretForeground(self.carret_color)
263
263
264 self.StyleClearAll()
264 self.StyleClearAll()
265 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
265 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
266 "fore:#FF0000,back:#0000FF,bold")
266 "fore:#FF0000,back:#0000FF,bold")
267 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
267 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
268 "fore:#000000,back:#FF0000,bold")
268 "fore:#000000,back:#FF0000,bold")
269
269
270 for style in self.ANSI_STYLES.values():
270 for style in self.ANSI_STYLES.values():
271 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
271 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
272
272
273
273
274 def writeCompletion(self, possibilities):
274 def writeCompletion(self, possibilities):
275 if self.autocomplete_mode == 'IPYTHON':
275 if self.autocomplete_mode == 'IPYTHON':
276 max_len = len(max(possibilities, key=len))
276 max_len = len(max(possibilities, key=len))
277 max_symbol = ' '*max_len
277 max_symbol = ' '*max_len
278
278
279 #now we check how much symbol we can put on a line...
279 #now we check how much symbol we can put on a line...
280 test_buffer = max_symbol + ' '*4
280 test_buffer = max_symbol + ' '*4
281
281
282 allowed_symbols = 80/len(test_buffer)
282 allowed_symbols = 80/len(test_buffer)
283 if allowed_symbols == 0:
283 if allowed_symbols == 0:
284 allowed_symbols = 1
284 allowed_symbols = 1
285
285
286 pos = 1
286 pos = 1
287 buf = ''
287 buf = ''
288 for symbol in possibilities:
288 for symbol in possibilities:
289 #buf += symbol+'\n'#*spaces)
289 #buf += symbol+'\n'#*spaces)
290 if pos < allowed_symbols:
290 if pos < allowed_symbols:
291 spaces = max_len - len(symbol) + 4
291 spaces = max_len - len(symbol) + 4
292 buf += symbol+' '*spaces
292 buf += symbol+' '*spaces
293 pos += 1
293 pos += 1
294 else:
294 else:
295 buf += symbol+'\n'
295 buf += symbol+'\n'
296 pos = 1
296 pos = 1
297 self.write(buf)
297 self.write(buf)
298 else:
298 else:
299 possibilities.sort() # Python sorts are case sensitive
299 possibilities.sort() # Python sorts are case sensitive
300 self.AutoCompSetIgnoreCase(False)
300 self.AutoCompSetIgnoreCase(False)
301 self.AutoCompSetAutoHide(False)
301 self.AutoCompSetAutoHide(False)
302 #let compute the length ot text)last word
302 #let compute the length ot text)last word
303 splitter = [' ', '(', '[', '{']
303 splitter = [' ', '(', '[', '{']
304 last_word = self.get_current_edit_buffer()
304 last_word = self.get_current_edit_buffer()
305 for breaker in splitter:
305 for breaker in splitter:
306 last_word = last_word.split(breaker)[-1]
306 last_word = last_word.split(breaker)[-1]
307 self.AutoCompShow(len(last_word), " ".join(possibilities))
307 self.AutoCompShow(len(last_word), " ".join(possibilities))
308
308
309
309
310 def scroll_to_bottom(self):
310 def scroll_to_bottom(self):
311 maxrange = self.GetScrollRange(wx.VERTICAL)
311 maxrange = self.GetScrollRange(wx.VERTICAL)
312 self.ScrollLines(maxrange)
312 self.ScrollLines(maxrange)
313
313
314
314
315 def _on_enter(self):
315 def _on_enter(self):
316 """ Called when the return key is hit.
316 """ Called when the return key is hit.
317 """
317 """
318 pass
318 pass
319
319
320
320
321 def _on_key_down(self, event, skip=True):
321 def _on_key_down(self, event, skip=True):
322 """ Key press callback used for correcting behavior for
322 """ Key press callback used for correcting behavior for
323 console-like interfaces: the cursor is constraint to be after
323 console-like interfaces: the cursor is constraint to be after
324 the last prompt.
324 the last prompt.
325
325
326 Return True if event as been catched.
326 Return True if event as been catched.
327 """
327 """
328 catched = False
328 catched = False
329 # Intercept some specific keys.
329 # Intercept some specific keys.
330 if event.KeyCode == ord('L') and event.ControlDown() :
330 if event.KeyCode == ord('L') and event.ControlDown() :
331 catched = True
331 catched = True
332 self.scroll_to_bottom()
332 self.scroll_to_bottom()
333 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
333 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
334 catched = True
334 catched = True
335 self.ScrollPages(-1)
335 self.ScrollPages(-1)
336 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
336 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
337 catched = True
337 catched = True
338 self.ScrollPages(1)
338 self.ScrollPages(1)
339
339
340 if self.AutoCompActive():
340 if self.AutoCompActive():
341 event.Skip()
341 event.Skip()
342 else:
342 else:
343 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
343 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
344 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
344 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
345 catched = True
345 catched = True
346 self.write('\n')
346 self._on_enter()
347 self._on_enter()
347
348
348 elif event.KeyCode == wx.WXK_HOME:
349 elif event.KeyCode == wx.WXK_HOME:
349 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
350 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
350 self.GotoPos(self.current_prompt_pos)
351 self.GotoPos(self.current_prompt_pos)
351 catched = True
352 catched = True
352
353
353 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
354 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
354 # FIXME: This behavior is not ideal: if the selection
355 # FIXME: This behavior is not ideal: if the selection
355 # is already started, it will jump.
356 # is already started, it will jump.
356 self.SetSelectionStart(self.current_prompt_pos)
357 self.SetSelectionStart(self.current_prompt_pos)
357 self.SetSelectionEnd(self.GetCurrentPos())
358 self.SetSelectionEnd(self.GetCurrentPos())
358 catched = True
359 catched = True
359
360
360 elif event.KeyCode == wx.WXK_UP:
361 elif event.KeyCode == wx.WXK_UP:
361 if self.GetCurrentLine() > self.current_prompt_line:
362 if self.GetCurrentLine() > self.current_prompt_line:
362 if self.GetCurrentLine() == self.current_prompt_line + 1 \
363 if self.GetCurrentLine() == self.current_prompt_line + 1 \
363 and self.GetColumn(self.GetCurrentPos()) < \
364 and self.GetColumn(self.GetCurrentPos()) < \
364 self.GetColumn(self.current_prompt_pos):
365 self.GetColumn(self.current_prompt_pos):
365 self.GotoPos(self.current_prompt_pos)
366 self.GotoPos(self.current_prompt_pos)
366 else:
367 else:
367 event.Skip()
368 event.Skip()
368 catched = True
369 catched = True
369
370
370 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
371 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
371 if self.GetCurrentPos() > self.current_prompt_pos:
372 if self.GetCurrentPos() > self.current_prompt_pos:
372 event.Skip()
373 event.Skip()
373 catched = True
374 catched = True
374
375
375 if skip and not catched:
376 if skip and not catched:
376 event.Skip()
377 event.Skip()
377
378
378 return catched
379 return catched
379
380
380
381
381 def _on_key_up(self, event, skip=True):
382 def _on_key_up(self, event, skip=True):
382 """ If cursor is outside the editing region, put it back.
383 """ If cursor is outside the editing region, put it back.
383 """
384 """
384 event.Skip()
385 event.Skip()
385 if self.GetCurrentPos() < self.current_prompt_pos:
386 if self.GetCurrentPos() < self.current_prompt_pos:
386 self.GotoPos(self.current_prompt_pos)
387 self.GotoPos(self.current_prompt_pos)
387
388
388
389
389
390
390
391
391 if __name__ == '__main__':
392 if __name__ == '__main__':
392 # Some simple code to test the console widget.
393 # Some simple code to test the console widget.
393 class MainWindow(wx.Frame):
394 class MainWindow(wx.Frame):
394 def __init__(self, parent, id, title):
395 def __init__(self, parent, id, title):
395 wx.Frame.__init__(self, parent, id, title, size=(300,250))
396 wx.Frame.__init__(self, parent, id, title, size=(300,250))
396 self._sizer = wx.BoxSizer(wx.VERTICAL)
397 self._sizer = wx.BoxSizer(wx.VERTICAL)
397 self.console_widget = ConsoleWidget(self)
398 self.console_widget = ConsoleWidget(self)
398 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
399 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
399 self.SetSizer(self._sizer)
400 self.SetSizer(self._sizer)
400 self.SetAutoLayout(1)
401 self.SetAutoLayout(1)
401 self.Show(True)
402 self.Show(True)
402
403
403 app = wx.PySimpleApp()
404 app = wx.PySimpleApp()
404 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
405 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
405 w.SetSize((780, 460))
406 w.SetSize((780, 460))
406 w.Show()
407 w.Show()
407
408
408 app.MainLoop()
409 app.MainLoop()
409
410
410
411
@@ -1,106 +1,106 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.cocoa.tests.test_cocoa_frontend -*-
3 # ipython1.frontend.cocoa.tests.test_cocoa_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 """
8 """
9
9
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
11
11
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
13 # Copyright (C) 2008 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22
22
23
23
24 import wx
24 import wx
25 from console_widget import ConsoleWidget
25 from console_widget import ConsoleWidget
26
26
27 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
27 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
28
28
29 #-------------------------------------------------------------------------------
29 #-------------------------------------------------------------------------------
30 # Classes to implement the Wx frontend
30 # Classes to implement the Wx frontend
31 #-------------------------------------------------------------------------------
31 #-------------------------------------------------------------------------------
32 class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
32 class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
33
33
34 output_prompt = \
34 output_prompt = \
35 '\n\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
35 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
36
36
37 #--------------------------------------------------------------------------
37 #--------------------------------------------------------------------------
38 # Public API
38 # Public API
39 #--------------------------------------------------------------------------
39 #--------------------------------------------------------------------------
40
40
41 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
41 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
42 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
42 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
43 *args, **kwds):
43 *args, **kwds):
44 """ Create Shell instance.
44 """ Create Shell instance.
45 """
45 """
46 ConsoleWidget.__init__(self, parent, id, pos, size, style)
46 ConsoleWidget.__init__(self, parent, id, pos, size, style)
47 PrefilterFrontEnd.__init__(self)
47 PrefilterFrontEnd.__init__(self)
48
48
49 # Capture Character keys
49 # Capture Character keys
50 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
50 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
51
51
52 #--------------------------------------------------------------------------
52 #--------------------------------------------------------------------------
53 # Private API
53 # Private API
54 #--------------------------------------------------------------------------
54 #--------------------------------------------------------------------------
55
55
56
56
57 def _on_key_down(self, event, skip=True):
57 def _on_key_down(self, event, skip=True):
58 """ Capture the character events, let the parent
58 """ Capture the character events, let the parent
59 widget handle them, and put our logic afterward.
59 widget handle them, and put our logic afterward.
60 """
60 """
61 current_line_number = self.GetCurrentLine()
61 current_line_number = self.GetCurrentLine()
62 # Up history
62 # Up history
63 if event.KeyCode == wx.WXK_UP and (
63 if event.KeyCode == wx.WXK_UP and (
64 ( current_line_number == self.current_prompt_line and
64 ( current_line_number == self.current_prompt_line and
65 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
65 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
66 or event.ControlDown() ):
66 or event.ControlDown() ):
67 new_buffer = self.get_history_previous(
67 new_buffer = self.get_history_previous(
68 self.get_current_edit_buffer())
68 self.get_current_edit_buffer())
69 if new_buffer is not None:
69 if new_buffer is not None:
70 self.replace_current_edit_buffer(new_buffer)
70 self.replace_current_edit_buffer(new_buffer)
71 if self.GetCurrentLine() > self.current_prompt_line:
71 if self.GetCurrentLine() > self.current_prompt_line:
72 # Go to first line, for seemless history up.
72 # Go to first line, for seemless history up.
73 self.GotoPos(self.current_prompt_pos)
73 self.GotoPos(self.current_prompt_pos)
74 # Down history
74 # Down history
75 elif event.KeyCode == wx.WXK_DOWN and (
75 elif event.KeyCode == wx.WXK_DOWN and (
76 ( current_line_number == self.LineCount -1 and
76 ( current_line_number == self.LineCount -1 and
77 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
77 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
78 or event.ControlDown() ):
78 or event.ControlDown() ):
79 new_buffer = self.get_history_next()
79 new_buffer = self.get_history_next()
80 if new_buffer is not None:
80 if new_buffer is not None:
81 self.replace_current_edit_buffer(new_buffer)
81 self.replace_current_edit_buffer(new_buffer)
82 else:
82 else:
83 ConsoleWidget._on_key_down(self, event, skip=skip)
83 ConsoleWidget._on_key_down(self, event, skip=skip)
84
84
85
85
86
86
87
87
88 if __name__ == '__main__':
88 if __name__ == '__main__':
89 class MainWindow(wx.Frame):
89 class MainWindow(wx.Frame):
90 def __init__(self, parent, id, title):
90 def __init__(self, parent, id, title):
91 wx.Frame.__init__(self, parent, id, title, size=(300,250))
91 wx.Frame.__init__(self, parent, id, title, size=(300,250))
92 self._sizer = wx.BoxSizer(wx.VERTICAL)
92 self._sizer = wx.BoxSizer(wx.VERTICAL)
93 self.shell = IPythonWxController(self)
93 self.shell = IPythonWxController(self)
94 self._sizer.Add(self.shell, 1, wx.EXPAND)
94 self._sizer.Add(self.shell, 1, wx.EXPAND)
95 self.SetSizer(self._sizer)
95 self.SetSizer(self._sizer)
96 self.SetAutoLayout(1)
96 self.SetAutoLayout(1)
97 self.Show(True)
97 self.Show(True)
98
98
99 app = wx.PySimpleApp()
99 app = wx.PySimpleApp()
100 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
100 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
101 frame.shell.SetFocus()
101 frame.shell.SetFocus()
102 frame.SetSize((660, 460))
102 frame.SetSize((660, 460))
103 self = frame.shell
103 self = frame.shell
104
104
105 app.MainLoop()
105 app.MainLoop()
106
106
General Comments 0
You need to be logged in to leave comments. Login now