diff --git a/plugins/yaml/resources/META-INF/plugin.xml b/plugins/yaml/resources/META-INF/plugin.xml
index c3e0c5467355..c11568bc15ad 100644
--- a/plugins/yaml/resources/META-INF/plugin.xml
+++ b/plugins/yaml/resources/META-INF/plugin.xml
@@ -13,6 +13,7 @@
+
diff --git a/plugins/yaml/src/org/jetbrains/yaml/formatter/YAMLCopyPasteProcessor.java b/plugins/yaml/src/org/jetbrains/yaml/formatter/YAMLCopyPasteProcessor.java
new file mode 100644
index 000000000000..ddd597105029
--- /dev/null
+++ b/plugins/yaml/src/org/jetbrains/yaml/formatter/YAMLCopyPasteProcessor.java
@@ -0,0 +1,100 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.formatter;
+
+import com.intellij.codeInsight.editorActions.CopyPastePreProcessor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.LineTokenizer;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.yaml.YAMLLanguage;
+import org.jetbrains.yaml.YAMLTextUtil;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class YAMLCopyPasteProcessor implements CopyPastePreProcessor {
+ @Nullable
+ @Override
+ public String preprocessOnCopy(PsiFile file, int[] startOffsets, int[] endOffsets, String text) {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public String preprocessOnPaste(Project project, PsiFile file, Editor editor, String text, RawText rawText) {
+ if (file.getLanguage() != YAMLLanguage.INSTANCE) return text;
+ CaretModel caretModel = editor.getCaretModel();
+ SelectionModel selectionModel = editor.getSelectionModel();
+ Document document = editor.getDocument();
+ int caretOffset = selectionModel.getSelectionStart() != selectionModel.getSelectionEnd() ?
+ selectionModel.getSelectionStart() : caretModel.getOffset();
+ int lineNumber = document.getLineNumber(caretOffset);
+ int lineStartOffset = YAMLTextUtil.getLineStartSafeOffset(document, lineNumber);
+ int indent = caretOffset - lineStartOffset;
+ if (indent == 0) {
+ // It could be copy and paste of lines
+ // User could fix indentation later if he wanted to copy some block into top-level block
+ return text;
+ }
+
+ return indentText(text, StringUtil.repeatSymbol(' ', indent));
+ }
+
+ @NotNull
+ private static String indentText(@NotNull String text, @NotNull String curLineIndent) {
+ List lines = LineTokenizer.tokenizeIntoList(text, false, false);
+ if (lines.isEmpty()) {
+ // Such situation should not be possible
+ Logger.getInstance(YAMLCopyPasteProcessor.class).error(text.isEmpty()
+ ? "Pasted empty text"
+ : "Text '" + text + "' was converted into empty line list");
+ return text;
+ }
+ int minIndent = calculateMinBlockIndent(lines);
+ String firstLine = lines.iterator().next();
+ if (lines.size() == 1) {
+ return firstLine;
+ }
+ return firstLine.substring(YAMLTextUtil.getStartIndentSize(firstLine)) + "\n" +
+ lines.stream().skip(1).map(line -> {
+ // remove common indent and add needed indent
+ if (isEmptyLine(line)) {
+ return curLineIndent + line.substring(minIndent);
+ }
+ else {
+ // do not indent empty lines at all
+ return "";
+ }
+ }).reduce((left, right) -> left + "\n" + right).orElse("");
+ }
+
+ private static int calculateMinBlockIndent(@NotNull List list) {
+ Iterator it = list.iterator();
+ String str = "";
+ while (it.hasNext()) {
+ str = it.next();
+ if (isEmptyLine(str)) {
+ break;
+ }
+ }
+ if (!it.hasNext()) {
+ return 0;
+ }
+ int minIndent = YAMLTextUtil.getStartIndentSize(str);
+ while (it.hasNext()) {
+ str = it.next();
+ if (isEmptyLine(str)) {
+ minIndent = Math.min(minIndent, YAMLTextUtil.getStartIndentSize(str));
+ }
+ }
+ return minIndent;
+ }
+
+ private static boolean isEmptyLine(@NotNull String str) {
+ return YAMLTextUtil.getStartIndentSize(str) < str.length();
+ }
+}
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/YAMLPasteTest.java b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/YAMLPasteTest.java
new file mode 100644
index 000000000000..49e41aec1ba5
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/YAMLPasteTest.java
@@ -0,0 +1,148 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.paste;
+
+import com.intellij.codeInsight.CodeInsightSettings;
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.openapi.application.ex.PathManagerEx;
+import com.intellij.openapi.ide.CopyPasteManager;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
+import com.intellij.util.ObjectUtils;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.datatransfer.StringSelection;
+
+public abstract class YAMLPasteTest extends LightPlatformCodeInsightFixtureTestCase {
+ private static final String ZERO_INDENT_SAMPLE = "key1:\n" +
+ " subKey: val1\n" +
+ "\n" +
+ "key2: val2";
+
+ private static final String INDENTED_SAMPLE = " key1:\n" +
+ " subKey: val1\n" +
+ "\n" +
+ " key2: val2";
+
+ private static final String EMPTY_LINES_SAMPLE = "\n" +
+ "\n" +
+ "\n";
+
+ private final int myReformatOnPaste;
+
+ private int myDefaultReformatOnPaste = CodeInsightSettings.INDENT_EACH_LINE;
+
+ @SuppressWarnings("JUnitTestCaseWithNonTrivialConstructors")
+ YAMLPasteTest(int paste) {
+ myReformatOnPaste = paste;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ myDefaultReformatOnPaste = CodeInsightSettings.getInstance().REFORMAT_ON_PASTE;
+ CodeInsightSettings.getInstance().REFORMAT_ON_PASTE = myReformatOnPaste;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ CodeInsightSettings.getInstance().REFORMAT_ON_PASTE = myDefaultReformatOnPaste;
+ }
+ finally {
+ super.tearDown();
+ }
+ }
+
+ @Override
+ protected String getTestDataPath() {
+ return PathManagerEx.getCommunityHomePath() + "/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/";
+ }
+
+ public void testOneLinePaste() {
+ doTest(" middle");
+ }
+
+ public void testPasteValue_zeroIndent() {
+ doTest(ZERO_INDENT_SAMPLE);
+ }
+
+ public void testPasteValue_indented() {
+ doTest(INDENTED_SAMPLE);
+ }
+
+ public void testPasteValue_empty() {
+ doTest(EMPTY_LINES_SAMPLE, ".empty");
+ }
+
+ public void testPasteLines() {
+ doTest(INDENTED_SAMPLE);
+ }
+
+ public void testPasteItem_zeroIndent() {
+ doTest(ZERO_INDENT_SAMPLE);
+ }
+
+ public void testPasteItem_indented() {
+ doTest(INDENTED_SAMPLE);
+ }
+
+ public void testPasteItem_empty() {
+ doTest(EMPTY_LINES_SAMPLE, ".empty");
+ }
+
+ public void testPasteWithReplace_zeroIndent() {
+ doInsertTest(ZERO_INDENT_SAMPLE);
+ }
+
+ public void testPasteWithReplace_indented() {
+ doInsertTest(INDENTED_SAMPLE);
+ }
+
+ private void doTest(@NotNull String insert) {
+ doTest(insert, "");
+ }
+
+ private void doInsertTest(@NotNull String insert) {
+ doTest(insert, "", true);
+ }
+
+ private void doTest(@NotNull String insert, @NotNull String resultSuffix) {
+ doTest(insert, resultSuffix, false);
+ }
+
+ private void doTest(@NotNull String insert, @NotNull String resultSuffix, boolean selectWord) {
+ String testName = getTestName(true);
+ String fileName = ObjectUtils.notNull(StringUtil.substringBefore(testName, "_"), testName);
+ myFixture.configureByFile(fileName + ".yml");
+ CopyPasteManager.getInstance().setContents(new StringSelection(insert));
+ if (selectWord) {
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET);
+ }
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE);
+ myFixture.checkResultByFile(fileName + resultSuffix + ".txt");
+ }
+
+ public static class YAMLPasteNoReformatTest extends YAMLPasteTest {
+ public YAMLPasteNoReformatTest() {
+ super(CodeInsightSettings.NO_REFORMAT);
+ }
+ }
+
+ public static class YAMLPasteIndentBlockTest extends YAMLPasteTest {
+ public YAMLPasteIndentBlockTest() {
+ super(CodeInsightSettings.INDENT_BLOCK);
+ }
+ }
+
+ public static class YAMLPasteIndentEachLineTest extends YAMLPasteTest {
+ public YAMLPasteIndentEachLineTest() {
+ super(CodeInsightSettings.INDENT_EACH_LINE);
+ }
+ }
+
+ public static class YAMLPasteReformatBlockTest extends YAMLPasteTest {
+ public YAMLPasteReformatBlockTest() {
+ super(CodeInsightSettings.REFORMAT_BLOCK);
+ }
+ }
+}
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/oneLinePaste.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/oneLinePaste.txt
new file mode 100644
index 000000000000..1edea2cd07a8
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/oneLinePaste.txt
@@ -0,0 +1,3 @@
+top:
+ someKey1: start middle end
+ someKey2: value2
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/oneLinePaste.yml b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/oneLinePaste.yml
new file mode 100644
index 000000000000..c1b46a0b54e7
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/oneLinePaste.yml
@@ -0,0 +1,3 @@
+top:
+ someKey1: start end
+ someKey2: value2
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.empty.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.empty.txt
new file mode 100644
index 000000000000..633f6452c3c2
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.empty.txt
@@ -0,0 +1,6 @@
+top:
+ someKey:
+ # use double list for clear indent
+ - -
+
+
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.txt
new file mode 100644
index 000000000000..af0f0ef16c7c
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.txt
@@ -0,0 +1,7 @@
+top:
+ someKey:
+ # use double list for clear indent
+ - - key1:
+ subKey: val1
+
+ key2: val2
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.yml b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.yml
new file mode 100644
index 000000000000..fa4adff018bd
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteItem.yml
@@ -0,0 +1,4 @@
+top:
+ someKey:
+ # use double list for clear indent
+ - -
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteLines.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteLines.txt
new file mode 100644
index 000000000000..379270a549d3
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteLines.txt
@@ -0,0 +1,6 @@
+top:
+ someKey: value
+ key1:
+ subKey: val1
+
+ key2: val2
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteLines.yml b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteLines.yml
new file mode 100644
index 000000000000..bdd6c093ed30
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteLines.yml
@@ -0,0 +1,3 @@
+top:
+ someKey: value
+
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.empty.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.empty.txt
new file mode 100644
index 000000000000..f7051eea223b
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.empty.txt
@@ -0,0 +1,5 @@
+top:
+ someKey:
+
+
+
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.txt
new file mode 100644
index 000000000000..aeb7110b9c09
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.txt
@@ -0,0 +1,6 @@
+top:
+ someKey:
+ key1:
+ subKey: val1
+
+ key2: val2
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.yml b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.yml
new file mode 100644
index 000000000000..32bd4c56ad8f
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteValue.yml
@@ -0,0 +1,3 @@
+top:
+ someKey:
+
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteWithReplace.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteWithReplace.txt
new file mode 100644
index 000000000000..aeb7110b9c09
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteWithReplace.txt
@@ -0,0 +1,6 @@
+top:
+ someKey:
+ key1:
+ subKey: val1
+
+ key2: val2
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteWithReplace.yml b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteWithReplace.yml
new file mode 100644
index 000000000000..380bb643c648
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/paste/data/pasteWithReplace.yml
@@ -0,0 +1,3 @@
+top:
+ someKey:
+ someWord
\ No newline at end of file