diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 95f792d0fde3..78322e0e29f1 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -69,6 +69,9 @@ QFIX.unresolved.reference.add.future=Add 'from __future__ import with_statement' # RemoveUnnecessaryBackslashQuickFix QFIX.remove.unnecessary.backslash=Remove unnecessary backslash in expression +# ConvertDocstringQuickFix +QFIX.convert.single.quoted.docstring=Convert docstring to the triple double-quoted string form + # Intentions: INTN INTN.Family.convert.import.unqualify=Convert 'import module' to 'from module import' INTN.Family.convert.import.qualify=Convert 'from module import' to 'import module' @@ -357,6 +360,10 @@ INSP.NAME.compatibility=Code compatibility inspection # PyUnnecessaryBackslashInspection INSP.NAME.unnecessary.backslash=Unnecessary backslash +# PySingleQuotedDocstringInspection +INSP.NAME.single.quoted.docstring=Single quoted docstring + + # Refactoring # introduce refactoring.introduce.name.error=Incorrect name diff --git a/python/src/com/jetbrains/python/actions/ConvertDocstringQuickFix.java b/python/src/com/jetbrains/python/actions/ConvertDocstringQuickFix.java new file mode 100644 index 000000000000..614b0422e2ba --- /dev/null +++ b/python/src/com/jetbrains/python/actions/ConvertDocstringQuickFix.java @@ -0,0 +1,53 @@ +package com.jetbrains.python.actions; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.psi.PyElementGenerator; +import com.jetbrains.python.psi.PyExpressionStatement; +import com.jetbrains.python.psi.PyStringLiteralExpression; +import org.jetbrains.annotations.NotNull; + +/** + * User: catherine + * + * QuickFix to convert docstrings to the common form according to PEP-257 + * For consistency, always use """triple double quotes""" around docstrings. + */ +public class ConvertDocstringQuickFix implements LocalQuickFix { + + public ConvertDocstringQuickFix() { + } + + @NotNull + public String getName() { + return PyBundle.message("QFIX.convert.single.quoted.docstring"); + } + + @NotNull + public String getFamilyName() { + return PyBundle.message("INSP.GROUP.python"); + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement expression = descriptor.getPsiElement(); + if (expression instanceof PyStringLiteralExpression) { + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + + String content = expression.getText(); + if (content.startsWith("'''") ) { + content = content.substring(3, content.length()-3); + } else { + content = content.substring(1, content.length()-1); + } + + PyStringLiteralExpression newString = (PyStringLiteralExpression)elementGenerator.createFromText(LanguageLevel.forElement(expression), + PyExpressionStatement.class,"\"\"\"" + content + "\"\"\"").getExpression(); + expression.replace(newString); + } + } + +} diff --git a/python/src/com/jetbrains/python/inspections/PySingleQuotedDocstringInspection.java b/python/src/com/jetbrains/python/inspections/PySingleQuotedDocstringInspection.java new file mode 100644 index 000000000000..5f78a9b4a366 --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PySingleQuotedDocstringInspection.java @@ -0,0 +1,50 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.actions.ConvertDocstringQuickFix; +import com.jetbrains.python.psi.PyDocStringOwner; +import com.jetbrains.python.psi.PyStringLiteralExpression; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +/** + * User: catherine + * + * Inspection to detect docstrings not using triple double-quoted string + */ +public class PySingleQuotedDocstringInspection extends PyInspection { + + @Nls + @NotNull + @Override + public String getDisplayName() { + return PyBundle.message("INSP.NAME.single.quoted.docstring"); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new Visitor(holder); + } + + private static class Visitor extends PyInspectionVisitor { + public Visitor(final ProblemsHolder holder) { + super(holder); + } + + @Override + public void visitPyStringLiteralExpression(final PyStringLiteralExpression string) { + String stringText = string.getText(); + final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(string, PyDocStringOwner.class); + if (docStringOwner != null) { + if (docStringOwner.getDocStringExpression() == string) { + if (!stringText.startsWith("\"\"\"") && !stringText.endsWith("\"\"\"")) + registerProblem(string, "Triple double-quoted strings should be used for docstrings.", new ConvertDocstringQuickFix()); + } + } + } + } +} diff --git a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java index ac783d929b70..a36ce50df6a2 100644 --- a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java +++ b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java @@ -55,6 +55,7 @@ public class PythonInspectionToolProvider implements InspectionToolProvider { PyCompatibilityInspection.class, PyListCreationInspection.class, PyUnnecessaryBackslashInspection.class, + PySingleQuotedDocstringInspection.class, }; } } diff --git a/python/testData/inspections/ConvertSingleQuotedDocstring.py b/python/testData/inspections/ConvertSingleQuotedDocstring.py new file mode 100644 index 000000000000..d8904c3301a1 --- /dev/null +++ b/python/testData/inspections/ConvertSingleQuotedDocstring.py @@ -0,0 +1,4 @@ +def foo(): + '''foo first line docstring + second line of docstring''' + pass \ No newline at end of file diff --git a/python/testData/inspections/ConvertSingleQuotedDocstring_after.py b/python/testData/inspections/ConvertSingleQuotedDocstring_after.py new file mode 100644 index 000000000000..5d84b37d7ef2 --- /dev/null +++ b/python/testData/inspections/ConvertSingleQuotedDocstring_after.py @@ -0,0 +1,4 @@ +def foo(): + """foo first line docstring + second line of docstring""" + pass \ No newline at end of file diff --git a/python/testData/inspections/PySingleQuotedDocstringInspection/test.py b/python/testData/inspections/PySingleQuotedDocstringInspection/test.py new file mode 100644 index 000000000000..948d9023140e --- /dev/null +++ b/python/testData/inspections/PySingleQuotedDocstringInspection/test.py @@ -0,0 +1,17 @@ +'''package docstring''' + +def foo(): + "foo docstring" + pass + +class Klass: + 'class docstring\ + second line' + pass + +def bar(): + """ bar docstring """ + pass + +a = '''some string''' +'''another string''' \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 2f1a567e31eb..3088d67afe3a 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -220,6 +220,11 @@ public class PyQuickFixTest extends PyLightFixtureTestCase { PyBundle.message("QFIX.list.creation"), true, true); } + public void testConvertSingleQuotedDocstring() { //PY-1445 + doInspectionTest("ConvertSingleQuotedDocstring.py", PySingleQuotedDocstringInspection.class, + PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); + } + public void testUnnecessaryBackslash() { String[] testFiles = new String[]{"UnnecessaryBackslash.py"}; myFixture.enableInspections(PyUnnecessaryBackslashInspection.class); diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java index f136436a3e8d..8c13b1f54e11 100644 --- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java @@ -308,4 +308,8 @@ public class PythonInspectionsTest extends PyLightFixtureTestCase { setLanguageLevel(LanguageLevel.PYTHON27); doHighlightingTest(PyUnnecessaryBackslashInspection.class); } + + public void testPySingleQuotedDocstringInspection() { //PY-1445 + doHighlightingTest(PySingleQuotedDocstringInspection.class); + } }