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