##// END OF EJS Templates
Rework multiline input
Gael Varoquaux -
Show More
@@ -1,152 +1,149 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 # Are we entering multi line input?
32 multi_line_input = False
33
34 # 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
35 # it when there is an exception.
32 # it when there is an exception.
36 prompt_number = 1
33 prompt_number = 1
37
34
38
35
39 #--------------------------------------------------------------------------
36 #--------------------------------------------------------------------------
40 # Public API
37 # Public API
41 #--------------------------------------------------------------------------
38 #--------------------------------------------------------------------------
42
39
43 def __init__(self, shell=None, history=None):
40 def __init__(self, shell=None, history=None):
44 if shell is None:
41 if shell is None:
45 shell = Interpreter()
42 shell = Interpreter()
46 FrontEndBase.__init__(self, shell=shell, history=history)
43 FrontEndBase.__init__(self, shell=shell, history=history)
47
44
48 #FIXME: print banner.
45 #FIXME: print banner.
49 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
46 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
50 % IPython.__version__
47 % IPython.__version__
51
48
52
49
53 def complete(self, token):
50 def complete(self, token):
54 """Complete token in engine's user_ns
51 """Complete token in engine's user_ns
55
52
56 Parameters
53 Parameters
57 ----------
54 ----------
58 token : string
55 token : string
59
56
60 Result
57 Result
61 ------
58 ------
62 Deferred result of
59 Deferred result of
63 IPython.kernel.engineservice.IEngineBase.complete
60 IPython.kernel.engineservice.IEngineBase.complete
64 """
61 """
65
62
66 return self.shell.complete(token)
63 return self.shell.complete(token)
67
64
68
65
69 def render_result(self, result):
66 def render_result(self, result):
70 if 'stdout' in result and result['stdout']:
67 if 'stdout' in result and result['stdout']:
71 self.write('\n' + result['stdout'])
68 self.write('\n' + result['stdout'])
72 if 'display' in result and result['display']:
69 if 'display' in result and result['display']:
73 self.write("%s%s\n" % (
70 self.write("%s%s\n" % (
74 self.output_prompt % result['number'],
71 self.output_prompt % result['number'],
75 result['display']['pprint']
72 result['display']['pprint']
76 ) )
73 ) )
77
74
78
75
79 def render_error(self, failure):
76 def render_error(self, failure):
80 self.insert_text('\n\n'+str(failure)+'\n\n')
77 self.insert_text('\n\n'+str(failure)+'\n\n')
81 return failure
78 return failure
82
79
83
80
84 def prefilter_input(self, string):
81 def prefilter_input(self, string):
85 string = string.replace('\r\n', '\n')
82 string = string.replace('\r\n', '\n')
86 string = string.replace('\t', 4*' ')
83 string = string.replace('\t', 4*' ')
87 # Clean the trailing whitespace
84 # Clean the trailing whitespace
88 string = '\n'.join(l.rstrip() for l in string.split('\n'))
85 string = '\n'.join(l.rstrip() for l in string.split('\n'))
89 return string
86 return string
90
87
91
88
92 def is_complete(self, string):
89 def is_complete(self, string):
93 if ( self.multi_line_input and not re.findall(r"\n[\t ]*$", string)):
90 if ( len(self.get_current_edit_buffer().split('\n'))>1
91 and not re.findall(r"\n[\t ]*$", string)):
94 return False
92 return False
95 else:
93 else:
96 return FrontEndBase.is_complete(self, string)
94 return FrontEndBase.is_complete(self, string)
97
95
98
96
99 def execute(self, python_string, raw_string=None):
97 def execute(self, python_string, raw_string=None):
100 """ Send the python_string to the interpreter, stores the
98 """ Send the python_string to the interpreter, stores the
101 raw_string in the history and starts a new prompt.
99 raw_string in the history and starts a new prompt.
102 """
100 """
103 if raw_string is None:
101 if raw_string is None:
104 raw_string = string
102 raw_string = string
105 # Create a false result, in case there is an exception
103 # Create a false result, in case there is an exception
106 result = dict(number=self.prompt_number)
104 result = dict(number=self.prompt_number)
107 try:
105 try:
108 self.history.input_cache[-1] = raw_string
106 self.history.input_cache[-1] = raw_string
109 result = self.shell.execute(python_string)
107 result = self.shell.execute(python_string)
110 self.render_result(result)
108 self.render_result(result)
111 except Exception, e:
109 except Exception, e:
112 self.show_traceback()
110 self.show_traceback()
113 finally:
111 finally:
114 self.prompt_number += 1
112 self.prompt_number += 1
115 self.new_prompt(self.prompt % (result['number'] + 1))
113 self.new_prompt(self.prompt % (result['number'] + 1))
116 self.multi_line_input = False
117 # Start a new empty history entry
114 # Start a new empty history entry
118 self._add_history(None, '')
115 self._add_history(None, '')
119 # The result contains useful information that can be used
116 # The result contains useful information that can be used
120 # elsewhere.
117 # elsewhere.
121 self.last_result = result
118 self.last_result = result
122
119
123
120
124 def _on_enter(self):
121 def _on_enter(self):
125 """ Called when the return key is pressed in a line editing
122 """ Called when the return key is pressed in a line editing
126 buffer.
123 buffer.
127 """
124 """
128 current_buffer = self.get_current_edit_buffer()
125 current_buffer = self.get_current_edit_buffer()
129 cleaned_buffer = self.prefilter_input(current_buffer)
126 cleaned_buffer = self.prefilter_input(current_buffer)
130 if self.is_complete(cleaned_buffer):
127 if self.is_complete(cleaned_buffer):
131 self.execute(cleaned_buffer, raw_string=current_buffer)
128 self.execute(cleaned_buffer, raw_string=current_buffer)
132 else:
129 else:
133 if self.multi_line_input:
130 if len(current_buffer.split('\n'))>1:
134 self.write('\n' + self._get_indent_string(current_buffer))
131 self.write('\n' + self._get_indent_string(current_buffer))
135 else:
132 else:
136 self.multi_line_input = True
137 self.write('\n\t')
133 self.write('\n\t')
138
134
139
135
140 #--------------------------------------------------------------------------
136 #--------------------------------------------------------------------------
141 # Private API
137 # Private API
142 #--------------------------------------------------------------------------
138 #--------------------------------------------------------------------------
143
139
144 def _get_indent_string(self, string):
140 def _get_indent_string(self, string):
141 print >>sys.__stderr__, string.split('\n')
145 string = string.split('\n')[-1]
142 string = string.split('\n')[-1]
146 indent_chars = len(string) - len(string.lstrip())
143 indent_chars = len(string) - len(string.lstrip())
147 indent_string = '\t'*(indent_chars // 4) + \
144 indent_string = '\t'*(indent_chars // 4) + \
148 ' '*(indent_chars % 4)
145 ' '*(indent_chars % 4)
149
146
150 return indent_string
147 return indent_string
151
148
152
149
@@ -1,407 +1,410 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
235 wx.Yield()
236 self.EnsureCaretVisible()
237
236
238
237 def replace_current_edit_buffer(self, text):
239 def replace_current_edit_buffer(self, text):
238 """ Replace currently entered command line with given text.
240 """ Replace currently entered command line with given text.
239 """
241 """
240 self.SetSelection(self.current_prompt_pos, self.GetLength())
242 self.SetSelection(self.current_prompt_pos, self.GetLength())
241 self.ReplaceSelection(text)
243 self.ReplaceSelection(text)
242 self.GotoPos(self.GetLength())
244 self.GotoPos(self.GetLength())
243
245
244
246
245 def get_current_edit_buffer(self):
247 def get_current_edit_buffer(self):
246 """ Returns the text in current edit buffer.
248 """ Returns the text in current edit buffer.
247 """
249 """
248 return self.GetTextRange(self.current_prompt_pos,
250 return self.GetTextRange(self.current_prompt_pos,
249 self.GetLength())
251 self.GetLength())
250
252
251
253
252 #--------------------------------------------------------------------------
254 #--------------------------------------------------------------------------
253 # Private API
255 # Private API
254 #--------------------------------------------------------------------------
256 #--------------------------------------------------------------------------
255
257
256 def _apply_style(self):
258 def _apply_style(self):
257 """ Applies the colors for the different text elements and the
259 """ Applies the colors for the different text elements and the
258 carret.
260 carret.
259 """
261 """
260 self.SetCaretForeground(self.carret_color)
262 self.SetCaretForeground(self.carret_color)
261
263
262 self.StyleClearAll()
264 self.StyleClearAll()
263 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
265 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
264 "fore:#FF0000,back:#0000FF,bold")
266 "fore:#FF0000,back:#0000FF,bold")
265 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
267 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
266 "fore:#000000,back:#FF0000,bold")
268 "fore:#000000,back:#FF0000,bold")
267
269
268 for style in self.ANSI_STYLES.values():
270 for style in self.ANSI_STYLES.values():
269 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
271 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
270
272
271
273
272 def writeCompletion(self, possibilities):
274 def writeCompletion(self, possibilities):
273 if self.autocomplete_mode == 'IPYTHON':
275 if self.autocomplete_mode == 'IPYTHON':
274 max_len = len(max(possibilities, key=len))
276 max_len = len(max(possibilities, key=len))
275 max_symbol = ' '*max_len
277 max_symbol = ' '*max_len
276
278
277 #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...
278 test_buffer = max_symbol + ' '*4
280 test_buffer = max_symbol + ' '*4
279
281
280 allowed_symbols = 80/len(test_buffer)
282 allowed_symbols = 80/len(test_buffer)
281 if allowed_symbols == 0:
283 if allowed_symbols == 0:
282 allowed_symbols = 1
284 allowed_symbols = 1
283
285
284 pos = 1
286 pos = 1
285 buf = ''
287 buf = ''
286 for symbol in possibilities:
288 for symbol in possibilities:
287 #buf += symbol+'\n'#*spaces)
289 #buf += symbol+'\n'#*spaces)
288 if pos < allowed_symbols:
290 if pos < allowed_symbols:
289 spaces = max_len - len(symbol) + 4
291 spaces = max_len - len(symbol) + 4
290 buf += symbol+' '*spaces
292 buf += symbol+' '*spaces
291 pos += 1
293 pos += 1
292 else:
294 else:
293 buf += symbol+'\n'
295 buf += symbol+'\n'
294 pos = 1
296 pos = 1
295 self.write(buf)
297 self.write(buf)
296 else:
298 else:
297 possibilities.sort() # Python sorts are case sensitive
299 possibilities.sort() # Python sorts are case sensitive
298 self.AutoCompSetIgnoreCase(False)
300 self.AutoCompSetIgnoreCase(False)
299 self.AutoCompSetAutoHide(False)
301 self.AutoCompSetAutoHide(False)
300 #let compute the length ot text)last word
302 #let compute the length ot text)last word
301 splitter = [' ', '(', '[', '{']
303 splitter = [' ', '(', '[', '{']
302 last_word = self.get_current_edit_buffer()
304 last_word = self.get_current_edit_buffer()
303 for breaker in splitter:
305 for breaker in splitter:
304 last_word = last_word.split(breaker)[-1]
306 last_word = last_word.split(breaker)[-1]
305 self.AutoCompShow(len(last_word), " ".join(possibilities))
307 self.AutoCompShow(len(last_word), " ".join(possibilities))
306
308
307
309
308 def scroll_to_bottom(self):
310 def scroll_to_bottom(self):
309 maxrange = self.GetScrollRange(wx.VERTICAL)
311 maxrange = self.GetScrollRange(wx.VERTICAL)
310 self.ScrollLines(maxrange)
312 self.ScrollLines(maxrange)
311
313
314
312 def _on_enter(self):
315 def _on_enter(self):
313 """ Called when the return key is hit.
316 """ Called when the return key is hit.
314 """
317 """
315 pass
318 pass
316
319
317
320
318 def _on_key_down(self, event, skip=True):
321 def _on_key_down(self, event, skip=True):
319 """ Key press callback used for correcting behavior for
322 """ Key press callback used for correcting behavior for
320 console-like interfaces: the cursor is constraint to be after
323 console-like interfaces: the cursor is constraint to be after
321 the last prompt.
324 the last prompt.
322
325
323 Return True if event as been catched.
326 Return True if event as been catched.
324 """
327 """
325 catched = False
328 catched = False
326 # Intercept some specific keys.
329 # Intercept some specific keys.
327 if event.KeyCode == ord('L') and event.ControlDown() :
330 if event.KeyCode == ord('L') and event.ControlDown() :
328 catched = True
331 catched = True
329 self.scroll_to_bottom()
332 self.scroll_to_bottom()
330 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
333 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
331 catched = True
334 catched = True
332 self.ScrollPages(-1)
335 self.ScrollPages(-1)
333 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
336 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
334 catched = True
337 catched = True
335 self.ScrollPages(1)
338 self.ScrollPages(1)
336
339
337 if self.AutoCompActive():
340 if self.AutoCompActive():
338 event.Skip()
341 event.Skip()
339 else:
342 else:
340 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
343 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
341 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
344 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
342 catched = True
345 catched = True
343 self._on_enter()
346 self._on_enter()
344
347
345 elif event.KeyCode == wx.WXK_HOME:
348 elif event.KeyCode == wx.WXK_HOME:
346 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
349 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
347 self.GotoPos(self.current_prompt_pos)
350 self.GotoPos(self.current_prompt_pos)
348 catched = True
351 catched = True
349
352
350 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
353 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
351 # FIXME: This behavior is not ideal: if the selection
354 # FIXME: This behavior is not ideal: if the selection
352 # is already started, it will jump.
355 # is already started, it will jump.
353 self.SetSelectionStart(self.current_prompt_pos)
356 self.SetSelectionStart(self.current_prompt_pos)
354 self.SetSelectionEnd(self.GetCurrentPos())
357 self.SetSelectionEnd(self.GetCurrentPos())
355 catched = True
358 catched = True
356
359
357 elif event.KeyCode == wx.WXK_UP:
360 elif event.KeyCode == wx.WXK_UP:
358 if self.GetCurrentLine() > self.current_prompt_line:
361 if self.GetCurrentLine() > self.current_prompt_line:
359 if self.GetCurrentLine() == self.current_prompt_line + 1 \
362 if self.GetCurrentLine() == self.current_prompt_line + 1 \
360 and self.GetColumn(self.GetCurrentPos()) < \
363 and self.GetColumn(self.GetCurrentPos()) < \
361 self.GetColumn(self.current_prompt_pos):
364 self.GetColumn(self.current_prompt_pos):
362 self.GotoPos(self.current_prompt_pos)
365 self.GotoPos(self.current_prompt_pos)
363 else:
366 else:
364 event.Skip()
367 event.Skip()
365 catched = True
368 catched = True
366
369
367 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
370 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
368 if self.GetCurrentPos() > self.current_prompt_pos:
371 if self.GetCurrentPos() > self.current_prompt_pos:
369 event.Skip()
372 event.Skip()
370 catched = True
373 catched = True
371
374
372 if skip and not catched:
375 if skip and not catched:
373 event.Skip()
376 event.Skip()
374
377
375 return catched
378 return catched
376
379
377
380
378 def _on_key_up(self, event, skip=True):
381 def _on_key_up(self, event, skip=True):
379 """ If cursor is outside the editing region, put it back.
382 """ If cursor is outside the editing region, put it back.
380 """
383 """
381 event.Skip()
384 event.Skip()
382 if self.GetCurrentPos() < self.current_prompt_pos:
385 if self.GetCurrentPos() < self.current_prompt_pos:
383 self.GotoPos(self.current_prompt_pos)
386 self.GotoPos(self.current_prompt_pos)
384
387
385
388
386
389
387
390
388 if __name__ == '__main__':
391 if __name__ == '__main__':
389 # Some simple code to test the console widget.
392 # Some simple code to test the console widget.
390 class MainWindow(wx.Frame):
393 class MainWindow(wx.Frame):
391 def __init__(self, parent, id, title):
394 def __init__(self, parent, id, title):
392 wx.Frame.__init__(self, parent, id, title, size=(300,250))
395 wx.Frame.__init__(self, parent, id, title, size=(300,250))
393 self._sizer = wx.BoxSizer(wx.VERTICAL)
396 self._sizer = wx.BoxSizer(wx.VERTICAL)
394 self.console_widget = ConsoleWidget(self)
397 self.console_widget = ConsoleWidget(self)
395 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
398 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
396 self.SetSizer(self._sizer)
399 self.SetSizer(self._sizer)
397 self.SetAutoLayout(1)
400 self.SetAutoLayout(1)
398 self.Show(True)
401 self.Show(True)
399
402
400 app = wx.PySimpleApp()
403 app = wx.PySimpleApp()
401 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
404 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
402 w.SetSize((780, 460))
405 w.SetSize((780, 460))
403 w.Show()
406 w.Show()
404
407
405 app.MainLoop()
408 app.MainLoop()
406
409
407
410
General Comments 0
You need to be logged in to leave comments. Login now