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