##// END OF EJS Templates
bzr completer now gets commands from 'bzr help commands' (includes all commands provided by exensions also)
vivainio2 -
Show More
@@ -1,359 +1,354 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 = """
214 def bzr_commands():
215 add annotate bind branch break-lock bundle-revisions cat check
215 out = os.popen('bzr help commands')
216 checkout commit conflicts deleted diff export gannotate gbranch
216 return [l.split()[0] for l in out]
217 gcommit gdiff help ignore ignored info init init-repository inventory
217
218 log merge missing mkdir mv nick pull push reconcile register-branch
219 remerge remove renames resolve revert revno root serve sign-my-commits
220 status testament unbind uncommit unknowns update upgrade version
221 version-info visualise whoami
222 """
223
218
224 def bzr_completer(self,event):
219 def bzr_completer(self,event):
225 """ Completer for bazaar commands """
220 """ Completer for bazaar commands """
226 cmd_param = event.line.split()
221 cmd_param = event.line.split()
227 if event.line.endswith(' '):
222 if event.line.endswith(' '):
228 cmd_param.append('')
223 cmd_param.append('')
229
224
230 if len(cmd_param) > 2:
225 if len(cmd_param) > 2:
231 cmd = cmd_param[1]
226 cmd = cmd_param[1]
232 param = cmd_param[-1]
227 param = cmd_param[-1]
233 output_file = (param == '--output=')
228 output_file = (param == '--output=')
234 if cmd == 'help':
229 if cmd == 'help':
235 return bzr_commands.split()
230 return bzr_commands()
236 elif cmd in ['bundle-revisions','conflicts',
231 elif cmd in ['bundle-revisions','conflicts',
237 'deleted','nick','register-branch',
232 'deleted','nick','register-branch',
238 'serve','unbind','upgrade','version',
233 'serve','unbind','upgrade','version',
239 'whoami'] and not output_file:
234 'whoami'] and not output_file:
240 return []
235 return []
241 else:
236 else:
242 # the rest are probably file names
237 # the rest are probably file names
243 return ip.IP.Completer.file_matches(event.symbol)
238 return ip.IP.Completer.file_matches(event.symbol)
244
239
245 return bzr_commands.split()
240 return bzr_commands()
246
241
247
242
248 def shlex_split(x):
243 def shlex_split(x):
249 """Helper function to split lines into segments."""
244 """Helper function to split lines into segments."""
250 #shlex.split raise exception if syntax error in sh syntax
245 #shlex.split raise exception if syntax error in sh syntax
251 #for example if no closing " is found. This function keeps dropping
246 #for example if no closing " is found. This function keeps dropping
252 #the last character of the line until shlex.split does not raise
247 #the last character of the line until shlex.split does not raise
253 #exception. Adds end of the line to the result of shlex.split
248 #exception. Adds end of the line to the result of shlex.split
254 #example: %run "c:/python -> ['%run','"c:/python']
249 #example: %run "c:/python -> ['%run','"c:/python']
255 endofline=[]
250 endofline=[]
256 while x!="":
251 while x!="":
257 try:
252 try:
258 comps=shlex.split(x)
253 comps=shlex.split(x)
259 if len(endofline)>=1:
254 if len(endofline)>=1:
260 comps.append("".join(endofline))
255 comps.append("".join(endofline))
261 return comps
256 return comps
262 except ValueError:
257 except ValueError:
263 endofline=[x[-1:]]+endofline
258 endofline=[x[-1:]]+endofline
264 x=x[:-1]
259 x=x[:-1]
265 return ["".join(endofline)]
260 return ["".join(endofline)]
266
261
267 def runlistpy(self, event):
262 def runlistpy(self, event):
268 comps = shlex_split(event.line)
263 comps = shlex_split(event.line)
269 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
264 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
270
265
271 #print "\nev=",event # dbg
266 #print "\nev=",event # dbg
272 #print "rp=",relpath # dbg
267 #print "rp=",relpath # dbg
273 #print 'comps=',comps # dbg
268 #print 'comps=',comps # dbg
274
269
275 lglob = glob.glob
270 lglob = glob.glob
276 isdir = os.path.isdir
271 isdir = os.path.isdir
277 if relpath.startswith('~'):
272 if relpath.startswith('~'):
278 relpath = os.path.expanduser(relpath)
273 relpath = os.path.expanduser(relpath)
279 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
274 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
280 if isdir(f)]
275 if isdir(f)]
281
276
282 # Find if the user has already typed the first filename, after which we
277 # Find if the user has already typed the first filename, after which we
283 # should complete on all files, since after the first one other files may
278 # should complete on all files, since after the first one other files may
284 # be arguments to the input script.
279 # be arguments to the input script.
285 #filter(
280 #filter(
286 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
281 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
287 f.endswith('.pyw'),comps):
282 f.endswith('.pyw'),comps):
288 pys = [f.replace('\\','/') for f in lglob('*')]
283 pys = [f.replace('\\','/') for f in lglob('*')]
289 else:
284 else:
290 pys = [f.replace('\\','/')
285 pys = [f.replace('\\','/')
291 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
286 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
292 lglob(relpath + '*.pyw')]
287 lglob(relpath + '*.pyw')]
293 return dirs + pys
288 return dirs + pys
294
289
295
290
296 def cd_completer(self, event):
291 def cd_completer(self, event):
297 relpath = event.symbol
292 relpath = event.symbol
298 #print event # dbg
293 #print event # dbg
299 if '-b' in event.line:
294 if '-b' in event.line:
300 # return only bookmark completions
295 # return only bookmark completions
301 bkms = self.db.get('bookmarks',{})
296 bkms = self.db.get('bookmarks',{})
302 return bkms.keys()
297 return bkms.keys()
303
298
304
299
305 if event.symbol == '-':
300 if event.symbol == '-':
306 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
301 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
307 # jump in directory history by number
302 # jump in directory history by number
308 fmt = '-%0' + width_dh +'d [%s]'
303 fmt = '-%0' + width_dh +'d [%s]'
309 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
304 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
310 if len(ents) > 1:
305 if len(ents) > 1:
311 return ents
306 return ents
312 return []
307 return []
313
308
314 if relpath.startswith('~'):
309 if relpath.startswith('~'):
315 relpath = os.path.expanduser(relpath).replace('\\','/')
310 relpath = os.path.expanduser(relpath).replace('\\','/')
316 found = []
311 found = []
317 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
312 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
318 if os.path.isdir(f)]:
313 if os.path.isdir(f)]:
319 if ' ' in d:
314 if ' ' in d:
320 # we don't want to deal with any of that, complex code
315 # we don't want to deal with any of that, complex code
321 # for this is elsewhere
316 # for this is elsewhere
322 raise IPython.ipapi.TryNext
317 raise IPython.ipapi.TryNext
323 found.append( d )
318 found.append( d )
324
319
325 if not found:
320 if not found:
326 if os.path.isdir(relpath):
321 if os.path.isdir(relpath):
327 return [relpath]
322 return [relpath]
328 raise IPython.ipapi.TryNext
323 raise IPython.ipapi.TryNext
329 return found
324 return found
330
325
331 def apt_get_packages(prefix):
326 def apt_get_packages(prefix):
332 out = os.popen('apt-cache pkgnames')
327 out = os.popen('apt-cache pkgnames')
333 for p in out:
328 for p in out:
334 if p.startswith(prefix):
329 if p.startswith(prefix):
335 yield p.rstrip()
330 yield p.rstrip()
336
331
337
332
338 apt_commands = """\
333 apt_commands = """\
339 update upgrade install remove purge source build-dep dist-upgrade
334 update upgrade install remove purge source build-dep dist-upgrade
340 dselect-upgrade clean autoclean check"""
335 dselect-upgrade clean autoclean check"""
341
336
342 def apt_completer(self, event):
337 def apt_completer(self, event):
343 """ Completer for apt-get (uses apt-cache internally)
338 """ Completer for apt-get (uses apt-cache internally)
344
339
345 """
340 """
346
341
347
342
348 cmd_param = event.line.split()
343 cmd_param = event.line.split()
349 if event.line.endswith(' '):
344 if event.line.endswith(' '):
350 cmd_param.append('')
345 cmd_param.append('')
351
346
352 if cmd_param[0] == 'sudo':
347 if cmd_param[0] == 'sudo':
353 cmd_param = cmd_param[1:]
348 cmd_param = cmd_param[1:]
354
349
355 if len(cmd_param) == 2 or 'help' in cmd_param:
350 if len(cmd_param) == 2 or 'help' in cmd_param:
356 return apt_commands.split()
351 return apt_commands.split()
357
352
358 return list(apt_get_packages(event.symbol))
353 return list(apt_get_packages(event.symbol))
359
354
General Comments 0
You need to be logged in to leave comments. Login now