/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.editor.impl;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.impl.view.FontLayoutService;
import com.intellij.testFramework.EditorTestUtil;
import com.intellij.testFramework.LightPlatformCodeInsightTestCase;
import com.intellij.testFramework.MockFontLayoutService;
import com.intellij.testFramework.TestFileType;
import com.intellij.testFramework.fixtures.EditorMouseFixture;
import com.intellij.util.ThrowableRunnable;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.junit.Assert.assertArrayEquals;
/**
* Base super class for tests that check various IJ editor functionality on managed document modification.
* <p/>
* It's main purpose is to provide utility methods like fold regions addition and setup; typing etc.
*
* @author Denis Zhdanov
* @since 11/18/10 7:43 PM
*/
public abstract class AbstractEditorTest extends LightPlatformCodeInsightTestCase {
public static final int TEST_CHAR_WIDTH = 10; // char width matches the one in EditorTestUtil.configureSoftWraps
public static final int TEST_LINE_HEIGHT = 10;
public static final int TEST_DESCENT = 2;
public static final String LOREM_IPSUM =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
public static final String SURROGATE_PAIR = new String(new int[]{Character.MIN_SUPPLEMENTARY_CODE_POINT}, 0, 1);
public static final String HIGH_SURROGATE = SURROGATE_PAIR.substring(0, 1);
public static final String LOW_SURROGATE = SURROGATE_PAIR.substring(1, 2);
@Override
protected void setUp() throws Exception {
super.setUp();
FontLayoutService.setInstance(new MockFontLayoutService(TEST_CHAR_WIDTH, TEST_LINE_HEIGHT, TEST_DESCENT));
}
@Override
protected void tearDown() throws Exception {
try {
FontLayoutService.setInstance(null);
}
finally {
super.tearDown();
}
}
protected void initText(@NotNull @NonNls String fileText) throws IOException {
init(fileText, TestFileType.TEXT);
}
protected void init(@NotNull @NonNls String fileText, @NotNull TestFileType type) throws IOException {
configureFromFileText(getFileName(type), fileText);
}
private String getFileName(TestFileType type) {
return getTestName(false) + type.getExtension();
}
protected static FoldRegion addFoldRegion(final int startOffset, final int endOffset, final String placeholder) {
final FoldRegion[] result = new FoldRegion[1];
myEditor.getFoldingModel().runBatchFoldingOperation(
() -> result[0] = myEditor.getFoldingModel().addFoldRegion(startOffset, endOffset, placeholder));
return result[0];
}
protected static FoldRegion addCollapsedFoldRegion(final int startOffset, final int endOffset, final String placeholder) {
FoldRegion region = addFoldRegion(startOffset, endOffset, placeholder);
toggleFoldRegionState(region, false);
return region;
}
protected static void toggleFoldRegionState(final FoldRegion foldRegion, final boolean expanded) {
myEditor.getFoldingModel().runBatchFoldingOperation(() -> foldRegion.setExpanded(expanded));
}
protected static FoldRegion getFoldRegion(int startOffset) {
FoldRegion[] foldRegions = myEditor.getFoldingModel().getAllFoldRegions();
for (FoldRegion foldRegion : foldRegions) {
if (foldRegion.getStartOffset() == startOffset) {
return foldRegion;
}
}
throw new IllegalArgumentException(String.format(
"Can't find fold region with start offset %d. Registered fold regions: %s. Document text: '%s'",
startOffset, Arrays.toString(foldRegions), myEditor.getDocument().getCharsSequence()
));
}
protected static void foldOccurrences(String textToFoldRegexp, final String placeholder) {
final Matcher matcher = Pattern.compile(textToFoldRegexp).matcher(myEditor.getDocument().getCharsSequence());
myEditor.getFoldingModel().runBatchFoldingOperation(() -> {
while (matcher.find()) {
FoldRegion foldRegion = myEditor.getFoldingModel().addFoldRegion(matcher.start(), matcher.end(), placeholder);
assertNotNull(foldRegion);
foldRegion.setExpanded(false);
}
});
}
public void assertSelectionRanges(int[][] ranges) {
int[] selectionStarts = myEditor.getSelectionModel().getBlockSelectionStarts();
int[] selectionEnds = myEditor.getSelectionModel().getBlockSelectionEnds();
int actualRangeCount = selectionStarts.length;
int[][] actualRanges = new int[actualRangeCount][];
for (int i = 0; i < actualRangeCount; i++) {
actualRanges[i] = new int[] {selectionStarts[i], selectionEnds[i]};
}
assertEquals("Wrong selected ranges", Arrays.deepToString(ranges), Arrays.deepToString(actualRanges));
}
public EditorMouseFixture mouse() {
return new EditorMouseFixture((EditorImpl)myEditor);
}
public void setEditorVisibleSize(int widthInChars, int heightInChars) {
EditorTestUtil.setEditorVisibleSize(myEditor, widthInChars, heightInChars);
}
/**
* Verifies visual positions of carets and their selection ranges. It's assumed that for each caret its position and selection range
* are within the same visual line.
*
* For each caret its visual position and visual positions of selection start an and should be provided in the following order:
* line, caretColumn, selectionStartColumn, selectionEndColumn
*/
public static void verifyCaretsAndSelections(int... coordinates) {
int caretCount = coordinates.length / 4;
List<Caret> carets = myEditor.getCaretModel().getAllCarets();
assertEquals("Unexpected caret count", caretCount, carets.size());
for (int i = 0; i < caretCount; i++) {
Caret caret = carets.get(i);
assertEquals("Unexpected position for caret " + (i + 1), new VisualPosition(coordinates[i * 4], coordinates[i * 4 + 1]), caret.getVisualPosition());
assertEquals("Unexpected selection start for caret " + (i + 1), new VisualPosition(coordinates[i * 4], coordinates[i * 4 + 2]), caret.getSelectionStartPosition());
assertEquals("Unexpected selection end for caret " + (i + 1), new VisualPosition(coordinates[i * 4], coordinates[i * 4 + 3]), caret.getSelectionEndPosition());
}
}
public static void verifySoftWrapPositions(Integer... positions) {
List<Integer> softWrapPositions = new ArrayList<>();
for (SoftWrap softWrap : myEditor.getSoftWrapModel().getSoftWrapsForRange(0, myEditor.getDocument().getTextLength())) {
softWrapPositions.add(softWrap.getStart());
}
assertArrayEquals(positions, softWrapPositions.toArray());
}
protected static void configureSoftWraps(int charCountToWrapAt) {
EditorTestUtil.configureSoftWraps(myEditor, charCountToWrapAt);
}
public static Inlay addInlay(int offset) {
return EditorTestUtil.addInlay(myEditor, offset);
}
protected static void runWriteCommand(ThrowableRunnable r) {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
r.run();
}
}.execute();
}
protected static void runFoldingOperation(Runnable r) {
myEditor.getFoldingModel().runBatchFoldingOperation(r);
}
}