diff --git a/platform/testFramework/src/com/intellij/testFramework/LiveTemplateTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/LiveTemplateTestUtil.java
new file mode 100644
index 000000000000..07dc83699c30
--- /dev/null
+++ b/platform/testFramework/src/com/intellij/testFramework/LiveTemplateTestUtil.java
@@ -0,0 +1,111 @@
+// Copyright 2000-2020 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 com.intellij.testFramework;
+
+import com.intellij.codeInsight.template.TemplateActionContext;
+import com.intellij.codeInsight.template.impl.TemplateImpl;
+import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
+import com.intellij.codeInsight.template.impl.TemplateSettings;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.SelectionModel;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiFile;
+import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
+import com.intellij.util.containers.ContainerUtil;
+import junit.framework.TestCase;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LiveTemplateTestUtil {
+ /**
+ * Moves caret though opened file from start to the end and checks if template with {@code templateKey} from the {@code templateGroup} is
+ * available in every position. Marks up opened file with {@code } and {@code } tags for the ranges where template
+ * is available according to configured contexts and compares result with file from {@code answerFilePath}
+ */
+ public static void doTestTemplateExpandingAvailability(@NotNull CodeInsightTestFixture codeInsightTestFixture,
+ @NotNull String templateKey,
+ @NotNull String templateGroup,
+ @NotNull String answerFilePath) {
+ TemplateImpl template = TemplateSettings.getInstance().getTemplate(templateKey, templateGroup);
+ TestCase.assertNotNull("Unable to find template: " + templateKey + "@" + templateGroup, template);
+
+ Editor editor = codeInsightTestFixture.getEditor();
+ Document document = editor.getDocument();
+ CharSequence documentCharsSequence = document.getCharsSequence();
+ CaretModel caretModel = editor.getCaretModel();
+ PsiFile psiFile = codeInsightTestFixture.getFile();
+ boolean isAvailable = false;
+ List> markers = new ArrayList<>();
+ for (int offset = 0; offset < documentCharsSequence.length(); offset++) {
+ caretModel.moveToOffset(offset);
+ TemplateActionContext templateActionContext = TemplateActionContext.expanding(psiFile, editor);
+ if (TemplateManagerImpl.isApplicable(template, templateActionContext)) {
+ if (!isAvailable) {
+ isAvailable = true;
+ markers.add(Pair.create(offset, ""));
+ }
+ }
+ else if (isAvailable) {
+ markers.add(Pair.create(offset - 1, ""));
+ isAvailable = false;
+ }
+ }
+ if (isAvailable) {
+ markers.add(Pair.create(documentCharsSequence.length(), ""));
+ }
+ StringBuilder result = new StringBuilder(documentCharsSequence);
+ ContainerUtil.reverse(markers).forEach(it -> result.insert(it.first, it.second));
+ UsefulTestCase.assertSameLinesWithFile(answerFilePath, result.toString());
+ }
+
+ /**
+ * Makes a selection in editor from every offset to the caret and checks if if template with {@code templateKey} from the
+ * {@code templateGroup} is available. Marks up opened file with {@code } and {@code } tags for the ranges where
+ * template is available according to configured contexts and compares result with file from {@code answerFilePath}
+ */
+ public static void doTestTemplateSurroundingAvailability(@NotNull CodeInsightTestFixture fixture,
+ @NotNull String templateKey,
+ @NotNull String templateGroup,
+ @NotNull String answerFilePath) {
+ TemplateImpl template = TemplateSettings.getInstance().getTemplate(templateKey, templateGroup);
+ String templateId = templateKey + "@" + templateGroup;
+ TestCase.assertNotNull("Unable to find template: " + templateId, template);
+ TestCase.assertTrue(templateId + " is not a selection template", template.isSelectionTemplate());
+
+ Editor editor = fixture.getEditor();
+ SelectionModel selectionModel = editor.getSelectionModel();
+ Document document = editor.getDocument();
+ CharSequence documentCharsSequence = document.getCharsSequence();
+ CaretModel caretModel = editor.getCaretModel();
+ int caretOffset = caretModel.getOffset();
+ PsiFile psiFile = fixture.getFile();
+ boolean isAvailable = false;
+ List> markers = new ArrayList<>();
+ for (int offset = 0; offset < documentCharsSequence.length(); offset++) {
+ selectionModel.setSelection(Math.min(offset, caretOffset), Math.max(offset, caretOffset));
+ if (caretOffset == offset) {
+ markers.add(Pair.create(offset, ""));
+ }
+ TemplateActionContext templateActionContext = TemplateActionContext.surrounding(psiFile, editor);
+ if (TemplateManagerImpl.isApplicable(template, templateActionContext)) {
+ if (!isAvailable) {
+ isAvailable = true;
+ markers.add(Pair.create(offset, ""));
+ }
+ }
+ else if (isAvailable) {
+ markers.add(Pair.create(offset - 1, ""));
+ isAvailable = false;
+ }
+ }
+ if (isAvailable) {
+ markers.add(Pair.create(documentCharsSequence.length(), ""));
+ }
+ StringBuilder result = new StringBuilder(documentCharsSequence);
+ ContainerUtil.reverse(markers).forEach(it -> result.insert(it.first, it.second));
+ UsefulTestCase.assertSameLinesWithFile(answerFilePath, result.toString());
+ }
+}