##// END OF EJS Templates
Expand tabs to 4 spaces in %history.
Fernando Perez -
Show More
@@ -1,279 +1,282 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """ History related magics and functionality """
2 """ History related magics and functionality """
3
3
4 # Stdlib imports
4 # Stdlib imports
5 import fnmatch
5 import fnmatch
6 import os
6 import os
7
7
8 import IPython.utils.io
8 import IPython.utils.io
9 from IPython.utils.io import ask_yes_no
9 from IPython.utils.io import ask_yes_no
10 from IPython.utils.warn import warn
10 from IPython.utils.warn import warn
11 from IPython.core import ipapi
11 from IPython.core import ipapi
12
12
13 def magic_history(self, parameter_s = ''):
13 def magic_history(self, parameter_s = ''):
14 """Print input history (_i<n> variables), with most recent last.
14 """Print input history (_i<n> variables), with most recent last.
15
15
16 %history -> print at most 40 inputs (some may be multi-line)\\
16 %history -> print at most 40 inputs (some may be multi-line)\\
17 %history n -> print at most n inputs\\
17 %history n -> print at most n inputs\\
18 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
18 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
19
19
20 By default, input history is printed without line numbers so it can be
20 By default, input history is printed without line numbers so it can be
21 directly pasted into an editor.
21 directly pasted into an editor.
22
22
23 With -n, each input's number <n> is shown, and is accessible as the
23 With -n, each input's number <n> is shown, and is accessible as the
24 automatically generated variable _i<n> as well as In[<n>]. Multi-line
24 automatically generated variable _i<n> as well as In[<n>]. Multi-line
25 statements are printed starting at a new line for easy copy/paste.
25 statements are printed starting at a new line for easy copy/paste.
26
26
27 Options:
27 Options:
28
28
29 -n: print line numbers for each input.
29 -n: print line numbers for each input.
30 This feature is only available if numbered prompts are in use.
30 This feature is only available if numbered prompts are in use.
31
31
32 -o: also print outputs for each input.
32 -o: also print outputs for each input.
33
33
34 -p: print classic '>>>' python prompts before each input. This is useful
34 -p: print classic '>>>' python prompts before each input. This is useful
35 for making documentation, and in conjunction with -o, for producing
35 for making documentation, and in conjunction with -o, for producing
36 doctest-ready output.
36 doctest-ready output.
37
37
38 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
38 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
39
39
40 -t: print the 'translated' history, as IPython understands it. IPython
40 -t: print the 'translated' history, as IPython understands it. IPython
41 filters your input and converts it all into valid Python source before
41 filters your input and converts it all into valid Python source before
42 executing it (things like magics or aliases are turned into function
42 executing it (things like magics or aliases are turned into function
43 calls, for example). With this option, you'll see the native history
43 calls, for example). With this option, you'll see the native history
44 instead of the user-entered version: '%cd /' will be seen as
44 instead of the user-entered version: '%cd /' will be seen as
45 'get_ipython().magic("%cd /")' instead of '%cd /'.
45 'get_ipython().magic("%cd /")' instead of '%cd /'.
46
46
47 -g: treat the arg as a pattern to grep for in (full) history.
47 -g: treat the arg as a pattern to grep for in (full) history.
48 This includes the "shadow history" (almost all commands ever written).
48 This includes the "shadow history" (almost all commands ever written).
49 Use '%hist -g' to show full shadow history (may be very long).
49 Use '%hist -g' to show full shadow history (may be very long).
50 In shadow history, every index nuwber starts with 0.
50 In shadow history, every index nuwber starts with 0.
51
51
52 -f FILENAME: instead of printing the output to the screen, redirect it to
52 -f FILENAME: instead of printing the output to the screen, redirect it to
53 the given file. The file is always overwritten, though IPython asks for
53 the given file. The file is always overwritten, though IPython asks for
54 confirmation first if it already exists.
54 confirmation first if it already exists.
55 """
55 """
56
56
57 if not self.displayhook.do_full_cache:
57 if not self.displayhook.do_full_cache:
58 print 'This feature is only available if numbered prompts are in use.'
58 print 'This feature is only available if numbered prompts are in use.'
59 return
59 return
60 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
60 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
61
61
62 # Check if output to specific file was requested.
62 # Check if output to specific file was requested.
63 try:
63 try:
64 outfname = opts['f']
64 outfname = opts['f']
65 except KeyError:
65 except KeyError:
66 outfile = IPython.utils.io.Term.cout # default
66 outfile = IPython.utils.io.Term.cout # default
67 # We don't want to close stdout at the end!
67 # We don't want to close stdout at the end!
68 close_at_end = False
68 close_at_end = False
69 else:
69 else:
70 if os.path.exists(outfname):
70 if os.path.exists(outfname):
71 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
71 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
72 print 'Aborting.'
72 print 'Aborting.'
73 return
73 return
74
74
75 outfile = open(outfname,'w')
75 outfile = open(outfname,'w')
76 close_at_end = True
76 close_at_end = True
77
77
78 if 't' in opts:
78 if 't' in opts:
79 input_hist = self.input_hist
79 input_hist = self.input_hist
80 elif 'r' in opts:
80 elif 'r' in opts:
81 input_hist = self.input_hist_raw
81 input_hist = self.input_hist_raw
82 else:
82 else:
83 # Raw history is the default
83 # Raw history is the default
84 input_hist = self.input_hist_raw
84 input_hist = self.input_hist_raw
85
85
86 default_length = 40
86 default_length = 40
87 pattern = None
87 pattern = None
88 if 'g' in opts:
88 if 'g' in opts:
89 init = 1
89 init = 1
90 final = len(input_hist)
90 final = len(input_hist)
91 parts = parameter_s.split(None, 1)
91 parts = parameter_s.split(None, 1)
92 if len(parts) == 1:
92 if len(parts) == 1:
93 parts += '*'
93 parts += '*'
94 head, pattern = parts
94 head, pattern = parts
95 pattern = "*" + pattern + "*"
95 pattern = "*" + pattern + "*"
96 elif len(args) == 0:
96 elif len(args) == 0:
97 final = len(input_hist)-1
97 final = len(input_hist)-1
98 init = max(1,final-default_length)
98 init = max(1,final-default_length)
99 elif len(args) == 1:
99 elif len(args) == 1:
100 final = len(input_hist)
100 final = len(input_hist)
101 init = max(1, final-int(args[0]))
101 init = max(1, final-int(args[0]))
102 elif len(args) == 2:
102 elif len(args) == 2:
103 init, final = map(int, args)
103 init, final = map(int, args)
104 else:
104 else:
105 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
105 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
106 print >> IPython.utils.io.Term.cout, self.magic_hist.__doc__
106 print >> IPython.utils.io.Term.cout, self.magic_hist.__doc__
107 return
107 return
108
108
109 width = len(str(final))
109 width = len(str(final))
110 line_sep = ['','\n']
110 line_sep = ['','\n']
111 print_nums = 'n' in opts
111 print_nums = 'n' in opts
112 print_outputs = 'o' in opts
112 print_outputs = 'o' in opts
113 pyprompts = 'p' in opts
113 pyprompts = 'p' in opts
114
114
115 found = False
115 found = False
116 if pattern is not None:
116 if pattern is not None:
117 sh = self.shadowhist.all()
117 sh = self.shadowhist.all()
118 for idx, s in sh:
118 for idx, s in sh:
119 if fnmatch.fnmatch(s, pattern):
119 if fnmatch.fnmatch(s, pattern):
120 print >> outfile, "0%d: %s" %(idx, s)
120 print >> outfile, "0%d: %s" %(idx, s.expandtabs(4))
121 found = True
121 found = True
122
122
123 if found:
123 if found:
124 print >> outfile, "==="
124 print >> outfile, "==="
125 print >> outfile, \
125 print >> outfile, \
126 "shadow history ends, fetch by %rep <number> (must start with 0)"
126 "shadow history ends, fetch by %rep <number> (must start with 0)"
127 print >> outfile, "=== start of normal history ==="
127 print >> outfile, "=== start of normal history ==="
128
128
129 for in_num in range(init,final):
129 for in_num in range(init,final):
130 inline = input_hist[in_num]
130 # Print user history with tabs expanded to 4 spaces. The GUI clients
131 # use hard tabs for easier usability in auto-indented code, but we want
132 # to produce PEP-8 compliant history for safe pasting into an editor.
133 inline = input_hist[in_num].expandtabs(4)
131 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
134 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
132 continue
135 continue
133
136
134 multiline = int(inline.count('\n') > 1)
137 multiline = int(inline.count('\n') > 1)
135 if print_nums:
138 if print_nums:
136 print >> outfile, \
139 print >> outfile, \
137 '%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
140 '%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
138 if pyprompts:
141 if pyprompts:
139 print >> outfile, '>>>',
142 print >> outfile, '>>>',
140 if multiline:
143 if multiline:
141 lines = inline.splitlines()
144 lines = inline.splitlines()
142 print >> outfile, '\n... '.join(lines)
145 print >> outfile, '\n... '.join(lines)
143 print >> outfile, '... '
146 print >> outfile, '... '
144 else:
147 else:
145 print >> outfile, inline,
148 print >> outfile, inline,
146 else:
149 else:
147 print >> outfile, inline,
150 print >> outfile, inline,
148 if print_outputs:
151 if print_outputs:
149 output = self.shell.user_ns['Out'].get(in_num)
152 output = self.shell.user_ns['Out'].get(in_num)
150 if output is not None:
153 if output is not None:
151 print >> outfile, repr(output)
154 print >> outfile, repr(output)
152
155
153 if close_at_end:
156 if close_at_end:
154 outfile.close()
157 outfile.close()
155
158
156
159
157 def magic_hist(self, parameter_s=''):
160 def magic_hist(self, parameter_s=''):
158 """Alternate name for %history."""
161 """Alternate name for %history."""
159 return self.magic_history(parameter_s)
162 return self.magic_history(parameter_s)
160
163
161
164
162 def rep_f(self, arg):
165 def rep_f(self, arg):
163 r""" Repeat a command, or get command to input line for editing
166 r""" Repeat a command, or get command to input line for editing
164
167
165 - %rep (no arguments):
168 - %rep (no arguments):
166
169
167 Place a string version of last computation result (stored in the special '_'
170 Place a string version of last computation result (stored in the special '_'
168 variable) to the next input prompt. Allows you to create elaborate command
171 variable) to the next input prompt. Allows you to create elaborate command
169 lines without using copy-paste::
172 lines without using copy-paste::
170
173
171 $ l = ["hei", "vaan"]
174 $ l = ["hei", "vaan"]
172 $ "".join(l)
175 $ "".join(l)
173 ==> heivaan
176 ==> heivaan
174 $ %rep
177 $ %rep
175 $ heivaan_ <== cursor blinking
178 $ heivaan_ <== cursor blinking
176
179
177 %rep 45
180 %rep 45
178
181
179 Place history line 45 to next input prompt. Use %hist to find out the
182 Place history line 45 to next input prompt. Use %hist to find out the
180 number.
183 number.
181
184
182 %rep 1-4 6-7 3
185 %rep 1-4 6-7 3
183
186
184 Repeat the specified lines immediately. Input slice syntax is the same as
187 Repeat the specified lines immediately. Input slice syntax is the same as
185 in %macro and %save.
188 in %macro and %save.
186
189
187 %rep foo
190 %rep foo
188
191
189 Place the most recent line that has the substring "foo" to next input.
192 Place the most recent line that has the substring "foo" to next input.
190 (e.g. 'svn ci -m foobar').
193 (e.g. 'svn ci -m foobar').
191 """
194 """
192
195
193 opts,args = self.parse_options(arg,'',mode='list')
196 opts,args = self.parse_options(arg,'',mode='list')
194 if not args:
197 if not args:
195 self.set_next_input(str(self.user_ns["_"]))
198 self.set_next_input(str(self.user_ns["_"]))
196 return
199 return
197
200
198 if len(args) == 1 and not '-' in args[0]:
201 if len(args) == 1 and not '-' in args[0]:
199 arg = args[0]
202 arg = args[0]
200 if len(arg) > 1 and arg.startswith('0'):
203 if len(arg) > 1 and arg.startswith('0'):
201 # get from shadow hist
204 # get from shadow hist
202 num = int(arg[1:])
205 num = int(arg[1:])
203 line = self.shadowhist.get(num)
206 line = self.shadowhist.get(num)
204 self.set_next_input(str(line))
207 self.set_next_input(str(line))
205 return
208 return
206 try:
209 try:
207 num = int(args[0])
210 num = int(args[0])
208 self.set_next_input(str(self.input_hist_raw[num]).rstrip())
211 self.set_next_input(str(self.input_hist_raw[num]).rstrip())
209 return
212 return
210 except ValueError:
213 except ValueError:
211 pass
214 pass
212
215
213 for h in reversed(self.input_hist_raw):
216 for h in reversed(self.input_hist_raw):
214 if 'rep' in h:
217 if 'rep' in h:
215 continue
218 continue
216 if fnmatch.fnmatch(h,'*' + arg + '*'):
219 if fnmatch.fnmatch(h,'*' + arg + '*'):
217 self.set_next_input(str(h).rstrip())
220 self.set_next_input(str(h).rstrip())
218 return
221 return
219
222
220 try:
223 try:
221 lines = self.extract_input_slices(args, True)
224 lines = self.extract_input_slices(args, True)
222 print "lines",lines
225 print "lines",lines
223 self.runlines(lines)
226 self.runlines(lines)
224 except ValueError:
227 except ValueError:
225 print "Not found in recent history:", args
228 print "Not found in recent history:", args
226
229
227
230
228 _sentinel = object()
231 _sentinel = object()
229
232
230 class ShadowHist(object):
233 class ShadowHist(object):
231 def __init__(self,db):
234 def __init__(self,db):
232 # cmd => idx mapping
235 # cmd => idx mapping
233 self.curidx = 0
236 self.curidx = 0
234 self.db = db
237 self.db = db
235 self.disabled = False
238 self.disabled = False
236
239
237 def inc_idx(self):
240 def inc_idx(self):
238 idx = self.db.get('shadowhist_idx', 1)
241 idx = self.db.get('shadowhist_idx', 1)
239 self.db['shadowhist_idx'] = idx + 1
242 self.db['shadowhist_idx'] = idx + 1
240 return idx
243 return idx
241
244
242 def add(self, ent):
245 def add(self, ent):
243 if self.disabled:
246 if self.disabled:
244 return
247 return
245 try:
248 try:
246 old = self.db.hget('shadowhist', ent, _sentinel)
249 old = self.db.hget('shadowhist', ent, _sentinel)
247 if old is not _sentinel:
250 if old is not _sentinel:
248 return
251 return
249 newidx = self.inc_idx()
252 newidx = self.inc_idx()
250 #print "new",newidx # dbg
253 #print "new",newidx # dbg
251 self.db.hset('shadowhist',ent, newidx)
254 self.db.hset('shadowhist',ent, newidx)
252 except:
255 except:
253 ipapi.get().showtraceback()
256 ipapi.get().showtraceback()
254 print "WARNING: disabling shadow history"
257 print "WARNING: disabling shadow history"
255 self.disabled = True
258 self.disabled = True
256
259
257 def all(self):
260 def all(self):
258 d = self.db.hdict('shadowhist')
261 d = self.db.hdict('shadowhist')
259 items = [(i,s) for (s,i) in d.items()]
262 items = [(i,s) for (s,i) in d.items()]
260 items.sort()
263 items.sort()
261 return items
264 return items
262
265
263 def get(self, idx):
266 def get(self, idx):
264 all = self.all()
267 all = self.all()
265
268
266 for k, v in all:
269 for k, v in all:
267 #print k,v
270 #print k,v
268 if k == idx:
271 if k == idx:
269 return v
272 return v
270
273
271
274
272 def init_ipython(ip):
275 def init_ipython(ip):
273 ip.define_magic("rep",rep_f)
276 ip.define_magic("rep",rep_f)
274 ip.define_magic("hist",magic_hist)
277 ip.define_magic("hist",magic_hist)
275 ip.define_magic("history",magic_history)
278 ip.define_magic("history",magic_history)
276
279
277 # XXX - ipy_completers are in quarantine, need to be updated to new apis
280 # XXX - ipy_completers are in quarantine, need to be updated to new apis
278 #import ipy_completers
281 #import ipy_completers
279 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
282 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
General Comments 0
You need to be logged in to leave comments. Login now