logic for indenting after pasting does not based on copying now

PY-6884, PY-6965, PY-6966, PY-6907
This commit is contained in:
Ekaterina Tuzova
2012-07-16 19:18:49 +04:00
parent 6899581a33
commit 6aa655c180
17 changed files with 117 additions and 46 deletions
@@ -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<String> 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<String> 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;
@@ -2,6 +2,5 @@ class C:
def foo(self):
x = 1
y = 2
def bar(self):
pass
@@ -0,0 +1,5 @@
def f():
try:
a = 1
except:
b = 1
@@ -0,0 +1,6 @@
def f():
try:
<selection> a = 1
except:
b = 1
</selection><caret>
@@ -0,0 +1,6 @@
def f():
try:
<selection> a = 1
except:
b = 1
</selection><caret>
@@ -0,0 +1,5 @@
def f():
if True:
c = 1
a = 1
@@ -0,0 +1,5 @@
def f():
if True:
<caret>
a = 1
@@ -0,0 +1,7 @@
<selection>c = 1
</selection>
def f():
try:
a = 1
except:
b = 1
@@ -0,0 +1,6 @@
def f():
try:
a = 1
except:
b = 1
@@ -0,0 +1,5 @@
def f():
<caret>try:
a = 1
except:
b = 1
@@ -0,0 +1,7 @@
<selection>
</selection>
def f():
try:
a = 1
except:
b = 1
@@ -4,4 +4,3 @@ def f():
print(0.1)
else:
print(0.1)
<caret>
@@ -1,5 +1,5 @@
class C:
<selection>def foo(self):
<selection>def foo(self):
x = 1
y = 2
</selection>
@@ -1,6 +1,8 @@
def f():
a = 1
b = 2
def f():
a = 1
b = 2
@@ -6,4 +6,3 @@ def foo():
b = 1
graph.clear = 0.1
<caret>
@@ -3,4 +3,3 @@ def f(self):
if True:
b = 2
x = 1
<caret>
@@ -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);
}
}