From 40f9d95817021fee18fc046e6c88df9241e166a9 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 31 Mar 2016 16:05:11 +0300 Subject: [PATCH] PY-18522 Always try to infer expected indent when pasting into empty definition Use indent based on caret position only when we're pasting neither inside existing statement list (e.g. in the middle of a function), nor inside empty statement list --- .../editor/PythonCopyPasteProcessor.java | 40 ++++++++++--------- .../EmptyFunctionCaretAtBodyIndent.after.py | 14 +++++++ .../EmptyFunctionCaretAtBodyIndent.dst.py | 9 +++++ .../EmptyFunctionCaretAtBodyIndent.src.py | 10 +++++ .../EmptyFunctionCaretAtDefIndent.after.py | 14 +++++++ .../EmptyFunctionCaretAtDefIndent.dst.py | 9 +++++ .../EmptyFunctionCaretAtDefIndent.src.py | 10 +++++ .../EmptyFunctionCaretAtNoneIndent.after.py | 14 +++++++ .../EmptyFunctionCaretAtNoneIndent.dst.py | 9 +++++ .../EmptyFunctionCaretAtNoneIndent.src.py | 10 +++++ .../com/jetbrains/python/PyCopyPasteTest.java | 15 +++++++ 11 files changed, 136 insertions(+), 18 deletions(-) create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.after.py create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.dst.py create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.src.py create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.after.py create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.dst.py create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.src.py create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.after.py create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.dst.py create mode 100644 python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.src.py diff --git a/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java b/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java index 621f80215901..e4704a45158e 100644 --- a/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java +++ b/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java @@ -30,6 +30,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ObjectUtils; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonFileType; import com.jetbrains.python.PythonLanguage; @@ -39,6 +40,8 @@ import org.jetbrains.annotations.Nullable; import java.util.List; +import static com.jetbrains.python.psi.PyUtil.as; + /** * User : catherine */ @@ -84,7 +87,7 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { text = addLeadingSpaces(text, NOT_INDENT_FILTER, indentSize, indentChar); int firstLineIndent = StringUtil.findFirst(text, NOT_INDENT_FILTER); - final String indentText = getIndentText(file, document, caretOffset, lineNumber, firstLineIndent); + final String indentText = getIndentText(file, document, caretOffset, lineNumber); int toRemove = calculateIndentToRemove(text, NOT_INDENT_FILTER); @@ -135,7 +138,7 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { private static String getIndentText(@NotNull final PsiFile file, @NotNull final Document document, int caretOffset, - int lineNumber, int firstLineIndent) { + int lineNumber) { PsiElement nonWS = PyUtil.findNextAtOffset(file, caretOffset, PsiWhiteSpace.class); if (nonWS != null) { @@ -147,27 +150,28 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { } } - int lineStartOffset = getLineStartSafeOffset(document, lineNumber); - String indentText = document.getText(TextRange.create(lineStartOffset, caretOffset)); - if (nonWS != null && document.getLineNumber(nonWS.getTextOffset()) == lineNumber) { - indentText = document.getText(TextRange.create(lineStartOffset, nonWS.getTextOffset())); + return PyIndentUtil.getLineIndent(document, lineNumber); } - else if (caretOffset == lineStartOffset) { - final PsiElement ws = file.findElementAt(lineStartOffset); - if (ws != null) { - final String wsText = ws.getText(); - final List strings = StringUtil.split(wsText, "\n"); - if (strings.size() >= 1) { - indentText = strings.get(0); - } + + int lineStartOffset = getLineStartSafeOffset(document, lineNumber); + final PsiElement ws = file.findElementAt(lineStartOffset); + if (ws != null) { + // Beginning of empty definition, e.g. class or function header without the actual body + PyStatementList statementList = ObjectUtils.chooseNotNull(as(ws.getNextSibling(), PyStatementList.class), + as(ws.getPrevSibling(), PyStatementList.class)); + + if (statementList != null && statementList.getStatements().length == 0) { + return PyIndentUtil.getExpectedElementIndent(statementList); } - // Top-level, there should be no indentation - if (PsiTreeUtil.getParentOfType(ws, PyStatementList.class) == null) { - return ""; + + final PyStatementListContainer container = PsiTreeUtil.getParentOfType(ws, PyStatementListContainer.class); + if (container != null) { + return PyIndentUtil.getElementIndent(container.getStatementList()); } + } - return indentText; + return document.getText(TextRange.create(lineStartOffset, caretOffset)); } private static int calculateIndentToRemove(@NotNull String text, @NotNull final CharFilter filter) { diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.after.py b/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.after.py new file mode 100644 index 000000000000..cb147b103ec8 --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.after.py @@ -0,0 +1,14 @@ +class C: + def empty(self): + """ + line 1 + line 2 + """ + pass + + def another(self): + """ + line 1 + line 2 + """ + pass diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.dst.py b/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.dst.py new file mode 100644 index 000000000000..9ae3efe6d623 --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.dst.py @@ -0,0 +1,9 @@ +class C: + def empty(self): + + def another(self): + """ + line 1 + line 2 + """ + pass diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.src.py b/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.src.py new file mode 100644 index 000000000000..2370adc7104f --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtBodyIndent.src.py @@ -0,0 +1,10 @@ +class C: + def empty(self): + + def another(self): + """ + line 1 + line 2 + """ + pass + diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.after.py b/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.after.py new file mode 100644 index 000000000000..cb147b103ec8 --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.after.py @@ -0,0 +1,14 @@ +class C: + def empty(self): + """ + line 1 + line 2 + """ + pass + + def another(self): + """ + line 1 + line 2 + """ + pass diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.dst.py b/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.dst.py new file mode 100644 index 000000000000..dfa76aca0918 --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.dst.py @@ -0,0 +1,9 @@ +class C: + def empty(self): + + def another(self): + """ + line 1 + line 2 + """ + pass diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.src.py b/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.src.py new file mode 100644 index 000000000000..2370adc7104f --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtDefIndent.src.py @@ -0,0 +1,10 @@ +class C: + def empty(self): + + def another(self): + """ + line 1 + line 2 + """ + pass + diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.after.py b/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.after.py new file mode 100644 index 000000000000..cb147b103ec8 --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.after.py @@ -0,0 +1,14 @@ +class C: + def empty(self): + """ + line 1 + line 2 + """ + pass + + def another(self): + """ + line 1 + line 2 + """ + pass diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.dst.py b/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.dst.py new file mode 100644 index 000000000000..1ce22a75edc0 --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.dst.py @@ -0,0 +1,9 @@ +class C: + def empty(self): + + def another(self): + """ + line 1 + line 2 + """ + pass diff --git a/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.src.py b/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.src.py new file mode 100644 index 000000000000..2370adc7104f --- /dev/null +++ b/python/testData/copyPaste/EmptyFunctionCaretAtNoneIndent.src.py @@ -0,0 +1,10 @@ +class C: + def empty(self): + + def another(self): + """ + line 1 + line 2 + """ + pass + diff --git a/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java b/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java index 1ec2599e697d..185f54b0e15f 100644 --- a/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java +++ b/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java @@ -376,4 +376,19 @@ public class PyCopyPasteTest extends PyTestCase { public void testSameIndentPreserved() { doTest(); } + + // PY-18522 + public void testEmptyFunctionCaretAtNoneIndent() { + doTest(); + } + + // PY-18522 + public void testEmptyFunctionCaretAtDefIndent() { + doTest(); + } + + // PY-18522 + public void testEmptyFunctionCaretAtBodyIndent() { + doTest(); + } }