/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on Mar 29, 2004
*
*/
package org.python.pydev.editor.codecompletion;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ContentAssistEvent;
import org.eclipse.jface.text.contentassist.ICompletionListener;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.python.pydev.core.IInterpreterManager;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.PythonNatureWithoutProjectException;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.IPySyntaxHighlightingAndCodeCompletionEditor;
import org.python.pydev.editor.codecompletion.templates.PyTemplateCompletionProcessor;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.nature.SystemPythonNature;
import org.python.pydev.ui.interpreters.ChooseInterpreterManager;
/**
* @author Dmoore
* @author Fabio Zadrozny
*
* This class is responsible for code completion / template completion.
*/
public class PythonCompletionProcessor extends AbstractCompletionProcessorWithCycling {
/**
* This makes the templates completion
*/
private PyTemplateCompletionProcessor templatesCompletion = new PyTemplateCompletionProcessor();
/**
* This makes python code completion
*/
private IPyCodeCompletion codeCompletion;
/**
* Edit.
*/
private IPySyntaxHighlightingAndCodeCompletionEditor edit;
/**
* Some error...
*/
private String error;
/**
* These are the activation chars (cache)
*/
private volatile static char[] activationChars = null;
/**
* This is the class that manages the context information (validates it and
* changes its presentation).
*/
private PyContextInformationValidator contextInformationValidator;
/**
* @param edit the editor that works with this processor
* @param pyContentAssistant the content assistant that will invoke this completion
*/
public PythonCompletionProcessor(IPySyntaxHighlightingAndCodeCompletionEditor edit,
PyContentAssistant pyContentAssistant) {
super(pyContentAssistant);
this.edit = edit;
this.pyContentAssistant = pyContentAssistant;
this.codeCompletion = getCodeCompletionEngine();
contextInformationValidator = new PyContextInformationValidator();
pyContentAssistant.addCompletionListener(new ICompletionListener() {
public void assistSessionEnded(ContentAssistEvent event) {
}
public void assistSessionStarted(ContentAssistEvent event) {
startCycle();
}
public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
//ignore
}
});
}
protected IPyCodeCompletion getCodeCompletionEngine() {
return new PyCodeCompletion();
}
/**
* This is the interface implemented to get the completions.
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
*/
@SuppressWarnings("unchecked")
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) {
updateStatus();
ICompletionProposal[] proposals;
try {
//FIRST: discover activation token and qualifier.
IDocument doc = viewer.getDocument();
//list for storing the proposals
ArrayList<ICompletionProposal> pythonAndTemplateProposals = new ArrayList<ICompletionProposal>();
IPythonNature nature = edit.getPythonNature();
if (nature == null) {
IInterpreterManager manager = ChooseInterpreterManager.chooseInterpreterManager();
if (manager != null) {
nature = new SystemPythonNature(manager);
} else {
CompletionError completionError = new CompletionError(new RuntimeException(
"No interpreter configured."));
this.error = completionError.getErrorMessage();
return new ICompletionProposal[] { completionError };
}
}
if (nature == null || !nature.startRequests()) {
return new ICompletionProposal[0];
}
try {
CompletionRequest request = new CompletionRequest(edit.getEditorFile(), nature, doc, documentOffset,
codeCompletion);
//SECOND: getting code completions and deciding if templates should be shown too.
//Get code completion proposals
if (PyCodeCompletionPreferencesPage.useCodeCompletion()) {
if (whatToShow == SHOW_ALL) {
try {
pythonAndTemplateProposals.addAll(getPythonProposals(viewer, documentOffset, doc, request));
} catch (Throwable e) {
Log.log(e);
CompletionError completionError = new CompletionError(e);
this.error = completionError.getErrorMessage();
//Make the error visible to the user!
return new ICompletionProposal[] { completionError };
}
}
}
String[] strs = PySelection.getActivationTokenAndQual(doc, documentOffset, false);
String activationToken = strs[0];
String qualifier = strs[1];
//THIRD: Get template proposals (if asked for)
if (request.showTemplates && (activationToken == null || activationToken.trim().length() == 0)) {
List templateProposals = getTemplateProposals(viewer, documentOffset, activationToken, qualifier);
pythonAndTemplateProposals.addAll(templateProposals);
}
//to show the valid ones, we'll get the qualifier from the initial request
proposals = PyCodeCompletionUtils.onlyValidSorted(pythonAndTemplateProposals, request.qualifier,
request.isInCalltip);
} finally {
nature.endRequests();
}
} catch (Exception e) {
Log.log(e);
CompletionError completionError = new CompletionError(e);
this.error = completionError.getErrorMessage();
//Make the error visible to the user!
return new ICompletionProposal[] { completionError };
}
doCycle();
// Return the proposals
return proposals;
}
/**
* Returns the python proposals as a list.
* First parameter of tuple is a list and second is a Boolean object indicating whether the templates
* should be also shown or not.
* @param viewer
* @throws CoreException
* @throws BadLocationException
* @throws MisconfigurationException
* @throws IOException
* @throws PythonNatureWithoutProjectException
*/
private List getPythonProposals(ITextViewer viewer, int documentOffset, IDocument doc, CompletionRequest request)
throws CoreException, BadLocationException, IOException, MisconfigurationException,
PythonNatureWithoutProjectException {
//if non empty string, we're in imports section.
String importsTipperStr = request.codeCompletion.getImportsTipperStr(request).importsTipperStr;
if (importsTipperStr.length() != 0 || request.isInCalltip) {
request.showTemplates = false; //don't show templates if we are in the imports section or inside a calltip.
}
List allProposals = request.codeCompletion.getCodeCompletionProposals(viewer, request);
return allProposals;
}
/**
* Returns the template proposals as a list.
*/
private List getTemplateProposals(ITextViewer viewer, int documentOffset, String activationToken,
java.lang.String qualifier) {
List<ICompletionProposal> propList = new ArrayList<ICompletionProposal>();
this.templatesCompletion.addTemplateProposals(viewer, documentOffset, propList);
return propList;
}
/**
* Ok, if we have
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int)
*/
public IContextInformation[] computeContextInformation(ITextViewer viewer, int documentOffset) {
//System.out.println("computeContextInformation");
if (viewer.getDocument() != this.contextInformationValidator.doc) {
return null;
}
//System.out.println("this.contextInformationValidator.returnedFalseOnce:"+this.contextInformationValidator.returnedFalseOnce);
//if we didn't return false at least once, it is already installed.
if (this.contextInformationValidator.returnedFalseOnce
&& this.contextInformationValidator.isContextInformationValid(documentOffset)) {
return new IContextInformation[] { this.contextInformationValidator.fInformation };
}
return null;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
*/
public char[] getCompletionProposalAutoActivationCharacters() {
return getStaticCompletionProposalAutoActivationCharacters();
}
/**
* Attribute that determines if the listener that'll clear the auto activation chars is already in place.
*/
private volatile static boolean listenerToClearAutoActivationAlreadySetup = false;
/**
* @return the auto-activation chars that should be used.
*/
public static char[] getStaticCompletionProposalAutoActivationCharacters() {
if (!listenerToClearAutoActivationAlreadySetup) {
//clears the cache when the preferences are changed.
IPreferenceStore preferenceStore = PydevPlugin.getDefault().getPreferenceStore();
preferenceStore.addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
activationChars = null; //clear the cache when it changes
}
});
listenerToClearAutoActivationAlreadySetup = true;
}
if (activationChars == null) { //let's cache this
if (!PyCodeCompletionPreferencesPage.useAutocomplete()) {
activationChars = new char[0];
} else {
char[] c = new char[0];
if (PyCodeCompletionPreferencesPage.isToAutocompleteOnDot()) {
c = StringUtils.addChar(c, '.');
}
if (PyCodeCompletionPreferencesPage.isToAutocompleteOnPar()) {
c = StringUtils.addChar(c, '(');
}
activationChars = c;
}
}
return activationChars;
}
/**
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
*/
public char[] getContextInformationAutoActivationCharacters() {
return null;
}
/**
* If completion fails for some reason, we could give it here...
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
*/
public java.lang.String getErrorMessage() {
String ret = this.error;
this.error = null;
return ret;
}
/**
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
*/
public IContextInformationValidator getContextInformationValidator() {
return this.contextInformationValidator;
}
}