Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TextMultiCaretSelectionCommandsTest.java297
-rw-r--r--org.eclipse.ui.workbench.texteditor/plugin.properties4
-rw-r--r--org.eclipse.ui.workbench.texteditor/plugin.xml30
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/AbstractMultiSelectionHandler.java55
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiCaretDownHandler.java59
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiCaretUpHandler.java59
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionDownHandler.java7
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionUpHandler.java7
8 files changed, 482 insertions, 36 deletions
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TextMultiCaretSelectionCommandsTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TextMultiCaretSelectionCommandsTest.java
index 1f700b1672c..85b43634ae8 100644
--- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TextMultiCaretSelectionCommandsTest.java
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TextMultiCaretSelectionCommandsTest.java
@@ -56,10 +56,12 @@ public class TextMultiCaretSelectionCommandsTest {
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 MULTI_CARET_DOWN = "org.eclipse.ui.edit.text.select.multiCaretDown";
+ private static final String MULTI_CARET_UP = "org.eclipse.ui.edit.text.select.multiCaretUp";
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_2 = "private static String b; // this is a little longer\n";
+ private static final String LINE_3 = "\t\tprivate static String c;\n";
private static final String LINE_4 = "private static String d";
private static final int L1_LEN = LINE_1.length();
@@ -101,7 +103,7 @@ public class TextMultiCaretSelectionCommandsTest {
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) },
+ assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN + 2, 7) },
getSelection());
executeCommand(STOP_MULTI_SELECTION);
@@ -130,7 +132,7 @@ public class TextMultiCaretSelectionCommandsTest {
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) },
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 10, 6) },
getSelection());
}
@@ -153,12 +155,12 @@ public class TextMultiCaretSelectionCommandsTest {
// 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) },
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 10, 6) },
getSelection());
executeCommand(MULTI_SELECTION_DOWN);
- assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 8, 6),
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 10, 6),
new Region(L1_LEN + L2_LEN + L3_LEN + 8, 6) }, getSelection());
}
@@ -241,7 +243,7 @@ public class TextMultiCaretSelectionCommandsTest {
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),
+ assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN + 2, 7),
new Region(L1_LEN + L2_LEN + L3_LEN, 7) }, getSelection());
}
@@ -253,7 +255,7 @@ public class TextMultiCaretSelectionCommandsTest {
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),
+ assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN + 2, 7),
new Region(L1_LEN + L2_LEN + L3_LEN, 7) }, getSelection());
}
@@ -276,7 +278,7 @@ public class TextMultiCaretSelectionCommandsTest {
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),
+ assertArrayEquals(new IRegion[] { new Region(0, 7), new Region(L1_LEN, 7), new Region(L1_LEN + L2_LEN + 2, 7),
new Region(L1_LEN + L2_LEN + L3_LEN, 7) }, getSelection());
}
@@ -292,8 +294,7 @@ public class TextMultiCaretSelectionCommandsTest {
}
@Test
- public void testMultiSelectionUp_withSingleSelectionAndNoPreviousMatch_doesNothing()
- throws Exception {
+ public void testMultiSelectionUp_withSingleSelectionAndNoPreviousMatch_doesNothing() throws Exception {
setSelection(new IRegion[] { new Region(8, 6) });
assertEquals(14, widget.getCaretOffset());
@@ -316,7 +317,8 @@ public class TextMultiCaretSelectionCommandsTest {
@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) });
+ setSelection(
+ new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 10, 6) });
assertEquals(14, widget.getCaretOffset());
executeCommand(MULTI_SELECTION_UP);
@@ -326,8 +328,7 @@ public class TextMultiCaretSelectionCommandsTest {
}
@Test
- public void testMultiSelectionUp_withTwoSelectionsAndAnchorAbove_reducesSelection()
- throws Exception {
+ 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
@@ -340,19 +341,18 @@ public class TextMultiCaretSelectionCommandsTest {
}
@Test
- public void testMultiSelectionUp_withTwoSelectionsAndAnchorBelow_extendsSelection()
- throws Exception {
- setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN + 8, 6) });
+ public void testMultiSelectionUp_withTwoSelectionsAndAnchorBelow_extendsSelection() throws Exception {
+ setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN + 10, 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) },
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 10, 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) },
+ new IRegion[] { new Region(8, 6), new Region(L1_LEN + 8, 6), new Region(L1_LEN + L2_LEN + 10, 6) },
getSelection());
}
@@ -407,6 +407,265 @@ public class TextMultiCaretSelectionCommandsTest {
assertArrayEquals(new IRegion[] { new Region(7, 0) }, getSelection());
}
+ @Test
+ public void testMultiCaretDown_withCaret_addsCaretsInNextLines() throws Exception {
+ setSelection(new IRegion[] { new Region(0, 0) });
+ assertEquals(0, widget.getCaretOffset());
+
+ executeCommand(MULTI_CARET_DOWN);
+
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0), new Region(L1_LEN, 0) }, getSelection());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_DOWN);
+
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0), new Region(L1_LEN, 0), new Region(L1_LEN + L2_LEN, 0) },
+ getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretDown_withTwoCaretsAndAnchorRegionBelow_removesFirstCaret() throws Exception {
+ setSelection(new IRegion[] { new Region(L1_LEN, 0) });
+ assertEquals(L1_LEN, widget.getCaretOffset());
+
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0), new Region(L1_LEN, 0) }, getSelection());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_DOWN);
+
+ assertEquals(L1_LEN, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN, 0) }, getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(L1_LEN, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretDown_withSingleSelection_addsSelectionInNextLine() throws Exception {
+ setSelection(new IRegion[] { new Region(0, 3) });
+ assertEquals(3, widget.getCaretOffset());
+
+ executeCommand(MULTI_CARET_DOWN);
+
+ assertEquals(3, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 3), new Region(L1_LEN, 3) }, getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(3, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(3, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretDown_withSingleCaretAtEndOfLongerLine_addsCaretAtEndOfNextLine() throws Exception {
+ setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN, 0) });
+ assertEquals(L1_LEN + L2_LEN, widget.getCaretOffset());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_DOWN);
+
+ assertEquals(L1_LEN + L2_LEN, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + L2_LEN, 0), new Region(L1_LEN + L2_LEN + L3_LEN, 0) },
+ getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(L1_LEN + L2_LEN, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + L2_LEN, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretDown_withSingleCaretInLineAboveLineWithTabs_addsCaretInNextLineRespectingTabs()
+ throws Exception {
+ widget.setTabs(4); // Make default explicit
+ setSelection(new IRegion[] { new Region(L1_LEN + 8, 0) });
+ assertEquals(L1_LEN + 8, widget.getCaretOffset());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_DOWN);
+
+ assertEquals(L1_LEN + 8, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 0), new Region(L1_LEN + L2_LEN + 2, 0) },
+ getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(L1_LEN + 8, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretDown_withSingleCaretInLineWithTabs_addsCaretInNextLineRespectingTabs() throws Exception {
+ widget.setTabs(4); // Make default explicit
+ setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN + 2, 0) });
+ assertEquals(L1_LEN + L2_LEN + 2, widget.getCaretOffset());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_DOWN);
+
+ assertEquals(L1_LEN + L2_LEN + 2, widget.getCaretOffset());
+ assertArrayEquals(
+ new IRegion[] { new Region(L1_LEN + L2_LEN + 2, 0), new Region(L1_LEN + L2_LEN + L3_LEN + 8, 0) },
+ getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(L1_LEN + L2_LEN + 2, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + L2_LEN + 2, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretDown_withSingleCaretAtEndOfText_doesNotChangeCaret() 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());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_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, 0) }, getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(L1_LEN + L2_LEN + L3_LEN + L4_LEN, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + L2_LEN + L3_LEN + L4_LEN, 0) }, getSelection());
+ }
+
+ /////////////////////////////////////////////////////
+ @Test
+ public void testMultiCaretUp_withCaret_addsCaretsInPreviousLines() throws Exception {
+ setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN, 0) });
+ assertEquals(L1_LEN + L2_LEN, widget.getCaretOffset());
+
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(L1_LEN, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN, 0), new Region(L1_LEN + L2_LEN, 0) }, getSelection());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0), new Region(L1_LEN, 0), new Region(L1_LEN + L2_LEN, 0) },
+ getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretUp_withTwoCaretsAndAnchorRegionAbove_removesLastCaret() throws Exception {
+ setSelection(new IRegion[] { new Region(0, 0) });
+ assertEquals(0, widget.getCaretOffset());
+
+ executeCommand(MULTI_CARET_DOWN);
+
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0), new Region(L1_LEN, 0) }, getSelection());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0) }, getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretUp_withSingleSelection_addsSelectionInPreviousLine() throws Exception {
+ setSelection(new IRegion[] { new Region(L1_LEN, 3) });
+ assertEquals(L1_LEN + 3, widget.getCaretOffset());
+
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(3, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 3), new Region(L1_LEN, 3) }, getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(3, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(3, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretUp_withSingleCaretAtEndOfLongerLine_addsCaretAtEndOfPreviousLine() throws Exception {
+ setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN, 0) });
+ assertEquals(L1_LEN + L2_LEN, widget.getCaretOffset());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(L1_LEN, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN, 0), new Region(L1_LEN + L2_LEN, 0) }, getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(L1_LEN, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretUp_withSingleCaretInLineBelowLineWithTabs_addsCaretInPreviousLineRespectingTabs()
+ throws Exception {
+ widget.setTabs(4); // Make default explicit
+ setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN + L3_LEN + 8, 0) });
+ assertEquals(L1_LEN + L2_LEN + L3_LEN + 8, widget.getCaretOffset());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(L1_LEN + L2_LEN + 2, widget.getCaretOffset());
+ assertArrayEquals(
+ new IRegion[] { new Region(L1_LEN + L2_LEN + 2, 0), new Region(L1_LEN + L2_LEN + L3_LEN + 8, 0) },
+ getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(L1_LEN + L2_LEN + 2, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + L2_LEN + 2, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretUp_withSingleCaretInLineWithTabs_addsCaretInPreviousLineRespectingTabs()
+ throws Exception {
+ widget.setTabs(4); // Make default explicit
+ setSelection(new IRegion[] { new Region(L1_LEN + L2_LEN + 2, 0) });
+ assertEquals(L1_LEN + L2_LEN + 2, widget.getCaretOffset());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(L1_LEN + 8, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 0), new Region(L1_LEN + L2_LEN + 2, 0) },
+ getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(L1_LEN + 8, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(L1_LEN + 8, 0) }, getSelection());
+ }
+
+ @Test
+ public void testMultiCaretUp_withSingleCaretAtBeginningOfText_doesNotChangeCaret() throws Exception {
+ setSelection(new IRegion[] { new Region(0, 0) });
+ assertEquals(0, widget.getCaretOffset());
+
+ widget.setSize(800, 500); // make sure the widget is not size (0,0)
+ executeCommand(MULTI_CARET_UP);
+
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0) }, getSelection());
+
+ executeCommand(STOP_MULTI_SELECTION);
+ assertEquals(0, widget.getCaretOffset());
+ assertArrayEquals(new IRegion[] { new Region(0, 0) }, getSelection());
+ }
+
// Helper methods
private void executeCommand(String commandId) throws Exception {
diff --git a/org.eclipse.ui.workbench.texteditor/plugin.properties b/org.eclipse.ui.workbench.texteditor/plugin.properties
index 2e204f51a14..f647d810d8d 100644
--- a/org.eclipse.ui.workbench.texteditor/plugin.properties
+++ b/org.eclipse.ui.workbench.texteditor/plugin.properties
@@ -167,6 +167,10 @@ command.selectMultiSelectionUp.description = Search next matching region above a
command.selectMultiSelectionUp.name = Multi selection up relative to anchor selection
command.stopMultiSelection.description = Unselects all multi-selections returning to a single cursor
command.stopMultiSelection.name = End multi-selection
+command.multiCaretUp.description=Add a new caret/multi selection above the current line, or remove the last caret/multi selection
+command.multiCaretUp.name=Multi caret up
+command.multiCaretDown.description=Add a new caret/multi selection below the current line, or remove the first caret/multi selection
+command.multiCaretDown.name=Multi caret down
command.selectWordNext.description = Select the next word
command.selectWordNext.name = Select Next Word
command.selectWordPrevious.description = Select the previous word
diff --git a/org.eclipse.ui.workbench.texteditor/plugin.xml b/org.eclipse.ui.workbench.texteditor/plugin.xml
index 9dea32dcd87..9c861c23c22 100644
--- a/org.eclipse.ui.workbench.texteditor/plugin.xml
+++ b/org.eclipse.ui.workbench.texteditor/plugin.xml
@@ -329,6 +329,18 @@
id="org.eclipse.ui.edit.text.select.stopMultiSelection">
</command>
<command
+ name="%command.multiCaretUp.name"
+ description="%command.multiCaretUp.description"
+ categoryId="org.eclipse.ui.category.textEditor"
+ id="org.eclipse.ui.edit.text.select.multiCaretUp">
+ </command>
+ <command
+ name="%command.multiCaretDown.name"
+ description="%command.multiCaretDown.description"
+ categoryId="org.eclipse.ui.category.textEditor"
+ id="org.eclipse.ui.edit.text.select.multiCaretDown">
+ </command>
+ <command
name="%command.deletePrevious.name"
description="%command.deletePrevious.description"
categoryId="org.eclipse.ui.category.textEditor"
@@ -1422,6 +1434,24 @@
</with>
</enabledWhen>
</handler>
+ <handler
+ class="org.eclipse.ui.internal.texteditor.multiselection.MultiCaretUpHandler"
+ commandId="org.eclipse.ui.edit.text.select.multiCaretUp">
+ <enabledWhen>
+ <with variable="activeEditor">
+ <adapt type="org.eclipse.ui.texteditor.ITextEditor"/>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="org.eclipse.ui.internal.texteditor.multiselection.MultiCaretDownHandler"
+ commandId="org.eclipse.ui.edit.text.select.multiCaretDown">
+ <enabledWhen>
+ <with variable="activeEditor">
+ <adapt type="org.eclipse.ui.texteditor.ITextEditor"/>
+ </with>
+ </enabledWhen>
+ </handler>
</extension>
<extension
point="org.eclipse.ui.menus">
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/AbstractMultiSelectionHandler.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/AbstractMultiSelectionHandler.java
index e8f7a40eab1..986afd6c063 100644
--- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/AbstractMultiSelectionHandler.java
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/AbstractMultiSelectionHandler.java
@@ -18,6 +18,7 @@ import java.util.Arrays;
import java.util.List;
import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.core.commands.AbstractHandler;
@@ -32,6 +33,8 @@ import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IMultiTextSelection;
import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.MultiTextSelection;
import org.eclipse.jface.text.Region;
@@ -61,6 +64,11 @@ abstract class AbstractMultiSelectionHandler extends AbstractHandler {
private ExecutionEvent event;
private ITextEditor textEditor;
private IDocument document;
+ /**
+ * SourceViewer Might be <code>null</code>, if {@link #textEditor} doesn't
+ * implement this interface.
+ */
+ private ITextViewerExtension5 sourceViewer;
/**
* This method needs to be overwritten from subclasses to handle the event.
@@ -264,7 +272,7 @@ abstract class AbstractMultiSelectionHandler extends AbstractHandler {
}
}
- private IRegion createRegionIfValid(int offset, int length) {
+ protected IRegion createRegionIfValid(int offset, int length) {
if ((offset < 0) || (offset > document.getLength()))
return null;
@@ -294,24 +302,43 @@ abstract class AbstractMultiSelectionHandler extends AbstractHandler {
return regions;
}
- private int offsetInNextLine(int offset) throws BadLocationException {
+ protected int offsetInNextLine(int offset) throws BadLocationException {
return moveOffsetByLines(offset, 1);
}
- private int offsetInPreviousLine(int offset) throws BadLocationException {
+ protected int offsetInPreviousLine(int offset) throws BadLocationException {
return moveOffsetByLines(offset, -1);
}
private int moveOffsetByLines(int offset, int lineDelta) throws BadLocationException {
- int lineNo = document.getLineOfOffset(offset);
- int newLineNo = lineNo + lineDelta;
+ int newLineNo = document.getLineOfOffset(offset) + lineDelta;
if ((newLineNo < 0) || (newLineNo >= document.getNumberOfLines()))
return -1;
- int newLineOffset = document.getLineOffset(newLineNo);
- int delta = offset - document.getLineOffset(lineNo);
+ int newOffset;
+ if (sourceViewer == null) {
+ // we don't have a sourceViewer and thus as a fallback
+ // assume the widget offsets are identical to the document offsets
+ newOffset = moveWidgetOffsetByLines(offset, lineDelta);
+ } else {
+ int widgetOffset = sourceViewer.modelOffset2WidgetOffset(offset);
+ int newWidgetOffset = moveWidgetOffsetByLines(widgetOffset, lineDelta);
+ newOffset = sourceViewer.widgetOffset2ModelOffset(newWidgetOffset);
+ }
+ if (newOffset == -1) {
+ return endOfLineOffset(newLineNo);
+ }
+ return newOffset;
+ }
+
+ private int moveWidgetOffsetByLines(int widgetOffset, int lineDelta) throws BadLocationException {
+ Point location = getWidget().getLocationAtOffset(widgetOffset);
+ Point newLocation = new Point(location.x, location.y + lineDelta * getWidget().getLineHeight(widgetOffset));
+ return getWidget().getOffsetAtPoint(newLocation);
+ }
- return newLineOffset + delta;
+ private int endOfLineOffset(int lineNo) throws BadLocationException {
+ return document.getLineOffset(lineNo) + document.getLineInformation(lineNo).getLength();
}
private boolean initFrom(ExecutionEvent event) {
@@ -319,7 +346,8 @@ abstract class AbstractMultiSelectionHandler extends AbstractHandler {
initTextEditor();
if (textEditor == null)
return false;
- document = getDocument();
+ initDocument();
+ initSourceViewer();
initAnchorRegion();
return true;
}
@@ -329,8 +357,13 @@ abstract class AbstractMultiSelectionHandler extends AbstractHandler {
textEditor = Adapters.adapt(editor, ITextEditor.class);
}
- private IDocument getDocument() {
- return textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
+ private void initDocument() {
+ document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
+ }
+
+ private void initSourceViewer() {
+ ITextViewer textViewer = textEditor.getAdapter(ITextViewer.class);
+ sourceViewer = Adapters.adapt(textViewer, ITextViewerExtension5.class);
}
private IRegion[] toArray(List<IRegion> regions) {
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiCaretDownHandler.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiCaretDownHandler.java
new file mode 100644
index 00000000000..6d445421550
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiCaretDownHandler.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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.internal.texteditor.multiselection;
+
+import org.eclipse.core.commands.ExecutionException;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IRegion;
+
+/**
+ * Handler to change the current set of multi carets/selections downwards. This
+ * might either mean to add a caret/selection below by adding a new
+ * caret/selection at the same line offset in the next line, or reduce the
+ * number of carets/selections by removing the first caret/selection range. This
+ * depends on the selection a multi caret/selection command was invoked with the
+ * first time -- that selection is remembered as an "anchor" to which successive
+ * calls are related as reference selection.<br>
+ */
+public class MultiCaretDownHandler extends AbstractMultiSelectionHandler {
+
+ @Override
+ public void execute() throws ExecutionException {
+ if (selectionIsAboveAnchorRegion()) {
+ removeFirstRegionFromSelection();
+ } else {
+ extendSelectionWithSamePositionInNextLine();
+ }
+ }
+
+ private void extendSelectionWithSamePositionInNextLine() throws ExecutionException {
+ IRegion[] regions = getSelectedRegions();
+ if (regions == null || regions.length == 0) {
+ return;
+ }
+ try {
+ IRegion lastRegion = regions[regions.length - 1];
+ int newOffset = offsetInNextLine(lastRegion.getOffset());
+ IRegion nextLineRegion = createRegionIfValid(newOffset, lastRegion.getLength());
+ selectRegions(addRegion(regions, nextLineRegion));
+ } catch (BadLocationException e) {
+ throw new ExecutionException("Internal error in extendSelectionWithSamePositionInNextLine", e);
+ }
+ }
+
+ private void removeFirstRegionFromSelection() {
+ selectRegions(removeFirstRegionButOne(getSelectedRegions()));
+ }
+}
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiCaretUpHandler.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiCaretUpHandler.java
new file mode 100644
index 00000000000..1f12b4cd361
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiCaretUpHandler.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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.internal.texteditor.multiselection;
+
+import org.eclipse.core.commands.ExecutionException;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IRegion;
+
+/**
+ * Handler to change the current set of multi carets/selections upwards. This
+ * might either mean to add a caret/selection above by adding a new
+ * caret/selection at the same line offset in the previous line, or reduce the
+ * number of carets/selections by removing the last caret/selection range. This
+ * depends on the selection a multi caret/selection command was invoked with the
+ * first time -- that selection is remembered as an "anchor" to which successive
+ * calls are related as reference selection.<br>
+ */
+public class MultiCaretUpHandler extends AbstractMultiSelectionHandler {
+
+ @Override
+ public void execute() throws ExecutionException {
+ if (selectionIsBelowAnchorRegion()) {
+ removeLastRegionFromSelection();
+ } else {
+ extendSelectionWithSamePositionInPreviousLine();
+ }
+ }
+
+ private void extendSelectionWithSamePositionInPreviousLine() throws ExecutionException {
+ IRegion[] regions = getSelectedRegions();
+ if (regions == null || regions.length == 0) {
+ return;
+ }
+ try {
+ IRegion firstRegion = regions[0];
+ int newOffset = offsetInPreviousLine(firstRegion.getOffset());
+ IRegion previousLineRegion = createRegionIfValid(newOffset, firstRegion.getLength());
+ selectRegions(addRegion(regions, previousLineRegion));
+ } catch (BadLocationException e) {
+ throw new ExecutionException("Internal error in extendSelectionWithSamePositionInPreviousLine", e);
+ }
+ }
+
+ private void removeLastRegionFromSelection() {
+ selectRegions(removeLastRegionButOne(getSelectedRegions()));
+ }
+}
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionDownHandler.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionDownHandler.java
index 678077e20a9..20ed24c66f2 100644
--- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionDownHandler.java
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionDownHandler.java
@@ -20,9 +20,10 @@ import org.eclipse.jface.text.IRegion;
/**
* Handler to change the current multi selection downwards. This might either
* mean to extend the selection by adding a match below, or shrink the selection
- * by removing the first selection range. This depends on the selection the
- * command was invoked with the first time -- that selection is remembered as an
- * "anchor" to which successive calls are related as reference selection.<br>
+ * by removing the first selection range. This depends on the selection a multi
+ * caret/selection command was invoked with the first time -- that selection is
+ * remembered as an "anchor" to which successive calls are related as reference
+ * selection.<br>
* If no word is selected, an implicit selection of the word under the cursor is
* performed.
*/
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionUpHandler.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionUpHandler.java
index 8205b3a296c..6a31f556dce 100644
--- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionUpHandler.java
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/multiselection/MultiSelectionUpHandler.java
@@ -20,9 +20,10 @@ import org.eclipse.jface.text.IRegion;
/**
* Handler to change the current multi selection upwards. This might either mean
* to extend the selection by adding a match above, or shrink the selection by
- * removing the last selection range. This depends on the selection the command
- * was invoked with the first time -- that selection is remembered as an
- * "anchor" to which successive calls are related as reference selection.<br>
+ * removing the last selection range. This depends on the selection a multi
+ * caret/selection command was invoked with the first time -- that selection is
+ * remembered as an "anchor" to which successive calls are related as reference
+ * selection.<br>
* If no word is selected, an implicit selection of the word under the cursor is
* performed.
*/

Back to the top