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