##// END OF EJS Templates
Applied Olivier Lauzannes patch for cleaner import completer
vivainio -
Show More
@@ -1,226 +1,297 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
11
12 import IPython.ipapi
12 import IPython.ipapi
13 import glob,os,shlex,sys
13 import glob,os,shlex,sys
14
14 import inspect
15 ip = IPython.ipapi.get()
15 ip = IPython.ipapi.get()
16
16
17 def getRootModules():
18 """
19 Returns a list containing the names of all the modules available in the
20 folders of the pythonpath.
21 """
22 modules = []
23 for path in sys.path:
24 modules += moduleList(path)
25 modules += sys.builtin_module_names
26 modules = list(set(modules))
27 if '__init__' in modules:
28 modules.remove('__init__')
29 return list(set(modules))
30
31 def moduleList(path):
32 """
33 Return the list containing the names of the modules available in the given
34 folder.
35 """
36 folder_list = glob.glob(os.path.join(path,'*'))
37 folder_list = [path for path in folder_list \
38 if (os.path.isdir(path) and os.path.exists(os.path.join(path,'__init__.py')))\
39 or path[-3:] in ('.py','.so')\
40 or path[-4:] in ('.pyc','.pyo')]
41 folder_list += folder_list
42 folder_list = [os.path.basename(path).split('.')[0] for path in folder_list]
43 return folder_list
44
45 def moduleCompletion(line):
46 """
47 Returns a list containing the completion possibilities for an import line.
48 The line looks like this :
49 'import xml.d'
50 'from xml.dom import'
51 """
52 def tryImport(mod, only_modules=False):
53 def isImportable(module, attr):
54 if only_modules:
55 return inspect.ismodule(getattr(module, attr))
56 else:
57 return not(attr[:2] == '__' and attr[-2:] == '__')
58 try:
59 m = __import__(mod)
60 except:
61 return []
62 mods = mod.split('.')
63 for module in mods[1:]:
64 m = getattr(m,module)
65 if (not hasattr(m, '__file__')) or (not only_modules) or\
66 (hasattr(m, '__file__') and '__init__' in m.__file__):
67 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
68 completion_list.extend(getattr(m,'__all__',[]))
69 if hasattr(m, '__file__') and '__init__' in m.__file__:
70 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
71 completion_list = list(set(completion_list))
72 if '__init__' in completion_list:
73 completion_list.remove('__init__')
74 return completion_list
75
76 words = line.split(' ')
77 if len(words) == 3 and words[0] == 'from':
78 return ['import ']
79 if len(words) < 3 and (words[0] in ['import','from']) :
80 if len(words) == 1:
81 return getRootModules()
82 mod = words[1].split('.')
83 if len(mod) < 2:
84 return getRootModules()
85 completion_list = tryImport('.'.join(mod[:-1]), True)
86 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
87 return completion_list
88 if len(words) >= 3 and words[0] == 'from':
89 mod = words[1]
90 return tryImport(mod)
91
17 def vcs_completer(commands, event):
92 def vcs_completer(commands, event):
18 """ utility to make writing typical version control app completers easier
93 """ utility to make writing typical version control app completers easier
19
94
20 VCS command line apps typically have the format:
95 VCS command line apps typically have the format:
21
96
22 [sudo ]PROGNAME [help] [command] file file...
97 [sudo ]PROGNAME [help] [command] file file...
23
98
24 """
99 """
25
100
26
101
27 cmd_param = event.line.split()
102 cmd_param = event.line.split()
28 if event.line.endswith(' '):
103 if event.line.endswith(' '):
29 cmd_param.append('')
104 cmd_param.append('')
30
105
31 if cmd_param[0] == 'sudo':
106 if cmd_param[0] == 'sudo':
32 cmd_param = cmd_param[1:]
107 cmd_param = cmd_param[1:]
33
108
34 if len(cmd_param) == 2 or 'help' in cmd_param:
109 if len(cmd_param) == 2 or 'help' in cmd_param:
35 return commands.split()
110 return commands.split()
36
111
37 return ip.IP.Completer.file_matches(event.symbol)
112 return ip.IP.Completer.file_matches(event.symbol)
38
113
39
114
40
115
41 def apt_completers(self, event):
116 def apt_completers(self, event):
42 """ This should return a list of strings with possible completions.
117 """ This should return a list of strings with possible completions.
43
118
44 Note that all the included strings that don't start with event.symbol
119 Note that all the included strings that don't start with event.symbol
45 are removed, in order to not confuse readline.
120 are removed, in order to not confuse readline.
46
121
47 """
122 """
48 # print event # dbg
123 # print event # dbg
49
124
50 # commands are only suggested for the 'command' part of package manager
125 # commands are only suggested for the 'command' part of package manager
51 # invocation
126 # invocation
52
127
53 cmd = (event.line + "<placeholder>").rsplit(None,1)[0]
128 cmd = (event.line + "<placeholder>").rsplit(None,1)[0]
54 # print cmd
129 # print cmd
55 if cmd.endswith('apt-get') or cmd.endswith('yum'):
130 if cmd.endswith('apt-get') or cmd.endswith('yum'):
56 return ['update', 'upgrade', 'install', 'remove']
131 return ['update', 'upgrade', 'install', 'remove']
57
132
58 # later on, add dpkg -l / whatever to get list of possible
133 # later on, add dpkg -l / whatever to get list of possible
59 # packages, add switches etc. for the rest of command line
134 # packages, add switches etc. for the rest of command line
60 # filling
135 # filling
61
136
62 raise IPython.ipapi.TryNext
137 raise IPython.ipapi.TryNext
63
138
64
139
65
140
66 pkg_cache = None
141 pkg_cache = None
67
142
68 def module_completer(self,event):
143 def module_completer(self,event):
69 """ Give completions after user has typed 'import'.
144 """ Give completions after user has typed 'import ...' or 'from ...'"""
70
71 Note that only possible completions in the local directory are returned."""
72
145
73 # This works in all versions of python. While 2.5 has
146 # This works in all versions of python. While 2.5 has
74 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
147 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
75 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
148 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
76 # of possibly problematic side effects. At some point we may implement
149 # of possibly problematic side effects.
77 # something that searches sys.path in a saner/safer way, but for now we'll
150 # This search the folders in the sys.path for available modules.
78 # restrict ourselves to local completions only.
151
79 for el in [f[:-3] for f in glob.glob("*.py")]:
152 return moduleCompletion(event.line)
80 yield el
81 return
82
153
83
154
84 svn_commands = """\
155 svn_commands = """\
85 add blame praise annotate ann cat checkout co cleanup commit ci copy
156 add blame praise annotate ann cat checkout co cleanup commit ci copy
86 cp delete del remove rm diff di export help ? h import info list ls
157 cp delete del remove rm diff di export help ? h import info list ls
87 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
158 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
88 pe propget pget pg proplist plist pl propset pset ps resolved revert
159 pe propget pget pg proplist plist pl propset pset ps resolved revert
89 status stat st switch sw unlock update
160 status stat st switch sw unlock update
90 """
161 """
91
162
92 def svn_completer(self,event):
163 def svn_completer(self,event):
93 return vcs_completer(svn_commands, event)
164 return vcs_completer(svn_commands, event)
94
165
95
166
96 hg_commands = """
167 hg_commands = """
97 add addremove annotate archive backout branch branches bundle cat
168 add addremove annotate archive backout branch branches bundle cat
98 clone commit copy diff export grep heads help identify import incoming
169 clone commit copy diff export grep heads help identify import incoming
99 init locate log manifest merge outgoing parents paths pull push
170 init locate log manifest merge outgoing parents paths pull push
100 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
171 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
101 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
172 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
102 qselect qseries qtop qunapplied recover remove rename revert rollback
173 qselect qseries qtop qunapplied recover remove rename revert rollback
103 root serve showconfig status strip tag tags tip unbundle update verify
174 root serve showconfig status strip tag tags tip unbundle update verify
104 version
175 version
105 """
176 """
106
177
107 def hg_completer(self,event):
178 def hg_completer(self,event):
108 """ Completer for mercurial commands """
179 """ Completer for mercurial commands """
109
180
110 return vcs_completer(hg_commands, event)
181 return vcs_completer(hg_commands, event)
111
182
112
183
113
184
114 bzr_commands = """
185 bzr_commands = """
115 add annotate bind branch break-lock bundle-revisions cat check
186 add annotate bind branch break-lock bundle-revisions cat check
116 checkout commit conflicts deleted diff export gannotate gbranch
187 checkout commit conflicts deleted diff export gannotate gbranch
117 gcommit gdiff help ignore ignored info init init-repository inventory
188 gcommit gdiff help ignore ignored info init init-repository inventory
118 log merge missing mkdir mv nick pull push reconcile register-branch
189 log merge missing mkdir mv nick pull push reconcile register-branch
119 remerge remove renames resolve revert revno root serve sign-my-commits
190 remerge remove renames resolve revert revno root serve sign-my-commits
120 status testament unbind uncommit unknowns update upgrade version
191 status testament unbind uncommit unknowns update upgrade version
121 version-info visualise whoami
192 version-info visualise whoami
122 """
193 """
123
194
124 def bzr_completer(self,event):
195 def bzr_completer(self,event):
125 """ Completer for bazaar commands """
196 """ Completer for bazaar commands """
126 cmd_param = event.line.split()
197 cmd_param = event.line.split()
127 if event.line.endswith(' '):
198 if event.line.endswith(' '):
128 cmd_param.append('')
199 cmd_param.append('')
129
200
130 if len(cmd_param) > 2:
201 if len(cmd_param) > 2:
131 cmd = cmd_param[1]
202 cmd = cmd_param[1]
132 param = cmd_param[-1]
203 param = cmd_param[-1]
133 output_file = (param == '--output=')
204 output_file = (param == '--output=')
134 if cmd == 'help':
205 if cmd == 'help':
135 return bzr_commands.split()
206 return bzr_commands.split()
136 elif cmd in ['bundle-revisions','conflicts',
207 elif cmd in ['bundle-revisions','conflicts',
137 'deleted','nick','register-branch',
208 'deleted','nick','register-branch',
138 'serve','unbind','upgrade','version',
209 'serve','unbind','upgrade','version',
139 'whoami'] and not output_file:
210 'whoami'] and not output_file:
140 return []
211 return []
141 else:
212 else:
142 # the rest are probably file names
213 # the rest are probably file names
143 return ip.IP.Completer.file_matches(event.symbol)
214 return ip.IP.Completer.file_matches(event.symbol)
144
215
145 return bzr_commands.split()
216 return bzr_commands.split()
146
217
147
218
148 def shlex_split(x):
219 def shlex_split(x):
149 """Helper function to split lines into segments."""
220 """Helper function to split lines into segments."""
150 #shlex.split raise exception if syntax error in sh syntax
221 #shlex.split raise exception if syntax error in sh syntax
151 #for example if no closing " is found. This function keeps dropping
222 #for example if no closing " is found. This function keeps dropping
152 #the last character of the line until shlex.split does not raise
223 #the last character of the line until shlex.split does not raise
153 #exception. Adds end of the line to the result of shlex.split
224 #exception. Adds end of the line to the result of shlex.split
154 #example: %run "c:/python -> ['%run','"c:/python']
225 #example: %run "c:/python -> ['%run','"c:/python']
155 endofline=[]
226 endofline=[]
156 while x!="":
227 while x!="":
157 try:
228 try:
158 comps=shlex.split(x)
229 comps=shlex.split(x)
159 if len(endofline)>=1:
230 if len(endofline)>=1:
160 comps.append("".join(endofline))
231 comps.append("".join(endofline))
161 return comps
232 return comps
162 except ValueError:
233 except ValueError:
163 endofline=[x[-1:]]+endofline
234 endofline=[x[-1:]]+endofline
164 x=x[:-1]
235 x=x[:-1]
165 return ["".join(endofline)]
236 return ["".join(endofline)]
166
237
167 def runlistpy(self, event):
238 def runlistpy(self, event):
168 comps = shlex_split(event.line)
239 comps = shlex_split(event.line)
169 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
240 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
170
241
171 #print "\nev=",event # dbg
242 #print "\nev=",event # dbg
172 #print "rp=",relpath # dbg
243 #print "rp=",relpath # dbg
173 #print 'comps=',comps # dbg
244 #print 'comps=',comps # dbg
174
245
175 lglob = glob.glob
246 lglob = glob.glob
176 isdir = os.path.isdir
247 isdir = os.path.isdir
177 if relpath.startswith('~'):
248 if relpath.startswith('~'):
178 relpath = os.path.expanduser(relpath)
249 relpath = os.path.expanduser(relpath)
179 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
250 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
180 if isdir(f)]
251 if isdir(f)]
181
252
182 # Find if the user has already typed the first filename, after which we
253 # Find if the user has already typed the first filename, after which we
183 # should complete on all files, since after the first one other files may
254 # should complete on all files, since after the first one other files may
184 # be arguments to the input script.
255 # be arguments to the input script.
185 #filter(
256 #filter(
186 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy'),comps):
257 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy'),comps):
187 pys = [f.replace('\\','/') for f in lglob('*')]
258 pys = [f.replace('\\','/') for f in lglob('*')]
188 else:
259 else:
189 pys = [f.replace('\\','/')
260 pys = [f.replace('\\','/')
190 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy')]
261 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy')]
191 return dirs + pys
262 return dirs + pys
192
263
193
264
194 def cd_completer(self, event):
265 def cd_completer(self, event):
195 relpath = event.symbol
266 relpath = event.symbol
196 #print event # dbg
267 #print event # dbg
197 if '-b' in event.line:
268 if '-b' in event.line:
198 # return only bookmark completions
269 # return only bookmark completions
199 bkms = self.db.get('bookmarks',{})
270 bkms = self.db.get('bookmarks',{})
200 return bkms.keys()
271 return bkms.keys()
201
272
202
273
203 if event.symbol == '-':
274 if event.symbol == '-':
204 # jump in directory history by number
275 # jump in directory history by number
205 ents = ['-%d [%s]' % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
276 ents = ['-%d [%s]' % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
206 if len(ents) > 1:
277 if len(ents) > 1:
207 return ents
278 return ents
208 return []
279 return []
209
280
210 if relpath.startswith('~'):
281 if relpath.startswith('~'):
211 relpath = os.path.expanduser(relpath).replace('\\','/')
282 relpath = os.path.expanduser(relpath).replace('\\','/')
212 found = []
283 found = []
213 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
284 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
214 if os.path.isdir(f)]:
285 if os.path.isdir(f)]:
215 if ' ' in d:
286 if ' ' in d:
216 # we don't want to deal with any of that, complex code
287 # we don't want to deal with any of that, complex code
217 # for this is elsewhere
288 # for this is elsewhere
218 raise IPython.ipapi.TryNext
289 raise IPython.ipapi.TryNext
219 found.append( d )
290 found.append( d )
220
291
221 if not found:
292 if not found:
222 if os.path.isdir(relpath):
293 if os.path.isdir(relpath):
223 return [relpath]
294 return [relpath]
224 raise IPython.ipapi.TryNext
295 raise IPython.ipapi.TryNext
225 return found
296 return found
226
297
General Comments 0
You need to be logged in to leave comments. Login now