From 3826afdf6c5b31ee5cf03e37eeb2ac9cf2536825 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 7 Sep 2015 22:01:24 +0300 Subject: [PATCH] PY-16765 In Google docstrings smart enter adds colon and indentation after section header --- .../smartEnter/PySmartEnterProcessor.java | 1 + .../fixers/GoogleDocStringSectionFixer.java | 66 +++++++++++++++++++ .../smartEnter/fixers/PyFixer.java | 10 ++- ...ogleDocStringColonAndIndentAfterSection.py | 4 ++ ...cStringColonAndIndentAfterSection_after.py | 5 ++ .../googleDocStringIndentAfterSection.py | 4 ++ ...googleDocStringIndentAfterSection_after.py | 5 ++ .../jetbrains/python/PySmartEnterTest.java | 18 +++++ 8 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/GoogleDocStringSectionFixer.java create mode 100644 python/testData/codeInsight/smartEnter/googleDocStringColonAndIndentAfterSection.py create mode 100644 python/testData/codeInsight/smartEnter/googleDocStringColonAndIndentAfterSection_after.py create mode 100644 python/testData/codeInsight/smartEnter/googleDocStringIndentAfterSection.py create mode 100644 python/testData/codeInsight/smartEnter/googleDocStringIndentAfterSection_after.py diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java index 27bc6e9f9e14..7b8bf908db3c 100644 --- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java +++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java @@ -51,6 +51,7 @@ public class PySmartEnterProcessor extends SmartEnterProcessor { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor"); private static final List ourFixers = ImmutableList.builder() .add(new PyStringLiteralFixer()) + .add(new GoogleDocStringSectionFixer()) .add(new PyParenthesizedFixer()) .add(new PyMissingBracesFixer()) .add(new PyConditionalStatementPartFixer()) diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/GoogleDocStringSectionFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/GoogleDocStringSectionFixer.java new file mode 100644 index 000000000000..6974ae8a13cd --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/GoogleDocStringSectionFixer.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; +import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor; +import com.jetbrains.python.documentation.docstrings.DocStringFormat; +import com.jetbrains.python.documentation.docstrings.DocStringUtil; +import com.jetbrains.python.documentation.docstrings.GoogleCodeStyleDocStringBuilder; +import com.jetbrains.python.documentation.docstrings.SectionBasedDocString; +import com.jetbrains.python.psi.PyIndentUtil; +import com.jetbrains.python.psi.PyStringLiteralExpression; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +public class GoogleDocStringSectionFixer extends PyFixer { + public GoogleDocStringSectionFixer() { + super(PyStringLiteralExpression.class); + } + + @Override + protected boolean isApplicable(@NotNull Editor editor, @NotNull PyStringLiteralExpression pyString) { + return DocStringUtil.getParentDefinitionDocString(pyString) == pyString && + DocStringUtil.guessDocStringFormat(pyString.getText(), pyString) == DocStringFormat.GOOGLE; + } + + @Override + public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyStringLiteralExpression pyString) { + final int offset = editor.getCaretModel().getOffset(); + final Document document = editor.getDocument(); + final int lineNum = document.getLineNumber(offset); + final int lineStart = document.getLineStartOffset(lineNum); + final int lineEnd = document.getLineEndOffset(lineNum); + final String line = document.getText(TextRange.create(lineStart, lineEnd)); + if (!StringUtil.isEmptyOrSpaces(line)) { + final String trimmedLine = line.trim(); + final String header = trimmedLine.endsWith(":") ? trimmedLine.substring(0, trimmedLine.length() - 1) : trimmedLine; + if (SectionBasedDocString.isValidSectionTitle(header)) { + final String patch = (trimmedLine.endsWith(":") ? "\n" : ":\n") + + PyIndentUtil.getLineIndent(line) + + GoogleCodeStyleDocStringBuilder.DEFAULT_SECTION_INDENT; + final int insertionOffset = lineStart + StringUtil.trimTrailing(line).length(); + document.replaceString(insertionOffset, lineEnd, patch); + processor.registerUnresolvedError(insertionOffset + patch.length()); + } + } + } +} diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java index f76328d4531e..91764b19649c 100644 --- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java +++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java @@ -35,13 +35,17 @@ public abstract class PyFixer { myClass = aClass; } + @SuppressWarnings("unchecked") public final void apply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PsiElement element) throws IncorrectOperationException { - if (myClass.isInstance(element)) { - //noinspection unchecked + if (myClass.isInstance(element) && isApplicable(editor, (T)element)) { doApply(editor, processor, (T)element); } } + + protected boolean isApplicable(@NotNull Editor editor, @NotNull T element) { + return myClass.isInstance(element); + } - public abstract void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull T element); + protected abstract void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull T element); } diff --git a/python/testData/codeInsight/smartEnter/googleDocStringColonAndIndentAfterSection.py b/python/testData/codeInsight/smartEnter/googleDocStringColonAndIndentAfterSection.py new file mode 100644 index 000000000000..45adde34f01b --- /dev/null +++ b/python/testData/codeInsight/smartEnter/googleDocStringColonAndIndentAfterSection.py @@ -0,0 +1,4 @@ +def func(): + """ + Args + """ \ No newline at end of file diff --git a/python/testData/codeInsight/smartEnter/googleDocStringColonAndIndentAfterSection_after.py b/python/testData/codeInsight/smartEnter/googleDocStringColonAndIndentAfterSection_after.py new file mode 100644 index 000000000000..c03317d64030 --- /dev/null +++ b/python/testData/codeInsight/smartEnter/googleDocStringColonAndIndentAfterSection_after.py @@ -0,0 +1,5 @@ +def func(): + """ + Args: + + """ \ No newline at end of file diff --git a/python/testData/codeInsight/smartEnter/googleDocStringIndentAfterSection.py b/python/testData/codeInsight/smartEnter/googleDocStringIndentAfterSection.py new file mode 100644 index 000000000000..9547d8320460 --- /dev/null +++ b/python/testData/codeInsight/smartEnter/googleDocStringIndentAfterSection.py @@ -0,0 +1,4 @@ +def f(): + """ + Raises: + """ \ No newline at end of file diff --git a/python/testData/codeInsight/smartEnter/googleDocStringIndentAfterSection_after.py b/python/testData/codeInsight/smartEnter/googleDocStringIndentAfterSection_after.py new file mode 100644 index 000000000000..cb370585781c --- /dev/null +++ b/python/testData/codeInsight/smartEnter/googleDocStringIndentAfterSection_after.py @@ -0,0 +1,5 @@ +def f(): + """ + Raises: + + """ \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PySmartEnterTest.java b/python/testSrc/com/jetbrains/python/PySmartEnterTest.java index e6f3302154fc..893bd425d89d 100644 --- a/python/testSrc/com/jetbrains/python/PySmartEnterTest.java +++ b/python/testSrc/com/jetbrains/python/PySmartEnterTest.java @@ -216,4 +216,22 @@ public class PySmartEnterTest extends PyTestCase { public void testSpaceInsertedAfterHashSignInComment() { doTest(); } + + // PY-16765 + public void testGoogleDocStringColonAndIndentAfterSection() { + runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() { + public void run() { + doTest(); + } + }); + } + + // PY-16765 + public void testGoogleDocStringIndentAfterSection() { + runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() { + public void run() { + doTest(); + } + }); + } }