/**
* 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.
*/
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.IAdaptable;
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.docutils.PySelection;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.actions.PyFormatStd;
import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule;
import org.python.pydev.editor.correctionassist.CheckAnalysisErrors;
import org.python.pydev.jython.IPythonInterpreter;
import org.python.pydev.jython.JythonPlugin;
import org.python.pydev.plugin.JythonModules;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;
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 = ['pycodestyle.py', r'%s'%s]\n"
+ "sys.argv=argv\n"
//It always accesses sys.argv[0] in process_options, so, it must be set.
+ "\n"
+ "\n"
+ "pep8style = pycodestyle.StyleGuide(parse_argv=True, config_file=False)\n"
+ "\n"
+ "checker = pycodestyle.Checker(options=pep8style.options, filename='%s', lines=lines)\n"
+ "\n"
+ "if ReportError is None: #Only redefine if it wasn't defined already\n"
+ " class ReportError:\n"
+ "\n"
+ " def __init__(self, checker, pep8style, visitor):\n"
+ " self.checker = checker\n"
+ " self.pep8style = pep8style\n"
+ " self.visitor = visitor\n"
+ " self.original = checker.report_error\n"
+ " checker.report_error = self\n"
+ " if not self.pep8style.excluded(self.checker.filename):\n"
+ " checker.check_all()\n"
+ " #Clear references\n"
+ " self.original = None\n"
+ " self.checker = None\n"
+ " self.pep8style = None\n"
+ " self.visitor = None\n"
+ " checker.report_error = None\n"
+ " \n"
+ " def __call__(self, line_number, offset, text, check):\n"
+ " code = text[:4]\n"
+ " if self.pep8style.options.ignore_code(code):\n"
+ " return\n"
+ " self.visitor.reportError(line_number, offset, text, check)\n"
+ " return self.original(line_number, offset, text, check)\n"
+ "\n"
+ "ReportError(checker, pep8style, visitor)\n"
+ "checker = None #Release checker\n"
+ "pep8style = None #Release pep8style\n"
+ "";
private final List<IMessage> messages = new ArrayList<IMessage>();
private IAnalysisPreferences prefs;
private IDocument document;
private volatile static PyObject reportError;
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;
}
this.prefs = prefs;
this.document = document;
messageToIgnore = prefs.getRequiredMessageToIgnore(IAnalysisPreferences.TYPE_PEP8);
File pep8Loc = JythonModules.getPep8Location();
if (pep8Loc == null) {
Log.log("Unable to get pycodestyle module.");
return messages;
}
IAdaptable projectAdaptable = prefs.getProjectAdaptable();
if (AnalysisPreferencesPage.useSystemInterpreter(projectAdaptable)) {
String parameters = AnalysisPreferencesPage.getPep8CommandLineAsStr(projectAdaptable);
String output = PyFormatStd.runWithPep8BaseScript(document.get(), parameters, "pycodestyle.py", "");
List<String> splitInLines = StringUtils.splitInLines(output, false);
for (String line : splitInLines) {
try {
List<String> lst = StringUtils.split(line, ':', 4);
int lineNumber = Integer.parseInt(lst.get(1));
int offset = Integer.parseInt(lst.get(2)) - 1;
String text = lst.get(3);
this.reportError(lineNumber, offset, text, null);
} catch (Exception e) {
Log.log("Error parsing line: " + line, e);
}
}
return messages;
}
String[] pep8CommandLine = AnalysisPreferencesPage.getPep8CommandLine(projectAdaptable);
FastStringBuffer args = new FastStringBuffer(pep8CommandLine.length * 20);
for (String string : pep8CommandLine) {
args.append(',').append("r'").append(string).append('\'');
}
//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(projectAdaptable);
IPythonInterpreter interpreter = JythonPlugin.newPythonInterpreter(useConsole, false);
String file = StringUtils.replaceAllSlashes(module.getFile().getAbsolutePath());
interpreter.set("visitor", this);
List<String> splitInLines = StringUtils.splitInLines(document.get());
interpreter.set("lines", splitInLines);
PyObject tempReportError = reportError;
if (tempReportError != null) {
interpreter.set("ReportError", tempReportError);
} else {
interpreter.set("ReportError", Py.None);
}
PyObject pep8Module = JythonModules.getPep8Module(interpreter);
interpreter.set("pycodestyle", pep8Module);
String formatted = StringUtils.format(EXECUTE_PEP8, file,
args.toString(),
file);
interpreter.exec(formatted);
if (reportError == null) {
synchronized (lock) {
if (reportError == null) {
reportError = interpreter.get("ReportError");
}
}
}
} 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 (CheckAnalysisErrors.isCodeAnalysisErrorHandled(line, messageToIgnore)) {
//keep going... nothing to see here...
return;
}
}
messages.add(new Message(IAnalysisPreferences.TYPE_PEP8, text, lineNumber, lineNumber, offset + 1, len, prefs));
}
}