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