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