##// END OF EJS Templates
bzr completer caches the commands (only runs bzr help commands on the first run)
Ville M. Vainio -
Show More
@@ -1,354 +1,359 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 """ Implementations for various useful completers
3 """ Implementations for various useful completers
4
4
5 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
5 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
6 but the basic idea is to do:
6 but the basic idea is to do:
7
7
8 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
8 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
9
9
10 """
10 """
11 import IPython.ipapi
11 import IPython.ipapi
12 import glob,os,shlex,sys
12 import glob,os,shlex,sys
13 import inspect
13 import inspect
14 from time import time
14 from time import time
15 ip = IPython.ipapi.get()
15 ip = IPython.ipapi.get()
16
16
17 try:
17 try:
18 set
18 set
19 except:
19 except:
20 from sets import Set as set
20 from sets import Set as set
21
21
22 TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored
22 TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored
23 TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up
23 TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up
24
24
25 def quick_completer(cmd, completions):
25 def quick_completer(cmd, completions):
26 """ Easily create a trivial completer for a command.
26 """ Easily create a trivial completer for a command.
27
27
28 Takes either a list of completions, or all completions in string
28 Takes either a list of completions, or all completions in string
29 (that will be split on whitespace)
29 (that will be split on whitespace)
30
30
31 Example::
31 Example::
32
32
33 [d:\ipython]|1> import ipy_completers
33 [d:\ipython]|1> import ipy_completers
34 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
34 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
35 [d:\ipython]|3> foo b<TAB>
35 [d:\ipython]|3> foo b<TAB>
36 bar baz
36 bar baz
37 [d:\ipython]|3> foo ba
37 [d:\ipython]|3> foo ba
38 """
38 """
39 if isinstance(completions, basestring):
39 if isinstance(completions, basestring):
40
40
41 completions = completions.split()
41 completions = completions.split()
42 def do_complete(self,event):
42 def do_complete(self,event):
43 return completions
43 return completions
44
44
45 ip.set_hook('complete_command',do_complete, str_key = cmd)
45 ip.set_hook('complete_command',do_complete, str_key = cmd)
46
46
47 def getRootModules():
47 def getRootModules():
48 """
48 """
49 Returns a list containing the names of all the modules available in the
49 Returns a list containing the names of all the modules available in the
50 folders of the pythonpath.
50 folders of the pythonpath.
51 """
51 """
52 modules = []
52 modules = []
53 if ip.db.has_key('rootmodules'):
53 if ip.db.has_key('rootmodules'):
54 return ip.db['rootmodules']
54 return ip.db['rootmodules']
55 t = time()
55 t = time()
56 store = False
56 store = False
57 for path in sys.path:
57 for path in sys.path:
58 modules += moduleList(path)
58 modules += moduleList(path)
59 if time() - t >= TIMEOUT_STORAGE and not store:
59 if time() - t >= TIMEOUT_STORAGE and not store:
60 store = True
60 store = True
61 print "\nCaching the list of root modules, please wait!"
61 print "\nCaching the list of root modules, please wait!"
62 print "(This will only be done once - type '%rehashx' to " + \
62 print "(This will only be done once - type '%rehashx' to " + \
63 "reset cache!)"
63 "reset cache!)"
64 print
64 print
65 if time() - t > TIMEOUT_GIVEUP:
65 if time() - t > TIMEOUT_GIVEUP:
66 print "This is taking too long, we give up."
66 print "This is taking too long, we give up."
67 print
67 print
68 ip.db['rootmodules'] = []
68 ip.db['rootmodules'] = []
69 return []
69 return []
70
70
71 modules += sys.builtin_module_names
71 modules += sys.builtin_module_names
72
72
73 modules = list(set(modules))
73 modules = list(set(modules))
74 if '__init__' in modules:
74 if '__init__' in modules:
75 modules.remove('__init__')
75 modules.remove('__init__')
76 modules = list(set(modules))
76 modules = list(set(modules))
77 if store:
77 if store:
78 ip.db['rootmodules'] = modules
78 ip.db['rootmodules'] = modules
79 return modules
79 return modules
80
80
81 def moduleList(path):
81 def moduleList(path):
82 """
82 """
83 Return the list containing the names of the modules available in the given
83 Return the list containing the names of the modules available in the given
84 folder.
84 folder.
85 """
85 """
86
86
87 if os.path.isdir(path):
87 if os.path.isdir(path):
88 folder_list = os.listdir(path)
88 folder_list = os.listdir(path)
89 else:
89 else:
90 folder_list = []
90 folder_list = []
91 #folder_list = glob.glob(os.path.join(path,'*'))
91 #folder_list = glob.glob(os.path.join(path,'*'))
92 folder_list = [p for p in folder_list \
92 folder_list = [p for p in folder_list \
93 if os.path.exists(os.path.join(path, p,'__init__.py'))\
93 if os.path.exists(os.path.join(path, p,'__init__.py'))\
94 or p[-3:] in ('.py','.so')\
94 or p[-3:] in ('.py','.so')\
95 or p[-4:] in ('.pyc','.pyo')]
95 or p[-4:] in ('.pyc','.pyo')]
96
96
97 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
97 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
98 return folder_list
98 return folder_list
99
99
100 def moduleCompletion(line):
100 def moduleCompletion(line):
101 """
101 """
102 Returns a list containing the completion possibilities for an import line.
102 Returns a list containing the completion possibilities for an import line.
103 The line looks like this :
103 The line looks like this :
104 'import xml.d'
104 'import xml.d'
105 'from xml.dom import'
105 'from xml.dom import'
106 """
106 """
107 def tryImport(mod, only_modules=False):
107 def tryImport(mod, only_modules=False):
108 def isImportable(module, attr):
108 def isImportable(module, attr):
109 if only_modules:
109 if only_modules:
110 return inspect.ismodule(getattr(module, attr))
110 return inspect.ismodule(getattr(module, attr))
111 else:
111 else:
112 return not(attr[:2] == '__' and attr[-2:] == '__')
112 return not(attr[:2] == '__' and attr[-2:] == '__')
113 try:
113 try:
114 m = __import__(mod)
114 m = __import__(mod)
115 except:
115 except:
116 return []
116 return []
117 mods = mod.split('.')
117 mods = mod.split('.')
118 for module in mods[1:]:
118 for module in mods[1:]:
119 m = getattr(m,module)
119 m = getattr(m,module)
120 if (not hasattr(m, '__file__')) or (not only_modules) or\
120 if (not hasattr(m, '__file__')) or (not only_modules) or\
121 (hasattr(m, '__file__') and '__init__' in m.__file__):
121 (hasattr(m, '__file__') and '__init__' in m.__file__):
122 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
122 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
123 completion_list.extend(getattr(m,'__all__',[]))
123 completion_list.extend(getattr(m,'__all__',[]))
124 if hasattr(m, '__file__') and '__init__' in m.__file__:
124 if hasattr(m, '__file__') and '__init__' in m.__file__:
125 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
125 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
126 completion_list = list(set(completion_list))
126 completion_list = list(set(completion_list))
127 if '__init__' in completion_list:
127 if '__init__' in completion_list:
128 completion_list.remove('__init__')
128 completion_list.remove('__init__')
129 return completion_list
129 return completion_list
130
130
131 words = line.split(' ')
131 words = line.split(' ')
132 if len(words) == 3 and words[0] == 'from':
132 if len(words) == 3 and words[0] == 'from':
133 return ['import ']
133 return ['import ']
134 if len(words) < 3 and (words[0] in ['import','from']) :
134 if len(words) < 3 and (words[0] in ['import','from']) :
135 if len(words) == 1:
135 if len(words) == 1:
136 return getRootModules()
136 return getRootModules()
137 mod = words[1].split('.')
137 mod = words[1].split('.')
138 if len(mod) < 2:
138 if len(mod) < 2:
139 return getRootModules()
139 return getRootModules()
140 completion_list = tryImport('.'.join(mod[:-1]), True)
140 completion_list = tryImport('.'.join(mod[:-1]), True)
141 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
141 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
142 return completion_list
142 return completion_list
143 if len(words) >= 3 and words[0] == 'from':
143 if len(words) >= 3 and words[0] == 'from':
144 mod = words[1]
144 mod = words[1]
145 return tryImport(mod)
145 return tryImport(mod)
146
146
147 def vcs_completer(commands, event):
147 def vcs_completer(commands, event):
148 """ utility to make writing typical version control app completers easier
148 """ utility to make writing typical version control app completers easier
149
149
150 VCS command line apps typically have the format:
150 VCS command line apps typically have the format:
151
151
152 [sudo ]PROGNAME [help] [command] file file...
152 [sudo ]PROGNAME [help] [command] file file...
153
153
154 """
154 """
155
155
156
156
157 cmd_param = event.line.split()
157 cmd_param = event.line.split()
158 if event.line.endswith(' '):
158 if event.line.endswith(' '):
159 cmd_param.append('')
159 cmd_param.append('')
160
160
161 if cmd_param[0] == 'sudo':
161 if cmd_param[0] == 'sudo':
162 cmd_param = cmd_param[1:]
162 cmd_param = cmd_param[1:]
163
163
164 if len(cmd_param) == 2 or 'help' in cmd_param:
164 if len(cmd_param) == 2 or 'help' in cmd_param:
165 return commands.split()
165 return commands.split()
166
166
167 return ip.IP.Completer.file_matches(event.symbol)
167 return ip.IP.Completer.file_matches(event.symbol)
168
168
169
169
170 pkg_cache = None
170 pkg_cache = None
171
171
172 def module_completer(self,event):
172 def module_completer(self,event):
173 """ Give completions after user has typed 'import ...' or 'from ...'"""
173 """ Give completions after user has typed 'import ...' or 'from ...'"""
174
174
175 # This works in all versions of python. While 2.5 has
175 # This works in all versions of python. While 2.5 has
176 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
176 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
177 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
177 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
178 # of possibly problematic side effects.
178 # of possibly problematic side effects.
179 # This search the folders in the sys.path for available modules.
179 # This search the folders in the sys.path for available modules.
180
180
181 return moduleCompletion(event.line)
181 return moduleCompletion(event.line)
182
182
183
183
184 svn_commands = """\
184 svn_commands = """\
185 add blame praise annotate ann cat checkout co cleanup commit ci copy
185 add blame praise annotate ann cat checkout co cleanup commit ci copy
186 cp delete del remove rm diff di export help ? h import info list ls
186 cp delete del remove rm diff di export help ? h import info list ls
187 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
187 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
188 pe propget pget pg proplist plist pl propset pset ps resolved revert
188 pe propget pget pg proplist plist pl propset pset ps resolved revert
189 status stat st switch sw unlock update
189 status stat st switch sw unlock update
190 """
190 """
191
191
192 def svn_completer(self,event):
192 def svn_completer(self,event):
193 return vcs_completer(svn_commands, event)
193 return vcs_completer(svn_commands, event)
194
194
195
195
196 hg_commands = """
196 hg_commands = """
197 add addremove annotate archive backout branch branches bundle cat
197 add addremove annotate archive backout branch branches bundle cat
198 clone commit copy diff export grep heads help identify import incoming
198 clone commit copy diff export grep heads help identify import incoming
199 init locate log manifest merge outgoing parents paths pull push
199 init locate log manifest merge outgoing parents paths pull push
200 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
200 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
201 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
201 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
202 qselect qseries qtop qunapplied recover remove rename revert rollback
202 qselect qseries qtop qunapplied recover remove rename revert rollback
203 root serve showconfig status strip tag tags tip unbundle update verify
203 root serve showconfig status strip tag tags tip unbundle update verify
204 version
204 version
205 """
205 """
206
206
207 def hg_completer(self,event):
207 def hg_completer(self,event):
208 """ Completer for mercurial commands """
208 """ Completer for mercurial commands """
209
209
210 return vcs_completer(hg_commands, event)
210 return vcs_completer(hg_commands, event)
211
211
212
212
213
213
214 __bzr_commands = None
215
214 def bzr_commands():
216 def bzr_commands():
217 global __bzr_commands
218 if __bzr_commands is not None:
219 return __bzr_commands
215 out = os.popen('bzr help commands')
220 out = os.popen('bzr help commands')
216 return [l.split()[0] for l in out]
221 __bzr_commands = [l.split()[0] for l in out]
217
222 return __bzr_commands
218
223
219 def bzr_completer(self,event):
224 def bzr_completer(self,event):
220 """ Completer for bazaar commands """
225 """ Completer for bazaar commands """
221 cmd_param = event.line.split()
226 cmd_param = event.line.split()
222 if event.line.endswith(' '):
227 if event.line.endswith(' '):
223 cmd_param.append('')
228 cmd_param.append('')
224
229
225 if len(cmd_param) > 2:
230 if len(cmd_param) > 2:
226 cmd = cmd_param[1]
231 cmd = cmd_param[1]
227 param = cmd_param[-1]
232 param = cmd_param[-1]
228 output_file = (param == '--output=')
233 output_file = (param == '--output=')
229 if cmd == 'help':
234 if cmd == 'help':
230 return bzr_commands()
235 return bzr_commands()
231 elif cmd in ['bundle-revisions','conflicts',
236 elif cmd in ['bundle-revisions','conflicts',
232 'deleted','nick','register-branch',
237 'deleted','nick','register-branch',
233 'serve','unbind','upgrade','version',
238 'serve','unbind','upgrade','version',
234 'whoami'] and not output_file:
239 'whoami'] and not output_file:
235 return []
240 return []
236 else:
241 else:
237 # the rest are probably file names
242 # the rest are probably file names
238 return ip.IP.Completer.file_matches(event.symbol)
243 return ip.IP.Completer.file_matches(event.symbol)
239
244
240 return bzr_commands()
245 return bzr_commands()
241
246
242
247
243 def shlex_split(x):
248 def shlex_split(x):
244 """Helper function to split lines into segments."""
249 """Helper function to split lines into segments."""
245 #shlex.split raise exception if syntax error in sh syntax
250 #shlex.split raise exception if syntax error in sh syntax
246 #for example if no closing " is found. This function keeps dropping
251 #for example if no closing " is found. This function keeps dropping
247 #the last character of the line until shlex.split does not raise
252 #the last character of the line until shlex.split does not raise
248 #exception. Adds end of the line to the result of shlex.split
253 #exception. Adds end of the line to the result of shlex.split
249 #example: %run "c:/python -> ['%run','"c:/python']
254 #example: %run "c:/python -> ['%run','"c:/python']
250 endofline=[]
255 endofline=[]
251 while x!="":
256 while x!="":
252 try:
257 try:
253 comps=shlex.split(x)
258 comps=shlex.split(x)
254 if len(endofline)>=1:
259 if len(endofline)>=1:
255 comps.append("".join(endofline))
260 comps.append("".join(endofline))
256 return comps
261 return comps
257 except ValueError:
262 except ValueError:
258 endofline=[x[-1:]]+endofline
263 endofline=[x[-1:]]+endofline
259 x=x[:-1]
264 x=x[:-1]
260 return ["".join(endofline)]
265 return ["".join(endofline)]
261
266
262 def runlistpy(self, event):
267 def runlistpy(self, event):
263 comps = shlex_split(event.line)
268 comps = shlex_split(event.line)
264 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
269 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
265
270
266 #print "\nev=",event # dbg
271 #print "\nev=",event # dbg
267 #print "rp=",relpath # dbg
272 #print "rp=",relpath # dbg
268 #print 'comps=',comps # dbg
273 #print 'comps=',comps # dbg
269
274
270 lglob = glob.glob
275 lglob = glob.glob
271 isdir = os.path.isdir
276 isdir = os.path.isdir
272 if relpath.startswith('~'):
277 if relpath.startswith('~'):
273 relpath = os.path.expanduser(relpath)
278 relpath = os.path.expanduser(relpath)
274 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
279 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
275 if isdir(f)]
280 if isdir(f)]
276
281
277 # Find if the user has already typed the first filename, after which we
282 # Find if the user has already typed the first filename, after which we
278 # should complete on all files, since after the first one other files may
283 # should complete on all files, since after the first one other files may
279 # be arguments to the input script.
284 # be arguments to the input script.
280 #filter(
285 #filter(
281 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
286 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
282 f.endswith('.pyw'),comps):
287 f.endswith('.pyw'),comps):
283 pys = [f.replace('\\','/') for f in lglob('*')]
288 pys = [f.replace('\\','/') for f in lglob('*')]
284 else:
289 else:
285 pys = [f.replace('\\','/')
290 pys = [f.replace('\\','/')
286 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
291 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
287 lglob(relpath + '*.pyw')]
292 lglob(relpath + '*.pyw')]
288 return dirs + pys
293 return dirs + pys
289
294
290
295
291 def cd_completer(self, event):
296 def cd_completer(self, event):
292 relpath = event.symbol
297 relpath = event.symbol
293 #print event # dbg
298 #print event # dbg
294 if '-b' in event.line:
299 if '-b' in event.line:
295 # return only bookmark completions
300 # return only bookmark completions
296 bkms = self.db.get('bookmarks',{})
301 bkms = self.db.get('bookmarks',{})
297 return bkms.keys()
302 return bkms.keys()
298
303
299
304
300 if event.symbol == '-':
305 if event.symbol == '-':
301 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
306 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
302 # jump in directory history by number
307 # jump in directory history by number
303 fmt = '-%0' + width_dh +'d [%s]'
308 fmt = '-%0' + width_dh +'d [%s]'
304 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
309 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
305 if len(ents) > 1:
310 if len(ents) > 1:
306 return ents
311 return ents
307 return []
312 return []
308
313
309 if relpath.startswith('~'):
314 if relpath.startswith('~'):
310 relpath = os.path.expanduser(relpath).replace('\\','/')
315 relpath = os.path.expanduser(relpath).replace('\\','/')
311 found = []
316 found = []
312 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
317 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
313 if os.path.isdir(f)]:
318 if os.path.isdir(f)]:
314 if ' ' in d:
319 if ' ' in d:
315 # we don't want to deal with any of that, complex code
320 # we don't want to deal with any of that, complex code
316 # for this is elsewhere
321 # for this is elsewhere
317 raise IPython.ipapi.TryNext
322 raise IPython.ipapi.TryNext
318 found.append( d )
323 found.append( d )
319
324
320 if not found:
325 if not found:
321 if os.path.isdir(relpath):
326 if os.path.isdir(relpath):
322 return [relpath]
327 return [relpath]
323 raise IPython.ipapi.TryNext
328 raise IPython.ipapi.TryNext
324 return found
329 return found
325
330
326 def apt_get_packages(prefix):
331 def apt_get_packages(prefix):
327 out = os.popen('apt-cache pkgnames')
332 out = os.popen('apt-cache pkgnames')
328 for p in out:
333 for p in out:
329 if p.startswith(prefix):
334 if p.startswith(prefix):
330 yield p.rstrip()
335 yield p.rstrip()
331
336
332
337
333 apt_commands = """\
338 apt_commands = """\
334 update upgrade install remove purge source build-dep dist-upgrade
339 update upgrade install remove purge source build-dep dist-upgrade
335 dselect-upgrade clean autoclean check"""
340 dselect-upgrade clean autoclean check"""
336
341
337 def apt_completer(self, event):
342 def apt_completer(self, event):
338 """ Completer for apt-get (uses apt-cache internally)
343 """ Completer for apt-get (uses apt-cache internally)
339
344
340 """
345 """
341
346
342
347
343 cmd_param = event.line.split()
348 cmd_param = event.line.split()
344 if event.line.endswith(' '):
349 if event.line.endswith(' '):
345 cmd_param.append('')
350 cmd_param.append('')
346
351
347 if cmd_param[0] == 'sudo':
352 if cmd_param[0] == 'sudo':
348 cmd_param = cmd_param[1:]
353 cmd_param = cmd_param[1:]
349
354
350 if len(cmd_param) == 2 or 'help' in cmd_param:
355 if len(cmd_param) == 2 or 'help' in cmd_param:
351 return apt_commands.split()
356 return apt_commands.split()
352
357
353 return list(apt_get_packages(event.symbol))
358 return list(apt_get_packages(event.symbol))
354
359
General Comments 0
You need to be logged in to leave comments. Login now