1
by Vladimír Lapáček
Initial import of upstream |
1 |
/*
|
2 |
* Created on Aug 11, 2004
|
|
3 |
*
|
|
4 |
* @author Fabio Zadrozny
|
|
5 |
*/
|
|
6 |
package org.python.pydev.editor.codecompletion; |
|
7 |
||
8 |
import java.io.File; |
|
9 |
import java.util.ArrayList; |
|
10 |
import java.util.Arrays; |
|
11 |
import java.util.Collection; |
|
12 |
import java.util.HashSet; |
|
13 |
import java.util.Iterator; |
|
14 |
import java.util.List; |
|
15 |
import java.util.StringTokenizer; |
|
16 |
||
17 |
import org.eclipse.core.runtime.CoreException; |
|
18 |
import org.eclipse.jface.text.BadLocationException; |
|
19 |
import org.eclipse.jface.text.IDocument; |
|
20 |
import org.eclipse.jface.text.IRegion; |
|
21 |
import org.eclipse.jface.text.ITextViewer; |
|
22 |
import org.eclipse.jface.text.contentassist.CompletionProposal; |
|
23 |
import org.eclipse.jface.text.contentassist.ICompletionProposal; |
|
24 |
import org.eclipse.swt.graphics.Image; |
|
25 |
import org.python.pydev.core.ExtensionHelper; |
|
26 |
import org.python.pydev.core.ICodeCompletionASTManager; |
|
27 |
import org.python.pydev.core.ICompletionState; |
|
28 |
import org.python.pydev.core.IModule; |
|
29 |
import org.python.pydev.core.IPythonNature; |
|
30 |
import org.python.pydev.core.IToken; |
|
31 |
import org.python.pydev.core.bundle.ImageCache; |
|
32 |
import org.python.pydev.core.docutils.DocUtils; |
|
33 |
import org.python.pydev.editor.codecompletion.revisited.ASTManager; |
|
34 |
import org.python.pydev.editor.codecompletion.revisited.CompletionRecursionException; |
|
35 |
import org.python.pydev.editor.codecompletion.revisited.CompletionState; |
|
36 |
import org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule; |
|
37 |
import org.python.pydev.editor.codecompletion.revisited.modules.CompiledModule; |
|
38 |
import org.python.pydev.editor.codecompletion.revisited.visitors.FindScopeVisitor; |
|
39 |
import org.python.pydev.editor.codecompletion.revisited.visitors.Scope; |
|
40 |
import org.python.pydev.editor.codecompletion.shell.AbstractShell; |
|
41 |
import org.python.pydev.parser.PyParser; |
|
42 |
import org.python.pydev.parser.jython.SimpleNode; |
|
43 |
import org.python.pydev.parser.jython.ast.ClassDef; |
|
44 |
import org.python.pydev.parser.jython.ast.Name; |
|
45 |
import org.python.pydev.parser.jython.ast.NameTok; |
|
46 |
import org.python.pydev.parser.visitors.NodeUtils; |
|
47 |
import org.python.pydev.plugin.PydevPlugin; |
|
48 |
import org.python.pydev.ui.UIConstants; |
|
49 |
||
50 |
/**
|
|
51 |
* @author Dmoore
|
|
52 |
* @author Fabio Zadrozny
|
|
53 |
*/
|
|
54 |
public class PyCodeCompletion { |
|
55 |
||
56 |
/**
|
|
57 |
* Type for unknown.
|
|
58 |
*/
|
|
59 |
public static final int TYPE_UNKNOWN = -1; |
|
60 |
||
61 |
/**
|
|
62 |
* Type for import (used to decide the icon)
|
|
63 |
*/
|
|
64 |
public static final int TYPE_IMPORT = 0; |
|
65 |
||
66 |
/**
|
|
67 |
* Type for class (used to decide the icon)
|
|
68 |
*/
|
|
69 |
public static final int TYPE_CLASS = 1; |
|
70 |
||
71 |
/**
|
|
72 |
* Type for function (used to decide the icon)
|
|
73 |
*/
|
|
74 |
public static final int TYPE_FUNCTION = 2; |
|
75 |
||
76 |
/**
|
|
77 |
* Type for attr (used to decide the icon)
|
|
78 |
*/
|
|
79 |
public static final int TYPE_ATTR = 3; |
|
80 |
||
81 |
/**
|
|
82 |
* Type for attr (used to decide the icon)
|
|
83 |
*/
|
|
84 |
public static final int TYPE_BUILTIN = 4; |
|
85 |
||
86 |
/**
|
|
87 |
* Type for parameter (used to decide the icon)
|
|
88 |
*/
|
|
89 |
public static final int TYPE_PARAM = 5; |
|
90 |
||
91 |
/**
|
|
92 |
* Type for package (used to decide the icon)
|
|
93 |
*/
|
|
94 |
public static final int TYPE_PACKAGE = 6; |
|
95 |
||
96 |
/**
|
|
97 |
* Type for relative import
|
|
98 |
*/
|
|
99 |
public static final int TYPE_RELATIVE_IMPORT = 7; |
|
100 |
||
101 |
||
102 |
/**
|
|
103 |
* Returns an image for the given type
|
|
104 |
* @param type
|
|
105 |
* @return
|
|
106 |
*/
|
|
107 |
public static Image getImageForType(int type){ |
|
108 |
try { |
|
109 |
ImageCache imageCache = PydevPlugin.getImageCache(); |
|
110 |
if (imageCache == null) |
|
111 |
return null; |
|
112 |
||
113 |
switch (type) { |
|
114 |
case PyCodeCompletion.TYPE_IMPORT: |
|
115 |
return imageCache.get(UIConstants.COMPLETION_IMPORT_ICON); |
|
116 |
||
117 |
case PyCodeCompletion.TYPE_CLASS: |
|
118 |
return imageCache.get(UIConstants.COMPLETION_CLASS_ICON); |
|
119 |
||
120 |
case PyCodeCompletion.TYPE_FUNCTION: |
|
121 |
return imageCache.get(UIConstants.PUBLIC_METHOD_ICON); |
|
122 |
||
123 |
case PyCodeCompletion.TYPE_ATTR: |
|
124 |
return imageCache.get(UIConstants.PUBLIC_ATTR_ICON); |
|
125 |
||
126 |
case PyCodeCompletion.TYPE_BUILTIN: |
|
127 |
return imageCache.get(UIConstants.BUILTINS_ICON); |
|
128 |
||
129 |
case PyCodeCompletion.TYPE_PARAM: |
|
130 |
return imageCache.get(UIConstants.COMPLETION_PARAMETERS_ICON); |
|
131 |
||
132 |
case PyCodeCompletion.TYPE_PACKAGE: |
|
133 |
return imageCache.get(UIConstants.COMPLETION_PACKAGE_ICON); |
|
134 |
||
135 |
case PyCodeCompletion.TYPE_RELATIVE_IMPORT: |
|
136 |
return imageCache.get(UIConstants.COMPLETION_RELATIVE_IMPORT_ICON); |
|
137 |
||
138 |
default:
|
|
139 |
return null; |
|
140 |
}
|
|
141 |
||
142 |
} catch (Exception e) { |
|
143 |
PydevPlugin.log(e, false); |
|
144 |
return null; |
|
145 |
}
|
|
146 |
}
|
|
147 |
||
148 |
/**
|
|
149 |
* Returns a list with the tokens to use for autocompletion.
|
|
150 |
*
|
|
151 |
* The list is composed from tuples containing the following:
|
|
152 |
*
|
|
153 |
* 0 - String - token name
|
|
154 |
* 1 - String - token description
|
|
155 |
* 2 - Integer - token type (see constants)
|
|
156 |
* @param viewer
|
|
157 |
*
|
|
158 |
* @return list of IToken.
|
|
159 |
*
|
|
160 |
* (This is where we do the "REAL" work).
|
|
161 |
* @throws BadLocationException
|
|
162 |
*/
|
|
163 |
public List getCodeCompletionProposals(ITextViewer viewer, CompletionRequest request) throws CoreException, BadLocationException { |
|
164 |
||
165 |
ArrayList ret = new ArrayList(); |
|
166 |
try { |
|
167 |
IPythonNature pythonNature = request.nature; |
|
168 |
if (pythonNature == null) { |
|
169 |
throw new RuntimeException("Unable to get python nature."); |
|
170 |
}
|
|
171 |
ICodeCompletionASTManager astManager = pythonNature.getAstManager(); |
|
172 |
if (astManager == null) { //we're probably still loading it. |
|
173 |
return new ArrayList(); |
|
174 |
}
|
|
175 |
||
176 |
List theList = new ArrayList(); |
|
177 |
try { |
|
178 |
if (CompiledModule.COMPILED_MODULES_ENABLED) { |
|
179 |
AbstractShell.getServerShell(request.nature, AbstractShell.COMPLETION_SHELL); //just start it |
|
180 |
}
|
|
181 |
} catch (Exception e) { |
|
182 |
throw new RuntimeException(e); |
|
183 |
}
|
|
184 |
||
185 |
String trimmed = request.activationToken.replace('.', ' ').trim(); |
|
186 |
||
187 |
String importsTipper = getImportsTipperStr(request); |
|
188 |
||
189 |
int line = request.doc.getLineOfOffset(request.documentOffset); |
|
190 |
IRegion region = request.doc.getLineInformation(line); |
|
191 |
||
192 |
CompletionState state = new CompletionState(line, request.documentOffset - region.getOffset(), null, request.nature); |
|
193 |
||
194 |
boolean importsTip = false; |
|
195 |
//code completion in imports
|
|
196 |
if (importsTipper.length() != 0) { |
|
197 |
||
198 |
//get the project and make the code completion!!
|
|
199 |
//so, we want to do a code completion for imports...
|
|
200 |
//let's see what we have...
|
|
201 |
||
202 |
importsTip = true; |
|
203 |
importsTipper = importsTipper.trim(); |
|
204 |
IToken[] imports = astManager.getCompletionsForImport(importsTipper, request); |
|
205 |
theList.addAll(Arrays.asList(imports)); |
|
206 |
||
207 |
//code completion for a token
|
|
208 |
} else if (trimmed.equals("") == false && request.activationToken.indexOf('.') != -1) { |
|
209 |
||
210 |
if (request.activationToken.endsWith(".")) { |
|
211 |
request.activationToken = request.activationToken.substring(0, request.activationToken.length() - 1); |
|
212 |
}
|
|
213 |
||
214 |
List completions = new ArrayList(); |
|
215 |
if (trimmed.equals("self") || trimmed.startsWith("self")) { |
|
216 |
getSelfCompletions(request, theList, state); |
|
217 |
||
218 |
} else { |
|
219 |
||
220 |
state.activationToken = request.activationToken; |
|
221 |
||
222 |
//Ok, looking for a token in globals.
|
|
223 |
IToken[] comps = astManager.getCompletionsForToken(request.editorFile, request.doc, state); |
|
224 |
theList.addAll(Arrays.asList(comps)); |
|
225 |
}
|
|
226 |
theList.addAll(completions); |
|
227 |
||
228 |
} else { //go to globals |
|
229 |
||
230 |
state.activationToken = request.activationToken; |
|
231 |
IToken[] comps = astManager.getCompletionsForToken(request.editorFile, request.doc, state); |
|
232 |
||
233 |
theList.addAll(Arrays.asList(comps)); |
|
234 |
||
235 |
theList.addAll(getGlobalsFromParticipants(request, state)); |
|
236 |
}
|
|
237 |
||
238 |
changeItokenToCompletionPropostal(viewer, request, ret, theList, importsTip); |
|
239 |
} catch (CompletionRecursionException e) { |
|
240 |
ret.add(new CompletionProposal("",request.documentOffset,0,0,null,e.getMessage(), null,null)); |
|
241 |
}
|
|
242 |
||
243 |
return ret; |
|
244 |
}
|
|
245 |
||
246 |
private Collection getGlobalsFromParticipants(CompletionRequest request, ICompletionState state) { |
|
247 |
ArrayList ret = new ArrayList(); |
|
248 |
||
249 |
List participants = ExtensionHelper.getParticipants(ExtensionHelper.PYDEV_COMPLETION); |
|
250 |
for (Iterator iter = participants.iterator(); iter.hasNext();) { |
|
251 |
IPyDevCompletionParticipant participant = (IPyDevCompletionParticipant) iter.next(); |
|
252 |
ret.addAll(participant.getGlobalCompletions(request, state)); |
|
253 |
}
|
|
254 |
return ret; |
|
255 |
}
|
|
256 |
||
257 |
/**
|
|
258 |
* @param request
|
|
259 |
* @param pythonNature
|
|
260 |
* @param astManager
|
|
261 |
* @param theList OUT - returned completions are added here. (IToken instances)
|
|
262 |
* @param line
|
|
263 |
* @param state
|
|
264 |
* @return the same tokens added in theList
|
|
265 |
*/
|
|
266 |
public static IToken[] getSelfCompletions(CompletionRequest request, List theList, CompletionState state) { |
|
267 |
return getSelfCompletions(request, theList, state, false); |
|
268 |
}
|
|
269 |
||
270 |
/**
|
|
271 |
* @param request
|
|
272 |
* @param pythonNature
|
|
273 |
* @param astManager
|
|
274 |
* @param theList OUT - returned completions are added here. (IToken instances)
|
|
275 |
* @param line
|
|
276 |
* @param state
|
|
277 |
* @return the same tokens added in theList
|
|
278 |
*/
|
|
279 |
public static IToken[] getSelfCompletions(CompletionRequest request, List theList, CompletionState state, boolean getOnlySupers) { |
|
280 |
IToken[] comps = new IToken[0]; |
|
281 |
SimpleNode s = PyParser.reparseDocument(new PyParser.ParserInfo(request.doc, true, request.nature, state.line)).o1; |
|
282 |
if(s != null){ |
|
283 |
FindScopeVisitor visitor = new FindScopeVisitor(state.line, 0); |
|
284 |
try { |
|
285 |
s.accept(visitor); |
|
286 |
comps = getSelfCompletions(visitor.scope, request, theList, state, getOnlySupers); |
|
287 |
} catch (Exception e1) { |
|
288 |
e1.printStackTrace(); |
|
289 |
}
|
|
290 |
}
|
|
291 |
return comps; |
|
292 |
}
|
|
293 |
||
294 |
/**
|
|
295 |
* Get self completions when you already have a scope
|
|
296 |
*/
|
|
297 |
public static IToken[] getSelfCompletions(Scope scope, CompletionRequest request, List theList, CompletionState state, boolean getOnlySupers) throws BadLocationException { |
|
298 |
IToken[] comps = new IToken[0]; |
|
299 |
while(scope.scope.size() > 0){ |
|
300 |
SimpleNode node = (SimpleNode) scope.scope.pop(); |
|
301 |
if(node instanceof ClassDef){ |
|
302 |
ClassDef d = (ClassDef) node; |
|
303 |
||
304 |
if(getOnlySupers){ |
|
305 |
List gottenComps = new ArrayList(); |
|
306 |
for (int i = 0; i < d.bases.length; i++) { |
|
307 |
if(d.bases[i] instanceof Name){ |
|
308 |
Name n = (Name) d.bases[i]; |
|
309 |
state.activationToken = n.id; |
|
310 |
IToken[] completions = request.nature.getAstManager().getCompletionsForToken(request.editorFile, request.doc, state); |
|
311 |
gottenComps.addAll(Arrays.asList(completions)); |
|
312 |
}
|
|
313 |
}
|
|
314 |
comps = (IToken[]) gottenComps.toArray(new IToken[0]); |
|
315 |
}else{ |
|
316 |
//ok, get the completions for the class, only thing we have to take care now is that we may
|
|
317 |
//not have only 'self' for completion, but somthing lile self.foo.
|
|
318 |
//so, let's analyze our activation token to see what should we do.
|
|
319 |
||
320 |
String trimmed = request.activationToken.replace('.', ' ').trim(); |
|
321 |
String[] actTokStrs = trimmed.split(" "); |
|
322 |
if(actTokStrs.length == 0 || actTokStrs[0].equals("self") == false){ |
|
323 |
throw new AssertionError("We need to have at least one token (self) for doing completions in the class."); |
|
324 |
}
|
|
325 |
||
326 |
if(actTokStrs.length == 1){ |
|
327 |
//ok, it's just really self, let's get on to get the completions
|
|
328 |
state.activationToken = NodeUtils.getNameFromNameTok((NameTok) d.name); |
|
329 |
comps = request.nature.getAstManager().getCompletionsForToken(request.editorFile, request.doc, state); |
|
330 |
||
331 |
}else{ |
|
332 |
//it's not only self, so, first we have to get the definition of the token
|
|
333 |
//the first one is self, so, just discard it, and go on, token by token to know what is the last
|
|
334 |
//one we are completing (e.g.: self.foo.bar)
|
|
335 |
int line = request.doc.getLineOfOffset(request.documentOffset); |
|
336 |
IRegion region = request.doc.getLineInformationOfOffset(request.documentOffset); |
|
337 |
int col = request.documentOffset - region.getOffset(); |
|
338 |
IModule module = AbstractModule.createModuleFromDoc("", null, request.doc, request.nature, line); |
|
339 |
||
340 |
ASTManager astMan = ((ASTManager)request.nature.getAstManager()); |
|
341 |
comps = astMan.getAssignCompletions(module, new CompletionState(line, col, request.activationToken, request.nature)); |
|
342 |
||
343 |
}
|
|
344 |
}
|
|
345 |
theList.addAll(Arrays.asList(comps)); |
|
346 |
}
|
|
347 |
}
|
|
348 |
return comps; |
|
349 |
||
350 |
}
|
|
351 |
||
352 |
/**
|
|
353 |
* @param viewer
|
|
354 |
* @param request
|
|
355 |
* @param convertedProposals
|
|
356 |
* @param iTokenList
|
|
357 |
* @param importsTip
|
|
358 |
*/
|
|
359 |
private void changeItokenToCompletionPropostal(ITextViewer viewer, CompletionRequest request, List convertedProposals, List iTokenList, boolean importsTip) { |
|
360 |
//TODO: check org.eclipse.jface.text.templates.TemplateCompletionProcessor to see how to do custom 'selections' in completions
|
|
361 |
// int offset = request.documentOffset;
|
|
362 |
// ITextSelection selection= (ITextSelection) viewer.getSelectionProvider().getSelection();
|
|
363 |
//
|
|
364 |
// // adjust offset to end of normalized selection
|
|
365 |
// if (selection.getOffset() == offset)
|
|
366 |
// offset= selection.getOffset() + selection.getLength();
|
|
367 |
//
|
|
368 |
// String prefix= extractPrefix(viewer, offset);
|
|
369 |
// Region region= new Region(offset - prefix.length(), prefix.length());
|
|
370 |
//
|
|
371 |
// TemplateContextType contextType= getContextType(viewer, region);
|
|
372 |
// if (contextType != null) {
|
|
373 |
// IDocument document= viewer.getDocument();
|
|
374 |
// new DocumentTemplateContext(contextType, document, region.getOffset(), region.getLength());
|
|
375 |
// }
|
|
376 |
||
377 |
||
378 |
for (Iterator iter = iTokenList.iterator(); iter.hasNext();) { |
|
379 |
||
380 |
Object obj = iter.next(); |
|
381 |
||
382 |
if(obj instanceof IToken){ |
|
383 |
IToken element = (IToken) obj; |
|
384 |
||
385 |
String name = element.getRepresentation(); |
|
386 |
||
387 |
//GET the ARGS
|
|
388 |
int l = name.length(); |
|
389 |
||
390 |
String args = ""; |
|
391 |
if(! importsTip){ |
|
392 |
args = getArgs(element); |
|
393 |
if(args.length()>0){ |
|
394 |
l++; //cursor position is name + '(' |
|
395 |
}
|
|
396 |
}
|
|
397 |
//END
|
|
398 |
||
399 |
String docStr = element.getDocStr(); |
|
400 |
int type = element.getType(); |
|
401 |
||
402 |
int priority = IPyCompletionProposal.PRIORITY_DEFAULT; |
|
403 |
if(type == PyCodeCompletion.TYPE_PARAM){ |
|
404 |
priority = IPyCompletionProposal.PRIORITY_LOCALS; |
|
405 |
}
|
|
406 |
||
407 |
PyCompletionProposal proposal = new PyCompletionProposal(name+args, |
|
408 |
request.documentOffset - request.qlen, request.qlen, l, getImageForType(type), null, null, docStr, priority); |
|
409 |
||
410 |
convertedProposals.add(proposal); |
|
411 |
||
412 |
}else if(obj instanceof Object[]){ |
|
413 |
Object element[] = (Object[]) obj; |
|
414 |
||
415 |
String name = (String) element[0]; |
|
416 |
String docStr = (String) element [1]; |
|
417 |
int type = -1; |
|
418 |
if(element.length > 2){ |
|
419 |
type = ((Integer) element [2]).intValue(); |
|
420 |
}
|
|
421 |
||
422 |
int priority = IPyCompletionProposal.PRIORITY_DEFAULT; |
|
423 |
if(type == PyCodeCompletion.TYPE_PARAM){ |
|
424 |
priority = IPyCompletionProposal.PRIORITY_LOCALS; |
|
425 |
}
|
|
426 |
||
427 |
PyCompletionProposal proposal = new PyCompletionProposal(name, |
|
428 |
request.documentOffset - request.qlen, request.qlen, name.length(), getImageForType(type), null, null, docStr, priority); |
|
429 |
||
430 |
convertedProposals.add(proposal); |
|
431 |
||
432 |
}else if(obj instanceof ICompletionProposal){ |
|
433 |
//no need to convert
|
|
434 |
convertedProposals.add(obj); |
|
435 |
}
|
|
436 |
||
437 |
}
|
|
438 |
}
|
|
439 |
||
440 |
||
441 |
||
442 |
/**
|
|
443 |
* @param element
|
|
444 |
* @param args
|
|
445 |
* @return
|
|
446 |
*/
|
|
447 |
private String getArgs(IToken element) { |
|
448 |
String args = ""; |
|
449 |
if(element.getArgs().trim().length() > 0){ |
|
450 |
StringBuffer buffer = new StringBuffer("("); |
|
451 |
StringTokenizer strTok = new StringTokenizer(element.getArgs(), "( ,)"); |
|
452 |
||
453 |
while(strTok.hasMoreTokens()){ |
|
454 |
String tok = strTok.nextToken(); |
|
455 |
if(tok.equals("self") == false){ |
|
456 |
if(buffer.length() > 1){ |
|
457 |
buffer.append(", "); |
|
458 |
}
|
|
459 |
buffer.append(tok); |
|
460 |
}
|
|
461 |
}
|
|
462 |
buffer.append(")"); |
|
463 |
args = buffer.toString(); |
|
464 |
} else if (element.getType() == PyCodeCompletion.TYPE_FUNCTION){ |
|
465 |
args = "()"; |
|
466 |
}
|
|
467 |
||
468 |
return args; |
|
469 |
}
|
|
470 |
||
471 |
||
472 |
||
473 |
/**
|
|
474 |
* Returns non empty string if we are in imports section
|
|
475 |
*
|
|
476 |
* @param theActivationToken
|
|
477 |
* @param edit
|
|
478 |
* @param doc
|
|
479 |
* @param documentOffset
|
|
480 |
* @return single space string if we are in imports but without any module
|
|
481 |
* string with current module (e.g. foo.bar.
|
|
482 |
*/
|
|
483 |
public String getImportsTipperStr(CompletionRequest request) { |
|
484 |
||
485 |
IDocument doc = request.doc; |
|
486 |
int documentOffset = request.documentOffset; |
|
487 |
||
488 |
return getImportsTipperStr(doc, documentOffset); |
|
489 |
}
|
|
490 |
||
491 |
public static String getImportsTipperStr(IDocument doc, int documentOffset) { |
|
492 |
IRegion region; |
|
493 |
try { |
|
494 |
region = doc.getLineInformationOfOffset(documentOffset); |
|
495 |
String trimmedLine = doc.get(region.getOffset(), documentOffset-region.getOffset()); |
|
496 |
trimmedLine = trimmedLine.trim(); |
|
497 |
return getImportsTipperStr(trimmedLine, true); |
|
498 |
} catch (BadLocationException e) { |
|
499 |
throw new RuntimeException(e); |
|
500 |
}
|
|
501 |
}
|
|
502 |
||
503 |
/**
|
|
504 |
* @param doc
|
|
505 |
* @param documentOffset
|
|
506 |
* @return
|
|
507 |
*/
|
|
508 |
public static String getImportsTipperStr(String trimmedLine, boolean returnEvenEmpty) { |
|
509 |
String importMsg = ""; |
|
510 |
||
511 |
if(!trimmedLine.startsWith("from") && !trimmedLine.startsWith("import")){ |
|
512 |
return ""; |
|
513 |
}
|
|
514 |
||
515 |
int fromIndex = trimmedLine.indexOf("from"); |
|
516 |
int importIndex = trimmedLine.indexOf("import"); |
|
517 |
||
518 |
//check if we have a from or an import.
|
|
519 |
if(fromIndex != -1 || importIndex != -1){ |
|
520 |
trimmedLine = trimmedLine.replaceAll("#.*", ""); //remove comments |
|
521 |
String[] strings = trimmedLine.split(" "); |
|
522 |
||
523 |
if(fromIndex != -1 && importIndex == -1){ |
|
524 |
if(strings.length > 2){ |
|
525 |
//user has spaces as in 'from xxx uuu'
|
|
526 |
return ""; |
|
527 |
}
|
|
528 |
}
|
|
529 |
||
530 |
||
531 |
for (int i = 0; i < strings.length; i++) { |
|
532 |
if(strings[i].equals("from")==false && strings[i].equals("import")==false){ |
|
533 |
if(importMsg.length() != 0){ |
|
534 |
importMsg += '.'; |
|
535 |
}
|
|
536 |
importMsg += strings[i]; |
|
537 |
}
|
|
538 |
}
|
|
539 |
||
540 |
if(fromIndex != -1 && importIndex != -1){ |
|
541 |
if(strings.length == 3){ |
|
542 |
importMsg += '.'; |
|
543 |
}
|
|
544 |
}
|
|
545 |
}else{ |
|
546 |
return ""; |
|
547 |
}
|
|
548 |
if (importMsg.indexOf(".") == -1){ |
|
549 |
if(returnEvenEmpty || importMsg.trim().length() > 0){ |
|
550 |
return " "; //we have only import fff or from iii (so, we're going for all imports). |
|
551 |
}else{ |
|
552 |
return ""; //we have only import fff or from iii (so, we're going for all imports). |
|
553 |
}
|
|
554 |
}
|
|
555 |
||
556 |
//now, we may still have something like 'unittest.test,' or 'unittest.test.,'
|
|
557 |
//so, we have to remove this comma (s).
|
|
558 |
int i; |
|
559 |
while ( ( i = importMsg.indexOf(',')) != -1){ |
|
560 |
if(importMsg.charAt(i-1) == '.'){ |
|
561 |
int j = importMsg.lastIndexOf('.'); |
|
562 |
importMsg = importMsg.substring(0, j); |
|
563 |
}
|
|
564 |
||
565 |
int j = importMsg.lastIndexOf('.'); |
|
566 |
importMsg = importMsg.substring(0, j); |
|
567 |
}
|
|
568 |
||
569 |
//if it is something like aaa.sss.bb : removes the bb because it is the qualifier
|
|
570 |
//if it is something like aaa.sss. : removes only the last point
|
|
571 |
if (importMsg.length() > 0 && importMsg.indexOf('.') != -1){ |
|
572 |
importMsg = importMsg.substring(0, importMsg.lastIndexOf('.')); |
|
573 |
}
|
|
574 |
||
575 |
||
576 |
return importMsg; |
|
577 |
}
|
|
578 |
||
579 |
/**
|
|
580 |
* Return a document to parse, using some heuristics to make it parseable.
|
|
581 |
*
|
|
582 |
* @param doc
|
|
583 |
* @param documentOffset
|
|
584 |
* @return
|
|
585 |
*/
|
|
586 |
public static String getDocToParse(IDocument doc, int documentOffset) { |
|
587 |
int lineOfOffset = -1; |
|
588 |
try { |
|
589 |
lineOfOffset = doc.getLineOfOffset(documentOffset); |
|
590 |
} catch (BadLocationException e) { |
|
591 |
e.printStackTrace(); |
|
592 |
}
|
|
593 |
||
594 |
if(lineOfOffset!=-1){ |
|
595 |
String docToParseFromLine = getDocToParseFromLine(doc, lineOfOffset); |
|
596 |
if(docToParseFromLine != null) |
|
597 |
return docToParseFromLine; |
|
598 |
// return "\n"+docToParseFromLine;
|
|
599 |
else
|
|
600 |
return ""; |
|
601 |
}else{ |
|
602 |
return ""; |
|
603 |
}
|
|
604 |
}
|
|
605 |
||
606 |
/**
|
|
607 |
* Return a document to parse, using some heuristics to make it parseable.
|
|
608 |
* (Changes the line specified by a pass)
|
|
609 |
*
|
|
610 |
* @param doc
|
|
611 |
* @param documentOffset
|
|
612 |
* @param lineOfOffset
|
|
613 |
* @return
|
|
614 |
*/
|
|
615 |
public static String getDocToParseFromLine(IDocument doc, int lineOfOffset) { |
|
616 |
return DocUtils.getDocToParseFromLine(doc, lineOfOffset); |
|
617 |
}
|
|
618 |
||
619 |
/**
|
|
620 |
*
|
|
621 |
* @param useSimpleTipper
|
|
622 |
* @return the script to get the variables.
|
|
623 |
*
|
|
624 |
* @throws CoreException
|
|
625 |
*/
|
|
626 |
public static File getAutoCompleteScript() throws CoreException { |
|
627 |
return PydevPlugin.getScriptWithinPySrc("simpleTipper.py"); |
|
628 |
}
|
|
629 |
||
630 |
||
631 |
/**
|
|
632 |
* @param pythonAndTemplateProposals
|
|
633 |
* @param qualifier
|
|
634 |
* @return
|
|
635 |
*/
|
|
636 |
public ICompletionProposal[] onlyValidSorted(List pythonAndTemplateProposals, String qualifier) { |
|
637 |
//FOURTH: Now, we have all the proposals, only thing is deciding wich ones are valid (depending on
|
|
638 |
//qualifier) and sorting them correctly.
|
|
639 |
Collection returnProposals = new HashSet(); |
|
640 |
String lowerCaseQualifier = qualifier.toLowerCase(); |
|
641 |
||
642 |
for (Iterator iter = pythonAndTemplateProposals.iterator(); iter.hasNext();) { |
|
643 |
Object o = iter.next(); |
|
644 |
if (o instanceof ICompletionProposal) { |
|
645 |
ICompletionProposal proposal = (ICompletionProposal) o; |
|
646 |
||
647 |
if (proposal.getDisplayString().toLowerCase().startsWith(lowerCaseQualifier)) { |
|
648 |
returnProposals.add(proposal); |
|
649 |
}
|
|
650 |
}else{ |
|
651 |
throw new RuntimeException("Error: expected instanceof ICompletionProposal and received: "+o.getClass().getName()); |
|
652 |
}
|
|
653 |
}
|
|
654 |
||
655 |
ICompletionProposal[] proposals = new ICompletionProposal[returnProposals.size()]; |
|
656 |
||
657 |
// and fill with list elements
|
|
658 |
returnProposals.toArray(proposals); |
|
659 |
||
660 |
Arrays.sort(proposals, PROPOSAL_COMPARATOR); |
|
661 |
return proposals; |
|
662 |
}
|
|
663 |
||
664 |
/**
|
|
665 |
* Compares proposals so that we can order them.
|
|
666 |
*/
|
|
667 |
public static final ProposalsComparator PROPOSAL_COMPARATOR = new ProposalsComparator(); |
|
668 |
||
669 |
}
|