##// END OF EJS Templates
Cleaner newline handling.
Gael Varoquaux -
Show More
@@ -1,179 +1,177 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 def common_prefix(strings):
27 def common_prefix(strings):
28 ref = strings[0]
28 ref = strings[0]
29 prefix = ''
29 prefix = ''
30 for size in range(len(ref)):
30 for size in range(len(ref)):
31 test_prefix = ref[:size+1]
31 test_prefix = ref[:size+1]
32 for string in strings[1:]:
32 for string in strings[1:]:
33 if not string.startswith(test_prefix):
33 if not string.startswith(test_prefix):
34 return prefix
34 return prefix
35 prefix = test_prefix
35 prefix = test_prefix
36
36
37 return prefix
37 return prefix
38
38
39 #-------------------------------------------------------------------------------
39 #-------------------------------------------------------------------------------
40 # Base class for the line-oriented front ends
40 # Base class for the line-oriented front ends
41 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
42 class LineFrontEndBase(FrontEndBase):
42 class LineFrontEndBase(FrontEndBase):
43
43
44 # 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
45 # it when there is an exception.
45 # it when there is an exception.
46 prompt_number = 1
46 prompt_number = 1
47
47
48 # To bootstrap
48 # To bootstrap
49 last_result = dict(number=0)
49 last_result = dict(number=0)
50
50
51 #--------------------------------------------------------------------------
51 #--------------------------------------------------------------------------
52 # Public API
52 # Public API
53 #--------------------------------------------------------------------------
53 #--------------------------------------------------------------------------
54
54
55 def __init__(self, shell=None, history=None):
55 def __init__(self, shell=None, history=None):
56 if shell is None:
56 if shell is None:
57 shell = Interpreter()
57 shell = Interpreter()
58 FrontEndBase.__init__(self, shell=shell, history=history)
58 FrontEndBase.__init__(self, shell=shell, history=history)
59
59
60 #FIXME: print banner.
60 #FIXME: print banner.
61 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
61 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
62 % IPython.__version__
62 % IPython.__version__
63
63
64
64
65 def complete(self, line):
65 def complete(self, line):
66 """Complete line in engine's user_ns
66 """Complete line in engine's user_ns
67
67
68 Parameters
68 Parameters
69 ----------
69 ----------
70 line : string
70 line : string
71
71
72 Result
72 Result
73 ------
73 ------
74 The replacement for the line and the list of possible completions.
74 The replacement for the line and the list of possible completions.
75 """
75 """
76 completions = self.shell.complete(line)
76 completions = self.shell.complete(line)
77 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
77 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
78 if completions:
78 if completions:
79 prefix = common_prefix(completions)
79 prefix = common_prefix(completions)
80 residual = complete_sep.split(line)[:-1]
80 residual = complete_sep.split(line)[:-1]
81 line = line[:-len(residual)] + prefix
81 line = line[:-len(residual)] + prefix
82 return line, completions
82 return line, completions
83
83
84
84
85 def render_result(self, result):
85 def render_result(self, result):
86 if 'stdout' in result and result['stdout']:
86 if 'stdout' in result and result['stdout']:
87 self.write('\n' + result['stdout'])
87 self.write('\n' + result['stdout'])
88 if 'display' in result and result['display']:
88 if 'display' in result and result['display']:
89 self.write("%s%s\n" % (
89 self.write("%s%s\n" % (
90 self.output_prompt % result['number'],
90 self.output_prompt % result['number'],
91 result['display']['pprint']
91 result['display']['pprint']
92 ) )
92 ) )
93
93
94
94
95 def render_error(self, failure):
95 def render_error(self, failure):
96 self.insert_text('\n\n'+str(failure)+'\n\n')
96 self.insert_text('\n\n'+str(failure)+'\n\n')
97 return failure
97 return failure
98
98
99
99
100 def prefilter_input(self, string):
100 def prefilter_input(self, string):
101 string = string.replace('\r\n', '\n')
101 string = string.replace('\r\n', '\n')
102 string = string.replace('\t', 4*' ')
102 string = string.replace('\t', 4*' ')
103 # Clean the trailing whitespace
103 # Clean the trailing whitespace
104 string = '\n'.join(l.rstrip() for l in string.split('\n'))
104 string = '\n'.join(l.rstrip() for l in string.split('\n'))
105 return string
105 return string
106
106
107
107
108 def is_complete(self, string):
108 def is_complete(self, string):
109 if string in ('', '\n'):
109 if string in ('', '\n'):
110 return True
110 return True
111 elif ( len(self.get_current_edit_buffer().split('\n'))>2
111 elif ( len(self.get_current_edit_buffer().split('\n'))>2
112 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
112 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
113 return False
113 return False
114 else:
114 else:
115 # Add line returns here, to make sure that the statement is
115 # Add line returns here, to make sure that the statement is
116 # complete.
116 # complete.
117 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
117 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
118
118
119
119
120 def execute(self, python_string, raw_string=None):
120 def execute(self, python_string, raw_string=None):
121 """ Send the python_string to the interpreter, stores the
121 """ Send the python_string to the interpreter, stores the
122 raw_string in the history and starts a new prompt.
122 raw_string in the history and starts a new prompt.
123 """
123 """
124 if raw_string is None:
124 if raw_string is None:
125 raw_string = python_string
125 raw_string = python_string
126 # Create a false result, in case there is an exception
126 # Create a false result, in case there is an exception
127 self.last_result = dict(number=self.prompt_number)
127 self.last_result = dict(number=self.prompt_number)
128 try:
128 try:
129 self.history.input_cache[-1] = raw_string.rstrip()
129 self.history.input_cache[-1] = raw_string.rstrip()
130 result = self.shell.execute(python_string)
130 result = self.shell.execute(python_string)
131 self.last_result = result
131 self.last_result = result
132 self.render_result(result)
132 self.render_result(result)
133 except:
133 except:
134 self.show_traceback()
134 self.show_traceback()
135 finally:
135 finally:
136 self.after_execute()
136 self.after_execute()
137
137
138
138
139 def after_execute(self):
139 def after_execute(self):
140 """ All the operations required after an execution to put the
140 """ All the operations required after an execution to put the
141 terminal back in a shape where it is usable.
141 terminal back in a shape where it is usable.
142 """
142 """
143 self.prompt_number += 1
143 self.prompt_number += 1
144 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
144 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
145 # Start a new empty history entry
145 # Start a new empty history entry
146 self._add_history(None, '')
146 self._add_history(None, '')
147 self.history_cursor = len(self.history.input_cache) - 1
147 self.history_cursor = len(self.history.input_cache) - 1
148
148
149
149
150 def _on_enter(self):
150 def _on_enter(self):
151 """ Called when the return key is pressed in a line editing
151 """ Called when the return key is pressed in a line editing
152 buffer.
152 buffer.
153 """
153 """
154 current_buffer = self.get_current_edit_buffer()
154 current_buffer = self.get_current_edit_buffer()
155 cleaned_buffer = self.prefilter_input(current_buffer)
155 cleaned_buffer = self.prefilter_input(current_buffer)
156 if self.is_complete(cleaned_buffer):
156 if self.is_complete(cleaned_buffer):
157 self.execute(cleaned_buffer, raw_string=current_buffer)
157 self.execute(cleaned_buffer, raw_string=current_buffer)
158 else:
158 else:
159 if len(current_buffer.split('\n'))>2:
159 self.write(self._get_indent_string(current_buffer[:-1]))
160 # We need to clean the trailing '\n'
160 if current_buffer.rstrip().endswith(':'):
161 self.write(self._get_indent_string(current_buffer[:-1]))
162 else:
163 self.write('\t')
161 self.write('\t')
164
162
165
163
166 #--------------------------------------------------------------------------
164 #--------------------------------------------------------------------------
167 # Private API
165 # Private API
168 #--------------------------------------------------------------------------
166 #--------------------------------------------------------------------------
169
167
170 def _get_indent_string(self, string):
168 def _get_indent_string(self, string):
171 string = string.replace('\t', ' '*4)
169 string = string.replace('\t', ' '*4)
172 string = string.split('\n')[-1]
170 string = string.split('\n')[-1]
173 indent_chars = len(string) - len(string.lstrip())
171 indent_chars = len(string) - len(string.lstrip())
174 indent_string = '\t'*(indent_chars // 4) + \
172 indent_string = '\t'*(indent_chars // 4) + \
175 ' '*(indent_chars % 4)
173 ' '*(indent_chars % 4)
176
174
177 return indent_string
175 return indent_string
178
176
179
177
General Comments 0
You need to be logged in to leave comments. Login now