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