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