/**
* 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.
*/
package org.python.pydev.builder.pep8;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.python.core.Py;
import org.python.core.PyObject;
import org.python.pydev.core.NullOutputStream;
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.codecompletion.revisited.modules.SourceModule;
import org.python.pydev.jython.IPythonInterpreter;
import org.python.pydev.jython.JythonPlugin;
import com.aptana.shared_core.string.FastStringBuffer;
import com.python.pydev.analysis.IAnalysisPreferences;
import com.python.pydev.analysis.messages.IMessage;
import com.python.pydev.analysis.messages.Message;
import com.python.pydev.analysis.ui.AnalysisPreferencesPage;
/**
* @author Fabio
*
*/
public class Pep8Visitor {
private static final String EXECUTE_PEP8 = "import sys\n"
+ "argv = ['pep8.py', r'%s'%s]\n"
+ "sys.argv=argv\n"
+ //It always accesses sys.argv[0] in process_options, so, it must be set.
"\n"
+ "if pep8 == None:\n"
+ //Optimization: if possible don't import pep8 (the import was the slowest thing in this code).
" add_to_pythonpath = '%s'\n"
+ " if add_to_pythonpath not in sys.path:\n"
+ " sys.path.append(add_to_pythonpath)\n"
+ " import pep8\n"
+ "\n"
+ "options, args = pep8.process_options(argv[1:])\n"
+ //don't use sys.argv (it seems it doesn't get updated as it should).
//"print options\n" + uncomment for debugging options
"checker = pep8.Checker(options, '%s', lines)\n" +
"\n"
+ "def report_error(line_number, offset, text, check):\n" +
" code = text[:4]\n"
+ " if pep8.ignore_code(checker.options, code) or code in checker.expected:\n" +
" return\n"
+ " visitor.reportError(line_number, offset, text, check)\n"
+ " return original(line_number, offset, text, check)\n" +
"\n" +
"\n"
+ "original = checker.report_error\n" +
"checker.report_error = report_error\n" +
"\n"
+ "checker.check_all()\n" +
"\n" +
"";
private final List<IMessage> messages = new ArrayList<IMessage>();
private IAnalysisPreferences prefs;
private IDocument document;
private volatile static PyObject pep8;
private static final Object lock = new Object();
private String messageToIgnore;
public List<IMessage> getMessages(SourceModule module, IDocument document, IProgressMonitor monitor,
IAnalysisPreferences prefs) {
try {
if (prefs.getSeverityForType(IAnalysisPreferences.TYPE_PEP8) < IMarker.SEVERITY_WARNING) {
return messages;
}
messageToIgnore = prefs.getRequiredMessageToIgnore(IAnalysisPreferences.TYPE_PEP8);
String[] pep8CommandLine = AnalysisPreferencesPage.getPep8CommandLine();
FastStringBuffer args = new FastStringBuffer(pep8CommandLine.length * 20);
for (String string : pep8CommandLine) {
args.append(',').append("r'").append(string).append('\'');
}
String pep8Location = AnalysisPreferencesPage.getPep8Location();
File pep8Loc = new File(pep8Location);
if (!pep8Loc.exists()) {
Log.log("Specified location for pep8.py does not exist (" + pep8Location + ").");
return messages;
}
this.prefs = prefs;
this.document = document;
//It's important that the interpreter is created in the Thread and not outside the thread (otherwise
//it may be that the output ends up being shared, which is not what we want.)
boolean useConsole = AnalysisPreferencesPage.useConsole();
IPythonInterpreter interpreter = JythonPlugin.newPythonInterpreter(useConsole, false);
if (!useConsole) {
interpreter.setErr(NullOutputStream.singleton);
interpreter.setOut(NullOutputStream.singleton);
}
String file = StringUtils.replaceAllSlashes(module.getFile().getAbsolutePath());
interpreter.set("visitor", this);
List<String> splitInLines = StringUtils.splitInLines(document.get());
interpreter.set("lines", splitInLines);
PyObject tempPep8 = pep8;
if (tempPep8 != null) {
interpreter.set("pep8", tempPep8);
} else {
interpreter.set("pep8", Py.None);
}
String formatted = com.aptana.shared_core.string.StringUtils.format(EXECUTE_PEP8, file, args.toString(),
StringUtils.replaceAllSlashes(pep8Loc.getParentFile().getAbsolutePath()), //put the parent dir of pep8.py in the pythonpath.
file);
interpreter.exec(formatted);
if (pep8 == null) {
synchronized (lock) {
if (pep8 == null) {
pep8 = interpreter.get("pep8");
}
}
}
} catch (Exception e) {
Log.log("Error analyzing: " + module, e);
}
return messages;
}
/**
*
*/
public void reportError(int lineNumber, int offset, String text, Object check) {
int len;
try {
len = this.document.getLineLength(lineNumber - 1);
} catch (BadLocationException e) {
return; // the document changed in the meanwhile...
}
if (messageToIgnore != null) {
int startLine = lineNumber - 1;
String line = PySelection.getLine(document, startLine);
if (line.indexOf(messageToIgnore) != -1) {
//keep going... nothing to see here...
return;
}
}
messages.add(new Message(IAnalysisPreferences.TYPE_PEP8, text, lineNumber, lineNumber, offset + 1, len, prefs));
}
}