diff --git a/platform/platform-tests/intellij.platform.tests.iml b/platform/platform-tests/intellij.platform.tests.iml
index f43d45322aa0..327fd4e50c4e 100644
--- a/platform/platform-tests/intellij.platform.tests.iml
+++ b/platform/platform-tests/intellij.platform.tests.iml
@@ -68,5 +68,6 @@
+
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/afterAutoWrapOnTypingAtLineEnd.txt b/platform/platform-tests/testData/codeInsight/editing/afterAutoWrapOnTypingAtLineEnd.txt
new file mode 100644
index 000000000000..02b8c7a9e9d5
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/afterAutoWrapOnTypingAtLineEnd.txt
@@ -0,0 +1,2 @@
+a b c d e f g h i j k l
+mnop
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/afterAutoWrapsMerge.txt b/platform/platform-tests/testData/codeInsight/editing/afterAutoWrapsMerge.txt
new file mode 100644
index 000000000000..a26acb6efe04
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/afterAutoWrapsMerge.txt
@@ -0,0 +1,2 @@
+a b 1 2 3 4 5
+c d e f g
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/afterAvoidAutoWrapForIndentedLine.txt b/platform/platform-tests/testData/codeInsight/editing/afterAvoidAutoWrapForIndentedLine.txt
new file mode 100644
index 000000000000..767fd459c70e
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/afterAvoidAutoWrapForIndentedLine.txt
@@ -0,0 +1 @@
+ abc12345
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/afterAvoidAutoWrapForNonIndentedLine.txt b/platform/platform-tests/testData/codeInsight/editing/afterAvoidAutoWrapForNonIndentedLine.txt
new file mode 100644
index 000000000000..68fff2097535
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/afterAvoidAutoWrapForNonIndentedLine.txt
@@ -0,0 +1 @@
+abc12345
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/afterCopyPasteWithoutUnnecessaryIndent.txt b/platform/platform-tests/testData/codeInsight/editing/afterCopyPasteWithoutUnnecessaryIndent.txt
new file mode 100644
index 000000000000..23575b3438ba
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/afterCopyPasteWithoutUnnecessaryIndent.txt
@@ -0,0 +1,2 @@
+this is line 1
+this is line 2
diff --git a/platform/platform-tests/testData/codeInsight/editing/beforeAutoWrapOnTypingAtLineEnd.txt b/platform/platform-tests/testData/codeInsight/editing/beforeAutoWrapOnTypingAtLineEnd.txt
new file mode 100644
index 000000000000..1b974354be76
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/beforeAutoWrapOnTypingAtLineEnd.txt
@@ -0,0 +1 @@
+a b c d e f g h i j k l mn
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/beforeAutoWrapsMerge.txt b/platform/platform-tests/testData/codeInsight/editing/beforeAutoWrapsMerge.txt
new file mode 100644
index 000000000000..0ebd6b4522ce
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/beforeAutoWrapsMerge.txt
@@ -0,0 +1 @@
+a b c d e f g
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/beforeAvoidAutoWrapForIndentedLine.txt b/platform/platform-tests/testData/codeInsight/editing/beforeAvoidAutoWrapForIndentedLine.txt
new file mode 100644
index 000000000000..9eb866138b93
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/beforeAvoidAutoWrapForIndentedLine.txt
@@ -0,0 +1 @@
+ abc
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/beforeAvoidAutoWrapForNonIndentedLine.txt b/platform/platform-tests/testData/codeInsight/editing/beforeAvoidAutoWrapForNonIndentedLine.txt
new file mode 100644
index 000000000000..a81fae9f04d8
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/beforeAvoidAutoWrapForNonIndentedLine.txt
@@ -0,0 +1 @@
+abc
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeInsight/editing/beforeCopyPasteWithoutUnnecessaryIndent.txt b/platform/platform-tests/testData/codeInsight/editing/beforeCopyPasteWithoutUnnecessaryIndent.txt
new file mode 100644
index 000000000000..23575b3438ba
--- /dev/null
+++ b/platform/platform-tests/testData/codeInsight/editing/beforeCopyPasteWithoutUnnecessaryIndent.txt
@@ -0,0 +1,2 @@
+this is line 1
+this is line 2
diff --git a/platform/platform-tests/testSrc/com/intellij/codeInsight/PlainTextEditingTest.java b/platform/platform-tests/testSrc/com/intellij/codeInsight/PlainTextEditingTest.java
new file mode 100644
index 000000000000..9cbc35a0d99b
--- /dev/null
+++ b/platform/platform-tests/testSrc/com/intellij/codeInsight/PlainTextEditingTest.java
@@ -0,0 +1,310 @@
+// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.codeInsight;
+
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.openapi.fileTypes.PlainTextFileType;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.testFramework.PlatformTestUtil;
+import gnu.trove.TIntArrayList;
+import org.jetbrains.annotations.NotNull;
+
+public class PlainTextEditingTest extends EditingTestBase {
+
+ @NotNull
+ @Override
+ protected String getTestDataPath() {
+ return PlatformTestUtil.getPlatformTestDataPath();
+ }
+
+ public void testAutoWrapOnTypingAtLineEnd() {
+ mySettings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN = true;
+ mySettings.setDefaultRightMargin(27);
+ doTest(PlainTextFileType.INSTANCE, 'o', 'p');
+ }
+
+ public void testAutoWrapOnTyping_WrapsOnNewlyAddedSymbol_WhenTotalLengthExceedsRightMargin() {
+ boolean oldWrapValue = mySettings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN;
+ int oldMarginValue = mySettings.getDefaultRightMargin();
+ mySettings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN = true;
+ mySettings.setDefaultRightMargin(10);
+
+ try {
+ init("qqqq aaaa", PlainTextFileType.INSTANCE);
+ type('b');
+ checkResultByText("qqqq aaaab");
+ type('c');
+ checkResultByText("qqqq \naaaabc");
+ }
+ finally {
+ mySettings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN = oldWrapValue;
+ mySettings.setDefaultRightMargin(oldMarginValue);
+ }
+ }
+
+ public void testAutoWrapsMerge() {
+ mySettings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN = true;
+ mySettings.setDefaultRightMargin(14);
+ doTest(PlainTextFileType.INSTANCE, '1', ' ', '2', ' ', '3', ' ', '4', ' ', '5');
+ }
+
+ public void testAvoidAutoWrapForIndentedLine() {
+ mySettings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN = true;
+ mySettings.setDefaultRightMargin(10);
+ doTest(PlainTextFileType.INSTANCE, '1', '2', '3', '4', '5');
+ }
+
+ public void testAvoidAutoWrapForNonIndentedLine() {
+ mySettings.WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN = true;
+ mySettings.setDefaultRightMargin(5);
+ doTest(PlainTextFileType.INSTANCE, '1', '2', '3', '4', '5');
+ }
+
+ public void testCopyPasteWithoutUnnecessaryIndent() {
+ CodeInsightSettings settings = CodeInsightSettings.getInstance();
+ int oldValue = settings.REFORMAT_ON_PASTE;
+ settings.REFORMAT_ON_PASTE = CodeInsightSettings.INDENT_BLOCK;
+ try {
+ doTest(PlainTextFileType.INSTANCE, () -> {
+ // Move caret to the non-zero column.
+ getEditor().getCaretModel().moveToOffset(3);
+
+ // Select all text.
+ getEditor().getSelectionModel().setSelection(0, getEditor().getDocument().getTextLength());
+
+ // Perform 'copy-paste' action. Expecting to get the same file.
+ copy();
+ paste();
+ });
+ }
+ finally {
+ settings.REFORMAT_ON_PASTE = oldValue;
+ }
+ }
+
+ public void testCamelHumpsSelectionAndDigits() {
+ // Inspired by IDEA-63313
+
+ String text =
+ "class Test {\n" +
+ " String my1FieldWithLongName;\n" +
+ "}";
+
+ init(text, PlainTextFileType.INSTANCE);
+
+ CaretModel caretModel = getEditor().getCaretModel();
+ SelectionModel selectionModel = getEditor().getSelectionModel();
+
+
+ TIntArrayList expectedBoundaries = new TIntArrayList();
+ expectedBoundaries.add(text.indexOf("my"));
+ expectedBoundaries.add(text.indexOf("Field"));
+ expectedBoundaries.add(text.indexOf("With"));
+ expectedBoundaries.add(text.indexOf("Long"));
+ expectedBoundaries.add(text.indexOf("Name"));
+
+
+ boolean camelWords = getEditor().getSettings().isCamelWords();
+ getEditor().getSettings().setCamelWords(true);
+ try {
+ int selectionStart;
+ int selectionEnd = text.indexOf("Name") + 1;
+ caretModel.moveToOffset(selectionEnd);
+
+ // Check backward selection.
+ for (int i = expectedBoundaries.size() - 1; i >= 0; i--) {
+ selectionStart = expectedBoundaries.get(i);
+ moveCaretToPreviousWordWithSelection();
+ assertEquals(selectionStart, caretModel.getOffset());
+ assertEquals(selectionStart, selectionModel.getSelectionStart());
+ assertEquals(selectionEnd, selectionModel.getSelectionEnd());
+ }
+
+ // Check forward selection.
+ selectionStart = text.indexOf("my") - 1;
+ selectionModel.removeSelection();
+ caretModel.moveToOffset(selectionStart);
+ for (int i = 1; i < expectedBoundaries.size(); i++) {
+ selectionEnd = expectedBoundaries.get(i);
+ moveCaretToNextWordWithSelection();
+ assertEquals(selectionEnd, caretModel.getOffset());
+ assertEquals(selectionStart, selectionModel.getSelectionStart());
+ assertEquals(selectionEnd, selectionModel.getSelectionEnd());
+ }
+ }
+ finally {
+ getEditor().getSettings().setCamelWords(camelWords);
+ }
+ }
+
+ public void testNewLineAboveCurrentAction() {
+ // Inspired by IDEA-69728
+ String initial =
+ "class Test {\n" +
+ " public void test() {\n" +
+ " String s =\n" +
+ " \"test string\";\n" +
+ " }\n" +
+ "}";
+
+ init(initial, PlainTextFileType.INSTANCE);
+ executeAction("EditorStartNewLineBefore");
+
+ String expected =
+ "class Test {\n" +
+ " public void test() {\n" +
+ " String s =\n" +
+ "\n" +
+ " \"test string\";\n" +
+ " }\n" +
+ "}";
+
+ checkResultByText(expected);
+ }
+
+ public void testHungryBackspace() {
+ String text =
+ " a b ";
+ init(text, PlainTextFileType.INSTANCE);
+
+ executeAction("EditorHungryBackSpace");
+ checkResultByText(" a b");
+
+ executeAction("EditorHungryBackSpace");
+ checkResultByText(" a ");
+
+ executeAction("EditorHungryBackSpace");
+ checkResultByText(" a");
+
+ executeAction("EditorHungryBackSpace");
+ checkResultByText(" ");
+
+ executeAction("EditorHungryBackSpace");
+ checkResultByText("");
+
+ executeAction("EditorHungryBackSpace");
+ checkResultByText("");
+ }
+
+
+ public void testMoveToPrevWordStartInsideStringLiteral() {
+ String text = "one \"two\"";
+ init(text, PlainTextFileType.INSTANCE);
+ executeAction("EditorPreviousWord");
+ checkResultByText("one \"two\"");
+ }
+
+ public void testPrefixAwareEditorWithTabOnNonFirstLine() {
+ init("", PlainTextFileType.INSTANCE);
+ ((EditorEx)getEditor()).setPrefixTextAndAttributes("prefix", new TextAttributes());
+ type("first");
+ type("\n");
+ type("\tsecond");
+ // Expected that no exception is thrown here
+ }
+
+ public void testCutFromStickySelection() {
+ init("abcd", PlainTextFileType.INSTANCE);
+ EditorEx editor = (EditorEx)getEditor();
+ CaretModel caretModel = editor.getCaretModel();
+ caretModel.moveToOffset(1);
+ executeAction("EditorToggleStickySelection");
+ caretModel.moveToOffset(3);
+ assertEquals("bc", editor.getSelectionModel().getSelectedText());
+
+ executeAction("EditorCut");
+ checkResultByText("ad");
+ }
+
+
+ public void testSurroundByQuotesAndStickySelection() {
+ init("abcdef", PlainTextFileType.INSTANCE);
+ EditorEx editorEx = (EditorEx)getEditor();
+ editorEx.setStickySelection(true);
+ editorEx.getCaretModel().moveCaretRelatively(2, 0, false, false, false);
+ CodeInsightSettings settings = CodeInsightSettings.getInstance();
+ boolean oldValue = settings.SURROUND_SELECTION_ON_QUOTE_TYPED;
+ settings.SURROUND_SELECTION_ON_QUOTE_TYPED = true;
+ try {
+ type('\'');
+ }
+ finally {
+ settings.SURROUND_SELECTION_ON_QUOTE_TYPED = oldValue;
+ }
+ checkResultByText("ab'cd'ef");
+ }
+
+ public void testStickySelectionIsCanceledByEscapeAction() {
+ init("abcde", PlainTextFileType.INSTANCE);
+ EditorEx editorEx = (EditorEx)getEditor();
+ editorEx.setStickySelection(true);
+ editorEx.getCaretModel().moveCaretRelatively(2, 0, false, false, false);
+ SelectionModel selectionModel = editorEx.getSelectionModel();
+ assertEquals(2, selectionModel.getSelectionStart());
+ assertEquals(4, selectionModel.getSelectionEnd());
+ executeAction(IdeActions.ACTION_EDITOR_ESCAPE);
+ assertFalse("sticky selection is not dropped after 'escape' action", selectionModel.hasSelection());
+ assertFalse(editorEx.isStickySelection());
+ }
+
+ public void testDifferentHumpsMode() {
+ String text = "testVar1 testVar2";
+ init(text, PlainTextFileType.INSTANCE);
+
+ CaretModel caretModel = getEditor().getCaretModel();
+ caretModel.moveToOffset(0);
+ Document document = getEditor().getDocument();
+ EditorSettings settings = getEditor().getSettings();
+ boolean camelModeToRestore = settings.isCamelWords();
+ settings.setCamelWords(false);
+
+ try {
+ executeAction("EditorNextWord");
+ assertEquals(text.indexOf(" "), caretModel.getOffset());
+
+ executeAction("EditorPreviousWordInDifferentHumpsMode");
+ assertEquals(text.indexOf("Var1"), caretModel.getOffset());
+
+ caretModel.moveToOffset(text.length());
+ executeAction("EditorPreviousWord");
+ assertEquals(text.indexOf("testVar2"), caretModel.getOffset());
+
+ executeAction("EditorNextWordInDifferentHumpsMode");
+ assertEquals(text.indexOf("Var2"), caretModel.getOffset());
+
+ caretModel.moveToOffset(text.length());
+ settings.setCamelWords(true);
+ executeAction("EditorPreviousWordInDifferentHumpsMode");
+ assertEquals(text.indexOf("testVar2"), caretModel.getOffset());
+
+ executeAction("EditorNextWordInDifferentHumpsMode");
+ assertEquals(text.length(), caretModel.getOffset());
+
+ settings.setCamelWords(false);
+ executeAction("EditorDeleteToWordStartInDifferentHumpsMode");
+ assertEquals("testVar1 test", document.getText());
+
+ caretModel.moveToOffset(0);
+ executeAction("EditorDeleteToWordEndInDifferentHumpsMode");
+ assertEquals("Var1 test", document.getText());
+ }
+ finally {
+ settings.setCamelWords(camelModeToRestore);
+ }
+ }
+
+ public void testCalculatingLongLinesPositionPerformanceInEditorWithNoTabs() {
+ final String longLine = StringUtil.repeatSymbol(' ', 1000000);
+ configureFromFileText("x.txt", longLine);
+ PlatformTestUtil.startPerformanceTest("calcOffset", 10, () -> {
+ for (int i = 0; i < 1000; i++) {
+ int off = getEditor().logicalPositionToOffset(new LogicalPosition(0, longLine.length() - 1));
+ assertEquals(longLine.length() - 1, off);
+ int col = getEditor().offsetToLogicalPosition(longLine.length() - 1).column;
+ assertEquals(longLine.length() - 1, col);
+ }
+ }).assertTiming();
+ }
+}