##// END OF EJS Templates
ileo: ...
Ville M. Vainio -
Show More
@@ -1,660 +1,734 b''
1 """ ILeo - Leo plugin for IPython
1 """ ILeo - Leo plugin for IPython
2
2
3
3
4 """
4 """
5 import IPython.ipapi
5 import IPython.ipapi
6 import IPython.genutils
6 import IPython.genutils
7 import IPython.generics
7 import IPython.generics
8 from IPython.hooks import CommandChainDispatcher
8 from IPython.hooks import CommandChainDispatcher
9 import re
9 import re
10 import UserDict
10 import UserDict
11 from IPython.ipapi import TryNext
11 from IPython.ipapi import TryNext
12 import IPython.macro
12 import IPython.macro
13 import IPython.Shell
13 import IPython.Shell
14
14
15 _leo_push_history = set()
15 _leo_push_history = set()
16
16
17 def init_ipython(ipy):
17 def init_ipython(ipy):
18 """ This will be run by _ip.load('ipy_leo')
18 """ This will be run by _ip.load('ipy_leo')
19
19
20 Leo still needs to run update_commander() after this.
20 Leo still needs to run update_commander() after this.
21
21
22 """
22 """
23 global ip
23 global ip
24 ip = ipy
24 ip = ipy
25 IPython.Shell.hijack_tk()
25 IPython.Shell.hijack_tk()
26 ip.set_hook('complete_command', mb_completer, str_key = '%mb')
26 ip.set_hook('complete_command', mb_completer, str_key = '%mb')
27 ip.expose_magic('mb',mb_f)
27 ip.expose_magic('mb',mb_f)
28 ip.expose_magic('lee',lee_f)
28 ip.expose_magic('lee',lee_f)
29 ip.expose_magic('leoref',leoref_f)
29 ip.expose_magic('leoref',leoref_f)
30 ip.expose_magic('lleo',lleo_f)
30 ip.expose_magic('lleo',lleo_f)
31 ip.expose_magic('lshadow', lshadow_f)
31 # Note that no other push command should EVER have lower than 0
32 # Note that no other push command should EVER have lower than 0
32 expose_ileo_push(push_mark_req, -1)
33 expose_ileo_push(push_mark_req, -1)
33 expose_ileo_push(push_cl_node,100)
34 expose_ileo_push(push_cl_node,100)
34 # this should be the LAST one that will be executed, and it will never raise TryNext
35 # this should be the LAST one that will be executed, and it will never raise TryNext
35 expose_ileo_push(push_ipython_script, 1000)
36 expose_ileo_push(push_ipython_script, 1000)
36 expose_ileo_push(push_plain_python, 100)
37 expose_ileo_push(push_plain_python, 100)
37 expose_ileo_push(push_ev_node, 100)
38 expose_ileo_push(push_ev_node, 100)
38 ip.set_hook('pre_prompt_hook', ileo_pre_prompt_hook)
39 ip.set_hook('pre_prompt_hook', ileo_pre_prompt_hook)
39 global wb
40 global wb
40 wb = LeoWorkbook()
41 wb = LeoWorkbook()
41 ip.user_ns['wb'] = wb
42 ip.user_ns['wb'] = wb
42
43
43
44
44 first_launch = True
45 first_launch = True
45
46
46 def update_commander(new_leox):
47 def update_commander(new_leox):
47 """ Set the Leo commander to use
48 """ Set the Leo commander to use
48
49
49 This will be run every time Leo does ipython-launch; basically,
50 This will be run every time Leo does ipython-launch; basically,
50 when the user switches the document he is focusing on, he should do
51 when the user switches the document he is focusing on, he should do
51 ipython-launch to tell ILeo what document the commands apply to.
52 ipython-launch to tell ILeo what document the commands apply to.
52
53
53 """
54 """
54
55
55 global first_launch
56 global first_launch
56 if first_launch:
57 if first_launch:
57 show_welcome()
58 show_welcome()
58 first_launch = False
59 first_launch = False
59
60
60 global c,g
61 global c,g
61 c,g = new_leox.c, new_leox.g
62 c,g = new_leox.c, new_leox.g
62 print "Set Leo Commander:",c.frame.getTitle()
63 print "Set Leo Commander:",c.frame.getTitle()
63
64
64 # will probably be overwritten by user, but handy for experimentation early on
65 # will probably be overwritten by user, but handy for experimentation early on
65 ip.user_ns['c'] = c
66 ip.user_ns['c'] = c
66 ip.user_ns['g'] = g
67 ip.user_ns['g'] = g
67 ip.user_ns['_leo'] = new_leox
68 ip.user_ns['_leo'] = new_leox
68
69
69 new_leox.push = push_position_from_leo
70 new_leox.push = push_position_from_leo
70 run_leo_startup_node()
71 run_leo_startup_node()
71
72
72 from IPython.external.simplegeneric import generic
73 from IPython.external.simplegeneric import generic
73 import pprint
74 import pprint
74
75
75 def es(s):
76 def es(s):
76 g.es(s, tabName = 'IPython')
77 g.es(s, tabName = 'IPython')
77 pass
78 pass
78
79
79 @generic
80 @generic
80 def format_for_leo(obj):
81 def format_for_leo(obj):
81 """ Convert obj to string representiation (for editing in Leo)"""
82 """ Convert obj to string representiation (for editing in Leo)"""
82 return pprint.pformat(obj)
83 return pprint.pformat(obj)
83
84
84 # Just an example - note that this is a bad to actually do!
85 # Just an example - note that this is a bad to actually do!
85 #@format_for_leo.when_type(list)
86 #@format_for_leo.when_type(list)
86 #def format_list(obj):
87 #def format_list(obj):
87 # return "\n".join(str(s) for s in obj)
88 # return "\n".join(str(s) for s in obj)
88
89
89
90
90 attribute_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
91 attribute_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
91 def valid_attribute(s):
92 def valid_attribute(s):
92 return attribute_re.match(s)
93 return attribute_re.match(s)
93
94
94 _rootnode = None
95 _rootnode = None
95 def rootnode():
96 def rootnode():
96 """ Get ileo root node (@ipy-root)
97 """ Get ileo root node (@ipy-root)
97
98
98 if node has become invalid or has not been set, return None
99 if node has become invalid or has not been set, return None
99
100
100 Note that the root is the *first* @ipy-root item found
101 Note that the root is the *first* @ipy-root item found
101 """
102 """
102 global _rootnode
103 global _rootnode
103 if _rootnode is None:
104 if _rootnode is None:
104 return None
105 return None
105 if c.positionExists(_rootnode.p):
106 if c.positionExists(_rootnode.p):
106 return _rootnode
107 return _rootnode
107 _rootnode = None
108 _rootnode = None
108 return None
109 return None
109
110
110 def all_cells():
111 def all_cells():
111 global _rootnode
112 global _rootnode
112 d = {}
113 d = {}
113 r = rootnode()
114 r = rootnode()
114 if r is not None:
115 if r is not None:
115 nodes = r.p.children_iter()
116 nodes = r.p.children_iter()
116 else:
117 else:
117 nodes = c.allNodes_iter()
118 nodes = c.allNodes_iter()
118
119
119 for p in nodes:
120 for p in nodes:
120 h = p.headString()
121 h = p.headString()
121 if h.strip() == '@ipy-root':
122 if h.strip() == '@ipy-root':
122 # update root node (found it for the first time)
123 # update root node (found it for the first time)
123 _rootnode = LeoNode(p)
124 _rootnode = LeoNode(p)
124 # the next recursive call will use the children of new root
125 # the next recursive call will use the children of new root
125 return all_cells()
126 return all_cells()
126
127
127 if h.startswith('@a '):
128 if h.startswith('@a '):
128 d[h.lstrip('@a ').strip()] = p.parent().copy()
129 d[h.lstrip('@a ').strip()] = p.parent().copy()
129 elif not valid_attribute(h):
130 elif not valid_attribute(h):
130 continue
131 continue
131 d[h] = p.copy()
132 d[h] = p.copy()
132 return d
133 return d
133
134
134 def eval_node(n):
135 def eval_node(n):
135 body = n.b
136 body = n.b
136 if not body.startswith('@cl'):
137 if not body.startswith('@cl'):
137 # plain python repr node, just eval it
138 # plain python repr node, just eval it
138 return ip.ev(n.b)
139 return ip.ev(n.b)
139 # @cl nodes deserve special treatment - first eval the first line (minus cl), then use it to call the rest of body
140 # @cl nodes deserve special treatment - first eval the first line (minus cl), then use it to call the rest of body
140 first, rest = body.split('\n',1)
141 first, rest = body.split('\n',1)
141 tup = first.split(None, 1)
142 tup = first.split(None, 1)
142 # @cl alone SPECIAL USE-> dump var to user_ns
143 # @cl alone SPECIAL USE-> dump var to user_ns
143 if len(tup) == 1:
144 if len(tup) == 1:
144 val = ip.ev(rest)
145 val = ip.ev(rest)
145 ip.user_ns[n.h] = val
146 ip.user_ns[n.h] = val
146 es("%s = %s" % (n.h, repr(val)[:20] ))
147 es("%s = %s" % (n.h, repr(val)[:20] ))
147 return val
148 return val
148
149
149 cl, hd = tup
150 cl, hd = tup
150
151
151 xformer = ip.ev(hd.strip())
152 xformer = ip.ev(hd.strip())
152 es('Transform w/ %s' % repr(xformer))
153 es('Transform w/ %s' % repr(xformer))
153 return xformer(rest, n)
154 return xformer(rest, n)
154
155
155 class LeoNode(object, UserDict.DictMixin):
156 class LeoNode(object, UserDict.DictMixin):
156 """ Node in Leo outline
157 """ Node in Leo outline
157
158
158 Most important attributes (getters/setters available:
159 Most important attributes (getters/setters available:
159 .v - evaluate node, can also be alligned
160 .v - evaluate node, can also be alligned
160 .b, .h - body string, headline string
161 .b, .h - body string, headline string
161 .l - value as string list
162 .l - value as string list
162
163
163 Also supports iteration,
164 Also supports iteration,
164
165
165 setitem / getitem (indexing):
166 setitem / getitem (indexing):
166 wb.foo['key'] = 12
167 wb.foo['key'] = 12
167 assert wb.foo['key'].v == 12
168 assert wb.foo['key'].v == 12
168
169
169 Note the asymmetry on setitem and getitem! Also other
170 Note the asymmetry on setitem and getitem! Also other
170 dict methods are available.
171 dict methods are available.
171
172
172 .ipush() - run push-to-ipython
173 .ipush() - run push-to-ipython
173
174
174 Minibuffer command access (tab completion works):
175 Minibuffer command access (tab completion works):
175
176
176 mb save-to-file
177 mb save-to-file
177
178
178 """
179 """
179 def __init__(self,p):
180 def __init__(self,p):
180 self.p = p.copy()
181 self.p = p.copy()
181
182
182 def __str__(self):
183 def __str__(self):
183 return "<LeoNode %s>" % str(self.p)
184 return "<LeoNode %s>" % str(self.p)
184
185
185 __repr__ = __str__
186 __repr__ = __str__
186
187
187 def __get_h(self): return self.p.headString()
188 def __get_h(self): return self.p.headString()
188 def __set_h(self,val):
189 def __set_h(self,val):
189 c.setHeadString(self.p,val)
190 c.setHeadString(self.p,val)
190 LeoNode.last_edited = self
191 LeoNode.last_edited = self
191 c.redraw()
192 c.redraw()
192
193
193 h = property( __get_h, __set_h, doc = "Node headline string")
194 h = property( __get_h, __set_h, doc = "Node headline string")
194
195
195 def __get_b(self): return self.p.bodyString()
196 def __get_b(self): return self.p.bodyString()
196 def __set_b(self,val):
197 def __set_b(self,val):
197 c.setBodyString(self.p, val)
198 c.setBodyString(self.p, val)
198 LeoNode.last_edited = self
199 LeoNode.last_edited = self
199 c.redraw()
200 c.redraw()
200
201
201 b = property(__get_b, __set_b, doc = "Nody body string")
202 b = property(__get_b, __set_b, doc = "Nody body string")
202
203
203 def __set_val(self, val):
204 def __set_val(self, val):
204 self.b = format_for_leo(val)
205 self.b = format_for_leo(val)
205
206
206 v = property(lambda self: eval_node(self), __set_val, doc = "Node evaluated value")
207 v = property(lambda self: eval_node(self), __set_val, doc = "Node evaluated value")
207
208
208 def __set_l(self,val):
209 def __set_l(self,val):
209 self.b = '\n'.join(val )
210 self.b = '\n'.join(val )
210 l = property(lambda self : IPython.genutils.SList(self.b.splitlines()),
211 l = property(lambda self : IPython.genutils.SList(self.b.splitlines()),
211 __set_l, doc = "Node value as string list")
212 __set_l, doc = "Node value as string list")
212
213
213 def __iter__(self):
214 def __iter__(self):
214 """ Iterate through nodes direct children """
215 """ Iterate through nodes direct children """
215
216
216 return (LeoNode(p) for p in self.p.children_iter())
217 return (LeoNode(p) for p in self.p.children_iter())
217
218
218 def __children(self):
219 def __children(self):
219 d = {}
220 d = {}
220 for child in self:
221 for child in self:
221 head = child.h
222 head = child.h
222 tup = head.split(None,1)
223 tup = head.split(None,1)
223 if len(tup) > 1 and tup[0] == '@k':
224 if len(tup) > 1 and tup[0] == '@k':
224 d[tup[1]] = child
225 d[tup[1]] = child
225 continue
226 continue
226
227
227 if not valid_attribute(head):
228 if not valid_attribute(head):
228 d[head] = child
229 d[head] = child
229 continue
230 continue
230 return d
231 return d
231 def keys(self):
232 def keys(self):
232 d = self.__children()
233 d = self.__children()
233 return d.keys()
234 return d.keys()
234 def __getitem__(self, key):
235 def __getitem__(self, key):
235 """ wb.foo['Some stuff'] Return a child node with headline 'Some stuff'
236 """ wb.foo['Some stuff'] Return a child node with headline 'Some stuff'
236
237
237 If key is a valid python name (e.g. 'foo'), look for headline '@k foo' as well
238 If key is a valid python name (e.g. 'foo'), look for headline '@k foo' as well
238 """
239 """
239 key = str(key)
240 key = str(key)
240 d = self.__children()
241 d = self.__children()
241 return d[key]
242 return d[key]
242 def __setitem__(self, key, val):
243 def __setitem__(self, key, val):
243 """ You can do wb.foo['My Stuff'] = 12 to create children
244 """ You can do wb.foo['My Stuff'] = 12 to create children
244
245
245 This will create 'My Stuff' as a child of foo (if it does not exist), and
246 This will create 'My Stuff' as a child of foo (if it does not exist), and
246 do .v = 12 assignment.
247 do .v = 12 assignment.
247
248
248 Exception:
249 Exception:
249
250
250 wb.foo['bar'] = 12
251 wb.foo['bar'] = 12
251
252
252 will create a child with headline '@k bar', because bar is a valid python name
253 will create a child with headline '@k bar', because bar is a valid python name
253 and we don't want to crowd the WorkBook namespace with (possibly numerous) entries
254 and we don't want to crowd the WorkBook namespace with (possibly numerous) entries
254 """
255 """
255 key = str(key)
256 key = str(key)
256 d = self.__children()
257 d = self.__children()
257 if key in d:
258 if key in d:
258 d[key].v = val
259 d[key].v = val
259 return
260 return
260
261
261 if not valid_attribute(key):
262 if not valid_attribute(key):
262 head = key
263 head = key
263 else:
264 else:
264 head = '@k ' + key
265 head = '@k ' + key
265 p = c.createLastChildNode(self.p, head, '')
266 p = c.createLastChildNode(self.p, head, '')
266 LeoNode(p).v = val
267 LeoNode(p).v = val
267
268
268 def __delitem__(self, key):
269 def __delitem__(self, key):
269 """ Remove child
270 """ Remove child
270
271
271 Allows stuff like wb.foo.clear() to remove all children
272 Allows stuff like wb.foo.clear() to remove all children
272 """
273 """
273 self[key].p.doDelete()
274 self[key].p.doDelete()
274 c.redraw()
275 c.redraw()
275
276
276 def ipush(self):
277 def ipush(self):
277 """ Does push-to-ipython on the node """
278 """ Does push-to-ipython on the node """
278 push_from_leo(self)
279 push_from_leo(self)
279
280
280 def go(self):
281 def go(self):
281 """ Set node as current node (to quickly see it in Outline) """
282 """ Set node as current node (to quickly see it in Outline) """
282 c.setCurrentPosition(self.p)
283 c.setCurrentPosition(self.p)
283 c.redraw()
284 c.redraw()
284
285
285 def append(self):
286 def append(self):
286 """ Add new node as the last child, return the new node """
287 """ Add new node as the last child, return the new node """
287 p = self.p.insertAsLastChild()
288 p = self.p.insertAsLastChild()
288 return LeoNode(p)
289 return LeoNode(p)
289
290
290
291
291 def script(self):
292 def script(self):
292 """ Method to get the 'tangled' contents of the node
293 """ Method to get the 'tangled' contents of the node
293
294
294 (parse @others, << section >> references etc.)
295 (parse @others, << section >> references etc.)
295 """
296 """
296 return g.getScript(c,self.p,useSelectedText=False,useSentinels=False)
297 return g.getScript(c,self.p,useSelectedText=False,useSentinels=False)
297
298
298 def __get_uA(self):
299 def __get_uA(self):
299 p = self.p
300 p = self.p
300 # Create the uA if necessary.
301 # Create the uA if necessary.
301 if not hasattr(p.v.t,'unknownAttributes'):
302 if not hasattr(p.v.t,'unknownAttributes'):
302 p.v.t.unknownAttributes = {}
303 p.v.t.unknownAttributes = {}
303
304
304 d = p.v.t.unknownAttributes.setdefault('ipython', {})
305 d = p.v.t.unknownAttributes.setdefault('ipython', {})
305 return d
306 return d
306
307
307 uA = property(__get_uA, doc = "Access persistent unknownAttributes of node")
308 uA = property(__get_uA, doc = "Access persistent unknownAttributes of node")
308
309
309
310
310 class LeoWorkbook:
311 class LeoWorkbook:
311 """ class for 'advanced' node access
312 """ class for 'advanced' node access
312
313
313 Has attributes for all "discoverable" nodes. Node is discoverable if it
314 Has attributes for all "discoverable" nodes. Node is discoverable if it
314 either
315 either
315
316
316 - has a valid python name (Foo, bar_12)
317 - has a valid python name (Foo, bar_12)
317 - is a parent of an anchor node (if it has a child '@a foo', it is visible as foo)
318 - is a parent of an anchor node (if it has a child '@a foo', it is visible as foo)
318
319
319 """
320 """
320 def __getattr__(self, key):
321 def __getattr__(self, key):
321 if key.startswith('_') or key == 'trait_names' or not valid_attribute(key):
322 if key.startswith('_') or key == 'trait_names' or not valid_attribute(key):
322 raise AttributeError
323 raise AttributeError
323 cells = all_cells()
324 cells = all_cells()
324 p = cells.get(key, None)
325 p = cells.get(key, None)
325 if p is None:
326 if p is None:
326 return add_var(key)
327 return add_var(key)
327
328
328 return LeoNode(p)
329 return LeoNode(p)
329
330
330 def __str__(self):
331 def __str__(self):
331 return "<LeoWorkbook>"
332 return "<LeoWorkbook>"
332 def __setattr__(self,key, val):
333 def __setattr__(self,key, val):
333 raise AttributeError("Direct assignment to workbook denied, try wb.%s.v = %s" % (key,val))
334 raise AttributeError("Direct assignment to workbook denied, try wb.%s.v = %s" % (key,val))
334
335
335 __repr__ = __str__
336 __repr__ = __str__
336
337
337 def __iter__(self):
338 def __iter__(self):
338 """ Iterate all (even non-exposed) nodes """
339 """ Iterate all (even non-exposed) nodes """
339 cells = all_cells()
340 cells = all_cells()
340 return (LeoNode(p) for p in c.allNodes_iter())
341 return (LeoNode(p) for p in c.allNodes_iter())
341
342
342 current = property(lambda self: LeoNode(c.currentPosition()), doc = "Currently selected node")
343 current = property(lambda self: LeoNode(c.currentPosition()), doc = "Currently selected node")
343
344
344 def match_h(self, regex):
345 def match_h(self, regex):
345 cmp = re.compile(regex)
346 cmp = re.compile(regex)
346 for node in self:
347 for node in self:
347 if re.match(cmp, node.h, re.IGNORECASE):
348 if re.match(cmp, node.h, re.IGNORECASE):
348 yield node
349 yield node
349 return
350 return
350
351
351 def require(self, req):
352 def require(self, req):
352 """ Used to control node push dependencies
353 """ Used to control node push dependencies
353
354
354 Call this as first statement in nodes. If node has not been pushed, it will be pushed before proceeding
355 Call this as first statement in nodes. If node has not been pushed, it will be pushed before proceeding
355
356
356 E.g. wb.require('foo') will do wb.foo.ipush() if it hasn't been done already
357 E.g. wb.require('foo') will do wb.foo.ipush() if it hasn't been done already
357 """
358 """
358
359
359 if req not in _leo_push_history:
360 if req not in _leo_push_history:
360 es('Require: ' + req)
361 es('Require: ' + req)
361 getattr(self,req).ipush()
362 getattr(self,req).ipush()
362
363
363
364
364 @IPython.generics.complete_object.when_type(LeoWorkbook)
365 @IPython.generics.complete_object.when_type(LeoWorkbook)
365 def workbook_complete(obj, prev):
366 def workbook_complete(obj, prev):
366 return all_cells().keys() + [s for s in prev if not s.startswith('_')]
367 return all_cells().keys() + [s for s in prev if not s.startswith('_')]
367
368
368
369
369 def add_var(varname):
370 def add_var(varname):
370 r = rootnode()
371 r = rootnode()
371 try:
372 try:
372 if r is None:
373 if r is None:
373 p2 = g.findNodeAnywhere(c,varname)
374 p2 = g.findNodeAnywhere(c,varname)
374 else:
375 else:
375 p2 = g.findNodeInChildren(c, r.p, varname)
376 p2 = g.findNodeInChildren(c, r.p, varname)
376 if p2:
377 if p2:
377 return LeoNode(p2)
378 return LeoNode(p2)
378
379
379 if r is not None:
380 if r is not None:
380 p2 = r.p.insertAsLastChild()
381 p2 = r.p.insertAsLastChild()
381
382
382 else:
383 else:
383 p2 = c.currentPosition().insertAfter()
384 p2 = c.currentPosition().insertAfter()
384
385
385 c.setHeadString(p2,varname)
386 c.setHeadString(p2,varname)
386 return LeoNode(p2)
387 return LeoNode(p2)
387 finally:
388 finally:
388 c.redraw()
389 c.redraw()
389
390
390 def add_file(self,fname):
391 def add_file(self,fname):
391 p2 = c.currentPosition().insertAfter()
392 p2 = c.currentPosition().insertAfter()
392
393
393 push_from_leo = CommandChainDispatcher()
394 push_from_leo = CommandChainDispatcher()
394
395
395 def expose_ileo_push(f, prio = 0):
396 def expose_ileo_push(f, prio = 0):
396 push_from_leo.add(f, prio)
397 push_from_leo.add(f, prio)
397
398
398 def push_ipython_script(node):
399 def push_ipython_script(node):
399 """ Execute the node body in IPython, as if it was entered in interactive prompt """
400 """ Execute the node body in IPython, as if it was entered in interactive prompt """
400 try:
401 try:
401 ohist = ip.IP.output_hist
402 ohist = ip.IP.output_hist
402 hstart = len(ip.IP.input_hist)
403 hstart = len(ip.IP.input_hist)
403 script = node.script()
404 script = node.script()
404
405
405 # The current node _p needs to handle wb.require() and recursive ipushes
406 # The current node _p needs to handle wb.require() and recursive ipushes
406 old_p = ip.user_ns.get('_p',None)
407 old_p = ip.user_ns.get('_p',None)
407 ip.user_ns['_p'] = node
408 ip.user_ns['_p'] = node
408 ip.runlines(script)
409 ip.runlines(script)
409 ip.user_ns['_p'] = old_p
410 ip.user_ns['_p'] = old_p
410 if old_p is None:
411 if old_p is None:
411 del ip.user_ns['_p']
412 del ip.user_ns['_p']
412
413
413 has_output = False
414 has_output = False
414 for idx in range(hstart,len(ip.IP.input_hist)):
415 for idx in range(hstart,len(ip.IP.input_hist)):
415 val = ohist.get(idx,None)
416 val = ohist.get(idx,None)
416 if val is None:
417 if val is None:
417 continue
418 continue
418 has_output = True
419 has_output = True
419 inp = ip.IP.input_hist[idx]
420 inp = ip.IP.input_hist[idx]
420 if inp.strip():
421 if inp.strip():
421 es('In: %s' % (inp[:40], ))
422 es('In: %s' % (inp[:40], ))
422
423
423 es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40)))
424 es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40)))
424
425
425 if not has_output:
426 if not has_output:
426 es('ipy run: %s (%d LL)' %( node.h,len(script)))
427 es('ipy run: %s (%d LL)' %( node.h,len(script)))
427 finally:
428 finally:
428 c.redraw()
429 c.redraw()
429
430
430
431
431 def eval_body(body):
432 def eval_body(body):
432 try:
433 try:
433 val = ip.ev(body)
434 val = ip.ev(body)
434 except:
435 except:
435 # just use stringlist if it's not completely legal python expression
436 # just use stringlist if it's not completely legal python expression
436 val = IPython.genutils.SList(body.splitlines())
437 val = IPython.genutils.SList(body.splitlines())
437 return val
438 return val
438
439
439 def push_plain_python(node):
440 def push_plain_python(node):
440 if not node.h.endswith('P'):
441 if not node.h.endswith('P'):
441 raise TryNext
442 raise TryNext
442 script = node.script()
443 script = node.script()
443 lines = script.count('\n')
444 lines = script.count('\n')
444 try:
445 try:
445 exec script in ip.user_ns
446 exec script in ip.user_ns
446 except:
447 except:
447 print " -- Exception in script:\n"+script + "\n --"
448 print " -- Exception in script:\n"+script + "\n --"
448 raise
449 raise
449 es('ipy plain: %s (%d LL)' % (node.h,lines))
450 es('ipy plain: %s (%d LL)' % (node.h,lines))
450
451
451
452
452 def push_cl_node(node):
453 def push_cl_node(node):
453 """ If node starts with @cl, eval it
454 """ If node starts with @cl, eval it
454
455
455 The result is put as last child of @ipy-results node, if it exists
456 The result is put as last child of @ipy-results node, if it exists
456 """
457 """
457 if not node.b.startswith('@cl'):
458 if not node.b.startswith('@cl'):
458 raise TryNext
459 raise TryNext
459
460
460 p2 = g.findNodeAnywhere(c,'@ipy-results')
461 p2 = g.findNodeAnywhere(c,'@ipy-results')
461 val = node.v
462 val = node.v
462 if p2:
463 if p2:
463 es("=> @ipy-results")
464 es("=> @ipy-results")
464 LeoNode(p2).v = val
465 LeoNode(p2).v = val
465 es(val)
466 es(val)
466
467
467 def push_ev_node(node):
468 def push_ev_node(node):
468 """ If headline starts with @ev, eval it and put result in body """
469 """ If headline starts with @ev, eval it and put result in body """
469 if not node.h.startswith('@ev '):
470 if not node.h.startswith('@ev '):
470 raise TryNext
471 raise TryNext
471 expr = node.h.lstrip('@ev ')
472 expr = node.h.lstrip('@ev ')
472 es('ipy eval ' + expr)
473 es('ipy eval ' + expr)
473 res = ip.ev(expr)
474 res = ip.ev(expr)
474 node.v = res
475 node.v = res
475
476
476 def push_mark_req(node):
477 def push_mark_req(node):
477 """ This should be the first one that gets called.
478 """ This should be the first one that gets called.
478
479
479 It will mark the node as 'pushed', for wb.require.
480 It will mark the node as 'pushed', for wb.require.
480 """
481 """
481 _leo_push_history.add(node.h)
482 _leo_push_history.add(node.h)
482 raise TryNext
483 raise TryNext
483
484
484
485
485 def push_position_from_leo(p):
486 def push_position_from_leo(p):
486 try:
487 try:
487 push_from_leo(LeoNode(p))
488 push_from_leo(LeoNode(p))
488 except AttributeError,e:
489 except AttributeError,e:
489 if e.args == ("Commands instance has no attribute 'frame'",):
490 if e.args == ("Commands instance has no attribute 'frame'",):
490 es("Error: ILeo not associated with .leo document")
491 es("Error: ILeo not associated with .leo document")
491 es("Press alt+shift+I to fix!")
492 es("Press alt+shift+I to fix!")
492 else:
493 else:
493 raise
494 raise
494
495
495 @generic
496 @generic
496 def edit_object_in_leo(obj, varname):
497 def edit_object_in_leo(obj, varname):
497 """ Make it @cl node so it can be pushed back directly by alt+I """
498 """ Make it @cl node so it can be pushed back directly by alt+I """
498 node = add_var(varname)
499 node = add_var(varname)
499 formatted = format_for_leo(obj)
500 formatted = format_for_leo(obj)
500 if not formatted.startswith('@cl'):
501 if not formatted.startswith('@cl'):
501 formatted = '@cl\n' + formatted
502 formatted = '@cl\n' + formatted
502 node.b = formatted
503 node.b = formatted
503 node.go()
504 node.go()
504
505
505 @edit_object_in_leo.when_type(IPython.macro.Macro)
506 @edit_object_in_leo.when_type(IPython.macro.Macro)
506 def edit_macro(obj,varname):
507 def edit_macro(obj,varname):
507 bod = '_ip.defmacro("""\\\n' + obj.value + '""")'
508 bod = '_ip.defmacro("""\\\n' + obj.value + '""")'
508 node = add_var('Macro_' + varname)
509 node = add_var('Macro_' + varname)
509 node.b = bod
510 node.b = bod
510 node.go()
511 node.go()
511
512
512 def get_history(hstart = 0):
513 def get_history(hstart = 0):
513 res = []
514 res = []
514 ohist = ip.IP.output_hist
515 ohist = ip.IP.output_hist
515
516
516 for idx in range(hstart, len(ip.IP.input_hist)):
517 for idx in range(hstart, len(ip.IP.input_hist)):
517 val = ohist.get(idx,None)
518 val = ohist.get(idx,None)
518 has_output = True
519 has_output = True
519 inp = ip.IP.input_hist_raw[idx]
520 inp = ip.IP.input_hist_raw[idx]
520 if inp.strip():
521 if inp.strip():
521 res.append('In [%d]: %s' % (idx, inp))
522 res.append('In [%d]: %s' % (idx, inp))
522 if val:
523 if val:
523 res.append(pprint.pformat(val))
524 res.append(pprint.pformat(val))
524 res.append('\n')
525 res.append('\n')
525 return ''.join(res)
526 return ''.join(res)
526
527
527
528
528 def lee_f(self,s):
529 def lee_f(self,s):
529 """ Open file(s)/objects in Leo
530 """ Open file(s)/objects in Leo
530
531
531 - %lee hist -> open full session history in leo
532 - %lee hist -> open full session history in leo
532 - Takes an object. l = [1,2,"hello"]; %lee l. Alt+I in leo pushes the object back
533 - Takes an object. l = [1,2,"hello"]; %lee l. Alt+I in leo pushes the object back
533 - Takes an mglob pattern, e.g. '%lee *.cpp' or %lee 'rec:*.cpp'
534 - Takes an mglob pattern, e.g. '%lee *.cpp' or %lee 'rec:*.cpp'
534 - Takes input history indices: %lee 4 6-8 10 12-47
535 - Takes input history indices: %lee 4 6-8 10 12-47
535 """
536 """
536 import os
537 import os
537
538
538 try:
539 try:
539 if s == 'hist':
540 if s == 'hist':
540 wb.ipython_history.b = get_history()
541 wb.ipython_history.b = get_history()
541 wb.ipython_history.go()
542 wb.ipython_history.go()
542 return
543 return
543
544
544
545
545 if s and s[0].isdigit():
546 if s and s[0].isdigit():
546 # numbers; push input slices to leo
547 # numbers; push input slices to leo
547 lines = self.extract_input_slices(s.strip().split(), True)
548 lines = self.extract_input_slices(s.strip().split(), True)
548 v = add_var('stored_ipython_input')
549 v = add_var('stored_ipython_input')
549 v.b = '\n'.join(lines)
550 v.b = '\n'.join(lines)
550 return
551 return
551
552
552
553
553 # try editing the object directly
554 # try editing the object directly
554 obj = ip.user_ns.get(s, None)
555 obj = ip.user_ns.get(s, None)
555 if obj is not None:
556 if obj is not None:
556 edit_object_in_leo(obj,s)
557 edit_object_in_leo(obj,s)
557 return
558 return
558
559
559
560
560 # if it's not object, it's a file name / mglob pattern
561 # if it's not object, it's a file name / mglob pattern
561 from IPython.external import mglob
562 from IPython.external import mglob
562
563
563 files = (os.path.abspath(f) for f in mglob.expand(s))
564 files = (os.path.abspath(f) for f in mglob.expand(s))
564 for fname in files:
565 for fname in files:
565 p = g.findNodeAnywhere(c,'@auto ' + fname)
566 p = g.findNodeAnywhere(c,'@auto ' + fname)
566 if not p:
567 if not p:
567 p = c.currentPosition().insertAfter()
568 p = c.currentPosition().insertAfter()
568
569
569 p.setHeadString('@auto ' + fname)
570 p.setHeadString('@auto ' + fname)
570 if os.path.isfile(fname):
571 if os.path.isfile(fname):
571 c.setBodyString(p,open(fname).read())
572 c.setBodyString(p,open(fname).read())
572 c.selectPosition(p)
573 c.selectPosition(p)
573 print "Editing file(s), press ctrl+shift+w in Leo to write @auto nodes"
574 print "Editing file(s), press ctrl+shift+w in Leo to write @auto nodes"
574 finally:
575 finally:
575 c.redraw()
576 c.redraw()
576
577
577 def leoref_f(self,s):
578 def leoref_f(self,s):
578 """ Quick reference for ILeo """
579 """ Quick reference for ILeo """
579 import textwrap
580 import textwrap
580 print textwrap.dedent("""\
581 print textwrap.dedent("""\
581 %lee file/object - open file / object in leo
582 %lee file/object - open file / object in leo
582 %lleo Launch leo (use if you started ipython first!)
583 %lleo Launch leo (use if you started ipython first!)
583 wb.foo.v - eval node foo (i.e. headstring is 'foo' or '@ipy foo')
584 wb.foo.v - eval node foo (i.e. headstring is 'foo' or '@ipy foo')
584 wb.foo.v = 12 - assign to body of node foo
585 wb.foo.v = 12 - assign to body of node foo
585 wb.foo.b - read or write the body of node foo
586 wb.foo.b - read or write the body of node foo
586 wb.foo.l - body of node foo as string list
587 wb.foo.l - body of node foo as string list
587
588
588 for el in wb.foo:
589 for el in wb.foo:
589 print el.v
590 print el.v
590
591
591 """
592 """
592 )
593 )
593
594
594
595
595
596
596 def mb_f(self, arg):
597 def mb_f(self, arg):
597 """ Execute leo minibuffer commands
598 """ Execute leo minibuffer commands
598
599
599 Example:
600 Example:
600 mb save-to-file
601 mb save-to-file
601 """
602 """
602 c.executeMinibufferCommand(arg)
603 c.executeMinibufferCommand(arg)
603
604
604 def mb_completer(self,event):
605 def mb_completer(self,event):
605 """ Custom completer for minibuffer """
606 """ Custom completer for minibuffer """
606 cmd_param = event.line.split()
607 cmd_param = event.line.split()
607 if event.line.endswith(' '):
608 if event.line.endswith(' '):
608 cmd_param.append('')
609 cmd_param.append('')
609 if len(cmd_param) > 2:
610 if len(cmd_param) > 2:
610 return ip.IP.Completer.file_matches(event.symbol)
611 return ip.IP.Completer.file_matches(event.symbol)
611 cmds = c.commandsDict.keys()
612 cmds = c.commandsDict.keys()
612 cmds.sort()
613 cmds.sort()
613 return cmds
614 return cmds
614
615
615 def ileo_pre_prompt_hook(self):
616 def ileo_pre_prompt_hook(self):
616 # this will fail if leo is not running yet
617 # this will fail if leo is not running yet
617 try:
618 try:
618 c.outerUpdate()
619 c.outerUpdate()
619 except NameError:
620 except NameError:
620 pass
621 pass
621 raise TryNext
622 raise TryNext
622
623
623
624
624
625
625 def show_welcome():
626 def show_welcome():
626 print "------------------"
627 print "------------------"
627 print "Welcome to Leo-enabled IPython session!"
628 print "Welcome to Leo-enabled IPython session!"
628 print "Try %leoref for quick reference."
629 print "Try %leoref for quick reference."
629 import IPython.platutils
630 import IPython.platutils
630 IPython.platutils.set_term_title('ILeo')
631 IPython.platutils.set_term_title('ILeo')
631 IPython.platutils.freeze_term_title()
632 IPython.platutils.freeze_term_title()
632
633
633 def run_leo_startup_node():
634 def run_leo_startup_node():
634 p = g.findNodeAnywhere(c,'@ipy-startup')
635 p = g.findNodeAnywhere(c,'@ipy-startup')
635 if p:
636 if p:
636 print "Running @ipy-startup nodes"
637 print "Running @ipy-startup nodes"
637 for n in LeoNode(p):
638 for n in LeoNode(p):
638 push_from_leo(n)
639 push_from_leo(n)
639
640
640 def lleo_f(selg, args):
641 def lleo_f(selg, args):
641 """ Launch leo from within IPython
642 """ Launch leo from within IPython
642
643
643 This command will return immediately when Leo has been
644 This command will return immediately when Leo has been
644 launched, leaving a Leo session that is connected
645 launched, leaving a Leo session that is connected
645 with current IPython session (once you press alt+I in leo)
646 with current IPython session (once you press alt+I in leo)
646
647
647 Usage::
648 Usage::
648 lleo foo.leo
649 lleo foo.leo
649 lleo
650 lleo
650 """
651 """
651
652
652 import shlex, sys
653 import shlex, sys,os
653 argv = ['leo'] + shlex.split(args)
654 argv = shlex.split(args)
654 sys.argv = argv
655
656 # when run without args, leo will open ipython_notebook for
657 # quick note taking / experimentation
658
659 if not argv:
660 argv = [os.path.join(ip.options.ipythondir,'ipython_notebook.leo')]
661
662 sys.argv = ['leo'] + argv
655 # if this var exists and is true, leo will "launch" (connect)
663 # if this var exists and is true, leo will "launch" (connect)
656 # ipython immediately when it's started
664 # ipython immediately when it's started
657 global _request_immediate_connect
665 global _request_immediate_connect
658 _request_immediate_connect = True
666 _request_immediate_connect = True
659 import leo.core.runLeo
667 import leo.core.runLeo
660 leo.core.runLeo.run()
668 leo.core.runLeo.run()
669
670 def lshadow_f(self, arg):
671 """ lshadow [path]
672
673 Create shadow nodes for path (default .)
674
675 """
676 if not arg.split():
677 arg = '.'
678
679 p = c.currentPosition().insertAfter()
680 c.setCurrentPosition(p)
681 shadow_walk(arg)
682 c.redraw()
683
684
685 def shadow_walk(directory, parent=None, isroot=True):
686 """ source: http://leo.zwiki.org/CreateShadows
687
688 """
689
690 from os import listdir
691 from os.path import join, abspath, basename, normpath, isfile
692 from fnmatch import fnmatch
693
694 RELATIVE_PATHS = False
695
696 patterns_to_ignore = ['*.pyc', '*.leo', '*.gif', '*.png', '*.jpg', '*.json']
697
698 match = lambda s: any(fnmatch(s, p) for p in patterns_to_ignore)
699
700 is_ignorable = lambda s: any([ s.startswith('.'), match(s) ])
701
702 p = c.currentPosition()
703
704 if not RELATIVE_PATHS: directory = abspath(directory)
705 if isroot:
706 body = "@path %s" % normpath(directory)
707 c.setHeadString(p, body)
708 for name in listdir(directory):
709 if is_ignorable(name):
710 continue
711 path = join(directory, name)
712 if isfile(path):
713 g.es('file:', path)
714 headline = '@shadow %s' % basename(path)
715 if parent:
716 node = parent
717 else:
718 node = p
719 child = node.insertAsLastChild()
720 child.initHeadString(headline)
721 else:
722 g.es('dir:', path)
723 headline = basename(path)
724 body = "@path %s" % normpath(path)
725 if parent:
726 node = parent
727 else:
728 node = p
729 child = node.insertAsLastChild()
730 child.initHeadString(headline)
731 child.initBodyString(body)
732 shadow_walk(path, parent=child, isroot=False)
733
734
General Comments 0
You need to be logged in to leave comments. Login now