/**
* Copyright (c) 2005-2013 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 24/09/2005
*/
package com.python.pydev.analysis.ctrl_1;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.python.pydev.core.FullRepIterable;
import org.python.pydev.core.ICodeCompletionASTManager;
import org.python.pydev.core.IModulesManager;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.editor.PyEdit;
import org.python.pydev.editor.codecompletion.ProposalsComparator.CompareContext;
import org.python.pydev.editor.codefolding.MarkerAnnotationAndPosition;
import org.python.pydev.editor.codefolding.PySourceViewer;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.structure.Tuple;
import org.python.pydev.shared_ui.ImageCache;
import org.python.pydev.shared_ui.UIConstants;
import org.python.pydev.shared_ui.proposals.IPyCompletionProposal;
import org.python.pydev.shared_ui.proposals.IPyCompletionProposal.ICompareContext;
import com.python.pydev.analysis.AnalysisPlugin;
import com.python.pydev.analysis.CtxInsensitiveImportComplProposal;
import com.python.pydev.analysis.IAnalysisPreferences;
import com.python.pydev.analysis.additionalinfo.AbstractAdditionalTokensInfo;
import com.python.pydev.analysis.additionalinfo.AdditionalProjectInterpreterInfo;
import com.python.pydev.analysis.additionalinfo.IInfo;
import com.python.pydev.analysis.builder.AnalysisParserObserver;
import com.python.pydev.analysis.builder.AnalysisRunner;
import com.python.pydev.analysis.ui.AutoImportsPreferencesPage;
/**
* Class that'll create proposals for fixing an undefined variable found.
*
* @author Fabio
*/
public class UndefinedVariableFixParticipant implements IAnalysisMarkersParticipant {
/**
* Defines whether a reparse should be forced after applying the completion.
*/
private boolean forceReparseOnApply;
public UndefinedVariableFixParticipant() {
this(true);
}
public UndefinedVariableFixParticipant(boolean forceReparseOnApply) {
this.forceReparseOnApply = forceReparseOnApply;
}
/**
* @see IAnalysisMarkersParticipant#addProps(MarkerAnnotation, IAnalysisPreferences, String, PySelection, int, IPythonNature,
* PyEdit, List)
*
*/
@Override
public void addProps(MarkerAnnotationAndPosition markerAnnotation, IAnalysisPreferences analysisPreferences,
String line, PySelection ps, int offset, IPythonNature initialNature, PyEdit edit,
List<ICompletionProposal> props)
throws BadLocationException, CoreException {
IMarker marker = markerAnnotation.markerAnnotation.getMarker();
Integer id = (Integer) marker.getAttribute(AnalysisRunner.PYDEV_ANALYSIS_TYPE);
if (id != IAnalysisPreferences.TYPE_UNDEFINED_VARIABLE) {
return;
}
if (initialNature == null) {
return;
}
ICodeCompletionASTManager astManager = initialNature.getAstManager();
if (astManager == null) {
return;
}
if (markerAnnotation.position == null) {
return;
}
int start = markerAnnotation.position.offset;
int end = start + markerAnnotation.position.length;
ps.setSelection(start, end);
String markerContents = ps.getSelectedText();
String fullRep = ps.getFullRepAfterSelection();
ImageCache imageCache = PydevPlugin.getImageCache();
Image packageImage = null;
if (imageCache != null) { //making tests
packageImage = imageCache.get(UIConstants.COMPLETION_PACKAGE_ICON);
}
IModulesManager projectModulesManager = astManager.getModulesManager();
IModulesManager[] managersInvolved = projectModulesManager.getManagersInvolved(true);
boolean doIgnoreImportsStartingWithUnder = AutoImportsPreferencesPage.doIgnoreImportsStartingWithUnder();
// Use a single buffer to create all the strings
FastStringBuffer buffer = new FastStringBuffer();
// Helper so that we don't add the same module multiple times.
Set<Tuple<String, String>> mods = new HashSet<Tuple<String, String>>();
for (IModulesManager iModulesManager : managersInvolved) {
Set<String> allModules = iModulesManager.getAllModuleNames(false, markerContents.toLowerCase());
//when an undefined variable is found, we can:
// - add an auto import (if it is a class or a method or some global attribute)
// - declare it as a local or global variable
// - change its name to some other global or local (mistyped)
// - create a method or class for it (if it is a call)
//1. check if it is some module
CompareContext compareContext = new CompareContext(iModulesManager.getNature());
for (String completeName : allModules) {
FullRepIterable iterable = new FullRepIterable(completeName);
for (String mod : iterable) {
if (fullRep.startsWith(mod)) {
if (fullRep.length() == mod.length() //it does not only start with, but it is equal to it.
|| (fullRep.length() > mod.length() && fullRep.charAt(mod.length()) == '.')) {
buffer.clear();
String realImportRep = buffer.append("import ").append(mod).toString();
buffer.clear();
String displayString = buffer.append("Import ").append(mod).toString();
addProp(props, realImportRep, displayString, packageImage, offset, mods,
compareContext);
}
}
String[] strings = FullRepIterable.headAndTail(mod);
String packageName = strings[0];
String importRep = strings[1];
if (importRep.equals(markerContents)) {
if (packageName.length() > 0) {
buffer.clear();
String realImportRep = buffer.append("from ").append(packageName).append(" ")
.append("import ")
.append(strings[1]).toString();
buffer.clear();
String displayString = buffer.append("Import ").append(importRep).append(" (")
.append(packageName).append(")").toString();
addProp(props, realImportRep, displayString, packageImage, offset, mods,
compareContext);
} else {
buffer.clear();
String realImportRep = buffer.append("import ").append(strings[1]).toString();
buffer.clear();
String displayString = buffer.append("Import ").append(importRep).toString();
addProp(props, realImportRep, displayString, packageImage, offset, mods,
compareContext);
}
}
}
}
}
//2. check if it is some global class or method
List<AbstractAdditionalTokensInfo> additionalInfo;
try {
additionalInfo = AdditionalProjectInterpreterInfo.getAdditionalInfo(initialNature);
} catch (MisconfigurationException e) {
return;
}
FastStringBuffer tempBuf = new FastStringBuffer();
for (AbstractAdditionalTokensInfo info : additionalInfo) {
Collection<IInfo> tokensEqualTo = info.getTokensEqualTo(markerContents,
AbstractAdditionalTokensInfo.TOP_LEVEL);
for (IInfo found : tokensEqualTo) {
//there always is a declaring module
String name = found.getName();
String declPackage = found.getDeclaringModuleName();
String declPackageWithoutInit = declPackage;
if (declPackageWithoutInit.endsWith(".__init__")) {
declPackageWithoutInit = declPackageWithoutInit.substring(0,
declPackageWithoutInit.length() - 9);
}
declPackageWithoutInit = AutoImportsPreferencesPage.removeImportsStartingWithUnderIfNeeded(
declPackageWithoutInit, tempBuf, doIgnoreImportsStartingWithUnder);
buffer.clear();
String importDeclaration = buffer.append("from ").append(declPackageWithoutInit).append(" import ")
.append(name).toString();
buffer.clear();
String displayImport = buffer.append("Import ").append(name).append(" (").append(declPackage)
.append(")").toString();
addProp(props, importDeclaration, displayImport,
AnalysisPlugin.getImageForAutoImportTypeInfo(found),
offset, mods, new CompareContext(found.getNature()));
}
}
}
private void addProp(List<ICompletionProposal> props, String importDeclaration, String displayImport,
Image importImage, int offset, Set<Tuple<String, String>> mods, ICompareContext compareContext) {
Tuple<String, String> tuple = new Tuple<String, String>(importDeclaration, displayImport);
if (mods.contains(tuple)) {
return;
}
mods.add(tuple);
props.add(new CtxInsensitiveImportComplProposal("", offset, 0, 0, importImage, displayImport, null,
importDeclaration,
IPyCompletionProposal.PRIORITY_LOCALS, importDeclaration, compareContext) {
@Override
public void selected(ITextViewer viewer, boolean smartToggle) {
//Overridden to do nothing (i.e.: don't leave yellow when ctrl is pressed).
}
@Override
public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
if ((stateMask & SWT.SHIFT) != 0) {
this.setAddLocalImport(true);
}
super.apply(viewer, trigger, stateMask, offset);
if (forceReparseOnApply) {
//and after applying it, let's request a reanalysis
if (viewer instanceof PySourceViewer) {
PySourceViewer sourceViewer = (PySourceViewer) viewer;
PyEdit edit = sourceViewer.getEdit();
if (edit != null) {
edit.getParser().forceReparse(
new Tuple<String, Boolean>(AnalysisParserObserver.ANALYSIS_PARSER_OBSERVER_FORCE,
true));
}
}
}
}
});
}
}