| /******************************************************************************* |
| * Copyright (c) 2022 Dirk Steinkamp |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Dirk Steinkamp <dirk.steinkamp@gmx.de> - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.editors.tests; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.util.Collections; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.widgets.Control; |
| |
| import org.eclipse.core.commands.Command; |
| import org.eclipse.core.commands.ExecutionEvent; |
| import org.eclipse.core.filesystem.EFS; |
| |
| import org.eclipse.core.runtime.CoreException; |
| |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IMultiTextSelection; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.MultiTextSelection; |
| import org.eclipse.jface.text.Region; |
| |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.commands.ICommandService; |
| import org.eclipse.ui.ide.IDE; |
| |
| import org.eclipse.ui.texteditor.AbstractTextEditor; |
| |
| /* |
| * Note: this test would better fit in the org.eclipse.ui.workbench.texteditor bundle, but initializing |
| * an editor from this bundle is quite tricky without the IDE and EFS utils. |
| */ |
| public class TextMultiCaretSelectionCommandsTest { |
| private static final String MULTI_SELECTION_DOWN = "org.eclipse.ui.edit.text.select.selectMultiSelectionDown"; |
| private static final String ADD_ALL_MATCHES_TO_MULTI_SELECTION = "org.eclipse.ui.edit.text.select.addAllMatchesToMultiSelection"; |
| private static final String MULTI_SELECTION_UP = "org.eclipse.ui.edit.text.select.selectMultiSelectionUp"; |
| private static final String STOP_MULTI_SELECTION = "org.eclipse.ui.edit.text.select.stopMultiSelection"; |
| |
| private static final String LINE_1 = "private static String a;\n"; |
| private static final String LINE_2 = "private static String b;\n"; |
| private static final String LINE_3 = "private static String c;\n"; |
| private static final String LINE_4 = "private static String d"; |
| |
| private static final int L1_LEN = LINE_1.length(); |
| private static final int L2_LEN = LINE_2.length(); |
| private static final int L3_LEN = LINE_3.length(); |
| private static final int L4_LEN = LINE_4.length(); |
| |
| private static File file; |
| private static AbstractTextEditor editor; |
| private static StyledText widget; |
| |
| @Before |
| public void setUpBeforeClass() throws IOException, PartInitException, CoreException { |
| file = File.createTempFile(TextMultiCaretSelectionCommandsTest.class.getName(), ".txt"); |
| Files.write(file.toPath(), (LINE_1 + LINE_2 + LINE_3 + LINE_4) // |
| .getBytes()); |
| editor = (AbstractTextEditor) IDE.openEditorOnFileStore( |
| PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), EFS.getStore(file.toURI())); |
| widget = (StyledText) editor.getAdapter(Control.class); |
| } |
| |
| @After |
| public void tearDown() { |
| editor.dispose(); |
| file.delete(); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withFirstIdentifierSelected_addsIdenticalIdentifiersToSelection() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(0, 7) }); |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7) }, getSelection()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN, 7) }, |
| getSelection()); |
| |
| executeCommand(STOP_MULTI_SELECTION); |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(7, 0) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withSecondIdentifierSelectedIdentifier_addsNextOccurenceToSelection() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(8, 6) }); |
| assertEquals(14, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(14, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withSelectionInSecondRow_addsIdenticalIdentifierInThirdRowToSelection() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(L1_LEN + 8, 6) }); |
| assertEquals(L1_LEN + 14, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(L1_LEN + 14, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 8, 6) }, |
| getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withTwoSelectionsAndAnchorBelow_reducesSelection() throws Exception { |
| setSelection(new IRegion[] { new Region(L1_LEN + 8, 6) }); |
| // It is important here to build up the selection in steps, so the |
| // handler can determine an anchor region |
| executeCommand(MULTI_SELECTION_UP); |
| assertArrayEquals(new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6) }, getSelection()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withTwoSelectionsAndAnchorAbove_extendsSelection() throws Exception { |
| setSelection(new IRegion[] { new Region(L1_LEN + 8, 6) }); |
| // It is important here to build up the selection in steps, so the |
| // handler can determine an anchor region |
| executeCommand(MULTI_SELECTION_DOWN); |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 8, 6) }, |
| getSelection()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 8, 6), |
| new Region(L1_LEN + L2_LEN + L3_LEN + 8, 6) }, getSelection()); |
| } |
| |
| // Caret-related tests for ADD_NEXT_MATCH_TO_MULTI_SELECTION |
| // that check how the selection is expanded |
| |
| @Test |
| public void testMultiSelectionDown_withCaretInFirstIdentifier_selectsFullIdentifier() throws Exception { |
| setSelection(new IRegion[] { new Region(1, 0) }); |
| assertEquals(1, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(0, 7) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withCaretInSecondIdentifier_selectsFullIdentifier() throws Exception { |
| setSelection(new IRegion[] { new Region(11, 0) }); |
| assertEquals(11, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(14, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withCaretBetweenIdentifierCharAndNonIdentifierChar_selectsFullIdentifier() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(23, 0) }); |
| assertEquals(23, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(23, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(22, 1) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withCaretInSecondRow_selectsFullIdentifier() throws Exception { |
| setSelection(new IRegion[] { new Region(L1_LEN + 11, 0) }); |
| assertEquals(L1_LEN + 11, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(L1_LEN + 14, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withCaretInIdentifierWithNoFollowingMatch_selectsFullIdentifier() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN + L3_LEN + 11, 0) }); |
| assertEquals(L1_LEN + L2_LEN + L3_LEN + 11, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(L1_LEN + L2_LEN + L3_LEN + 14, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + L2_LEN + L3_LEN + 8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionDown_withCaretAtEndOfDocument_selectsFullIdentifier() throws Exception { |
| setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN + L3_LEN + L4_LEN, 0) }); |
| assertEquals(L1_LEN + L2_LEN + L3_LEN + L4_LEN, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| assertEquals(L1_LEN + L2_LEN + L3_LEN + L4_LEN, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + L2_LEN + L3_LEN + L4_LEN - 1, 1) }, getSelection()); |
| } |
| |
| @Test |
| public void testAddAllMatches_withSingleSelection_selectsAllOccurences() throws Exception { |
| setSelection(new IRegion[] { new Region(0, 7) }); |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(ADD_ALL_MATCHES_TO_MULTI_SELECTION); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN, 7), |
| new Region(L1_LEN + L2_LEN + L3_LEN, 7) }, getSelection()); |
| } |
| |
| @Test |
| public void testAddAllMatches_withDoubleSelectionOfSameText_selectsAllOccurences() throws Exception { |
| setSelection(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7) }); |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(ADD_ALL_MATCHES_TO_MULTI_SELECTION); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN, 7), |
| new Region(L1_LEN + L2_LEN + L3_LEN, 7) }, getSelection()); |
| } |
| |
| @Test |
| public void testAddAllMatches_withDoubleSelectionOfDifferentTexts_doesNotChangeSelection() throws Exception { |
| setSelection(new IRegion[] { new Region(0, 7), new Region(8, 7) }); |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(ADD_ALL_MATCHES_TO_MULTI_SELECTION); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(8, 7) }, getSelection()); |
| } |
| |
| @Test |
| public void testAddAllMatches_withCaretInIdentifier_selectsAllOccurencesOfIdentifier() throws Exception { |
| setSelection(new IRegion[] { new Region(2, 0) }); |
| assertEquals(2, widget.getCaretOffset()); |
| |
| executeCommand(ADD_ALL_MATCHES_TO_MULTI_SELECTION); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN, 7), |
| new Region(L1_LEN + L2_LEN + L3_LEN, 7) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionUp_withCaretInIdentifier_selectsFullIdentifier() throws Exception { |
| setSelection(new IRegion[] { new Region(L1_LEN + 11, 0) }); |
| assertEquals(L1_LEN + 11, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_UP); |
| |
| assertEquals(L1_LEN + 8 + 6, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionUp_withSingleSelectionAndNoPreviousMatch_doesNothing() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(8, 6) }); |
| assertEquals(14, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_UP); |
| |
| assertEquals(14, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionUp_withTwoSelections_removesSecondSelection() throws Exception { |
| setSelection(new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6) }); |
| assertEquals(14, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_UP); |
| |
| assertEquals(14, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionUp_withThreeSelections_removesThirdSelection() throws Exception { |
| setSelection(new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 8, 6) }); |
| assertEquals(14, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_UP); |
| |
| assertEquals(14, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionUp_withTwoSelectionsAndAnchorAbove_reducesSelection() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(8, 6) }); |
| // It is important here to build up the selection in steps, so the |
| // handler can determine an anchor region |
| executeCommand(MULTI_SELECTION_DOWN); |
| assertArrayEquals(new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6) }, getSelection()); |
| |
| executeCommand(MULTI_SELECTION_UP); |
| |
| assertArrayEquals(new IRegion[] { new Region(8, 6) }, getSelection()); |
| } |
| |
| @Test |
| public void testMultiSelectionUp_withTwoSelectionsAndAnchorBelow_extendsSelection() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN + 8, 6) }); |
| // It is important here to build up the selection in steps, so the |
| // handler can determine an anchor region |
| executeCommand(MULTI_SELECTION_UP); |
| assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 8, 6) }, |
| getSelection()); |
| |
| executeCommand(MULTI_SELECTION_UP); |
| |
| assertArrayEquals( |
| new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 8, 6) }, |
| getSelection()); |
| } |
| |
| @Test |
| public void testStopMultiSelection_withSingleSelection_doesNotChangeSelectionOrCaretOffset() throws Exception { |
| setSelection(new IRegion[] { new Region(0, 7) }); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(STOP_MULTI_SELECTION); |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(0, 7) }, getSelection()); |
| } |
| |
| @Test |
| public void testStopMultiSelection_withMultiSelection_revokesSelectionAndKeepsFirstCaretOffset() throws Exception { |
| setSelection(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7) }); |
| |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(STOP_MULTI_SELECTION); |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(7, 0) }, getSelection()); |
| } |
| |
| @Test |
| public void testStopMultiSelection_withMultiSelectionAndCaretAtBeginning_revokesSelectionAndKeepsFirstCaretOffset() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7) }); |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(STOP_MULTI_SELECTION); |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(7, 0) }, getSelection()); |
| } |
| |
| @Test |
| public void testStopMultiSelection_withMultiSelectionAndCaretAfterLastSelection_revokesSelectionAndKeepsCaretOffset() |
| throws Exception { |
| setSelection(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN, 7) }); |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(MULTI_SELECTION_DOWN); |
| executeCommand(MULTI_SELECTION_DOWN); |
| |
| // TODO How to place the caret at the end without dismissing the |
| // selection? Should rather be 57 |
| assertEquals(7, widget.getCaretOffset()); |
| |
| executeCommand(STOP_MULTI_SELECTION); |
| assertEquals(7, widget.getCaretOffset()); |
| assertArrayEquals(new IRegion[] { new Region(7, 0) }, getSelection()); |
| } |
| |
| // Helper methods |
| |
| private void executeCommand(String commandId) throws Exception { |
| Command command = PlatformUI.getWorkbench().getService(ICommandService.class).getCommand(commandId); |
| command.executeWithChecks(new ExecutionEvent(command, Collections.EMPTY_MAP, null, null)); |
| } |
| |
| private void setSelection(IRegion[] regions) { |
| IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput()); |
| editor.getSelectionProvider().setSelection(new MultiTextSelection(document, regions)); |
| } |
| |
| private IRegion[] getSelection() { |
| return ((IMultiTextSelection) editor.getSelectionProvider().getSelection()).getRegions(); |
| } |
| } |