From 6aa655c180693ec0bd23488998c531a5bf1db224 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 16 Jul 2012 19:18:49 +0400 Subject: [PATCH] logic for indenting after pasting does not based on copying now PY-6884, PY-6965, PY-6966, PY-6907 --- .../editor/PythonCopyPasteProcessor.java | 85 ++++++++++--------- .../testData/copyPaste/MethodInClass.after.py | 1 - .../testData/copyPaste/TheSamePlace.after.py | 5 ++ python/testData/copyPaste/TheSamePlace.dst.py | 6 ++ python/testData/copyPaste/TheSamePlace.src.py | 6 ++ .../copyPaste/UnfinishedCompound.after.py | 5 ++ .../copyPaste/UnfinishedCompound.dst.py | 5 ++ .../copyPaste/UnfinishedCompound.src.py | 7 ++ python/testData/copyPaste/Whitespace.after.py | 6 ++ python/testData/copyPaste/Whitespace.dst.py | 5 ++ python/testData/copyPaste/Whitespace.src.py | 7 ++ .../copyPaste/multiLine/IndentIfElse.after.py | 1 - .../multiLine/IndentInnerFunction.src.py | 2 +- .../multiLine/IndentTopLevel.after.py | 2 + .../multiLine/IndentWithEmptyLine.after.py | 1 - .../singleLine/IndentInIfInDef.after.py | 1 - .../com/jetbrains/python/PyCopyPasteTest.java | 18 +++- 17 files changed, 117 insertions(+), 46 deletions(-) create mode 100644 python/testData/copyPaste/TheSamePlace.after.py create mode 100644 python/testData/copyPaste/TheSamePlace.dst.py create mode 100644 python/testData/copyPaste/TheSamePlace.src.py create mode 100644 python/testData/copyPaste/UnfinishedCompound.after.py create mode 100644 python/testData/copyPaste/UnfinishedCompound.dst.py create mode 100644 python/testData/copyPaste/UnfinishedCompound.src.py create mode 100644 python/testData/copyPaste/Whitespace.after.py create mode 100644 python/testData/copyPaste/Whitespace.dst.py create mode 100644 python/testData/copyPaste/Whitespace.src.py diff --git a/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java b/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java index 0ddf46a7de80..acf077b23eb7 100644 --- a/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java +++ b/python/src/com/jetbrains/python/editor/PythonCopyPasteProcessor.java @@ -7,14 +7,13 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RawText; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.CharFilter; 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.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilCore; import com.jetbrains.python.psi.PyFile; -import com.jetbrains.python.psi.PyStatementList; import java.util.List; @@ -42,51 +41,61 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor { final Document document = editor.getDocument(); String newText = text; - if (file instanceof PyFile && (StringUtil.startsWithWhitespace(text) || StringUtil.endsWithLineBreak(text) || - StringUtil.splitByLines(text).length > 1)) { - if (text.endsWith("\n")) text = text.substring(0, text.length() - 1); + if (file instanceof PyFile) { final int caretOffset = caretModel.getOffset(); - int caretColumn = caretModel.getLogicalPosition().column; - final PsiElement element = PsiUtilCore.getElementAtOffset(file, caretOffset-1); final int lineNumber = document.getLineNumber(caretOffset); - final int offset = getLineStartSafeOffset(document, lineNumber); - final PsiElement element1 = PsiUtilCore.getElementAtOffset(file, offset); - boolean moved = false; - if (element instanceof PsiWhiteSpace && element == element1) { - PyStatementList statementList = PsiTreeUtil - .findElementOfClassAtOffset(file, element.getTextOffset() - 1, PyStatementList.class, false); - // Caret beyond actual indent -- move to the actual offset - if (statementList != null) { - final PsiElement lastChild = statementList.getLastChild(); - if (lastChild != null) { - final PsiElement whiteSpace = lastChild.getPrevSibling(); - if (whiteSpace instanceof PsiWhiteSpace) { - int relatedOffset = whiteSpace.getTextRange().getEndOffset(); - final int indent = relatedOffset - getLineStartSafeOffset(document, document.getLineNumber(relatedOffset)); - if (caretColumn > indent && document.getTextLength() > offset + indent) { - caretModel.moveToOffset(offset + indent); - moved = true; - } + final int lineStartOffset = getLineStartSafeOffset(document, lineNumber); + + final List strings = StringUtil.split(text, "\n"); + if (StringUtil.countChars(text, '\n') > 0 || StringUtil.startsWithWhitespace(text)) { //2, 3, 4 case from doc + final PsiElement element = PsiUtilCore.getElementAtOffset(file, caretOffset - 1); + + caretModel.moveToOffset(lineStartOffset); + String spaceString; + int indent = 0; + + //calculate indent to normalize text + if (strings.size() > 0) { + spaceString = strings.get(0); // insert single line + indent = StringUtil.findFirst(spaceString, CharFilter.NOT_WHITESPACE_FILTER); + if (indent < 0) + indent = StringUtil.isEmptyOrSpaces(spaceString) ? spaceString.length() : 0; + + if (!StringUtil.startsWithWhitespace(spaceString) && strings.size() > 1) { // insert multi-line + spaceString = strings.get(1); + indent = StringUtil.findFirst(spaceString, CharFilter.NOT_WHITESPACE_FILTER); + if (indent < 0) + indent = StringUtil.isEmptyOrSpaces(spaceString) ? spaceString.length() : 0; + + final String trimmed = StringUtil.trimLeading(strings.get(0)); //decrease indent if needed + if (trimmed.startsWith("def ") || trimmed.startsWith("if ") || trimmed.startsWith("try:") || + trimmed.startsWith("class ") || trimmed.startsWith("for ") || trimmed.startsWith("elif ") || + trimmed.startsWith("else:") || trimmed.startsWith("except") || trimmed.startsWith("while ")) { + indent = StringUtil.findFirst(spaceString, CharFilter.NOT_WHITESPACE_FILTER) / 2; + if (indent < 0) indent = 0; } } } - final List strings = StringUtil.split(element.getText(), "\n"); - //user already prepared place to paste to and we just want to indent right - if (StringUtil.countChars(element.getText(), '\n') > 2) { - newText = text + " "; - if (caretOffset == offset && !strings.isEmpty() && !(element.getParent() instanceof PyFile)) { - newText = strings.get(strings.size()-1) + newText; - } + if (!StringUtil.isEmptyOrSpaces(text)) // do not process empty lines + text = StringUtil.trimTrailing(text); + + if (!StringUtil.startsWithWhitespace(text)) { // add missed whitespaces + if (indent > 0) + newText = StringUtil.repeat(" ", indent) + text; + else + newText = new String(text); } else { - newText = text + "\n"; - if (!strings.isEmpty()) - newText += strings.get(strings.size()-1); - //pasted text'll be the only one statement in block - if (!element.getText().endsWith("\n") && !moved) - caretModel.moveToOffset(element.getTextRange().getEndOffset()); + newText = new String(text); // to indent correctly (see PasteHandler) } + + if (element instanceof PsiWhiteSpace && + (StringUtil.countChars(element.getText(), '\n') <= 2 && !StringUtil.isEmptyOrSpaces(text))) { + newText += "\n"; + } + else + newText = new String(text); //user already prepared place to paste to and we just want to indent right } } return newText; diff --git a/python/testData/copyPaste/MethodInClass.after.py b/python/testData/copyPaste/MethodInClass.after.py index 352ff68814d6..62080dda3bfa 100644 --- a/python/testData/copyPaste/MethodInClass.after.py +++ b/python/testData/copyPaste/MethodInClass.after.py @@ -2,6 +2,5 @@ class C: def foo(self): x = 1 y = 2 - def bar(self): pass \ No newline at end of file diff --git a/python/testData/copyPaste/TheSamePlace.after.py b/python/testData/copyPaste/TheSamePlace.after.py new file mode 100644 index 000000000000..da262a52e1d6 --- /dev/null +++ b/python/testData/copyPaste/TheSamePlace.after.py @@ -0,0 +1,5 @@ +def f(): + try: + a = 1 + except: + b = 1 diff --git a/python/testData/copyPaste/TheSamePlace.dst.py b/python/testData/copyPaste/TheSamePlace.dst.py new file mode 100644 index 000000000000..71f1daa90526 --- /dev/null +++ b/python/testData/copyPaste/TheSamePlace.dst.py @@ -0,0 +1,6 @@ +def f(): + try: + a = 1 + except: + b = 1 + \ No newline at end of file diff --git a/python/testData/copyPaste/TheSamePlace.src.py b/python/testData/copyPaste/TheSamePlace.src.py new file mode 100644 index 000000000000..71f1daa90526 --- /dev/null +++ b/python/testData/copyPaste/TheSamePlace.src.py @@ -0,0 +1,6 @@ +def f(): + try: + a = 1 + except: + b = 1 + \ No newline at end of file diff --git a/python/testData/copyPaste/UnfinishedCompound.after.py b/python/testData/copyPaste/UnfinishedCompound.after.py new file mode 100644 index 000000000000..473f5c3376ea --- /dev/null +++ b/python/testData/copyPaste/UnfinishedCompound.after.py @@ -0,0 +1,5 @@ +def f(): + if True: + c = 1 + +a = 1 \ No newline at end of file diff --git a/python/testData/copyPaste/UnfinishedCompound.dst.py b/python/testData/copyPaste/UnfinishedCompound.dst.py new file mode 100644 index 000000000000..b6adc51f7457 --- /dev/null +++ b/python/testData/copyPaste/UnfinishedCompound.dst.py @@ -0,0 +1,5 @@ +def f(): + if True: + + +a = 1 \ No newline at end of file diff --git a/python/testData/copyPaste/UnfinishedCompound.src.py b/python/testData/copyPaste/UnfinishedCompound.src.py new file mode 100644 index 000000000000..2fa70ae1494e --- /dev/null +++ b/python/testData/copyPaste/UnfinishedCompound.src.py @@ -0,0 +1,7 @@ +c = 1 + +def f(): + try: + a = 1 + except: + b = 1 diff --git a/python/testData/copyPaste/Whitespace.after.py b/python/testData/copyPaste/Whitespace.after.py new file mode 100644 index 000000000000..ce64be5b309f --- /dev/null +++ b/python/testData/copyPaste/Whitespace.after.py @@ -0,0 +1,6 @@ +def f(): + + try: + a = 1 + except: + b = 1 diff --git a/python/testData/copyPaste/Whitespace.dst.py b/python/testData/copyPaste/Whitespace.dst.py new file mode 100644 index 000000000000..e2db6f97e58b --- /dev/null +++ b/python/testData/copyPaste/Whitespace.dst.py @@ -0,0 +1,5 @@ +def f(): + try: + a = 1 + except: + b = 1 diff --git a/python/testData/copyPaste/Whitespace.src.py b/python/testData/copyPaste/Whitespace.src.py new file mode 100644 index 000000000000..4ad1fd83fbc3 --- /dev/null +++ b/python/testData/copyPaste/Whitespace.src.py @@ -0,0 +1,7 @@ + + +def f(): + try: + a = 1 + except: + b = 1 diff --git a/python/testData/copyPaste/multiLine/IndentIfElse.after.py b/python/testData/copyPaste/multiLine/IndentIfElse.after.py index efa8937d6b2e..dd6316ecff23 100644 --- a/python/testData/copyPaste/multiLine/IndentIfElse.after.py +++ b/python/testData/copyPaste/multiLine/IndentIfElse.after.py @@ -4,4 +4,3 @@ def f(): print(0.1) else: print(0.1) - \ No newline at end of file diff --git a/python/testData/copyPaste/multiLine/IndentInnerFunction.src.py b/python/testData/copyPaste/multiLine/IndentInnerFunction.src.py index 97e2a5be4679..91be90e8bdd3 100644 --- a/python/testData/copyPaste/multiLine/IndentInnerFunction.src.py +++ b/python/testData/copyPaste/multiLine/IndentInnerFunction.src.py @@ -1,5 +1,5 @@ class C: - def foo(self): + def foo(self): x = 1 y = 2 \ No newline at end of file diff --git a/python/testData/copyPaste/multiLine/IndentTopLevel.after.py b/python/testData/copyPaste/multiLine/IndentTopLevel.after.py index ad8dfc390761..675d73067acc 100644 --- a/python/testData/copyPaste/multiLine/IndentTopLevel.after.py +++ b/python/testData/copyPaste/multiLine/IndentTopLevel.after.py @@ -1,6 +1,8 @@ def f(): a = 1 b = 2 + + def f(): a = 1 b = 2 diff --git a/python/testData/copyPaste/multiLine/IndentWithEmptyLine.after.py b/python/testData/copyPaste/multiLine/IndentWithEmptyLine.after.py index 3ade603f37f3..976bf2268068 100644 --- a/python/testData/copyPaste/multiLine/IndentWithEmptyLine.after.py +++ b/python/testData/copyPaste/multiLine/IndentWithEmptyLine.after.py @@ -6,4 +6,3 @@ def foo(): b = 1 graph.clear = 0.1 - \ No newline at end of file diff --git a/python/testData/copyPaste/singleLine/IndentInIfInDef.after.py b/python/testData/copyPaste/singleLine/IndentInIfInDef.after.py index 5cf2aefa0834..eb362c158fe7 100644 --- a/python/testData/copyPaste/singleLine/IndentInIfInDef.after.py +++ b/python/testData/copyPaste/singleLine/IndentInIfInDef.after.py @@ -3,4 +3,3 @@ def f(self): if True: b = 2 x = 1 - \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java b/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java index 74d2cbab9e5e..28b485f08caa 100644 --- a/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java +++ b/python/testSrc/com/jetbrains/python/PyCopyPasteTest.java @@ -187,6 +187,18 @@ public class PyCopyPasteTest extends PyTestCase { doTestMultiLine(); } + public void testTheSamePlace() { //PY-6907 + doTest(); + } + + public void testWhitespace() { //PY-6966 + doTest(); + } + + public void testUnfinishedCompound() { //PY-6965 + doTest(); + } + private void doTest() { String name = getTestName(false); @@ -194,7 +206,7 @@ public class PyCopyPasteTest extends PyTestCase { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_COPY); myFixture.configureByFile("copyPaste/" + name + ".dst.py"); myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE); - myFixture.checkResultByFile("copyPaste/" + name + ".after.py"); + myFixture.checkResultByFile("copyPaste/" + name + ".after.py", true); } private void doTestSingleLine() { @@ -204,7 +216,7 @@ public class PyCopyPasteTest extends PyTestCase { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_COPY); myFixture.configureByFile("copyPaste/singleLine/" + name + ".dst.py"); myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE); - myFixture.checkResultByFile("copyPaste/singleLine/" + name + ".after.py"); + myFixture.checkResultByFile("copyPaste/singleLine/" + name + ".after.py", true); } private void doTestMultiLine() { @@ -214,6 +226,6 @@ public class PyCopyPasteTest extends PyTestCase { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_COPY); myFixture.configureByFile("copyPaste/multiLine/" + name + ".dst.py"); myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE); - myFixture.checkResultByFile("copyPaste/multiLine/" + name + ".after.py"); + myFixture.checkResultByFile("copyPaste/multiLine/" + name + ".after.py", true); } }