From b16f001f8f0f5704d2823783b7e9c56450e09a7d Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 1 Apr 2013 13:39:53 +0400 Subject: [PATCH] fixed PY-9337 Copy paste doesn't always preserve the relative indentation of what is pasted. check if we need to add leading spaces to pasted text --- .../editor/PythonCopyPasteProcessor.java | 29 ++++++++++++++----- .../copyPaste/InnerToOuterFunction.after.py | 15 ++++++++++ .../copyPaste/InnerToOuterFunction.dst.py | 9 ++++++ .../copyPaste/InnerToOuterFunction.src.py | 10 +++++++ .../com/jetbrains/python/PyCopyPasteTest.java | 4 +++ 5 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 python/testData/copyPaste/InnerToOuterFunction.after.py create mode 100644 python/testData/copyPaste/InnerToOuterFunction.dst.py create mode 100644 python/testData/copyPaste/InnerToOuterFunction.src.py diff --git a/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java b/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java index bcf7535a6d1d..43cc03571d08 100644 --- a/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java +++ b/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; @@ -44,13 +45,15 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { if (!CodeInsightSettings.getInstance().INDENT_TO_CARET_ON_PASTE) { return text; } - final boolean useTabs = - CodeStyleSettingsManager.getSettings(project).useTabCharacter(PythonFileType.INSTANCE); + final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project); + final boolean useTabs = codeStyleSettings.useTabCharacter(PythonFileType.INSTANCE); + final int indentSize = codeStyleSettings.getIndentSize(PythonFileType.INSTANCE); CharFilter NOT_INDENT_FILTER = new CharFilter() { public boolean accept(char ch) { return useTabs? ch != '\t' : ch != ' '; } }; + final String indentChar = useTabs ? "\t" : " "; final CaretModel caretModel = editor.getCaretModel(); final SelectionModel selectionModel = editor.getSelectionModel(); @@ -60,6 +63,7 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { final int lineNumber = document.getLineNumber(caretOffset); final int lineStartOffset = getLineStartSafeOffset(document, lineNumber); + text = addLeadingSpaces(text, NOT_INDENT_FILTER, indentSize, indentChar); final String indentText = getIndentText(file, document, caretOffset, lineNumber); int toRemove = calculateIndentToRemove(text, NOT_INDENT_FILTER); @@ -72,7 +76,7 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { String newText = ""; if (StringUtil.isEmptyOrSpaces(indentText)) { for (String s : strings) { - newText += indentText + StringUtil.trimStart(s, StringUtil.repeat(useTabs? "\t" : " ", toRemove)); + newText += indentText + StringUtil.trimStart(s, StringUtil.repeat(indentChar, toRemove)); } } else { @@ -85,6 +89,19 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { return newText; } + private static String addLeadingSpaces(String text, final CharFilter filter, int indentSize, String indentChar) { + final List strings = StringUtil.split(text, "\n", false); + if (strings.size() > 1) { + int firstLineIndent = StringUtil.findFirst(strings.get(0), filter); + int secondLineIndent = StringUtil.findFirst(strings.get(1), filter); + final int diff = secondLineIndent - firstLineIndent; + if (diff > indentSize) { + text = StringUtil.repeat(indentChar, diff - indentSize) + text; + } + } + return text; + } + private static String getIndentText(@NotNull final PsiFile file, @NotNull final Document document, int caretOffset, @@ -143,13 +160,9 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { private static boolean inStatementList(@NotNull final PsiFile file, int caretOffset) { final PsiElement element = file.findElementAt(caretOffset); - final PsiElement element1 = file.findElementAt(caretOffset); return PsiTreeUtil.getParentOfType(element, PyStatementList.class) != null || - PsiTreeUtil.getParentOfType(element1, PyStatementList.class) != null || PsiTreeUtil.getParentOfType(element, PyFunction.class) != null || - PsiTreeUtil.getParentOfType(element1, PyFunction.class) != null || - PsiTreeUtil.getParentOfType(element, PyClass.class) != null || - PsiTreeUtil.getParentOfType(element1, PyClass.class) != null; + PsiTreeUtil.getParentOfType(element, PyClass.class) != null; } private static boolean addLinebreak(@NotNull String text, @NotNull String toString, boolean useTabs) { diff --git a/python/testData/copyPaste/InnerToOuterFunction.after.py b/python/testData/copyPaste/InnerToOuterFunction.after.py new file mode 100644 index 000000000000..a47bcea0e8e3 --- /dev/null +++ b/python/testData/copyPaste/InnerToOuterFunction.after.py @@ -0,0 +1,15 @@ +# original function # this is line 1 of the code. +def foo(): + print 'f00' + def bar(num): + for _ in range(num): + print 'bar' + bar(7) + + +def bar(num): + for _ in range(num): + print 'bar' + + +bar(7) diff --git a/python/testData/copyPaste/InnerToOuterFunction.dst.py b/python/testData/copyPaste/InnerToOuterFunction.dst.py new file mode 100644 index 000000000000..4ceb5639a5e8 --- /dev/null +++ b/python/testData/copyPaste/InnerToOuterFunction.dst.py @@ -0,0 +1,9 @@ +# original function # this is line 1 of the code. +def foo(): + print 'f00' + def bar(num): + for _ in range(num): + print 'bar' + bar(7) + + diff --git a/python/testData/copyPaste/InnerToOuterFunction.src.py b/python/testData/copyPaste/InnerToOuterFunction.src.py new file mode 100644 index 000000000000..2d1d3224ad1e --- /dev/null +++ b/python/testData/copyPaste/InnerToOuterFunction.src.py @@ -0,0 +1,10 @@ +# original function # this is line 1 of the code. +def foo(): + print 'f00' + def bar(num): + for _ in range(num): + print 'bar' + bar(7) + + + diff --git a/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java b/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java index 11cbd69fc84b..62e1f216f1e9 100644 --- a/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java +++ b/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java @@ -297,6 +297,10 @@ public class PyCopyPasteTest extends PyTestCase { doTest(); } + public void testInnerToOuterFunction() { + doTest(); + } + private void doTestTabs() { final CommonCodeStyleSettings.IndentOptions indentOptions = CodeStyleSettingsManager.getSettings(myFixture.getProject()).getIndentOptions(PythonFileType.INSTANCE);