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
This commit is contained in:
Ekaterina Tuzova
2013-04-01 13:39:53 +04:00
parent a9dffdc97c
commit b16f001f8f
5 changed files with 59 additions and 8 deletions

View File

@@ -10,6 +10,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
@@ -44,13 +45,15 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor {
if (!CodeInsightSettings.getInstance().INDENT_TO_CARET_ON_PASTE) { if (!CodeInsightSettings.getInstance().INDENT_TO_CARET_ON_PASTE) {
return text; return text;
} }
final boolean useTabs = final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project);
CodeStyleSettingsManager.getSettings(project).useTabCharacter(PythonFileType.INSTANCE); final boolean useTabs = codeStyleSettings.useTabCharacter(PythonFileType.INSTANCE);
final int indentSize = codeStyleSettings.getIndentSize(PythonFileType.INSTANCE);
CharFilter NOT_INDENT_FILTER = new CharFilter() { CharFilter NOT_INDENT_FILTER = new CharFilter() {
public boolean accept(char ch) { public boolean accept(char ch) {
return useTabs? ch != '\t' : ch != ' '; return useTabs? ch != '\t' : ch != ' ';
} }
}; };
final String indentChar = useTabs ? "\t" : " ";
final CaretModel caretModel = editor.getCaretModel(); final CaretModel caretModel = editor.getCaretModel();
final SelectionModel selectionModel = editor.getSelectionModel(); final SelectionModel selectionModel = editor.getSelectionModel();
@@ -60,6 +63,7 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor {
final int lineNumber = document.getLineNumber(caretOffset); final int lineNumber = document.getLineNumber(caretOffset);
final int lineStartOffset = getLineStartSafeOffset(document, lineNumber); final int lineStartOffset = getLineStartSafeOffset(document, lineNumber);
text = addLeadingSpaces(text, NOT_INDENT_FILTER, indentSize, indentChar);
final String indentText = getIndentText(file, document, caretOffset, lineNumber); final String indentText = getIndentText(file, document, caretOffset, lineNumber);
int toRemove = calculateIndentToRemove(text, NOT_INDENT_FILTER); int toRemove = calculateIndentToRemove(text, NOT_INDENT_FILTER);
@@ -72,7 +76,7 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor {
String newText = ""; String newText = "";
if (StringUtil.isEmptyOrSpaces(indentText)) { if (StringUtil.isEmptyOrSpaces(indentText)) {
for (String s : strings) { for (String s : strings) {
newText += indentText + StringUtil.trimStart(s, StringUtil.repeat(useTabs? "\t" : " ", toRemove)); newText += indentText + StringUtil.trimStart(s, StringUtil.repeat(indentChar, toRemove));
} }
} }
else { else {
@@ -85,6 +89,19 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor {
return newText; return newText;
} }
private static String addLeadingSpaces(String text, final CharFilter filter, int indentSize, String indentChar) {
final List<String> 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, private static String getIndentText(@NotNull final PsiFile file,
@NotNull final Document document, @NotNull final Document document,
int caretOffset, int caretOffset,
@@ -143,13 +160,9 @@ public class PythonCopyPasteProcessor implements CopyPastePreProcessor {
private static boolean inStatementList(@NotNull final PsiFile file, int caretOffset) { private static boolean inStatementList(@NotNull final PsiFile file, int caretOffset) {
final PsiElement element = file.findElementAt(caretOffset); final PsiElement element = file.findElementAt(caretOffset);
final PsiElement element1 = file.findElementAt(caretOffset);
return PsiTreeUtil.getParentOfType(element, PyStatementList.class) != null || return PsiTreeUtil.getParentOfType(element, PyStatementList.class) != null ||
PsiTreeUtil.getParentOfType(element1, PyStatementList.class) != null ||
PsiTreeUtil.getParentOfType(element, PyFunction.class) != null || PsiTreeUtil.getParentOfType(element, PyFunction.class) != null ||
PsiTreeUtil.getParentOfType(element1, PyFunction.class) != null || PsiTreeUtil.getParentOfType(element, PyClass.class) != null;
PsiTreeUtil.getParentOfType(element, PyClass.class) != null ||
PsiTreeUtil.getParentOfType(element1, PyClass.class) != null;
} }
private static boolean addLinebreak(@NotNull String text, @NotNull String toString, boolean useTabs) { private static boolean addLinebreak(@NotNull String text, @NotNull String toString, boolean useTabs) {

View File

@@ -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)

View File

@@ -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)
<caret>

View File

@@ -0,0 +1,10 @@
# original function # this is line 1 of the code.
def foo():
print 'f00'
<selection>def bar(num):
for _ in range(num):
print 'bar'
bar(7)</selection><caret>

View File

@@ -297,6 +297,10 @@ public class PyCopyPasteTest extends PyTestCase {
doTest(); doTest();
} }
public void testInnerToOuterFunction() {
doTest();
}
private void doTestTabs() { private void doTestTabs() {
final CommonCodeStyleSettings.IndentOptions indentOptions = final CommonCodeStyleSettings.IndentOptions indentOptions =
CodeStyleSettingsManager.getSettings(myFixture.getProject()).getIndentOptions(PythonFileType.INSTANCE); CodeStyleSettingsManager.getSettings(myFixture.getProject()).getIndentOptions(PythonFileType.INSTANCE);