##// END OF EJS Templates
cd completer considers bookmarks if no other matches are available
Ville M. Vainio -
Show More
@@ -1,394 +1,400 b''
1 1
2 2 """ Implementations for various useful completers
3 3
4 4 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
5 5 but the basic idea is to do:
6 6
7 7 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
8 8
9 9 """
10 10 import IPython.ipapi
11 11 import glob,os,shlex,sys
12 12 import inspect
13 13 from time import time
14 14 from zipimport import zipimporter
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 elif path.endswith('.egg'):
90 90 try:
91 91 folder_list = [f for f in zipimporter(path)._files]
92 92 except:
93 93 folder_list = []
94 94 else:
95 95 folder_list = []
96 96 #folder_list = glob.glob(os.path.join(path,'*'))
97 97 folder_list = [p for p in folder_list \
98 98 if os.path.exists(os.path.join(path, p,'__init__.py'))\
99 99 or p[-3:] in ('.py','.so')\
100 100 or p[-4:] in ('.pyc','.pyo','.pyd')]
101 101
102 102 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
103 103 return folder_list
104 104
105 105 def moduleCompletion(line):
106 106 """
107 107 Returns a list containing the completion possibilities for an import line.
108 108 The line looks like this :
109 109 'import xml.d'
110 110 'from xml.dom import'
111 111 """
112 112 def tryImport(mod, only_modules=False):
113 113 def isImportable(module, attr):
114 114 if only_modules:
115 115 return inspect.ismodule(getattr(module, attr))
116 116 else:
117 117 return not(attr[:2] == '__' and attr[-2:] == '__')
118 118 try:
119 119 m = __import__(mod)
120 120 except:
121 121 return []
122 122 mods = mod.split('.')
123 123 for module in mods[1:]:
124 124 m = getattr(m,module)
125 125 if (not hasattr(m, '__file__')) or (not only_modules) or\
126 126 (hasattr(m, '__file__') and '__init__' in m.__file__):
127 127 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
128 128 completion_list.extend(getattr(m,'__all__',[]))
129 129 if hasattr(m, '__file__') and '__init__' in m.__file__:
130 130 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
131 131 completion_list = list(set(completion_list))
132 132 if '__init__' in completion_list:
133 133 completion_list.remove('__init__')
134 134 return completion_list
135 135
136 136 words = line.split(' ')
137 137 if len(words) == 3 and words[0] == 'from':
138 138 return ['import ']
139 139 if len(words) < 3 and (words[0] in ['import','from']) :
140 140 if len(words) == 1:
141 141 return getRootModules()
142 142 mod = words[1].split('.')
143 143 if len(mod) < 2:
144 144 return getRootModules()
145 145 completion_list = tryImport('.'.join(mod[:-1]), True)
146 146 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
147 147 return completion_list
148 148 if len(words) >= 3 and words[0] == 'from':
149 149 mod = words[1]
150 150 return tryImport(mod)
151 151
152 152 def vcs_completer(commands, event):
153 153 """ utility to make writing typical version control app completers easier
154 154
155 155 VCS command line apps typically have the format:
156 156
157 157 [sudo ]PROGNAME [help] [command] file file...
158 158
159 159 """
160 160
161 161
162 162 cmd_param = event.line.split()
163 163 if event.line.endswith(' '):
164 164 cmd_param.append('')
165 165
166 166 if cmd_param[0] == 'sudo':
167 167 cmd_param = cmd_param[1:]
168 168
169 169 if len(cmd_param) == 2 or 'help' in cmd_param:
170 170 return commands.split()
171 171
172 172 return ip.IP.Completer.file_matches(event.symbol)
173 173
174 174
175 175 pkg_cache = None
176 176
177 177 def module_completer(self,event):
178 178 """ Give completions after user has typed 'import ...' or 'from ...'"""
179 179
180 180 # This works in all versions of python. While 2.5 has
181 181 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
182 182 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
183 183 # of possibly problematic side effects.
184 184 # This search the folders in the sys.path for available modules.
185 185
186 186 return moduleCompletion(event.line)
187 187
188 188
189 189 svn_commands = """\
190 190 add blame praise annotate ann cat checkout co cleanup commit ci copy
191 191 cp delete del remove rm diff di export help ? h import info list ls
192 192 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
193 193 pe propget pget pg proplist plist pl propset pset ps resolved revert
194 194 status stat st switch sw unlock update
195 195 """
196 196
197 197 def svn_completer(self,event):
198 198 return vcs_completer(svn_commands, event)
199 199
200 200
201 201 hg_commands = """
202 202 add addremove annotate archive backout branch branches bundle cat
203 203 clone commit copy diff export grep heads help identify import incoming
204 204 init locate log manifest merge outgoing parents paths pull push
205 205 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
206 206 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
207 207 qselect qseries qtop qunapplied recover remove rename revert rollback
208 208 root serve showconfig status strip tag tags tip unbundle update verify
209 209 version
210 210 """
211 211
212 212 def hg_completer(self,event):
213 213 """ Completer for mercurial commands """
214 214
215 215 return vcs_completer(hg_commands, event)
216 216
217 217
218 218
219 219 __bzr_commands = None
220 220
221 221 def bzr_commands():
222 222 global __bzr_commands
223 223 if __bzr_commands is not None:
224 224 return __bzr_commands
225 225 out = os.popen('bzr help commands')
226 226 __bzr_commands = [l.split()[0] for l in out]
227 227 return __bzr_commands
228 228
229 229 def bzr_completer(self,event):
230 230 """ Completer for bazaar commands """
231 231 cmd_param = event.line.split()
232 232 if event.line.endswith(' '):
233 233 cmd_param.append('')
234 234
235 235 if len(cmd_param) > 2:
236 236 cmd = cmd_param[1]
237 237 param = cmd_param[-1]
238 238 output_file = (param == '--output=')
239 239 if cmd == 'help':
240 240 return bzr_commands()
241 241 elif cmd in ['bundle-revisions','conflicts',
242 242 'deleted','nick','register-branch',
243 243 'serve','unbind','upgrade','version',
244 244 'whoami'] and not output_file:
245 245 return []
246 246 else:
247 247 # the rest are probably file names
248 248 return ip.IP.Completer.file_matches(event.symbol)
249 249
250 250 return bzr_commands()
251 251
252 252
253 253 def shlex_split(x):
254 254 """Helper function to split lines into segments."""
255 255 #shlex.split raise exception if syntax error in sh syntax
256 256 #for example if no closing " is found. This function keeps dropping
257 257 #the last character of the line until shlex.split does not raise
258 258 #exception. Adds end of the line to the result of shlex.split
259 259 #example: %run "c:/python -> ['%run','"c:/python']
260 260 endofline=[]
261 261 while x!="":
262 262 try:
263 263 comps=shlex.split(x)
264 264 if len(endofline)>=1:
265 265 comps.append("".join(endofline))
266 266 return comps
267 267 except ValueError:
268 268 endofline=[x[-1:]]+endofline
269 269 x=x[:-1]
270 270 return ["".join(endofline)]
271 271
272 272 def runlistpy(self, event):
273 273 comps = shlex_split(event.line)
274 274 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
275 275
276 276 #print "\nev=",event # dbg
277 277 #print "rp=",relpath # dbg
278 278 #print 'comps=',comps # dbg
279 279
280 280 lglob = glob.glob
281 281 isdir = os.path.isdir
282 282 if relpath.startswith('~'):
283 283 relpath = os.path.expanduser(relpath)
284 284 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
285 285 if isdir(f)]
286 286
287 287 # Find if the user has already typed the first filename, after which we
288 288 # should complete on all files, since after the first one other files may
289 289 # be arguments to the input script.
290 290 #filter(
291 291 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
292 292 f.endswith('.pyw'),comps):
293 293 pys = [f.replace('\\','/') for f in lglob('*')]
294 294 else:
295 295 pys = [f.replace('\\','/')
296 296 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
297 297 lglob(relpath + '*.pyw')]
298 298 return dirs + pys
299 299
300 300
301 301 greedy_cd_completer = False
302 302
303 303 def cd_completer(self, event):
304 304 relpath = event.symbol
305 305 #print event # dbg
306 306 if '-b' in event.line:
307 307 # return only bookmark completions
308 308 bkms = self.db.get('bookmarks',{})
309 309 return bkms.keys()
310 310
311 311
312 312 if event.symbol == '-':
313 313 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
314 314 # jump in directory history by number
315 315 fmt = '-%0' + width_dh +'d [%s]'
316 316 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
317 317 if len(ents) > 1:
318 318 return ents
319 319 return []
320 320
321 321 if event.symbol.startswith('--'):
322 322 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
323 323
324 324 if relpath.startswith('~'):
325 325 relpath = os.path.expanduser(relpath).replace('\\','/')
326 326 found = []
327 327 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
328 328 if os.path.isdir(f)]:
329 329 if ' ' in d:
330 330 # we don't want to deal with any of that, complex code
331 331 # for this is elsewhere
332 332 raise IPython.ipapi.TryNext
333 333 found.append( d )
334 334
335 335 if not found:
336 336 if os.path.isdir(relpath):
337 337 return [relpath]
338
339 bks = self.db.get('bookmarks',{}).keys()
340 bkmatches = [s for s in bks if s.startswith(event.symbol)]
341 if bkmatches:
342 return bkmatches
343
338 344 raise IPython.ipapi.TryNext
339 345
340 346
341 347 def single_dir_expand(matches):
342 348 "Recursively expand match lists containing a single dir."
343 349
344 350 if len(matches) == 1 and os.path.isdir(matches[0]):
345 351 # Takes care of links to directories also. Use '/'
346 352 # explicitly, even under Windows, so that name completions
347 353 # don't end up escaped.
348 354 d = matches[0]
349 355 if d[-1] in ['/','\\']:
350 356 d = d[:-1]
351 357
352 358 subdirs = [p for p in os.listdir(d) if os.path.isdir( d + '/' + p) and not p.startswith('.')]
353 359 if subdirs:
354 360 matches = [ (d + '/' + p) for p in subdirs ]
355 361 return single_dir_expand(matches)
356 362 else:
357 363 return matches
358 364 else:
359 365 return matches
360 366
361 367 if greedy_cd_completer:
362 368 return single_dir_expand(found)
363 369 else:
364 370 return found
365 371
366 372 def apt_get_packages(prefix):
367 373 out = os.popen('apt-cache pkgnames')
368 374 for p in out:
369 375 if p.startswith(prefix):
370 376 yield p.rstrip()
371 377
372 378
373 379 apt_commands = """\
374 380 update upgrade install remove purge source build-dep dist-upgrade
375 381 dselect-upgrade clean autoclean check"""
376 382
377 383 def apt_completer(self, event):
378 384 """ Completer for apt-get (uses apt-cache internally)
379 385
380 386 """
381 387
382 388
383 389 cmd_param = event.line.split()
384 390 if event.line.endswith(' '):
385 391 cmd_param.append('')
386 392
387 393 if cmd_param[0] == 'sudo':
388 394 cmd_param = cmd_param[1:]
389 395
390 396 if len(cmd_param) == 2 or 'help' in cmd_param:
391 397 return apt_commands.split()
392 398
393 399 return list(apt_get_packages(event.symbol))
394 400
General Comments 0
You need to be logged in to leave comments. Login now