/* * Copyright 2000-2017 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.actionSystem.IdeActions; import com.intellij.openapi.editor.ex.BidiTextDirection; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; import com.intellij.testFramework.TestFileType; import java.io.IOException; public class EditorRtlTest extends AbstractRtlTest { public void testPositionCalculations() throws IOException { prepareText("LLRR"); checkOffsetConversions(0, lB(0), vL(0), vR(0), xy(0)); checkOffsetConversions(1, lB(1), vL(1), vR(1), xy(10)); checkOffsetConversions(2, lB(2), vL(2), vL(4), xy(20), xy(40)); checkOffsetConversions(3, lB(3), vR(3), vL(3), xy(30)); checkOffsetConversions(4, lB(4), vR(2), vR(4), xy(20), xy(40)); checkLPConversions(0, 0, vL(0), vR(0)); checkLPConversions(1, 1, vL(1), vR(1)); checkLPConversions(2, 2, vL(2), vL(4)); checkLPConversions(3, 3, vR(3), vL(3)); checkLPConversions(4, 4, vR(2), vR(4)); checkLPConversions(5, 4, vL(5), vR(5)); checkLPConversions(lB(1, 0), 4, vL(1, 0)); checkLPConversions(lF(1, 0), 4, vR(1, 0)); checkVPConversions(vL(0), lB(0), xy(0)); checkVPConversions(vR(0), lF(0), xy(0)); checkVPConversions(vL(1), lB(1), xy(10)); checkVPConversions(vR(1), lF(1), xy(10)); checkVPConversions(vL(2), lB(2), xy(20)); checkVPConversions(vR(2), lB(4), xy(20)); checkVPConversions(vL(3), lF(3), xy(30)); checkVPConversions(vR(3), lB(3), xy(30)); checkVPConversions(vL(4), lF(2), xy(40)); checkVPConversions(vR(4), lF(4), xy(40)); checkVPConversions(vL(5), lF(5), xy(50)); checkVPConversions(vR(5), lF(5), xy(50)); checkVPConversions(vL(1, 0), lB(1, 0), xy(0, 10)); checkVPConversions(vR(1, 0), lF(1, 0), xy(0, 10)); checkXYConversion(xy(0), vL(0)); checkXYConversion(xy(12), vR(1)); checkXYConversion(xy(19), vL(2)); checkXYConversion(xy(21), vR(2)); checkXYConversion(xy(27), vL(3)); checkXYConversion(xy(39), vL(4)); checkXYConversion(xy(41), vR(4)); checkXYConversion(xy(51), vR(5)); } public void testNumberInsideRtlText() throws IOException { prepareText("RR12RR"); checkOffsetConversions(0, lB(0), vL(0), vL(6), xy(0), xy(60)); checkOffsetConversions(1, lB(1), vR(5), vL(5), xy(50)); checkOffsetConversions(2, lB(2), vR(4), vR(2), xy(40), xy(20)); checkOffsetConversions(3, lB(3), vL(3), vR(3), xy(30)); checkOffsetConversions(4, lB(4), vL(4), vL(2), xy(40), xy(20)); checkOffsetConversions(5, lB(5), vR(1), vL(1), xy(10)); checkOffsetConversions(6, lB(6), vR(0), vR(6), xy(0), xy(60)); checkLPConversions(0, 0, vL(0), vL(6)); checkLPConversions(1, 1, vR(5), vL(5)); checkLPConversions(2, 2, vR(4), vR(2)); checkLPConversions(3, 3, vL(3), vR(3)); checkLPConversions(4, 4, vL(4), vL(2)); checkLPConversions(5, 5, vR(1), vL(1)); checkLPConversions(6, 6, vR(0), vR(6)); checkVPConversions(vL(0), lB(0), xy(0)); checkVPConversions(vR(0), lB(6), xy(0)); checkVPConversions(vL(1), lF(5), xy(10)); checkVPConversions(vR(1), lB(5), xy(10)); checkVPConversions(vL(2), lF(4), xy(20)); checkVPConversions(vR(2), lF(2), xy(20)); checkVPConversions(vL(3), lB(3), xy(30)); checkVPConversions(vR(3), lF(3), xy(30)); checkVPConversions(vL(4), lB(4), xy(40)); checkVPConversions(vR(4), lB(2), xy(40)); checkVPConversions(vL(5), lF(1), xy(50)); checkVPConversions(vR(5), lB(1), xy(50)); checkVPConversions(vL(6), lF(0), xy(60)); checkVPConversions(vR(6), lF(6), xy(60)); checkXYConversion(xy(1), vR(0)); checkXYConversion(xy(9), vL(1)); checkXYConversion(xy(19), vL(2)); checkXYConversion(xy(21), vR(2)); checkXYConversion(xy(30), vL(3)); checkXYConversion(xy(39), vL(4)); checkXYConversion(xy(41), vR(4)); checkXYConversion(xy(50), vL(5)); checkXYConversion(xy(59), vL(6)); checkXYConversion(xy(61), vR(6)); } public void testFolding() throws IOException { prepareText("RRRRRR"); addCollapsedFoldRegion(2, 4, "..."); checkOffsetConversions(0, lB(0), vL(0), vL(2), xy(0), xy(20)); checkOffsetConversions(1, lB(1), vR(1), vL(1), xy(10)); checkOffsetConversions(2, lB(2), vR(0), vR(2), xy(0), xy(20)); checkOffsetConversions(3, lB(3), vR(2), vR(2), xy(20)); checkOffsetConversions(4, lB(4), vL(5), vL(7), xy(50), xy(70)); checkOffsetConversions(5, lB(5), vR(6), vL(6), xy(60)); checkOffsetConversions(6, lB(6), vR(5), vR(7), xy(50), xy(70)); checkLPConversions(0, 0, vL(0), vL(2)); checkLPConversions(1, 1, vR(1), vL(1)); checkLPConversions(2, 2, vR(0), vR(2)); checkLPConversions(3, 3, vR(2), vR(2)); checkLPConversions(4, 4, vL(5), vL(7)); checkLPConversions(5, 5, vR(6), vL(6)); checkLPConversions(6, 6, vR(5), vR(7)); checkLPConversions(7, 6, vL(8), vR(8)); checkVPConversions(vL(0), lB(0), xy(0)); checkVPConversions(vR(0), lB(2), xy(0)); checkVPConversions(vL(1), lF(1), xy(10)); checkVPConversions(vR(1), lB(1), xy(10)); checkVPConversions(vL(2), lF(0), xy(20)); checkVPConversions(vR(2), lF(2), xy(20)); checkVPConversions(vL(3), lF(2), xy(30)); checkVPConversions(vR(3), lF(2), xy(30)); checkVPConversions(vL(4), lF(2), xy(40)); checkVPConversions(vR(4), lF(2), xy(40)); checkVPConversions(vL(5), lB(4), xy(50)); checkVPConversions(vR(5), lB(6), xy(50)); checkVPConversions(vL(6), lF(5), xy(60)); checkVPConversions(vR(6), lB(5), xy(60)); checkVPConversions(vL(7), lF(4), xy(70)); checkVPConversions(vR(7), lF(6), xy(70)); checkVPConversions(vL(8), lF(7), xy(80)); checkVPConversions(vR(8), lF(7), xy(80)); checkXYConversion(xy(1), vR(0)); checkXYConversion(xy(9), vL(1)); checkXYConversion(xy(19), vL(2)); checkXYConversion(xy(21), vR(2)); checkXYConversion(xy(30), vL(3)); checkXYConversion(xy(39), vL(4)); checkXYConversion(xy(41), vR(4)); checkXYConversion(xy(50), vL(5)); checkXYConversion(xy(59), vL(6)); checkXYConversion(xy(69), vL(7)); checkXYConversion(xy(71), vR(7)); checkXYConversion(xy(81), vR(8)); } public void testFoldingWhenLineContainsSeveralFragments() throws IOException { prepareText("R R"); addCollapsedFoldRegion(1, 2, "..."); checkOffsetConversions(0, lB(0), vL(0), vL(1), xy(0), xy(10)); checkOffsetConversions(1, lB(1), vR(0), vR(1), xy(0), xy(10)); checkOffsetConversions(2, lB(2), vL(4), vL(5), xy(40), xy(50)); checkOffsetConversions(3, lB(3), vR(4), vR(5), xy(40), xy(50)); } public void testFoldingInInnerBidiRun() throws IOException { prepareText("LRRL"); addCollapsedFoldRegion(1, 2, "..."); checkLPConversions(0, 0, vL(0), vR(0)); checkLPConversions(1, 1, vL(1), vR(1)); checkLPConversions(2, 2, vL(4), vL(5)); checkLPConversions(3, 3, vR(4), vR(5)); checkLPConversions(4, 4, vL(6), vR(6)); } public void testPositionCalculationsWithSoftWraps() throws Exception { prepareText("RR RR"); configureSoftWraps(3); checkOffsetConversions(0, lB(0), vL(0), vL(3), xy(0), xy(30)); checkOffsetConversions(1, lB(1), vR(2), vL(2), xy(20)); checkOffsetConversions(2, lB(2), vR(1), vL(1), xy(10)); checkOffsetConversions(3, lB(3), vL(1, 1), vL(1, 3), xy(10, 10), xy(30, 10)); checkOffsetConversions(4, lB(4), vR(1, 2), vL(1, 2), xy(20, 10)); checkOffsetConversions(5, lB(5), vR(1, 1), vR(1, 3), xy(10, 10), xy(30, 10)); checkOffsetConversions(6, lB(5), vR(1, 1), vR(1, 3), xy(10, 10), xy(30, 10)); checkLPConversions(0, 0, vL(0), vL(3)); checkLPConversions(1, 1, vR(2), vL(2)); checkLPConversions(2, 2, vR(1), vL(1)); checkLPConversions(3, 3, vL(1, 1), vL(1, 3)); checkLPConversions(4, 4, vR(1, 2), vL(1, 2)); checkLPConversions(5, 5, vR(1, 1), vR(1, 3)); checkLPConversions(6, 5, vL(1, 4), vR(1, 4)); checkLPConversions(lB(1, 0), 5, vL(2, 0)); checkLPConversions(lF(1, 0), 5, vR(2, 0)); checkLPConversions(lB(1, 1), 5, vL(2, 1)); checkLPConversions(lF(1, 1), 5, vR(2, 1)); checkVPConversions(0, lB(0), lB(3), xy(0)); checkVPConversions(1, lF(2), lB(2), xy(10)); checkVPConversions(2, lF(1), lB(1), xy(20)); checkVPConversions(3, lF(0), lF(3), xy(30)); checkVPConversions(4, lF(3), lF(3), xy(40)); checkVPConversions(5, lF(3), lF(3), xy(50)); checkVPConversions(vL(1, 0), lB(3), xy(0, 10)); checkVPConversions(vR(1, 0), lB(3), xy(0, 10)); checkVPConversions(vL(1, 1), lB(3), xy(10, 10)); checkVPConversions(vR(1, 1), lB(5), xy(10, 10)); checkVPConversions(vL(1, 2), lF(4), xy(20, 10)); checkVPConversions(vR(1, 2), lB(4), xy(20, 10)); checkVPConversions(vL(1, 3), lF(3), xy(30, 10)); checkVPConversions(vR(1, 3), lF(5), xy(30, 10)); checkVPConversions(vL(1, 4), lF(6), xy(40, 10)); checkVPConversions(vR(1, 4), lF(6), xy(40, 10)); checkXYConversion(xy(0), vL(0)); checkXYConversion(xy(2), vR(0)); checkXYConversion(xy(9), vL(1)); checkXYConversion(xy(13), vR(1)); checkXYConversion(xy(18), vL(2)); checkXYConversion(xy(24), vR(2)); checkXYConversion(xy(30), vL(3)); checkXYConversion(xy(31), vR(3)); checkXYConversion(xy(39), vL(4)); checkXYConversion(xy(42), vR(4)); checkXYConversion(xy(47), vL(5)); checkXYConversion(xy(54), vR(5)); checkXYConversion(xy(0, 10), vL(1, 0)); checkXYConversion(xy(2, 10), vR(1, 0)); checkXYConversion(xy(9, 10), vL(1, 1)); checkXYConversion(xy(11, 10), vR(1, 1)); checkXYConversion(xy(18, 10), vL(1, 2)); checkXYConversion(xy(23, 10), vR(1, 2)); checkXYConversion(xy(30, 10), vL(1, 3)); checkXYConversion(xy(34, 10), vR(1, 3)); checkXYConversion(xy(37, 10), vL(1, 4)); checkXYConversion(xy(41, 10), vR(1, 4)); } public void testSelectingRtlLineByDraggingMouseFromLeftToRight() throws IOException { prepareText("R"); setEditorVisibleSize(1000, 1000); mouse().pressAtXY(0, 5).dragToXY(15, 5).release(); checkResult("<selection>R</selection>"); } public void testSelectingRtlLineByDraggingMouseFromRightToLeft() throws IOException { prepareText("R"); setEditorVisibleSize(1000, 1000); mouse().pressAtXY(15, 5).dragToXY(0, 5).release(); checkResult("<selection>R</selection>"); } public void testMovingCaretToLogicalLineEnd() throws IOException { prepareText("R"); myEditor.getCaretModel().moveToLogicalPosition(lF(1)); assertVisualPositionsEqual("Wrong visual position", vR(1), myEditor.getCaretModel().getVisualPosition()); } public void testMovingCaretToVisualLineEnd() throws IOException { prepareText("R"); myEditor.getCaretModel().moveToVisualPosition(vR(1)); assertLogicalPositionsEqual("Wrong logical position", lF(1), myEditor.getCaretModel().getLogicalPosition()); } public void testNavigationWithArrowKeys() throws Exception { prepareText("LLRRLL\nLLRRLL"); assertCaretPosition(vL(0)); right(); assertCaretPosition(vL(1)); right(); assertCaretPosition(vL(2)); right(); assertCaretPosition(vR(2)); right(); assertCaretPosition(vL(3)); right(); assertCaretPosition(vL(4)); down(); assertCaretPosition(vL(1, 4)); left(); assertCaretPosition(vR(1, 3)); left(); assertCaretPosition(vR(1, 2)); up(); assertCaretPosition(vR(2)); } public void testMovingIntoVirtualSpace() throws Exception { prepareText("R"); myEditor.getSettings().setVirtualSpace(true); assertVisualCaretLocation(0, false); right(); assertVisualCaretLocation(0, true); right(); assertVisualCaretLocation(1, true); right(); assertVisualCaretLocation(1, false); right(); assertVisualCaretLocation(2, false); right(); assertVisualCaretLocation(3, false); } public void testMovingThroughFoldedRegion() throws Exception { prepareText("RRR"); addCollapsedFoldRegion(1, 2, ".."); assertVisualCaretLocation(0, false); right(); assertVisualCaretLocation(0, true); right(); assertVisualCaretLocation(1, true); right(); assertVisualCaretLocation(1, true); right(); assertVisualCaretLocation(2, true); right(); assertVisualCaretLocation(3, true); right(); assertVisualCaretLocation(3, true); right(); assertVisualCaretLocation(4, true); } public void testMovingCaretWhenSelectionExists() throws Exception { prepareText("RRR"); right(); right(); executeAction(IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION); checkResult("R<selection>R</selection>R"); right(); assertVisualCaretLocation(2, true); } public void testMovingCaretWhenSelectionStartsOnDirectionBoundary() throws Exception { prepareText("LR"); right(); right(); executeAction(IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION); right(); assertVisualCaretLocation(2, true); } public void testMoveCaretWithSelection() throws Exception { prepareText("LR"); executeAction(IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION); executeAction(IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION); checkResult("<selection>LR</selection>"); } public void testNextPrevWord() throws Exception { prepareText("LL RR RR LL"); nextWord(); assertVisualCaretLocation(3, false); nextWord(); assertVisualCaretLocation(3, true); nextWord(); assertVisualCaretLocation(5, true); nextWord(); assertVisualCaretLocation(8, true); nextWord(); assertVisualCaretLocation(8, false); nextWord(); assertVisualCaretLocation(9, false); nextWord(); assertVisualCaretLocation(11, false); previousWord(); assertVisualCaretLocation(9, false); previousWord(); assertVisualCaretLocation(8, false); previousWord(); assertVisualCaretLocation(8, true); previousWord(); assertVisualCaretLocation(5, true); previousWord(); assertVisualCaretLocation(3, true); previousWord(); assertVisualCaretLocation(3, false); previousWord(); assertVisualCaretLocation(0, false); } public void testNextPrevWordWithSelection() throws Exception { prepareText("LL RR RR LL"); moveCaretToNextWordWithSelection(); checkResult("<selection>LL </selection>RR RR LL"); moveCaretToNextWordWithSelection(); checkResult("<selection>LL RR RR</selection> LL"); moveCaretToNextWordWithSelection(); checkResult("<selection>LL RR </selection>RR LL"); moveCaretToNextWordWithSelection(); checkResult("<selection>LL </selection>RR RR LL"); moveCaretToNextWordWithSelection(); checkResult("<selection>LL RR RR</selection> LL"); moveCaretToNextWordWithSelection(); checkResult("<selection>LL RR RR </selection>LL"); moveCaretToNextWordWithSelection(); checkResult("<selection>LL RR RR LL</selection>"); moveCaretToPreviousWordWithSelection(); checkResult("<selection>LL RR RR </selection>LL"); moveCaretToPreviousWordWithSelection(); checkResult("<selection>LL RR RR</selection> LL"); moveCaretToPreviousWordWithSelection(); checkResult("<selection>LL </selection>RR RR LL"); moveCaretToPreviousWordWithSelection(); checkResult("<selection>LL RR </selection>RR LL"); moveCaretToPreviousWordWithSelection(); checkResult("<selection>LL RR RR</selection> LL"); moveCaretToPreviousWordWithSelection(); checkResult("<selection>LL </selection>RR RR LL"); moveCaretToPreviousWordWithSelection(); checkResult("<selection></selection>LL RR RR LL"); } public void testHomeEnd() throws Exception { prepareText("RR"); myEditor.getCaretModel().moveToOffset(1); home(); assertVisualCaretLocation(0, false); end(); assertVisualCaretLocation(2, false); } public void testHomeEndWithSelection() throws Exception { prepareText("RR"); myEditor.getCaretModel().moveToOffset(1); homeWithSelection(); assertVisualCaretLocation(0, false); checkResult("<selection>R</selection>R"); endWithSelection(); assertVisualCaretLocation(2, false); checkResult("R<selection>R</selection>"); } public void testTextStartEnd() throws Exception { prepareText("RR"); myEditor.getCaretModel().moveToOffset(1); executeAction(IdeActions.ACTION_EDITOR_TEXT_START); assertVisualCaretLocation(0, false); executeAction(IdeActions.ACTION_EDITOR_TEXT_END); assertVisualCaretLocation(2, false); } public void testTextStartEndWithSelection() throws Exception { prepareText("RR"); myEditor.getCaretModel().moveToOffset(1); executeAction(IdeActions.ACTION_EDITOR_TEXT_START_WITH_SELECTION); assertVisualCaretLocation(0, false); checkResult("<selection>R</selection>R"); executeAction(IdeActions.ACTION_EDITOR_TEXT_END_WITH_SELECTION); assertVisualCaretLocation(2, false); checkResult("R<selection>R</selection>"); } public void testUsingLexerForBidiLayout() throws Exception { prepare("class Foo {\n int<caret> R = 1;\n}", TestFileType.JAVA); right(); right(); checkResult("class Foo {\n int R<caret> = 1;\n}"); } public void testJavadocTokensAreMergedForBidiLayoutPurposes() throws Exception { prepare("<caret>/**R R*/ class Foo {}", TestFileType.JAVA); for (int i = 0; i < 4; i++) { right(); } checkResult("/**R R<caret>*/ class Foo {}"); } public void testXmlTextIsLaidOutCorrectly() throws Exception { prepare("<caret><L>R R</L>", TestFileType.XML); for (int i = 0; i < 4; i++) { right(); } checkResult("<L>R R<caret></L>"); } public void testMovingThroughBoundaryBetweenRunsWithNonadjacentLevels() throws Exception { prepareText("R=1"); right(); assertVisualCaretLocation(0, false); assertEquals(2, myEditor.getCaretModel().getOffset()); right(); assertVisualCaretLocation(1, false); assertEquals(3, myEditor.getCaretModel().getOffset()); right(); assertVisualCaretLocation(1, true); assertEquals(2, myEditor.getCaretModel().getOffset()); } public void testMovingCaretThroughSoftWrap() throws Exception { prepareText("RR RR"); configureSoftWraps(3); right(); assertVisualCaretLocation(0, true); right(); assertVisualCaretLocation(1, true); right(); assertVisualCaretLocation(2, true); right(); assertVisualCaretLocation(3, true); right(); assertVisualCaretLocation(3, false); right(); assertVisualCaretLocation(1, 1, false); right(); assertVisualCaretLocation(1, 1, true); right(); assertVisualCaretLocation(1, 2, true); right(); assertVisualCaretLocation(1, 3, true); } public void testLineGeneralDirectionAutodetection() throws Exception { prepareText("<caret>RLR"); right(); checkResult("RLR<caret>"); } public void testTabInsideRtlText() throws Exception { prepareText("<caret>R\tRR"); right(); checkResult("R<caret>\tRR"); right(); checkResult("<caret>R\tRR"); right(); checkResult("R<caret>\tRR"); right(); checkResult("R\t<caret>RR"); right(); checkResult("R\tRR<caret>"); right(); checkResult("R\tR<caret>R"); right(); checkResult("R\t<caret>RR"); right(); checkResult("R\tRR<caret>"); } public void testEscapedBackslashInStringLiteral() throws Exception { prepare("class C {\n String s = \"R\\<caret>\\R\";\n}", TestFileType.JAVA); right(); right(); checkResult("class C {\n String s = \"<caret>R\\\\R\";\n}"); } public void testRtlLayoutPersistsAfterEditing() throws Exception { prepare("<caret>\nRR", TestFileType.TEXT); delete(); checkResult("<caret>RR"); assertTrue(myEditor.getCaretModel().getPrimaryCaret().isAtBidiRunBoundary()); } public void testTokenOrderIsAlwaysLtr() throws Exception { BidiTextDirection savedValue = EditorSettingsExternalizable.getInstance().getBidiTextDirection(); try { EditorSettingsExternalizable.getInstance().setBidiTextDirection(BidiTextDirection.RTL); prepare("<a>R</a>", TestFileType.XML); right(); checkResult("<<caret>a>R</a>"); } finally { EditorSettingsExternalizable.getInstance().setBidiTextDirection(savedValue); } } public void testLineCommentLayout() throws Exception { prepare("<caret>// R", TestFileType.JAVA); right(); checkResult("/<caret>/ R"); } }