##// END OF EJS Templates
Refactor prompt handling into new prompt manager.
Thomas Kluyver -
Show More
@@ -25,7 +25,6 b' Authors:'
25 import __builtin__
25 import __builtin__
26
26
27 from IPython.config.configurable import Configurable
27 from IPython.config.configurable import Configurable
28 from IPython.core import prompts
29 from IPython.utils import io
28 from IPython.utils import io
30 from IPython.utils.traitlets import Instance, List
29 from IPython.utils.traitlets import Instance, List
31 from IPython.utils.warn import warn
30 from IPython.utils.warn import warn
@@ -34,32 +33,20 b' from IPython.utils.warn import warn'
34 # Main displayhook class
33 # Main displayhook class
35 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
36
35
37 # TODO: The DisplayHook class should be split into two classes, one that
36 # TODO: Move the various attributes (cache_size, [others now moved]). Some
38 # manages the prompts and their synchronization and another that just does the
37 # of these are also attributes of InteractiveShell. They should be on ONE object
39 # displayhook logic and calls into the prompt manager.
38 # only and the other objects should ask that one object for their values.
40
41 # TODO: Move the various attributes (cache_size, colors, input_sep,
42 # output_sep, output_sep2, ps1, ps2, ps_out, pad_left). Some of these are also
43 # attributes of InteractiveShell. They should be on ONE object only and the
44 # other objects should ask that one object for their values.
45
39
46 class DisplayHook(Configurable):
40 class DisplayHook(Configurable):
47 """The custom IPython displayhook to replace sys.displayhook.
41 """The custom IPython displayhook to replace sys.displayhook.
48
42
49 This class does many things, but the basic idea is that it is a callable
43 This class does many things, but the basic idea is that it is a callable
50 that gets called anytime user code returns a value.
44 that gets called anytime user code returns a value.
51
52 Currently this class does more than just the displayhook logic and that
53 extra logic should eventually be moved out of here.
54 """
45 """
55
46
56 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
47 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
57
48
58 def __init__(self, shell=None, cache_size=1000,
49 def __init__(self, shell=None, cache_size=1000, config=None):
59 colors='NoColor', input_sep='\n',
60 output_sep='\n', output_sep2='',
61 ps1 = None, ps2 = None, ps_out = None, pad_left=True,
62 config=None):
63 super(DisplayHook, self).__init__(shell=shell, config=config)
50 super(DisplayHook, self).__init__(shell=shell, config=config)
64
51
65 cache_size_min = 3
52 cache_size_min = 3
@@ -75,36 +62,10 b' class DisplayHook(Configurable):'
75 self.do_full_cache = 1
62 self.do_full_cache = 1
76
63
77 self.cache_size = cache_size
64 self.cache_size = cache_size
78 self.input_sep = input_sep
79
65
80 # we need a reference to the user-level namespace
66 # we need a reference to the user-level namespace
81 self.shell = shell
67 self.shell = shell
82
68
83 # Set input prompt strings and colors
84 if cache_size == 0:
85 if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
86 or ps1.find(r'\N') > -1:
87 ps1 = '>>> '
88 if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
89 or ps2.find(r'\N') > -1:
90 ps2 = '... '
91 self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
92 self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
93 self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')
94
95 self.color_table = prompts.PromptColors
96 self.prompt1 = prompts.Prompt1(self,sep=input_sep,prompt=self.ps1_str,
97 pad_left=pad_left)
98 self.prompt2 = prompts.Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
99 self.prompt_out = prompts.PromptOut(self,sep='',prompt=self.ps_out_str,
100 pad_left=pad_left)
101 self.set_colors(colors)
102
103 # Store the last prompt string each time, we need it for aligning
104 # continuation and auto-rewrite prompts
105 self.last_prompt = ''
106 self.output_sep = output_sep
107 self.output_sep2 = output_sep2
108 self._,self.__,self.___ = '','',''
69 self._,self.__,self.___ = '','',''
109
70
110 # these are deliberately global:
71 # these are deliberately global:
@@ -115,32 +76,6 b' class DisplayHook(Configurable):'
115 def prompt_count(self):
76 def prompt_count(self):
116 return self.shell.execution_count
77 return self.shell.execution_count
117
78
118 def _set_prompt_str(self,p_str,cache_def,no_cache_def):
119 if p_str is None:
120 if self.do_full_cache:
121 return cache_def
122 else:
123 return no_cache_def
124 else:
125 return p_str
126
127 def set_colors(self, colors):
128 """Set the active color scheme and configure colors for the three
129 prompt subsystems."""
130
131 # FIXME: This modifying of the global prompts.prompt_specials needs
132 # to be fixed. We need to refactor all of the prompts stuff to use
133 # proper configuration and traits notifications.
134 if colors.lower()=='nocolor':
135 prompts.prompt_specials = prompts.prompt_specials_nocolor
136 else:
137 prompts.prompt_specials = prompts.prompt_specials_color
138
139 self.color_table.set_active_scheme(colors)
140 self.prompt1.set_colors()
141 self.prompt2.set_colors()
142 self.prompt_out.set_colors()
143
144 #-------------------------------------------------------------------------
79 #-------------------------------------------------------------------------
145 # Methods used in __call__. Override these methods to modify the behavior
80 # Methods used in __call__. Override these methods to modify the behavior
146 # of the displayhook.
81 # of the displayhook.
@@ -180,8 +115,8 b' class DisplayHook(Configurable):'
180 ``io.stdout``.
115 ``io.stdout``.
181 """
116 """
182 # Use write, not print which adds an extra space.
117 # Use write, not print which adds an extra space.
183 io.stdout.write(self.output_sep)
118 io.stdout.write(self.shell.separate_out)
184 outprompt = str(self.prompt_out)
119 outprompt = self.shell.prompt_manager.render('out')
185 if self.do_full_cache:
120 if self.do_full_cache:
186 io.stdout.write(outprompt)
121 io.stdout.write(outprompt)
187
122
@@ -235,11 +170,12 b' class DisplayHook(Configurable):'
235 # So that multi-line strings line up with the left column of
170 # So that multi-line strings line up with the left column of
236 # the screen, instead of having the output prompt mess up
171 # the screen, instead of having the output prompt mess up
237 # their first line.
172 # their first line.
238 # We use the ps_out_str template instead of the expanded prompt
173 # We use the prompt template instead of the expanded prompt
239 # because the expansion may add ANSI escapes that will interfere
174 # because the expansion may add ANSI escapes that will interfere
240 # with our ability to determine whether or not we should add
175 # with our ability to determine whether or not we should add
241 # a newline.
176 # a newline.
242 if self.ps_out_str and not self.ps_out_str.endswith('\n'):
177 prompt_template = self.shell.prompt_manager.out_template
178 if prompt_template and not prompt_template.endswith('\n'):
243 # But avoid extraneous empty lines.
179 # But avoid extraneous empty lines.
244 result_repr = '\n' + result_repr
180 result_repr = '\n' + result_repr
245
181
@@ -286,7 +222,7 b' class DisplayHook(Configurable):'
286
222
287 def finish_displayhook(self):
223 def finish_displayhook(self):
288 """Finish up all displayhook activities."""
224 """Finish up all displayhook activities."""
289 io.stdout.write(self.output_sep2)
225 io.stdout.write(self.shell.separate_out2)
290 io.stdout.flush()
226 io.stdout.flush()
291
227
292 def __call__(self, result=None):
228 def __call__(self, result=None):
@@ -63,6 +63,7 b' from IPython.core.plugin import PluginManager'
63 from IPython.core.prefilter import PrefilterManager, ESC_MAGIC
63 from IPython.core.prefilter import PrefilterManager, ESC_MAGIC
64 from IPython.core.profiledir import ProfileDir
64 from IPython.core.profiledir import ProfileDir
65 from IPython.core.pylabtools import pylab_activate
65 from IPython.core.pylabtools import pylab_activate
66 from IPython.core.prompts import PromptManager
66 from IPython.external.Itpl import ItplNS
67 from IPython.external.Itpl import ItplNS
67 from IPython.utils import PyColorize
68 from IPython.utils import PyColorize
68 from IPython.utils import io
69 from IPython.utils import io
@@ -594,10 +595,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
594 io.stderr = io.IOStream(sys.stderr)
595 io.stderr = io.IOStream(sys.stderr)
595
596
596 def init_prompts(self):
597 def init_prompts(self):
597 # TODO: This is a pass for now because the prompts are managed inside
598 self.prompt_manager = PromptManager(shell=self, config=self.config)
598 # the DisplayHook. Once there is a separate prompt manager, this
599 # will initialize that object and all prompt related information.
600 pass
601
599
602 def init_display_formatter(self):
600 def init_display_formatter(self):
603 self.display_formatter = DisplayFormatter(config=self.config)
601 self.display_formatter = DisplayFormatter(config=self.config)
@@ -613,13 +611,6 b' class InteractiveShell(SingletonConfigurable, Magic):'
613 config=self.config,
611 config=self.config,
614 shell=self,
612 shell=self,
615 cache_size=self.cache_size,
613 cache_size=self.cache_size,
616 input_sep = self.separate_in,
617 output_sep = self.separate_out,
618 output_sep2 = self.separate_out2,
619 ps1 = self.prompt_in1,
620 ps2 = self.prompt_in2,
621 ps_out = self.prompt_out,
622 pad_left = self.prompts_pad_left
623 )
614 )
624 self.configurables.append(self.displayhook)
615 self.configurables.append(self.displayhook)
625 # This is a context manager that installs/revmoes the displayhook at
616 # This is a context manager that installs/revmoes the displayhook at
@@ -2149,7 +2140,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2149 after the user's input prompt. This helps the user understand that the
2140 after the user's input prompt. This helps the user understand that the
2150 input line was transformed automatically by IPython.
2141 input line was transformed automatically by IPython.
2151 """
2142 """
2152 rw = self.displayhook.prompt1.auto_rewrite() + cmd
2143 rw = self.prompt_manager.render('rewrite') + cmd
2153
2144
2154 try:
2145 try:
2155 # plain ascii works better w/ pyreadline, on some machines, so
2146 # plain ascii works better w/ pyreadline, on some machines, so
@@ -2554,12 +2554,12 b' Defaulting color scheme to \'NoColor\'"""'
2554
2554
2555 # Set prompt colors
2555 # Set prompt colors
2556 try:
2556 try:
2557 shell.displayhook.set_colors(new_scheme)
2557 shell.prompt_manager.color_scheme = new_scheme
2558 except:
2558 except:
2559 color_switch_err('prompt')
2559 color_switch_err('prompt')
2560 else:
2560 else:
2561 shell.colors = \
2561 shell.colors = \
2562 shell.displayhook.color_table.active_scheme_name
2562 shell.prompt_manager.color_scheme_table.active_scheme_name
2563 # Set exception colors
2563 # Set exception colors
2564 try:
2564 try:
2565 shell.InteractiveTB.set_colors(scheme = new_scheme)
2565 shell.InteractiveTB.set_colors(scheme = new_scheme)
@@ -3237,7 +3237,7 b' Defaulting color scheme to \'NoColor\'"""'
3237
3237
3238 # Shorthands
3238 # Shorthands
3239 shell = self.shell
3239 shell = self.shell
3240 oc = shell.displayhook
3240 pm = shell.prompt_manager
3241 meta = shell.meta
3241 meta = shell.meta
3242 disp_formatter = self.shell.display_formatter
3242 disp_formatter = self.shell.display_formatter
3243 ptformatter = disp_formatter.formatters['text/plain']
3243 ptformatter = disp_formatter.formatters['text/plain']
@@ -3252,23 +3252,23 b' Defaulting color scheme to \'NoColor\'"""'
3252 save_dstore('xmode',shell.InteractiveTB.mode)
3252 save_dstore('xmode',shell.InteractiveTB.mode)
3253 save_dstore('rc_separate_out',shell.separate_out)
3253 save_dstore('rc_separate_out',shell.separate_out)
3254 save_dstore('rc_separate_out2',shell.separate_out2)
3254 save_dstore('rc_separate_out2',shell.separate_out2)
3255 save_dstore('rc_prompts_pad_left',shell.prompts_pad_left)
3255 save_dstore('rc_prompts_pad_left',pm.justify)
3256 save_dstore('rc_separate_in',shell.separate_in)
3256 save_dstore('rc_separate_in',shell.separate_in)
3257 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
3257 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
3258 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
3258
3259
3259 if mode == False:
3260 if mode == False:
3260 # turn on
3261 # turn on
3261 oc.prompt1.p_template = '>>> '
3262 pm.in_template = '>>> '
3262 oc.prompt2.p_template = '... '
3263 pm.in2_template = '... '
3263 oc.prompt_out.p_template = ''
3264 pm.out_template = ''
3264
3265
3265 # Prompt separators like plain python
3266 # Prompt separators like plain python
3266 oc.input_sep = oc.prompt1.sep = ''
3267 shell.separate_in = ''
3267 oc.output_sep = ''
3268 shell.separate_out = ''
3268 oc.output_sep2 = ''
3269 shell.separate_out2 = ''
3269
3270
3270 oc.prompt1.pad_left = oc.prompt2.pad_left = \
3271 pm.justify = False
3271 oc.prompt_out.pad_left = False
3272
3272
3273 ptformatter.pprint = False
3273 ptformatter.pprint = False
3274 disp_formatter.plain_text_only = True
3274 disp_formatter.plain_text_only = True
@@ -3276,17 +3276,14 b' Defaulting color scheme to \'NoColor\'"""'
3276 shell.magic_xmode('Plain')
3276 shell.magic_xmode('Plain')
3277 else:
3277 else:
3278 # turn off
3278 # turn off
3279 oc.prompt1.p_template = shell.prompt_in1
3279 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
3280 oc.prompt2.p_template = shell.prompt_in2
3281 oc.prompt_out.p_template = shell.prompt_out
3282
3280
3283 oc.input_sep = oc.prompt1.sep = dstore.rc_separate_in
3281 shell.separate_in = dstore.rc_separate_in
3284
3282
3285 oc.output_sep = dstore.rc_separate_out
3283 shell.separate_out = dstore.rc_separate_out
3286 oc.output_sep2 = dstore.rc_separate_out2
3284 shell.separate_out2 = dstore.rc_separate_out2
3287
3285
3288 oc.prompt1.pad_left = oc.prompt2.pad_left = \
3286 pm.justify = dstore.rc_prompts_pad_left
3289 oc.prompt_out.pad_left = dstore.rc_prompts_pad_left
3290
3287
3291 ptformatter.pprint = dstore.rc_pprint
3288 ptformatter.pprint = dstore.rc_pprint
3292 disp_formatter.plain_text_only = dstore.rc_plain_text_only
3289 disp_formatter.plain_text_only = dstore.rc_plain_text_only
@@ -23,20 +23,23 b' import os'
23 import re
23 import re
24 import socket
24 import socket
25 import sys
25 import sys
26 import time
26
27
28 from IPython.config.configurable import Configurable
27 from IPython.core import release
29 from IPython.core import release
28 from IPython.external.Itpl import ItplNS
29 from IPython.utils import coloransi
30 from IPython.utils import coloransi
31 from IPython.utils.traitlets import (Unicode, Instance, Dict, Bool, Int)
30
32
31 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
32 # Color schemes for prompts
34 # Color schemes for prompts
33 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
34
36
35 PromptColors = coloransi.ColorSchemeTable()
36 InputColors = coloransi.InputTermColors # just a shorthand
37 InputColors = coloransi.InputTermColors # just a shorthand
37 Colors = coloransi.TermColors # just a shorthand
38 Colors = coloransi.TermColors # just a shorthand
38
39
39 PromptColors.add_scheme(coloransi.ColorScheme(
40 color_lists = dict(normal=Colors(), inp=InputColors(), nocolor=coloransi.NoColors())
41
42 PColNoColors = coloransi.ColorScheme(
40 'NoColor',
43 'NoColor',
41 in_prompt = InputColors.NoColor, # Input prompt
44 in_prompt = InputColors.NoColor, # Input prompt
42 in_number = InputColors.NoColor, # Input prompt number
45 in_number = InputColors.NoColor, # Input prompt number
@@ -47,10 +50,10 b' PromptColors.add_scheme(coloransi.ColorScheme('
47 out_number = Colors.NoColor, # Output prompt number
50 out_number = Colors.NoColor, # Output prompt number
48
51
49 normal = Colors.NoColor # color off (usu. Colors.Normal)
52 normal = Colors.NoColor # color off (usu. Colors.Normal)
50 ))
53 )
51
54
52 # make some schemes as instances so we can copy them for modification easily:
55 # make some schemes as instances so we can copy them for modification easily:
53 __PColLinux = coloransi.ColorScheme(
56 PColLinux = coloransi.ColorScheme(
54 'Linux',
57 'Linux',
55 in_prompt = InputColors.Green,
58 in_prompt = InputColors.Green,
56 in_number = InputColors.LightGreen,
59 in_number = InputColors.LightGreen,
@@ -62,25 +65,35 b' __PColLinux = coloransi.ColorScheme('
62
65
63 normal = Colors.Normal
66 normal = Colors.Normal
64 )
67 )
65 # Don't forget to enter it into the table!
66 PromptColors.add_scheme(__PColLinux)
67
68
68 # Slightly modified Linux for light backgrounds
69 # Slightly modified Linux for light backgrounds
69 __PColLightBG = __PColLinux.copy('LightBG')
70 PColLightBG = PColLinux.copy('LightBG')
70
71
71 __PColLightBG.colors.update(
72 PColLightBG.colors.update(
72 in_prompt = InputColors.Blue,
73 in_prompt = InputColors.Blue,
73 in_number = InputColors.LightBlue,
74 in_number = InputColors.LightBlue,
74 in_prompt2 = InputColors.Blue
75 in_prompt2 = InputColors.Blue
75 )
76 )
76 PromptColors.add_scheme(__PColLightBG)
77
78 del Colors,InputColors
79
77
80 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
81 # Utilities
79 # Utilities
82 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
83
81
82 class LazyEvaluate(object):
83 """This is used for formatting strings with values that need to be updated
84 at that time, such as the current time or line number."""
85 def __init__(self, func, *args, **kwargs):
86 self.func = func
87 self.args = args
88 self.kwargs = kwargs
89
90 def __call__(self, **kwargs):
91 self.kwargs.update(kwargs)
92 return self.func(*self.args, **self.kwargs)
93
94 def __str__(self):
95 return str(self())
96
84 def multiple_replace(dict, text):
97 def multiple_replace(dict, text):
85 """ Replace in 'text' all occurences of any key in the given
98 """ Replace in 'text' all occurences of any key in the given
86 dictionary by its corresponding value. Returns the new string."""
99 dictionary by its corresponding value. Returns the new string."""
@@ -121,51 +134,43 b' HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")'
121 USER = os.environ.get("USER")
134 USER = os.environ.get("USER")
122 HOSTNAME = socket.gethostname()
135 HOSTNAME = socket.gethostname()
123 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
136 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
124 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
137 ROOT_SYMBOL = "#" if (os.name=='nt' or os.getuid()==0) else "$"
125
138
126 prompt_specials_color = {
139 prompt_abbreviations = {
127 # Prompt/history count
140 # Prompt/history count
128 '%n' : '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
141 '%n' : '{color.number}' '{count}' '{color.prompt}',
129 r'\#': '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
142 r'\#': '{color.number}' '{count}' '{color.prompt}',
130 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
143 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
131 # can get numbers displayed in whatever color they want.
144 # can get numbers displayed in whatever color they want.
132 r'\N': '${self.cache.prompt_count}',
145 r'\N': '{count}',
133
146
134 # Prompt/history count, with the actual digits replaced by dots. Used
147 # Prompt/history count, with the actual digits replaced by dots. Used
135 # mainly in continuation prompts (prompt_in2)
148 # mainly in continuation prompts (prompt_in2)
136 #r'\D': '${"."*len(str(self.cache.prompt_count))}',
149 r'\D': '{dots}',
137
150
138 # More robust form of the above expression, that uses the __builtin__
139 # module. Note that we can NOT use __builtins__ (note the 's'), because
140 # that can either be a dict or a module, and can even mutate at runtime,
141 # depending on the context (Python makes no guarantees on it). In
142 # contrast, __builtin__ is always a module object, though it must be
143 # explicitly imported.
144 r'\D': '${"."*__builtin__.len(__builtin__.str(self.cache.prompt_count))}',
145
146 # Current working directory
147 r'\w': '${os.getcwd()}',
148 # Current time
151 # Current time
149 r'\t' : '${time.strftime("%H:%M:%S")}',
152 r'\t' : '{time}',
153 # Current working directory
154 r'\w': '{cwd}',
150 # Basename of current working directory.
155 # Basename of current working directory.
151 # (use os.sep to make this portable across OSes)
156 # (use os.sep to make this portable across OSes)
152 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
157 r'\W' : '{cwd_last}',
153 # These X<N> are an extension to the normal bash prompts. They return
158 # These X<N> are an extension to the normal bash prompts. They return
154 # N terms of the path, after replacing $HOME with '~'
159 # N terms of the path, after replacing $HOME with '~'
155 r'\X0': '${os.getcwd().replace("%s","~")}' % HOME,
160 r'\X0': '{cwd_x[0])}',
156 r'\X1': '${self.cwd_filt(1)}',
161 r'\X1': '{cwd_x[1])}',
157 r'\X2': '${self.cwd_filt(2)}',
162 r'\X2': '{cwd_x[2])}',
158 r'\X3': '${self.cwd_filt(3)}',
163 r'\X3': '{cwd_x[3])}',
159 r'\X4': '${self.cwd_filt(4)}',
164 r'\X4': '{cwd_x[4])}',
160 r'\X5': '${self.cwd_filt(5)}',
165 r'\X5': '{cwd_x[5])}',
161 # Y<N> are similar to X<N>, but they show '~' if it's the directory
166 # Y<N> are similar to X<N>, but they show '~' if it's the directory
162 # N+1 in the list. Somewhat like %cN in tcsh.
167 # N+1 in the list. Somewhat like %cN in tcsh.
163 r'\Y0': '${self.cwd_filt2(0)}',
168 r'\Y0': '{cwd_y[0])}',
164 r'\Y1': '${self.cwd_filt2(1)}',
169 r'\Y1': '{cwd_y[1])}',
165 r'\Y2': '${self.cwd_filt2(2)}',
170 r'\Y2': '{cwd_y[2])}',
166 r'\Y3': '${self.cwd_filt2(3)}',
171 r'\Y3': '{cwd_y[3])}',
167 r'\Y4': '${self.cwd_filt2(4)}',
172 r'\Y4': '{cwd_y[4])}',
168 r'\Y5': '${self.cwd_filt2(5)}',
173 r'\Y5': '{cwd_y[5])}',
169 # Hostname up to first .
174 # Hostname up to first .
170 r'\h': HOSTNAME_SHORT,
175 r'\h': HOSTNAME_SHORT,
171 # Full hostname
176 # Full hostname
@@ -184,28 +189,6 b' prompt_specials_color = {'
184 r'\$': ROOT_SYMBOL,
189 r'\$': ROOT_SYMBOL,
185 }
190 }
186
191
187 # A copy of the prompt_specials dictionary but with all color escapes removed,
188 # so we can correctly compute the prompt length for the auto_rewrite method.
189 prompt_specials_nocolor = prompt_specials_color.copy()
190 prompt_specials_nocolor['%n'] = '${self.cache.prompt_count}'
191 prompt_specials_nocolor[r'\#'] = '${self.cache.prompt_count}'
192
193 # Add in all the InputTermColors color escapes as valid prompt characters.
194 # They all get added as \\C_COLORNAME, so that we don't have any conflicts
195 # with a color name which may begin with a letter used by any other of the
196 # allowed specials. This of course means that \\C will never be allowed for
197 # anything else.
198 input_colors = coloransi.InputTermColors
199 for _color in dir(input_colors):
200 if _color[0] != '_':
201 c_name = r'\C_'+_color
202 prompt_specials_color[c_name] = getattr(input_colors,_color)
203 prompt_specials_nocolor[c_name] = ''
204
205 # we default to no color for safety. Note that prompt_specials is a global
206 # variable used by all prompt objects.
207 prompt_specials = prompt_specials_nocolor
208
209 #-----------------------------------------------------------------------------
192 #-----------------------------------------------------------------------------
210 # More utilities
193 # More utilities
211 #-----------------------------------------------------------------------------
194 #-----------------------------------------------------------------------------
@@ -230,207 +213,166 b' def str_safe(arg):'
230 #raise # dbg
213 #raise # dbg
231 return out
214 return out
232
215
233 #-----------------------------------------------------------------------------
216 def cwd_filt(self, depth):
234 # Prompt classes
217 """Return the last depth elements of the current working directory.
235 #-----------------------------------------------------------------------------
236
237 class BasePrompt(object):
238 """Interactive prompt similar to Mathematica's."""
239
240 def _get_p_template(self):
241 return self._p_template
242
243 def _set_p_template(self,val):
244 self._p_template = val
245 self.set_p_str()
246
218
247 p_template = property(_get_p_template,_set_p_template,
219 $HOME is always replaced with '~'.
248 doc='Template for prompt string creation')
220 If depth==0, the full path is returned."""
249
221
250 def __init__(self, cache, sep, prompt, pad_left=False):
222 cwd = os.getcwd().replace(HOME,"~")
223 out = os.sep.join(cwd.split(os.sep)[-depth:])
224 return out or os.sep
251
225
252 # Hack: we access information about the primary prompt through the
226 def cwd_filt2(self, depth):
253 # cache argument. We need this, because we want the secondary prompt
227 """Return the last depth elements of the current working directory.
254 # to be aligned with the primary one. Color table info is also shared
255 # by all prompt classes through the cache. Nice OO spaghetti code!
256 self.cache = cache
257 self.sep = sep
258
228
259 # regexp to count the number of spaces at the end of a prompt
229 $HOME is always replaced with '~'.
260 # expression, useful for prompt auto-rewriting
230 If depth==0, the full path is returned."""
261 self.rspace = re.compile(r'(\s*)$')
262 # Flag to left-pad prompt strings to match the length of the primary
263 # prompt
264 self.pad_left = pad_left
265
231
266 # Set template to create each actual prompt (where numbers change).
232 full_cwd = os.getcwd()
267 # Use a property
233 cwd = full_cwd.replace(HOME,"~").split(os.sep)
268 self.p_template = prompt
234 if '~' in cwd and len(cwd) == depth+1:
269 self.set_p_str()
235 depth += 1
236 drivepart = ''
237 if sys.platform == 'win32' and len(cwd) > depth:
238 drivepart = os.path.splitdrive(full_cwd)[0]
239 out = drivepart + '/'.join(cwd[-depth:])
270
240
271 def set_p_str(self):
241 return out or os.sep
272 """ Set the interpolating prompt strings.
273
242
274 This must be called every time the color settings change, because the
243 #-----------------------------------------------------------------------------
275 prompt_specials global may have changed."""
244 # Prompt classes
276
245 #-----------------------------------------------------------------------------
277 import os,time # needed in locals for prompt string handling
278 loc = locals()
279 try:
280 self.p_str = ItplNS('%s%s%s' %
281 ('${self.sep}${self.col_p}',
282 multiple_replace(prompt_specials, self.p_template),
283 '${self.col_norm}'),self.cache.shell.user_ns,loc)
284
285 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
286 self.p_template),
287 self.cache.shell.user_ns,loc)
288 except:
289 print "Illegal prompt template (check $ usage!):",self.p_template
290 self.p_str = self.p_template
291 self.p_str_nocolor = self.p_template
292
293 def write(self, msg):
294 sys.stdout.write(msg)
295 return ''
296
297 def __str__(self):
298 """Return a string form of the prompt.
299
300 This for is useful for continuation and output prompts, since it is
301 left-padded to match lengths with the primary one (if the
302 self.pad_left attribute is set)."""
303
304 out_str = str_safe(self.p_str)
305 if self.pad_left:
306 # We must find the amount of padding required to match lengths,
307 # taking the color escapes (which are invisible on-screen) into
308 # account.
309 esc_pad = len(out_str) - len(str_safe(self.p_str_nocolor))
310 format = '%%%ss' % (len(str(self.cache.last_prompt))+esc_pad)
311 return format % out_str
312 else:
313 return out_str
314
315 # these path filters are put in as methods so that we can control the
316 # namespace where the prompt strings get evaluated
317 def cwd_filt(self, depth):
318 """Return the last depth elements of the current working directory.
319
320 $HOME is always replaced with '~'.
321 If depth==0, the full path is returned."""
322
323 cwd = os.getcwd().replace(HOME,"~")
324 out = os.sep.join(cwd.split(os.sep)[-depth:])
325 if out:
326 return out
327 else:
328 return os.sep
329
330 def cwd_filt2(self, depth):
331 """Return the last depth elements of the current working directory.
332
333 $HOME is always replaced with '~'.
334 If depth==0, the full path is returned."""
335
336 full_cwd = os.getcwd()
337 cwd = full_cwd.replace(HOME,"~").split(os.sep)
338 if '~' in cwd and len(cwd) == depth+1:
339 depth += 1
340 drivepart = ''
341 if sys.platform == 'win32' and len(cwd) > depth:
342 drivepart = os.path.splitdrive(full_cwd)[0]
343 out = drivepart + '/'.join(cwd[-depth:])
344
246
345 if out:
247 lazily_evaluate = {'time': LazyEvaluate(time.strftime, "%H:%M:%S"),
346 return out
248 'cwd': LazyEvaluate(os.getcwd),
249 'cwd_last': LazyEvaluate(lambda: os.getcwd().split(os.sep)[-1]),
250 'cwd_x': [LazyEvaluate(lambda: os.getcwd().replace("%s","~"))] +\
251 [LazyEvaluate(cwd_filt, x) for x in range(1,6)],
252 'cwd_y': [LazyEvaluate(cwd_filt2, x) for x in range(6)]
253 }
254
255
256 class PromptManager(Configurable):
257 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
258
259 color_scheme_table = Instance(coloransi.ColorSchemeTable)
260 color_scheme = Unicode('Linux')
261 def _color_scheme_changed(self, name, new_value):
262 self.color_scheme_table.set_active_scheme(new_value)
263 for pname in ['in', 'in2', 'out', 'rewrite']:
264 # We need to recalculate the number of invisible characters
265 self.update_prompt(pname)
266
267 # These fields can be referenced in prompt templates, and are evaluated
268 # when the prompt is generated - for things like timestamps. They are only
269 # evaluated if a prompt uses them.
270 lazy_evaluate_fields = Dict()
271 def _lazy_evaluate_fields_default(self): return lazily_evaluate.copy()
272
273 in_template = Unicode('In [\\#]: ', config=True)
274 in2_template = Unicode(' .\\D.: ', config=True)
275 out_template = Unicode('Out[\\#]: ', config=True)
276 rewrite_template = Unicode("------> ", config=True)
277
278 # Justify prompts by default?
279 justify = Bool(True)
280
281 # We actually store the expanded templates here:
282 templates = Dict()
283
284 # The number of characters in the last prompt rendered, not including
285 # colour characters.
286 width = Int()
287
288 # The number of characters in each prompt which don't contribute to width
289 invisible_chars = Dict()
290 def _invisible_chars_default(self):
291 return {'in': 0, 'in2': 0, 'out': 0, 'rewrite': 0}
292
293 def __init__(self, shell, config=None):
294 super(PromptManager, self).__init__(shell=shell, config=config)
295
296 # Prepare colour scheme table
297 self.color_scheme_table = coloransi.ColorSchemeTable([PColNoColors,
298 PColLinux, PColLightBG], self.color_scheme)
299
300 # Prepare templates
301 self.update_prompt('in', self.in_template)
302 self.update_prompt('in2', self.in2_template)
303 self.update_prompt('out', self.out_template)
304 self.update_prompt('rewrite', self.rewrite_template)
305 self.on_trait_change(self._update_prompt_trait, ['in_template',
306 'in2_template', 'out_template', 'rewrite_template'])
307
308 def update_prompt(self, name, new_template=None):
309 if new_template is not None:
310 self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
311 invis_chars = len(self.render(name, color=True, just=False)) - \
312 len(self.render(name, color=False, just=False))
313 self.invisible_chars[name] = invis_chars
314
315 def _update_prompt_trait(self, traitname, new_template):
316 name = traitname[:-9] # Cut off '_template'
317 self.update_prompt(name, new_template)
318
319 def render(self, name, color=True, just=None, **kwargs):
320 """
321 Render the selected prompt.
322
323 Parameters
324 ----------
325 name : str
326 Which prompt to render. One of 'in', 'in2', 'out', 'rewrite'
327 color : bool
328 If True (default), include ANSI escape sequences for a coloured prompt.
329 just : bool
330 If True, justify the prompt to the width of the last prompt. The
331 default is stored in self.justify.
332 **kwargs :
333 Additional arguments will be passed to the string formatting operation,
334 so they can override the values that would otherwise fill in the
335 template.
336
337 Returns
338 -------
339 A string containing the rendered prompt.
340 """
341 if color:
342 scheme = self.color_scheme_table.active_colors
343 if name=='out':
344 colors = color_lists['normal']
345 colors.number, colors.prompt, colors.normal = \
346 scheme.out_number, scheme.out_prompt, scheme.normal
347 else:
348 colors = color_lists['inp']
349 colors.number, colors.prompt, colors.normal = \
350 scheme.in_number, scheme.in_prompt, scheme.in_normal
351 if name=='in2':
352 colors.prompt = scheme.in_prompt2
347 else:
353 else:
348 return os.sep
354 # No color
349
355 colors = color_lists['nocolor']
350 def __nonzero__(self):
356 colors.number, colors.prompt, colors.normal = '', '', ''
351 """Implement boolean behavior.
357
352
358 count = self.shell.execution_count # Shorthand
353 Checks whether the p_str attribute is non-empty"""
359 # Build the dictionary to be passed to string formatting
354
360 fmtargs = dict(color=colors, count=count,
355 return bool(self.p_template)
361 dots="."*len(str(count)) )
356
362 fmtargs.update(self.lazy_evaluate_fields)
357
363 fmtargs.update(kwargs)
358 class Prompt1(BasePrompt):
364
359 """Input interactive prompt similar to Mathematica's."""
365 # Prepare the prompt
360
366 prompt = colors.prompt + self.templates[name] + colors.normal
361 def __init__(self, cache, sep='\n', prompt='In [\\#]: ', pad_left=True):
367
362 BasePrompt.__init__(self, cache, sep, prompt, pad_left)
368 # Fill in required fields
363
369 res = prompt.format(**fmtargs)
364 def set_colors(self):
370
365 self.set_p_str()
371 # Handle justification of prompt
366 Colors = self.cache.color_table.active_colors # shorthand
372 invis_chars = self.invisible_chars[name] if color else 0
367 self.col_p = Colors.in_prompt
373 just = self.justify if (just is None) else just
368 self.col_num = Colors.in_number
374 if just:
369 self.col_norm = Colors.in_normal
375 res = res.rjust(self.width + invis_chars)
370 # We need a non-input version of these escapes for the '--->'
376 self.width = len(res) - invis_chars
371 # auto-call prompts used in the auto_rewrite() method.
377 return res
372 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
373 self.col_norm_ni = Colors.normal
374
375 def __str__(self):
376 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
377 return str_safe(self.p_str)
378
379 def auto_rewrite(self):
380 """Return a string of the form '--->' which lines up with the previous
381 input string. Useful for systems which re-write the user input when
382 handling automatically special syntaxes."""
383
384 curr = str(self.cache.last_prompt)
385 nrspaces = len(self.rspace.search(curr).group())
386 return '%s%s>%s%s' % (self.col_p_ni,'-'*(len(curr)-nrspaces-1),
387 ' '*nrspaces,self.col_norm_ni)
388
389
390 class PromptOut(BasePrompt):
391 """Output interactive prompt similar to Mathematica's."""
392
393 def __init__(self, cache, sep='', prompt='Out[\\#]: ', pad_left=True):
394 BasePrompt.__init__(self, cache, sep, prompt, pad_left)
395 if not self.p_template:
396 self.__str__ = lambda: ''
397
398 def set_colors(self):
399 self.set_p_str()
400 Colors = self.cache.color_table.active_colors # shorthand
401 self.col_p = Colors.out_prompt
402 self.col_num = Colors.out_number
403 self.col_norm = Colors.normal
404
405
406 class Prompt2(BasePrompt):
407 """Interactive continuation prompt."""
408
409 def __init__(self, cache, prompt=' .\\D.: ', pad_left=True):
410 self.cache = cache
411 self.p_template = prompt
412 self.pad_left = pad_left
413 self.set_p_str()
414
415 def set_p_str(self):
416 import os,time # needed in locals for prompt string handling
417 loc = locals()
418 self.p_str = ItplNS('%s%s%s' %
419 ('${self.col_p2}',
420 multiple_replace(prompt_specials, self.p_template),
421 '$self.col_norm'),
422 self.cache.shell.user_ns,loc)
423 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
424 self.p_template),
425 self.cache.shell.user_ns,loc)
426
427 def set_colors(self):
428 self.set_p_str()
429 Colors = self.cache.color_table.active_colors
430 self.col_p2 = Colors.in_prompt2
431 self.col_norm = Colors.in_normal
432 # FIXME (2004-06-16) HACK: prevent crashes for users who haven't
433 # updated their prompt_in2 definitions. Remove eventually.
434 self.col_p = Colors.out_prompt
435 self.col_num = Colors.out_number
436
378
@@ -148,7 +148,7 b' class TestMagicRunPass(tt.TempFileMixin):'
148 """Test that prompts correctly generate after %run"""
148 """Test that prompts correctly generate after %run"""
149 self.run_tmpfile()
149 self.run_tmpfile()
150 _ip = get_ipython()
150 _ip = get_ipython()
151 p2 = str(_ip.displayhook.prompt2).strip()
151 p2 = _ip.prompt_manager.render('in2').strip()
152 nt.assert_equals(p2[:3], '...')
152 nt.assert_equals(p2[:3], '...')
153
153
154 def test_run_profile( self ):
154 def test_run_profile( self ):
@@ -356,7 +356,7 b' class TerminalInteractiveShell(InteractiveShell):'
356 self.hooks.pre_prompt_hook()
356 self.hooks.pre_prompt_hook()
357 if more:
357 if more:
358 try:
358 try:
359 prompt = self.hooks.generate_prompt(True)
359 prompt = self.prompt_manager.render('in2')
360 except:
360 except:
361 self.showtraceback()
361 self.showtraceback()
362 if self.autoindent:
362 if self.autoindent:
@@ -364,7 +364,7 b' class TerminalInteractiveShell(InteractiveShell):'
364
364
365 else:
365 else:
366 try:
366 try:
367 prompt = self.hooks.generate_prompt(False)
367 prompt = self.separate_in + self.prompt_manager.render('in')
368 except:
368 except:
369 self.showtraceback()
369 self.showtraceback()
370 try:
370 try:
@@ -15,12 +15,7 b' import os'
15
15
16 from IPython.utils.ipstruct import Struct
16 from IPython.utils.ipstruct import Struct
17
17
18 def make_color_table(in_class):
18 color_templates = (
19 """Build a set of color attributes in a class.
20
21 Helper function for building the *TermColors classes."""
22
23 color_templates = (
24 # Dark colors
19 # Dark colors
25 ("Black" , "0;30"),
20 ("Black" , "0;30"),
26 ("Red" , "0;31"),
21 ("Red" , "0;31"),
@@ -50,6 +45,11 b' def make_color_table(in_class):'
50 ("BlinkLightGray", "5;37"),
45 ("BlinkLightGray", "5;37"),
51 )
46 )
52
47
48 def make_color_table(in_class):
49 """Build a set of color attributes in a class.
50
51 Helper function for building the *TermColors classes."""
52
53 for name,value in color_templates:
53 for name,value in color_templates:
54 setattr(in_class,name,in_class._base % value)
54 setattr(in_class,name,in_class._base % value)
55
55
@@ -98,6 +98,14 b' class InputTermColors:'
98 # Build the actual color table as a set of class attributes:
98 # Build the actual color table as a set of class attributes:
99 make_color_table(InputTermColors)
99 make_color_table(InputTermColors)
100
100
101 class NoColors:
102 """This defines all the same names as the colour classes, but maps them to
103 empty strings, so it can easily be substituted to turn off colours."""
104 NoColor = ''
105
106 for name, value in color_templates:
107 setattr(NoColors, name, '')
108
101 class ColorScheme:
109 class ColorScheme:
102 """Generic color scheme class. Just a name and a Struct."""
110 """Generic color scheme class. Just a name and a Struct."""
103 def __init__(self,__scheme_name_,colordict=None,**colormap):
111 def __init__(self,__scheme_name_,colordict=None,**colormap):
@@ -133,7 +133,7 b' class ZMQInteractiveShell(InteractiveShell):'
133 FIXME: this payload is currently not correctly processed by the
133 FIXME: this payload is currently not correctly processed by the
134 frontend.
134 frontend.
135 """
135 """
136 new = self.displayhook.prompt1.auto_rewrite() + cmd
136 new = self.prompt_manager.render('rewrite') + cmd
137 payload = dict(
137 payload = dict(
138 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
138 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
139 transformed_input=new,
139 transformed_input=new,
General Comments 0
You need to be logged in to leave comments. Login now