##// END OF EJS Templates
fix format of LazyEvaluate based on *actual* review
MinRK -
Show More
@@ -1,438 +1,438 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Classes for handling input/output prompts.
2 """Classes for handling input/output prompts.
3
3
4 Authors:
4 Authors:
5
5
6 * Fernando Perez
6 * Fernando Perez
7 * Brian Granger
7 * Brian Granger
8 * Thomas Kluyver
8 * Thomas Kluyver
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 The IPython Development Team
13 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
13 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
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 import os
23 import os
24 import re
24 import re
25 import socket
25 import socket
26 import sys
26 import sys
27 import time
27 import time
28
28
29 from string import Formatter
29 from string import Formatter
30
30
31 from IPython.config.configurable import Configurable
31 from IPython.config.configurable import Configurable
32 from IPython.core import release
32 from IPython.core import release
33 from IPython.utils import coloransi, py3compat
33 from IPython.utils import coloransi, py3compat
34 from IPython.utils.traitlets import (Unicode, Instance, Dict, Bool, Int)
34 from IPython.utils.traitlets import (Unicode, Instance, Dict, Bool, Int)
35
35
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 # Color schemes for prompts
37 # Color schemes for prompts
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39
39
40 InputColors = coloransi.InputTermColors # just a shorthand
40 InputColors = coloransi.InputTermColors # just a shorthand
41 Colors = coloransi.TermColors # just a shorthand
41 Colors = coloransi.TermColors # just a shorthand
42
42
43 color_lists = dict(normal=Colors(), inp=InputColors(), nocolor=coloransi.NoColors())
43 color_lists = dict(normal=Colors(), inp=InputColors(), nocolor=coloransi.NoColors())
44
44
45 PColNoColors = coloransi.ColorScheme(
45 PColNoColors = coloransi.ColorScheme(
46 'NoColor',
46 'NoColor',
47 in_prompt = InputColors.NoColor, # Input prompt
47 in_prompt = InputColors.NoColor, # Input prompt
48 in_number = InputColors.NoColor, # Input prompt number
48 in_number = InputColors.NoColor, # Input prompt number
49 in_prompt2 = InputColors.NoColor, # Continuation prompt
49 in_prompt2 = InputColors.NoColor, # Continuation prompt
50 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
50 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
51
51
52 out_prompt = Colors.NoColor, # Output prompt
52 out_prompt = Colors.NoColor, # Output prompt
53 out_number = Colors.NoColor, # Output prompt number
53 out_number = Colors.NoColor, # Output prompt number
54
54
55 normal = Colors.NoColor # color off (usu. Colors.Normal)
55 normal = Colors.NoColor # color off (usu. Colors.Normal)
56 )
56 )
57
57
58 # make some schemes as instances so we can copy them for modification easily:
58 # make some schemes as instances so we can copy them for modification easily:
59 PColLinux = coloransi.ColorScheme(
59 PColLinux = coloransi.ColorScheme(
60 'Linux',
60 'Linux',
61 in_prompt = InputColors.Green,
61 in_prompt = InputColors.Green,
62 in_number = InputColors.LightGreen,
62 in_number = InputColors.LightGreen,
63 in_prompt2 = InputColors.Green,
63 in_prompt2 = InputColors.Green,
64 in_normal = InputColors.Normal, # color off (usu. Colors.Normal)
64 in_normal = InputColors.Normal, # color off (usu. Colors.Normal)
65
65
66 out_prompt = Colors.Red,
66 out_prompt = Colors.Red,
67 out_number = Colors.LightRed,
67 out_number = Colors.LightRed,
68
68
69 normal = Colors.Normal
69 normal = Colors.Normal
70 )
70 )
71
71
72 # Slightly modified Linux for light backgrounds
72 # Slightly modified Linux for light backgrounds
73 PColLightBG = PColLinux.copy('LightBG')
73 PColLightBG = PColLinux.copy('LightBG')
74
74
75 PColLightBG.colors.update(
75 PColLightBG.colors.update(
76 in_prompt = InputColors.Blue,
76 in_prompt = InputColors.Blue,
77 in_number = InputColors.LightBlue,
77 in_number = InputColors.LightBlue,
78 in_prompt2 = InputColors.Blue
78 in_prompt2 = InputColors.Blue
79 )
79 )
80
80
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82 # Utilities
82 # Utilities
83 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
84
84
85 class LazyEvaluate(object):
85 class LazyEvaluate(object):
86 """This is used for formatting strings with values that need to be updated
86 """This is used for formatting strings with values that need to be updated
87 at that time, such as the current time or working directory."""
87 at that time, such as the current time or working directory."""
88 def __init__(self, func, *args, **kwargs):
88 def __init__(self, func, *args, **kwargs):
89 self.func = func
89 self.func = func
90 self.args = args
90 self.args = args
91 self.kwargs = kwargs
91 self.kwargs = kwargs
92
92
93 def __call__(self, **kwargs):
93 def __call__(self, **kwargs):
94 self.kwargs.update(kwargs)
94 self.kwargs.update(kwargs)
95 return self.func(*self.args, **self.kwargs)
95 return self.func(*self.args, **self.kwargs)
96
96
97 def __str__(self):
97 def __str__(self):
98 s = self()
98 s = self()
99 if isinstance(s, unicode):
99 if isinstance(s, unicode):
100 return py3compat.unicode_to_str(s)
100 return py3compat.unicode_to_str(s)
101 return str(s)
101 return str(s)
102
102
103 def __unicode__(self):
103 def __unicode__(self):
104 return unicode(self())
104 return unicode(self())
105
105
106 def __format__(self, format_spec):
106 def __format__(self, format_spec):
107 return format(unicode(self()), format_spec)
107 return format(self(), format_spec)
108
108
109 def multiple_replace(dict, text):
109 def multiple_replace(dict, text):
110 """ Replace in 'text' all occurences of any key in the given
110 """ Replace in 'text' all occurences of any key in the given
111 dictionary by its corresponding value. Returns the new string."""
111 dictionary by its corresponding value. Returns the new string."""
112
112
113 # Function by Xavier Defrang, originally found at:
113 # Function by Xavier Defrang, originally found at:
114 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330
114 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330
115
115
116 # Create a regular expression from the dictionary keys
116 # Create a regular expression from the dictionary keys
117 regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
117 regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
118 # For each match, look-up corresponding value in dictionary
118 # For each match, look-up corresponding value in dictionary
119 return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
119 return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
120
120
121 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
122 # Special characters that can be used in prompt templates, mainly bash-like
122 # Special characters that can be used in prompt templates, mainly bash-like
123 #-----------------------------------------------------------------------------
123 #-----------------------------------------------------------------------------
124
124
125 # If $HOME isn't defined (Windows), make it an absurd string so that it can
125 # If $HOME isn't defined (Windows), make it an absurd string so that it can
126 # never be expanded out into '~'. Basically anything which can never be a
126 # never be expanded out into '~'. Basically anything which can never be a
127 # reasonable directory name will do, we just want the $HOME -> '~' operation
127 # reasonable directory name will do, we just want the $HOME -> '~' operation
128 # to become a no-op. We pre-compute $HOME here so it's not done on every
128 # to become a no-op. We pre-compute $HOME here so it's not done on every
129 # prompt call.
129 # prompt call.
130
130
131 # FIXME:
131 # FIXME:
132
132
133 # - This should be turned into a class which does proper namespace management,
133 # - This should be turned into a class which does proper namespace management,
134 # since the prompt specials need to be evaluated in a certain namespace.
134 # since the prompt specials need to be evaluated in a certain namespace.
135 # Currently it's just globals, which need to be managed manually by code
135 # Currently it's just globals, which need to be managed manually by code
136 # below.
136 # below.
137
137
138 # - I also need to split up the color schemes from the prompt specials
138 # - I also need to split up the color schemes from the prompt specials
139 # somehow. I don't have a clean design for that quite yet.
139 # somehow. I don't have a clean design for that quite yet.
140
140
141 HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")
141 HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")
142
142
143 # We precompute a few more strings here for the prompt_specials, which are
143 # We precompute a few more strings here for the prompt_specials, which are
144 # fixed once ipython starts. This reduces the runtime overhead of computing
144 # fixed once ipython starts. This reduces the runtime overhead of computing
145 # prompt strings.
145 # prompt strings.
146 USER = os.environ.get("USER")
146 USER = os.environ.get("USER")
147 HOSTNAME = socket.gethostname()
147 HOSTNAME = socket.gethostname()
148 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
148 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
149 ROOT_SYMBOL = "#" if (os.name=='nt' or os.getuid()==0) else "$"
149 ROOT_SYMBOL = "#" if (os.name=='nt' or os.getuid()==0) else "$"
150
150
151 prompt_abbreviations = {
151 prompt_abbreviations = {
152 # Prompt/history count
152 # Prompt/history count
153 '%n' : '{color.number}' '{count}' '{color.prompt}',
153 '%n' : '{color.number}' '{count}' '{color.prompt}',
154 r'\#': '{color.number}' '{count}' '{color.prompt}',
154 r'\#': '{color.number}' '{count}' '{color.prompt}',
155 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
155 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
156 # can get numbers displayed in whatever color they want.
156 # can get numbers displayed in whatever color they want.
157 r'\N': '{count}',
157 r'\N': '{count}',
158
158
159 # Prompt/history count, with the actual digits replaced by dots. Used
159 # Prompt/history count, with the actual digits replaced by dots. Used
160 # mainly in continuation prompts (prompt_in2)
160 # mainly in continuation prompts (prompt_in2)
161 r'\D': '{dots}',
161 r'\D': '{dots}',
162
162
163 # Current time
163 # Current time
164 r'\T' : '{time}',
164 r'\T' : '{time}',
165 # Current working directory
165 # Current working directory
166 r'\w': '{cwd}',
166 r'\w': '{cwd}',
167 # Basename of current working directory.
167 # Basename of current working directory.
168 # (use os.sep to make this portable across OSes)
168 # (use os.sep to make this portable across OSes)
169 r'\W' : '{cwd_last}',
169 r'\W' : '{cwd_last}',
170 # These X<N> are an extension to the normal bash prompts. They return
170 # These X<N> are an extension to the normal bash prompts. They return
171 # N terms of the path, after replacing $HOME with '~'
171 # N terms of the path, after replacing $HOME with '~'
172 r'\X0': '{cwd_x[0]}',
172 r'\X0': '{cwd_x[0]}',
173 r'\X1': '{cwd_x[1]}',
173 r'\X1': '{cwd_x[1]}',
174 r'\X2': '{cwd_x[2]}',
174 r'\X2': '{cwd_x[2]}',
175 r'\X3': '{cwd_x[3]}',
175 r'\X3': '{cwd_x[3]}',
176 r'\X4': '{cwd_x[4]}',
176 r'\X4': '{cwd_x[4]}',
177 r'\X5': '{cwd_x[5]}',
177 r'\X5': '{cwd_x[5]}',
178 # Y<N> are similar to X<N>, but they show '~' if it's the directory
178 # Y<N> are similar to X<N>, but they show '~' if it's the directory
179 # N+1 in the list. Somewhat like %cN in tcsh.
179 # N+1 in the list. Somewhat like %cN in tcsh.
180 r'\Y0': '{cwd_y[0]}',
180 r'\Y0': '{cwd_y[0]}',
181 r'\Y1': '{cwd_y[1]}',
181 r'\Y1': '{cwd_y[1]}',
182 r'\Y2': '{cwd_y[2]}',
182 r'\Y2': '{cwd_y[2]}',
183 r'\Y3': '{cwd_y[3]}',
183 r'\Y3': '{cwd_y[3]}',
184 r'\Y4': '{cwd_y[4]}',
184 r'\Y4': '{cwd_y[4]}',
185 r'\Y5': '{cwd_y[5]}',
185 r'\Y5': '{cwd_y[5]}',
186 # Hostname up to first .
186 # Hostname up to first .
187 r'\h': HOSTNAME_SHORT,
187 r'\h': HOSTNAME_SHORT,
188 # Full hostname
188 # Full hostname
189 r'\H': HOSTNAME,
189 r'\H': HOSTNAME,
190 # Username of current user
190 # Username of current user
191 r'\u': USER,
191 r'\u': USER,
192 # Escaped '\'
192 # Escaped '\'
193 '\\\\': '\\',
193 '\\\\': '\\',
194 # Newline
194 # Newline
195 r'\n': '\n',
195 r'\n': '\n',
196 # Carriage return
196 # Carriage return
197 r'\r': '\r',
197 r'\r': '\r',
198 # Release version
198 # Release version
199 r'\v': release.version,
199 r'\v': release.version,
200 # Root symbol ($ or #)
200 # Root symbol ($ or #)
201 r'\$': ROOT_SYMBOL,
201 r'\$': ROOT_SYMBOL,
202 }
202 }
203
203
204 #-----------------------------------------------------------------------------
204 #-----------------------------------------------------------------------------
205 # More utilities
205 # More utilities
206 #-----------------------------------------------------------------------------
206 #-----------------------------------------------------------------------------
207
207
208 def cwd_filt(depth):
208 def cwd_filt(depth):
209 """Return the last depth elements of the current working directory.
209 """Return the last depth elements of the current working directory.
210
210
211 $HOME is always replaced with '~'.
211 $HOME is always replaced with '~'.
212 If depth==0, the full path is returned."""
212 If depth==0, the full path is returned."""
213
213
214 cwd = os.getcwdu().replace(HOME,"~")
214 cwd = os.getcwdu().replace(HOME,"~")
215 out = os.sep.join(cwd.split(os.sep)[-depth:])
215 out = os.sep.join(cwd.split(os.sep)[-depth:])
216 return out or os.sep
216 return out or os.sep
217
217
218 def cwd_filt2(depth):
218 def cwd_filt2(depth):
219 """Return the last depth elements of the current working directory.
219 """Return the last depth elements of the current working directory.
220
220
221 $HOME is always replaced with '~'.
221 $HOME is always replaced with '~'.
222 If depth==0, the full path is returned."""
222 If depth==0, the full path is returned."""
223
223
224 full_cwd = os.getcwdu()
224 full_cwd = os.getcwdu()
225 cwd = full_cwd.replace(HOME,"~").split(os.sep)
225 cwd = full_cwd.replace(HOME,"~").split(os.sep)
226 if '~' in cwd and len(cwd) == depth+1:
226 if '~' in cwd and len(cwd) == depth+1:
227 depth += 1
227 depth += 1
228 drivepart = ''
228 drivepart = ''
229 if sys.platform == 'win32' and len(cwd) > depth:
229 if sys.platform == 'win32' and len(cwd) > depth:
230 drivepart = os.path.splitdrive(full_cwd)[0]
230 drivepart = os.path.splitdrive(full_cwd)[0]
231 out = drivepart + '/'.join(cwd[-depth:])
231 out = drivepart + '/'.join(cwd[-depth:])
232
232
233 return out or os.sep
233 return out or os.sep
234
234
235 #-----------------------------------------------------------------------------
235 #-----------------------------------------------------------------------------
236 # Prompt classes
236 # Prompt classes
237 #-----------------------------------------------------------------------------
237 #-----------------------------------------------------------------------------
238
238
239 lazily_evaluate = {'time': LazyEvaluate(time.strftime, "%H:%M:%S"),
239 lazily_evaluate = {'time': LazyEvaluate(time.strftime, "%H:%M:%S"),
240 'cwd': LazyEvaluate(os.getcwdu),
240 'cwd': LazyEvaluate(os.getcwdu),
241 'cwd_last': LazyEvaluate(lambda: os.getcwdu().split(os.sep)[-1]),
241 'cwd_last': LazyEvaluate(lambda: os.getcwdu().split(os.sep)[-1]),
242 'cwd_x': [LazyEvaluate(lambda: os.getcwdu().replace("%s","~"))] +\
242 'cwd_x': [LazyEvaluate(lambda: os.getcwdu().replace("%s","~"))] +\
243 [LazyEvaluate(cwd_filt, x) for x in range(1,6)],
243 [LazyEvaluate(cwd_filt, x) for x in range(1,6)],
244 'cwd_y': [LazyEvaluate(cwd_filt2, x) for x in range(6)]
244 'cwd_y': [LazyEvaluate(cwd_filt2, x) for x in range(6)]
245 }
245 }
246
246
247 def _lenlastline(s):
247 def _lenlastline(s):
248 """Get the length of the last line. More intelligent than
248 """Get the length of the last line. More intelligent than
249 len(s.splitlines()[-1]).
249 len(s.splitlines()[-1]).
250 """
250 """
251 if not s or s.endswith(('\n', '\r')):
251 if not s or s.endswith(('\n', '\r')):
252 return 0
252 return 0
253 return len(s.splitlines()[-1])
253 return len(s.splitlines()[-1])
254
254
255
255
256 class UserNSFormatter(Formatter):
256 class UserNSFormatter(Formatter):
257 """A Formatter that falls back on a shell's user_ns and __builtins__ for name resolution"""
257 """A Formatter that falls back on a shell's user_ns and __builtins__ for name resolution"""
258 def __init__(self, shell):
258 def __init__(self, shell):
259 self.shell = shell
259 self.shell = shell
260
260
261 def get_value(self, key, args, kwargs):
261 def get_value(self, key, args, kwargs):
262 # try regular formatting first:
262 # try regular formatting first:
263 try:
263 try:
264 return Formatter.get_value(self, key, args, kwargs)
264 return Formatter.get_value(self, key, args, kwargs)
265 except Exception:
265 except Exception:
266 pass
266 pass
267 # next, look in user_ns and builtins:
267 # next, look in user_ns and builtins:
268 for container in (self.shell.user_ns, __builtins__):
268 for container in (self.shell.user_ns, __builtins__):
269 if key in container:
269 if key in container:
270 return container[key]
270 return container[key]
271 # nothing found, put error message in its place
271 # nothing found, put error message in its place
272 return "<ERROR: '%s' not found>" % key
272 return "<ERROR: '%s' not found>" % key
273
273
274
274
275 class PromptManager(Configurable):
275 class PromptManager(Configurable):
276 """This is the primary interface for producing IPython's prompts."""
276 """This is the primary interface for producing IPython's prompts."""
277 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
277 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
278
278
279 color_scheme_table = Instance(coloransi.ColorSchemeTable)
279 color_scheme_table = Instance(coloransi.ColorSchemeTable)
280 color_scheme = Unicode('Linux', config=True)
280 color_scheme = Unicode('Linux', config=True)
281 def _color_scheme_changed(self, name, new_value):
281 def _color_scheme_changed(self, name, new_value):
282 self.color_scheme_table.set_active_scheme(new_value)
282 self.color_scheme_table.set_active_scheme(new_value)
283 for pname in ['in', 'in2', 'out', 'rewrite']:
283 for pname in ['in', 'in2', 'out', 'rewrite']:
284 # We need to recalculate the number of invisible characters
284 # We need to recalculate the number of invisible characters
285 self.update_prompt(pname)
285 self.update_prompt(pname)
286
286
287 lazy_evaluate_fields = Dict(help="""
287 lazy_evaluate_fields = Dict(help="""
288 This maps field names used in the prompt templates to functions which
288 This maps field names used in the prompt templates to functions which
289 will be called when the prompt is rendered. This allows us to include
289 will be called when the prompt is rendered. This allows us to include
290 things like the current time in the prompts. Functions are only called
290 things like the current time in the prompts. Functions are only called
291 if they are used in the prompt.
291 if they are used in the prompt.
292 """)
292 """)
293 def _lazy_evaluate_fields_default(self): return lazily_evaluate.copy()
293 def _lazy_evaluate_fields_default(self): return lazily_evaluate.copy()
294
294
295 in_template = Unicode('In [\\#]: ', config=True,
295 in_template = Unicode('In [\\#]: ', config=True,
296 help="Input prompt. '\\#' will be transformed to the prompt number")
296 help="Input prompt. '\\#' will be transformed to the prompt number")
297 in2_template = Unicode(' .\\D.: ', config=True,
297 in2_template = Unicode(' .\\D.: ', config=True,
298 help="Continuation prompt.")
298 help="Continuation prompt.")
299 out_template = Unicode('Out[\\#]: ', config=True,
299 out_template = Unicode('Out[\\#]: ', config=True,
300 help="Output prompt. '\\#' will be transformed to the prompt number")
300 help="Output prompt. '\\#' will be transformed to the prompt number")
301
301
302 justify = Bool(True, config=True, help="""
302 justify = Bool(True, config=True, help="""
303 If True (default), each prompt will be right-aligned with the
303 If True (default), each prompt will be right-aligned with the
304 preceding one.
304 preceding one.
305 """)
305 """)
306
306
307 # We actually store the expanded templates here:
307 # We actually store the expanded templates here:
308 templates = Dict()
308 templates = Dict()
309
309
310 # The number of characters in the last prompt rendered, not including
310 # The number of characters in the last prompt rendered, not including
311 # colour characters.
311 # colour characters.
312 width = Int()
312 width = Int()
313 txtwidth = Int() # Not including right-justification
313 txtwidth = Int() # Not including right-justification
314
314
315 # The number of characters in each prompt which don't contribute to width
315 # The number of characters in each prompt which don't contribute to width
316 invisible_chars = Dict()
316 invisible_chars = Dict()
317 def _invisible_chars_default(self):
317 def _invisible_chars_default(self):
318 return {'in': 0, 'in2': 0, 'out': 0, 'rewrite':0}
318 return {'in': 0, 'in2': 0, 'out': 0, 'rewrite':0}
319
319
320 def __init__(self, shell, config=None):
320 def __init__(self, shell, config=None):
321 super(PromptManager, self).__init__(shell=shell, config=config)
321 super(PromptManager, self).__init__(shell=shell, config=config)
322
322
323 # Prepare colour scheme table
323 # Prepare colour scheme table
324 self.color_scheme_table = coloransi.ColorSchemeTable([PColNoColors,
324 self.color_scheme_table = coloransi.ColorSchemeTable([PColNoColors,
325 PColLinux, PColLightBG], self.color_scheme)
325 PColLinux, PColLightBG], self.color_scheme)
326
326
327 self._formatter = UserNSFormatter(shell)
327 self._formatter = UserNSFormatter(shell)
328 # Prepare templates & numbers of invisible characters
328 # Prepare templates & numbers of invisible characters
329 self.update_prompt('in', self.in_template)
329 self.update_prompt('in', self.in_template)
330 self.update_prompt('in2', self.in2_template)
330 self.update_prompt('in2', self.in2_template)
331 self.update_prompt('out', self.out_template)
331 self.update_prompt('out', self.out_template)
332 self.update_prompt('rewrite')
332 self.update_prompt('rewrite')
333 self.on_trait_change(self._update_prompt_trait, ['in_template',
333 self.on_trait_change(self._update_prompt_trait, ['in_template',
334 'in2_template', 'out_template'])
334 'in2_template', 'out_template'])
335
335
336 def update_prompt(self, name, new_template=None):
336 def update_prompt(self, name, new_template=None):
337 """This is called when a prompt template is updated. It processes
337 """This is called when a prompt template is updated. It processes
338 abbreviations used in the prompt template (like \#) and calculates how
338 abbreviations used in the prompt template (like \#) and calculates how
339 many invisible characters (ANSI colour escapes) the resulting prompt
339 many invisible characters (ANSI colour escapes) the resulting prompt
340 contains.
340 contains.
341
341
342 It is also called for each prompt on changing the colour scheme. In both
342 It is also called for each prompt on changing the colour scheme. In both
343 cases, traitlets should take care of calling this automatically.
343 cases, traitlets should take care of calling this automatically.
344 """
344 """
345 if new_template is not None:
345 if new_template is not None:
346 self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
346 self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
347 # We count invisible characters (colour escapes) on the last line of the
347 # We count invisible characters (colour escapes) on the last line of the
348 # prompt, to calculate the width for lining up subsequent prompts.
348 # prompt, to calculate the width for lining up subsequent prompts.
349 invis_chars = _lenlastline(self._render(name, color=True)) - \
349 invis_chars = _lenlastline(self._render(name, color=True)) - \
350 _lenlastline(self._render(name, color=False))
350 _lenlastline(self._render(name, color=False))
351 self.invisible_chars[name] = invis_chars
351 self.invisible_chars[name] = invis_chars
352
352
353 def _update_prompt_trait(self, traitname, new_template):
353 def _update_prompt_trait(self, traitname, new_template):
354 name = traitname[:-9] # Cut off '_template'
354 name = traitname[:-9] # Cut off '_template'
355 self.update_prompt(name, new_template)
355 self.update_prompt(name, new_template)
356
356
357 def _render(self, name, color=True, **kwargs):
357 def _render(self, name, color=True, **kwargs):
358 """Render but don't justify, or update the width or txtwidth attributes.
358 """Render but don't justify, or update the width or txtwidth attributes.
359 """
359 """
360 if name == 'rewrite':
360 if name == 'rewrite':
361 return self._render_rewrite(color=color)
361 return self._render_rewrite(color=color)
362
362
363 if color:
363 if color:
364 scheme = self.color_scheme_table.active_colors
364 scheme = self.color_scheme_table.active_colors
365 if name=='out':
365 if name=='out':
366 colors = color_lists['normal']
366 colors = color_lists['normal']
367 colors.number, colors.prompt, colors.normal = \
367 colors.number, colors.prompt, colors.normal = \
368 scheme.out_number, scheme.out_prompt, scheme.normal
368 scheme.out_number, scheme.out_prompt, scheme.normal
369 else:
369 else:
370 colors = color_lists['inp']
370 colors = color_lists['inp']
371 colors.number, colors.prompt, colors.normal = \
371 colors.number, colors.prompt, colors.normal = \
372 scheme.in_number, scheme.in_prompt, scheme.in_normal
372 scheme.in_number, scheme.in_prompt, scheme.in_normal
373 if name=='in2':
373 if name=='in2':
374 colors.prompt = scheme.in_prompt2
374 colors.prompt = scheme.in_prompt2
375 else:
375 else:
376 # No color
376 # No color
377 colors = color_lists['nocolor']
377 colors = color_lists['nocolor']
378 colors.number, colors.prompt, colors.normal = '', '', ''
378 colors.number, colors.prompt, colors.normal = '', '', ''
379
379
380 count = self.shell.execution_count # Shorthand
380 count = self.shell.execution_count # Shorthand
381 # Build the dictionary to be passed to string formatting
381 # Build the dictionary to be passed to string formatting
382 fmtargs = dict(color=colors, count=count,
382 fmtargs = dict(color=colors, count=count,
383 dots="."*len(str(count)),
383 dots="."*len(str(count)),
384 width=self.width, txtwidth=self.txtwidth )
384 width=self.width, txtwidth=self.txtwidth )
385 fmtargs.update(self.lazy_evaluate_fields)
385 fmtargs.update(self.lazy_evaluate_fields)
386 fmtargs.update(kwargs)
386 fmtargs.update(kwargs)
387
387
388 # Prepare the prompt
388 # Prepare the prompt
389 prompt = colors.prompt + self.templates[name] + colors.normal
389 prompt = colors.prompt + self.templates[name] + colors.normal
390
390
391 # Fill in required fields
391 # Fill in required fields
392 return self._formatter.format(prompt, **fmtargs)
392 return self._formatter.format(prompt, **fmtargs)
393
393
394 def _render_rewrite(self, color=True):
394 def _render_rewrite(self, color=True):
395 """Render the ---> rewrite prompt."""
395 """Render the ---> rewrite prompt."""
396 if color:
396 if color:
397 scheme = self.color_scheme_table.active_colors
397 scheme = self.color_scheme_table.active_colors
398 # We need a non-input version of these escapes
398 # We need a non-input version of these escapes
399 color_prompt = scheme.in_prompt.replace("\001","").replace("\002","")
399 color_prompt = scheme.in_prompt.replace("\001","").replace("\002","")
400 color_normal = scheme.normal
400 color_normal = scheme.normal
401 else:
401 else:
402 color_prompt, color_normal = '', ''
402 color_prompt, color_normal = '', ''
403
403
404 return color_prompt + "-> ".rjust(self.txtwidth, "-") + color_normal
404 return color_prompt + "-> ".rjust(self.txtwidth, "-") + color_normal
405
405
406 def render(self, name, color=True, just=None, **kwargs):
406 def render(self, name, color=True, just=None, **kwargs):
407 """
407 """
408 Render the selected prompt.
408 Render the selected prompt.
409
409
410 Parameters
410 Parameters
411 ----------
411 ----------
412 name : str
412 name : str
413 Which prompt to render. One of 'in', 'in2', 'out', 'rewrite'
413 Which prompt to render. One of 'in', 'in2', 'out', 'rewrite'
414 color : bool
414 color : bool
415 If True (default), include ANSI escape sequences for a coloured prompt.
415 If True (default), include ANSI escape sequences for a coloured prompt.
416 just : bool
416 just : bool
417 If True, justify the prompt to the width of the last prompt. The
417 If True, justify the prompt to the width of the last prompt. The
418 default is stored in self.justify.
418 default is stored in self.justify.
419 **kwargs :
419 **kwargs :
420 Additional arguments will be passed to the string formatting operation,
420 Additional arguments will be passed to the string formatting operation,
421 so they can override the values that would otherwise fill in the
421 so they can override the values that would otherwise fill in the
422 template.
422 template.
423
423
424 Returns
424 Returns
425 -------
425 -------
426 A string containing the rendered prompt.
426 A string containing the rendered prompt.
427 """
427 """
428 res = self._render(name, color=color, **kwargs)
428 res = self._render(name, color=color, **kwargs)
429
429
430 # Handle justification of prompt
430 # Handle justification of prompt
431 invis_chars = self.invisible_chars[name] if color else 0
431 invis_chars = self.invisible_chars[name] if color else 0
432 self.txtwidth = _lenlastline(res) - invis_chars
432 self.txtwidth = _lenlastline(res) - invis_chars
433 just = self.justify if (just is None) else just
433 just = self.justify if (just is None) else just
434 # If the prompt spans more than one line, don't try to justify it:
434 # If the prompt spans more than one line, don't try to justify it:
435 if just and name != 'in' and ('\n' not in res) and ('\r' not in res):
435 if just and name != 'in' and ('\n' not in res) and ('\r' not in res):
436 res = res.rjust(self.width + invis_chars)
436 res = res.rjust(self.width + invis_chars)
437 self.width = _lenlastline(res) - invis_chars
437 self.width = _lenlastline(res) - invis_chars
438 return res
438 return res
General Comments 0
You need to be logged in to leave comments. Login now