IDEA-80056 Column selection mode improvement
authorDmitry Batrak <Dmitry.Batrak@jetbrains.com>
Fri, 14 Feb 2014 12:11:52 +0000 (16:11 +0400)
committerDmitry Batrak <Dmitry.Batrak@jetbrains.com>
Fri, 14 Feb 2014 12:13:29 +0000 (16:13 +0400)
CaretModel API changes - passing Caret instance to runForEachCaret callback, and some minor ones

30 files changed:
java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCase.java
platform/editor-ui-api/src/com/intellij/openapi/editor/Caret.java
platform/editor-ui-api/src/com/intellij/openapi/editor/CaretAction.java [new file with mode: 0644]
platform/editor-ui-api/src/com/intellij/openapi/editor/CaretModel.java
platform/lang-impl/src/com/intellij/codeInsight/editorActions/CopyHandler.java
platform/lang-impl/src/com/intellij/codeInsight/editorActions/CutHandler.java
platform/lang-impl/src/com/intellij/codeInsight/editorActions/PasteHandler.java
platform/lang-impl/src/com/intellij/injected/editor/CaretModelWindow.java
platform/platform-api/src/com/intellij/openapi/editor/EditorModificationUtil.java
platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorActionHandler.java
platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorWriteActionHandler.java
platform/platform-api/src/com/intellij/openapi/editor/actionSystem/TypedAction.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/CopyAction.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/CutAction.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/EscapeAction.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/MoveCaretDownWithSelectionAction.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/MoveCaretUpWithSelectionAction.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/PageDownWithSelectionAction.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/PageUpWithSelectionAction.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaretModel.java
platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentEditor.java
platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorProvider.java
platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java
platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java

index cb92c8ab55dd83d06806ffcf471c5de09231bcac..aecd3d604678681ac325e679656485e416769d39 100644 (file)
@@ -537,18 +537,12 @@ public abstract class CodeInsightTestCase extends PsiTestCase {
         assertEquals("Text mismatch in file " + filePath, newFileText1, text);
 
         CaretModel caretModel = myEditor.getCaretModel();
-        List<Caret> allCarets = caretModel.supportsMultipleCarets() ? new ArrayList<Caret>(caretModel.getAllCarets()) : null;
-        assertEquals("Unexpected number of carets", caretState.carets.size(), caretModel.supportsMultipleCarets() ? allCarets.size() : 1);
+        List<Caret> allCarets = new ArrayList<Caret>(caretModel.getAllCarets());
+        assertEquals("Unexpected number of carets", caretState.carets.size(), allCarets.size());
         for (int i = 0; i < caretState.carets.size(); i++) {
           String caretDescription = caretState.carets.size() == 1 ? "" : "caret " + (i + 1) + "/" + caretState.carets.size() + " ";
-          Caret currentCaret = caretModel.supportsMultipleCarets() ? allCarets.get(i) : null;
-          LogicalPosition actualCaretPosition;
-          if (caretModel.supportsMultipleCarets()) {
-            actualCaretPosition = currentCaret.getLogicalPosition();
-          }
-          else {
-            actualCaretPosition = caretModel.getLogicalPosition();
-          }
+          Caret currentCaret = allCarets.get(i);
+          LogicalPosition actualCaretPosition = currentCaret.getLogicalPosition();
           EditorTestUtil.Caret expected = caretState.carets.get(i);
           if (expected.offset != null) {
             int caretLine = StringUtil.offsetToLineNumber(newFileText, expected.offset);
@@ -557,8 +551,8 @@ public abstract class CodeInsightTestCase extends PsiTestCase {
             assertEquals(caretDescription + "caretLine", caretLine + 1, actualCaretPosition.line + 1);
             assertEquals(caretDescription + "caretColumn", caretCol + 1, actualCaretPosition.column + 1);
           }
-          int actualSelectionStart = caretModel.supportsMultipleCarets() ? currentCaret.getSelectionStart() : myEditor.getSelectionModel().getSelectionStart();
-          int actualSelectionEnd = caretModel.supportsMultipleCarets() ? currentCaret.getSelectionEnd() : myEditor.getSelectionModel().getSelectionEnd();
+          int actualSelectionStart = currentCaret.getSelectionStart();
+          int actualSelectionEnd = currentCaret.getSelectionEnd();
           if (expected.selection != null) {
             int selStartLine = StringUtil.offsetToLineNumber(newFileText, expected.selection.getStartOffset());
             int selStartCol = expected.selection.getStartOffset() - StringUtil.lineColToOffset(newFileText, selStartLine, 0);
@@ -580,7 +574,7 @@ public abstract class CodeInsightTestCase extends PsiTestCase {
           }
           else {
             assertFalse(caretDescription + "should has no selection, but was: (" + actualSelectionStart + ", " + actualSelectionEnd + ")",
-                        caretModel.supportsMultipleCarets() ? currentCaret.hasSelection() : myEditor.getSelectionModel().hasSelection());
+                        currentCaret.hasSelection());
           }
         }
       }
index 976dc1f1be40a475b914089b585728ce63fd0195..3648e477da8316289bd147af480b5c6c133eed51 100644 (file)
@@ -249,7 +249,8 @@ public interface Caret extends UserDataHolderEx, Disposable {
    * selection will be set for the new caret.
    *
    * @param above if <code>true</code>, new caret will be created at the previous line, if <code>false</code> - on the next line
-   * @return newly created caret instance, or null if the caret cannot be created because it already exists at the new location
+   * @return newly created caret instance, or null if the caret cannot be created because it already exists at the new location or caret
+   * model doesn't support multiple carets.
    */
   @Nullable
   Caret clone(boolean above);
diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/CaretAction.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/CaretAction.java
new file mode 100644 (file)
index 0000000..9c0b659
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2000-2014 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;
+
+/**
+ * Action to be performed on a specific editor caret
+ *
+ * @see com.intellij.openapi.editor.CaretModel#runForEachCaret(com.intellij.openapi.editor.CaretAction)
+ */
+public interface CaretAction {
+  void perform(Caret caret);
+}
index 2c9e3d6db26e1a7aa55963fea9b6baeab17fedf6..776566f900ec805501012bb6e9ba78a23b89acb9 100644 (file)
@@ -29,13 +29,19 @@ import java.util.List;
  *
  * May support several carets existing simultaneously in a document. {@link #supportsMultipleCarets()} method can be used to find out
  * whether particular instance of CaretModel does it. If it does, query and update methods for caret position operate on a certain 'primary'
- * caret. There exists a way to perform the same operation(s) on each caret - see {@link #runForEachCaret(Runnable)} method. Within its
- * context, query and update methods operate on the current caret in that iteration.
- * How 'primary' caret is determined by the model is not dictated.
+ * caret. There exists a way to perform the same operation(s) on each caret - see
+ * {@link #runForEachCaret(com.intellij.openapi.editor.CaretAction)} method. Within its context, query and update methods operate on the
+ * current caret in that iteration. This behaviour can change in future though, so using caret and selection query and update methods in
+ * actions that need to operate on multiple carets is discouraged - methods on {@link com.intellij.openapi.editor.Caret} instances obtained
+ * via {@link #getAllCarets()} or {@link #runForEachCaret(CaretAction)} should be used instead.
+ * <p>
+ * How 'primary' caret is determined by the model is not defined (currently it's the most recently added caret, but that can change).
+ * <p>
  * At all times at least one caret will exist in a document.
  * <p>
- * Update methods, and {@link #runForEachCaret(Runnable)} method should only be run from EDT. Query methods can be run from any thread, when
- * called not from EDT, those methods are 'not aware' of 'runForEachCaret' scope - they will always return information about primary caret.
+ * Update methods, {@link #runBatchCaretOperation(Runnable)} and {@link #runForEachCaret(com.intellij.openapi.editor.CaretAction)} methods
+ * should only be run from EDT. Query methods can be run from any thread, when called not from EDT, those methods are 'not aware' of
+ * 'runForEachCaret' scope - they will always return information about primary caret.
  *
  * @see Editor#getCaretModel()
  */
@@ -161,42 +167,29 @@ public interface CaretModel {
   boolean supportsMultipleCarets();
 
   /**
-   * Returns current caret - the one, query and update methods in the model operate at the moment. This is either an iteration-current
-   * caret within the context of {@link #runForEachCaret(Runnable)} method, or the 'primary' caret without that context.
-   * <p>
-   * If multiple carets are not supported, the behaviour is unspecified.
-   *
-   * @see #supportsMultipleCarets()
+   * Returns current caret - the one, query and update methods in the model operate at the moment. In the current implementation this is
+   * either an iteration-current caret within the context of {@link #runForEachCaret(Runnable)} method, or the 'primary' caret without that
+   * context. Users {@link #runForEachCaret(Runnable)} method should use caret parameter passed to
+   * {@link com.intellij.openapi.editor.CaretAction#perform(Caret)} method instead of this method, as the definition of current caret (as
+   * well as caret instance operated on by model methods) can potentially change.
    */
   @NotNull
   Caret getCurrentCaret();
 
   /**
    * Returns the 'primary' caret.
-   * <p>
-   * If multiple carets are not supported, the behaviour is unspecified.
-   *
-   * @see #supportsMultipleCarets()
    */
   @NotNull
   Caret getPrimaryCaret();
 
   /**
    * Returns all carets currently existing in the document, ordered by their position in the document.
-   * <p>
-   * If multiple carets are not supported, the behaviour is unspecified.
-   *
-   * @see #supportsMultipleCarets()
    */
   @NotNull
   Collection<Caret> getAllCarets();
 
   /**
    * Returns a caret at the given position in the document, or <code>null</code>, if there's no caret there.
-   * <p>
-   * If multiple carets are not supported, the behaviour is unspecified.
-   *
-   * @see #supportsMultipleCarets()
    */
   @Nullable
   Caret getCaretAt(@NotNull VisualPosition pos);
@@ -204,12 +197,8 @@ public interface CaretModel {
   /**
    * Adds a new caret at the given position, and returns corresponding Caret instance. Locations outside of possible values for the given
    * document are trimmed automatically.
-   * Does nothing if a caret already exists at specified location or selection of existing caret includes the specified location,
-   * <code>null</code> is returned in this case.
-   * <p>
-   * If multiple carets are not supported, the behaviour is unspecified.
-   *
-   * @see #supportsMultipleCarets()
+   * Does nothing if multiple carets are not supporeted, a caret already exists at specified location or selection of existing caret
+   * includes the specified location, <code>null</code> is returned in this case.
    */
   @Nullable
   Caret addCaret(@NotNull VisualPosition pos);
@@ -217,19 +206,11 @@ public interface CaretModel {
   /**
    * Removes a given caret if it's recognized by the model and is not the only existing caret in the document, returning <code>true</code>.
    * <code>false</code> is returned if any of the above condition doesn't hold, and the removal cannot happen.
-   * <p>
-   * If multiple carets are not supported, the behaviour is unspecified.
-   *
-   * @see #supportsMultipleCarets()
    */
   boolean removeCaret(@NotNull Caret caret);
 
   /**
    * Removes all carets except the 'primary' one from the document.
-   * <p>
-   * If multiple carets are not supported, does nothing.
-   *
-   * @see #supportsMultipleCarets()
    */
   void removeSecondaryCarets();
 
@@ -248,10 +229,11 @@ public interface CaretModel {
    * determined in the beginning and is not affected by the potential carets addition or removal by the task being executed.
    * At the end, merging of carets and selections is performed, so that no two carets will occur at the same logical position and
    * no two selection will overlap after this method is finished.
-   * <p>
-   * If multiple carets are not supported, the given task is just executed once.
-   *
-   * @see #supportsMultipleCarets()
    */
-  void runForEachCaret(@NotNull Runnable runnable);
+  void runForEachCaret(@NotNull CaretAction action);
+
+  /**
+   * Executes the given task, performing caret merging afterwards. Caret merging will not happen until the operation is finished.
+   */
+  void runBatchCaretOperation(@NotNull Runnable runnable);
 }
index 5490ccc0899e6d61cccc934aa832c46c51fbadf9..618e771f7e9e795478ab310d423d0a61fb011bfe 100644 (file)
@@ -20,9 +20,7 @@ import com.intellij.codeInsight.CodeInsightSettings;
 import com.intellij.ide.DataManager;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.RawText;
-import com.intellij.openapi.editor.SelectionModel;
+import com.intellij.openapi.editor.*;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
 import com.intellij.openapi.editor.actions.CopyAction;
 import com.intellij.openapi.editor.actions.EditorActionUtil;
@@ -70,16 +68,16 @@ public class CopyHandler extends EditorActionHandler {
       if (Registry.is(CopyAction.SKIP_COPY_AND_CUT_FOR_EMPTY_SELECTION_KEY)) {
         return;
       }
-      editor.getCaretModel().runForEachCaret(new Runnable() {
+      editor.getCaretModel().runForEachCaret(new CaretAction() {
         @Override
-        public void run() {
+        public void perform(Caret caret) {
           selectionModel.selectLineAtCaret();
         }
       });
       if (!selectionModel.hasSelection(true)) return;
-      editor.getCaretModel().runForEachCaret(new Runnable() {
+      editor.getCaretModel().runForEachCaret(new CaretAction() {
         @Override
-        public void run() {
+        public void perform(Caret caret) {
           EditorActionUtil.moveCaretToLineStartIgnoringSoftWraps(editor);
         }
       });
index 05279e35ccfea54d317c27d1549f00af5168adcf..b35fa165e0f405c385731ffd0d5adeb34c3fc086 100644 (file)
@@ -67,9 +67,9 @@ public class CutHandler extends EditorWriteActionHandler {
       if (Registry.is(CopyAction.SKIP_COPY_AND_CUT_FOR_EMPTY_SELECTION_KEY)) {
         return;
       }
-      editor.getCaretModel().runForEachCaret(new Runnable() {
+      editor.getCaretModel().runForEachCaret(new CaretAction() {
         @Override
-        public void run() {
+        public void perform(Caret caret) {
           selectionModel.selectLineAtCaret();
         }
       });
@@ -80,9 +80,9 @@ public class CutHandler extends EditorWriteActionHandler {
     int end = selectionModel.getSelectionEnd();
     final List<TextRange> selections = new ArrayList<TextRange>();
     if (editor.getCaretModel().supportsMultipleCarets()) {
-      editor.getCaretModel().runForEachCaret(new Runnable() {
+      editor.getCaretModel().runForEachCaret(new CaretAction() {
         @Override
-        public void run() {
+        public void perform(Caret caret) {
           selections.add(new TextRange(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()));
         }
       });
@@ -94,9 +94,9 @@ public class CutHandler extends EditorWriteActionHandler {
 
       Collections.reverse(selections);
       final Iterator<TextRange> it = selections.iterator();
-      editor.getCaretModel().runForEachCaret(new Runnable() {
+      editor.getCaretModel().runForEachCaret(new CaretAction() {
         @Override
-        public void run() {
+        public void perform(Caret caret) {
           TextRange range = it.next();
           editor.getCaretModel().moveToOffset(range.getStartOffset());
           selectionModel.removeSelection();
index ad293b738bb317d5cc8fd041ca52be7de9137a5a..9c15f4ffde8af7faebdad8017b77b143ca6edce9 100644 (file)
@@ -90,7 +90,7 @@ public class PasteHandler extends EditorActionHandler implements EditorTextInser
 
     final Project project = editor.getProject();
     if (project == null || editor.isColumnMode() || editor.getSelectionModel().hasBlockSelection()
-        || editor.getCaretModel().supportsMultipleCarets() && editor.getCaretModel().getAllCarets().size() > 1) {
+        || editor.getCaretModel().getAllCarets().size() > 1) {
       if (myOriginalHandler != null) {
         myOriginalHandler.execute(editor, context);
       }
index df41cc22f90afc475f259980515efbe2e1f5f64c..a3e4a80aa7560499908c6f608f49735809e48ef7 100644 (file)
 
 package com.intellij.injected.editor;
 
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.LogicalPosition;
-import com.intellij.openapi.editor.VisualPosition;
+import com.intellij.openapi.editor.*;
 import com.intellij.openapi.editor.event.CaretEvent;
 import com.intellij.openapi.editor.event.CaretListener;
 import com.intellij.openapi.editor.ex.EditorEx;
@@ -226,7 +223,17 @@ public class CaretModelWindow implements CaretModel {
   }
 
   @Override
-  public void runForEachCaret(@NotNull Runnable runnable) {
-    myDelegate.runForEachCaret(runnable);
+  public void runForEachCaret(final @NotNull CaretAction action) {
+    myDelegate.runForEachCaret(new CaretAction() {
+      @Override
+      public void perform(Caret caret) {
+        action.perform(createInjectedCaret(caret));
+      }
+    });
+  }
+
+  @Override
+  public void runBatchCaretOperation(@NotNull Runnable runnable) {
+    myDelegate.runBatchCaretOperation(runnable);
   }
 }
index 2535bf520a31c088919eb4421d4b772e9b0270fa..cf674d6abe72a5461fbb7a44eb3f4fd47aca088e 100644 (file)
@@ -141,9 +141,9 @@ public class EditorModificationUtil {
     if (editor.getCaretModel().supportsMultipleCarets()) {
       int caretCount = editor.getCaretModel().getAllCarets().size();
       final Iterator<String> segments = new ClipboardTextPerCaretSplitter().split(text, caretCount).iterator();
-      editor.getCaretModel().runForEachCaret(new Runnable() {
+      editor.getCaretModel().runForEachCaret(new CaretAction() {
         @Override
-        public void run() {
+        public void perform(Caret caret) {
           insertStringAtCaret(editor, segments.next(), false, true);
         }
       });
index 2ae76ae2b135da90e2bfcba6fb452d8a8be714e3..30368badf663ca37029defc468899a2ab1923422 100644 (file)
 package com.intellij.openapi.editor.actionSystem;
 
 import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretAction;
 import com.intellij.openapi.editor.Editor;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * Interface for actions activated by keystrokes in the editor.
+ * Implementations should override {@link #execute(com.intellij.openapi.editor.Editor, com.intellij.openapi.actionSystem.DataContext)} or
+ * {@link #execute(com.intellij.openapi.editor.Editor, com.intellij.openapi.editor.Caret, com.intellij.openapi.actionSystem.DataContext)}
+ * (preferrably).
  *
  * @see EditorActionManager#setActionHandler(String, EditorActionHandler)
  */
 public abstract class EditorActionHandler {
   private final boolean myRunForEachCaret;
+  private boolean inExecution;
 
   protected EditorActionHandler() {
     this(false);
@@ -51,7 +58,38 @@ public abstract class EditorActionHandler {
    * @param editor      the editor in which the action is invoked.
    * @param dataContext the data context for the action.
    */
-  public abstract void execute(Editor editor, DataContext dataContext);
+  public void execute(Editor editor, DataContext dataContext) {
+    if (inExecution) {
+      return;
+    }
+    try {
+      inExecution = true;
+      execute(editor, editor.getCaretModel().getCurrentCaret(), dataContext);
+    }
+    finally {
+      inExecution = false;
+    }
+  }
+
+  /**
+   * Executes the action for the given caret.
+   *
+   * @param editor      the editor in which the action is invoked.
+   * @param caret       the caret for which the action is performed at the moment
+   * @param dataContext the data context for the action.
+   */
+  public void execute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
+    if (inExecution) {
+      return;
+    }
+    try {
+      inExecution = true;
+      execute(editor, dataContext);
+    }
+    finally {
+      inExecution = false;
+    }
+  }
 
   public boolean executeInCommand(Editor editor, DataContext dataContext) {
     return true;
@@ -61,17 +99,23 @@ public abstract class EditorActionHandler {
     return myRunForEachCaret;
   }
 
+  /**
+   * Executes the action for all carets
+   *
+   * @param editor      the editor in which the action is invoked.
+   * @param dataContext the data context for the action.
+   */
   public void executeForAllCarets(final Editor editor, final DataContext dataContext) {
-    if (editor.getCaretModel().supportsMultipleCarets() && runForAllCarets()) {
-      editor.getCaretModel().runForEachCaret(new Runnable() {
+    if (runForAllCarets()) {
+      editor.getCaretModel().runForEachCaret(new CaretAction() {
         @Override
-        public void run() {
-          execute(editor, dataContext);
+        public void perform(Caret caret) {
+          execute(editor, caret, dataContext);
         }
       });
     }
     else {
-      execute(editor, dataContext);
+      execute(editor, editor.getCaretModel().getPrimaryCaret(), dataContext);
     }
   }
 
index 082f1a68bb2630b1b49e8e72442ea751a7cc818f..68f0fd31c7dc38ce76cdec994e07c8ef2e0918cc 100644 (file)
@@ -23,8 +23,11 @@ import com.intellij.openapi.editor.event.DocumentEvent;
 import com.intellij.openapi.editor.event.MockDocumentEvent;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
 
 public abstract class EditorWriteActionHandler extends EditorActionHandler {
+  private boolean inExecution;
+
   protected EditorWriteActionHandler() {
   }
 
@@ -33,7 +36,7 @@ public abstract class EditorWriteActionHandler extends EditorActionHandler {
   }
 
   @Override
-  public final void execute(final Editor editor, final DataContext dataContext) {
+  public final void execute(final Editor editor, @NotNull final Caret caret, final DataContext dataContext) {
     if (editor.isViewer()) return;
 
     if (dataContext != null) {
@@ -58,7 +61,7 @@ public abstract class EditorWriteActionHandler extends EditorActionHandler {
 
         doc.startGuardedBlockChecking();
         try {
-          executeWriteAction(editor, dataContext);
+          executeWriteAction(editor, caret, dataContext);
         }
         catch (ReadOnlyFragmentModificationException e) {
           EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(doc).handle(e);
@@ -70,5 +73,29 @@ public abstract class EditorWriteActionHandler extends EditorActionHandler {
     });
   }
 
-  public abstract void executeWriteAction(Editor editor, DataContext dataContext);
+  public void executeWriteAction(Editor editor, DataContext dataContext) {
+    if (inExecution) {
+      return;
+    }
+    try {
+      inExecution = true;
+      executeWriteAction(editor, editor.getCaretModel().getCurrentCaret(), dataContext);
+    }
+    finally {
+      inExecution = false;
+    }
+  }
+
+  public void executeWriteAction(Editor editor, @NotNull Caret caret, DataContext dataContext) {
+    if (inExecution) {
+      return;
+    }
+    try {
+      inExecution = true;
+      executeWriteAction(editor, dataContext);
+    }
+    finally {
+      inExecution = false;
+    }
+  }
 }
index 0ceeabb21b95d5cd1c7de653d6d426546808adfd..c52ec738af4eee9d18301388b166c8ba40133a34 100644 (file)
@@ -127,9 +127,9 @@ public class TypedAction {
       ApplicationManager.getApplication().runWriteAction(new DocumentRunnable(myEditor.getDocument(), myEditor.getProject()) {
         @Override
         public void run() {
-          myEditor.getCaretModel().runForEachCaret(new Runnable() {
+          myEditor.getCaretModel().runForEachCaret(new CaretAction() {
             @Override
-            public void run() {
+            public void perform(Caret caret) {
               Document doc = myEditor.getDocument();
               doc.startGuardedBlockChecking();
               try {
index 4b27db7a57736c4d8a75209de803eb06648a3819..4b91f5245eabffb00fac308a2b98c3ef825ce082 100644 (file)
 package com.intellij.openapi.editor.actions;
 
 import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.actionSystem.EditorAction;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
+import org.jetbrains.annotations.NotNull;
 
 public class CloneCaretAbove extends EditorAction {
   public CloneCaretAbove() {
@@ -32,11 +33,8 @@ public class CloneCaretAbove extends EditorAction {
     }
 
     @Override
-    public void execute(Editor editor, DataContext dataContext) {
-      CaretModel caretModel = editor.getCaretModel();
-      if (caretModel.supportsMultipleCarets()) {
-        caretModel.getCurrentCaret().clone(true);
-      }
+    public void execute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
+      caret.clone(true);
     }
 
     @Override
index 48f46bbeba040cca2ece7a3d62dd4fb201db0fb5..df3a966a405ab3ce75100e78735eedc32a524fc9 100644 (file)
 package com.intellij.openapi.editor.actions;
 
 import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.CaretModel;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.actionSystem.EditorAction;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
+import org.jetbrains.annotations.NotNull;
 
 public class CloneCaretBelow extends EditorAction {
   public CloneCaretBelow() {
@@ -32,10 +34,10 @@ public class CloneCaretBelow extends EditorAction {
     }
 
     @Override
-    public void execute(Editor editor, DataContext dataContext) {
+    public void execute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
       CaretModel caretModel = editor.getCaretModel();
       if (caretModel.supportsMultipleCarets()) {
-        caretModel.getCurrentCaret().clone(false);
+        caret.clone(false);
       }
     }
 
index 302948b7468aaa9d72f4fc1b9fd0112ad0281731..24e5e553b92334b995beafb412668f5a74bf04a7 100644 (file)
@@ -25,6 +25,8 @@
 package com.intellij.openapi.editor.actions;
 
 import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretAction;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.actionSystem.EditorAction;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
@@ -45,9 +47,9 @@ public class CopyAction extends EditorAction {
         if (Registry.is(SKIP_COPY_AND_CUT_FOR_EMPTY_SELECTION_KEY)) {
           return;
         }
-        editor.getCaretModel().runForEachCaret(new Runnable() {
+        editor.getCaretModel().runForEachCaret(new CaretAction() {
           @Override
-          public void run() {
+          public void perform(Caret caret) {
             editor.getSelectionModel().selectLineAtCaret();
             EditorActionUtil.moveCaretToLineStartIgnoringSoftWraps(editor);
           }
index 92e6b5b2f645d7a84a165ba55b7453c8789429d9..4fc378e566c63f1dd7e299a7caf747373c81286e 100644 (file)
  */
 package com.intellij.openapi.editor.actions;
 
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretAction;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.EditorModificationUtil;
 import com.intellij.openapi.editor.actionSystem.EditorAction;
 import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
-import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.util.registry.Registry;
 
 public class CutAction extends EditorAction {
@@ -43,17 +45,17 @@ public class CutAction extends EditorAction {
         if (Registry.is(CopyAction.SKIP_COPY_AND_CUT_FOR_EMPTY_SELECTION_KEY)) {
           return;
         }
-        editor.getCaretModel().runForEachCaret(new Runnable() {
+        editor.getCaretModel().runForEachCaret(new CaretAction() {
           @Override
-          public void run() {
+          public void perform(Caret caret) {
             editor.getSelectionModel().selectLineAtCaret();
           }
         });
       }
       editor.getSelectionModel().copySelectionToClipboard();
-      editor.getCaretModel().runForEachCaret(new Runnable() {
+      editor.getCaretModel().runForEachCaret(new CaretAction() {
         @Override
-        public void run() {
+        public void perform(Caret caret) {
           EditorModificationUtil.deleteSelectedText(editor);
         }
       });
index 8a19c8c19c27d50bda27f165f2b3558ccf609f27..16b66768d791410f213537a36740289fc61652b3 100644 (file)
@@ -50,8 +50,7 @@ public class EscapeAction extends EditorAction {
     public boolean isEnabled(Editor editor, DataContext dataContext) {
       SelectionModel selectionModel = editor.getSelectionModel();
       CaretModel caretModel = editor.getCaretModel();
-      return selectionModel.hasSelection() || selectionModel.hasBlockSelection()
-             || caretModel.supportsMultipleCarets() && caretModel.getAllCarets().size() > 1;
+      return selectionModel.hasSelection() || selectionModel.hasBlockSelection() || caretModel.getAllCarets().size() > 1;
     }
   }
 }
index 1e893e6d9ee6f1a93ce3ec660d61db8f82cabf0d..607def60832169a75b37fcb3dd2afabbce539c2e 100644 (file)
 package com.intellij.openapi.editor.actions;
 
 import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.actionSystem.EditorAction;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
+import org.jetbrains.annotations.NotNull;
 
 public class MoveCaretDownWithSelectionAction extends EditorAction {
   public MoveCaretDownWithSelectionAction() {
@@ -40,9 +42,9 @@ public class MoveCaretDownWithSelectionAction extends EditorAction {
     }
 
     @Override
-    public void execute(Editor editor, DataContext dataContext) {
+    public void execute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
       if (editor.isColumnMode() && editor.getCaretModel().supportsMultipleCarets()) {
-        editor.getCaretModel().getCurrentCaret().clone(false);
+        caret.clone(false);
       }
       else {
         editor.getCaretModel().moveCaretRelatively(0, 1, true, editor.isColumnMode(), true);
index a6028fd5d5d2613ee3dd8c030e38f1da79c032f3..f4bb99ec2fa4dd74f1cfaecbd3341a6b3cd742e9 100644 (file)
 package com.intellij.openapi.editor.actions;
 
 import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.actionSystem.EditorAction;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
+import org.jetbrains.annotations.NotNull;
 
 public class MoveCaretUpWithSelectionAction extends EditorAction {
   public MoveCaretUpWithSelectionAction() {
@@ -40,10 +42,9 @@ public class MoveCaretUpWithSelectionAction extends EditorAction {
     }
 
     @Override
-    public void execute(Editor editor, DataContext dataContext) {
+    public void execute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
       if (editor.isColumnMode() && editor.getCaretModel().supportsMultipleCarets()) {
-        editor.getCaretModel().getCurrentCaret().clone(true);
-
+        caret.clone(true);
       }
       else {
         editor.getCaretModel().moveCaretRelatively(0, -1, true, editor.isColumnMode(), true);
index 427a4d96e908e88b91dc62a719f7ece4e2754122..07b6b6672ce891c6d3bfcd5646fb62e68cb5e599 100644 (file)
  */
 package com.intellij.openapi.editor.actions;
 
+import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.actionSystem.EditorAction;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
-import com.intellij.openapi.actionSystem.DataContext;
+import org.jetbrains.annotations.NotNull;
 
 public class PageDownWithSelectionAction extends EditorAction {
   public static class Handler extends EditorActionHandler {
@@ -37,13 +38,13 @@ public class PageDownWithSelectionAction extends EditorAction {
     }
 
     @Override
-    public void execute(Editor editor, DataContext dataContext) {
+    public void execute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
       if (editor.isColumnMode() && editor.getCaretModel().supportsMultipleCarets()) {
         int lines = editor.getScrollingModel().getVisibleArea().height / editor.getLineHeight();
-        Caret caret = editor.getCaretModel().getCurrentCaret();
+        Caret currentCaret = caret;
         for (int i = 0; i < lines; i++) {
-          caret = caret.clone(false);
-          if (caret == null) {
+          currentCaret = currentCaret.clone(false);
+          if (currentCaret == null) {
             break;
           }
         }
index b392086dc1451ad99f24dd7d065f820b698a088e..e5a6a08fa256d26eda0dc32fdefbb311d744c818 100644 (file)
@@ -29,6 +29,7 @@ import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.actionSystem.EditorAction;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
 import com.intellij.openapi.actionSystem.DataContext;
+import org.jetbrains.annotations.NotNull;
 
 public class PageUpWithSelectionAction extends EditorAction {
   public static class Handler extends EditorActionHandler {
@@ -37,13 +38,13 @@ public class PageUpWithSelectionAction extends EditorAction {
     }
 
     @Override
-    public void execute(Editor editor, DataContext dataContext) {
+    public void execute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
       if (editor.isColumnMode() && editor.getCaretModel().supportsMultipleCarets()) {
         int lines = editor.getScrollingModel().getVisibleArea().height / editor.getLineHeight();
-        Caret caret = editor.getCaretModel().getCurrentCaret();
+        Caret currentCaret = caret;
         for (int i = 0; i < lines; i++) {
-          caret = caret.clone(true);
-          if (caret == null) {
+          currentCaret = currentCaret.clone(true);
+          if (currentCaret == null) {
             break;
           }
         }
index c93d7d6f0341d561e3a6e323fa4301b77f13d058..331b31a8ca17e3c4df216406032f5c5a5a790825 100644 (file)
@@ -357,10 +357,10 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
   }
 
   @Override
-  public void runForEachCaret(@NotNull final Runnable runnable) {
+  public void runForEachCaret(@NotNull final CaretAction action) {
     myEditor.assertIsDispatchThread();
     if (!supportsMultipleCarets()) {
-      runnable.run();
+      action.perform(getPrimaryCaret());
       return;
     }
     if (myCurrentCaret != null) {
@@ -372,7 +372,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
           Collection<Caret> sortedCarets = getAllCarets();
           for (Caret caret : sortedCarets) {
             myCurrentCaret = (CaretImpl)caret;
-            runnable.run();
+            action.perform(caret);
           }
         }
         finally {
@@ -382,6 +382,11 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
     });
   }
 
+  @Override
+  public void runBatchCaretOperation(@NotNull Runnable runnable) {
+    doWithCaretMerging(runnable);
+  }
+
   private void mergeOverlappingCaretsAndSelections() {
     if (!supportsMultipleCarets() || myCarets.size() <= 1) {
       return;
index ef49b827c7dc0937e085206aed8c9891a709890d..f602869ea45b1a209d4c63c21109a6335cbbe95e 100644 (file)
@@ -439,7 +439,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
       @Override
       public void caretRemoved(CaretEvent e) {
         ((CaretImpl)e.getCaret()).updateVisualPosition(); // repainting caret region
-        myPrimaryCaret = myCaretModel.getPrimaryCaret(); // repainting new primary caret's row backgroun
+        myPrimaryCaret = myCaretModel.getPrimaryCaret(); // repainting new primary caret's row background
         myPrimaryCaret.updateVisualPosition();
       }
     });
@@ -3531,17 +3531,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
                                 : 0;
 
     if (!myDocument.isInBulkUpdate()) {
-      if (myCaretModel.supportsMultipleCarets()) {
-        for (Caret caret : myCaretModel.getAllCarets()) {
-          if (caret.isUpToDate()) {
-            int caretX = visualPositionToXY(caret.getVisualPosition()).x;
-            draft.width = Math.max(caretX, draft.width);
-          }
-        }
-      }
-      else {
-        if (getCaretModel().isUpToDate()) {
-          int caretX = visualPositionToXY(getCaretModel().getVisualPosition()).x;
+      for (Caret caret : myCaretModel.getAllCarets()) {
+        if (caret.isUpToDate()) {
+          int caretX = visualPositionToXY(caret.getVisualPosition()).x;
           draft.width = Math.max(caretX, draft.width);
         }
       }
index 184f004d9bee4a1939326c75b1fec47556580fae..90780f19bf46132794225cb5d57ed12eb493f629 100644 (file)
@@ -138,7 +138,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi
 
   @Override
   public boolean hasSelection(boolean anyCaret) {
-    if (!anyCaret || !myEditor.getCaretModel().supportsMultipleCarets()) {
+    if (!anyCaret) {
       return myEditor.getCaretModel().getCurrentCaret().hasSelection();
     }
     for (Caret caret : myEditor.getCaretModel().getAllCarets()) {
@@ -197,7 +197,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi
 
   @Override
   public void removeSelection(boolean allCarets) {
-    if (!allCarets || !myEditor.getCaretModel().supportsMultipleCarets()) {
+    if (!allCarets) {
       myEditor.getCaretModel().getCurrentCaret().removeSelection();
     }
     else {
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java
new file mode 100644 (file)
index 0000000..5b14cbd
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2000-2014 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.textarea;
+
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.util.UserDataHolderBase;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class TextComponentCaret extends UserDataHolderBase implements Caret {
+  private final Editor myEditor;
+  private final CaretModel myCaretModel;
+  private final SelectionModel mySelectionModel;
+
+  public TextComponentCaret(Editor editor) {
+    myEditor = editor;
+    myCaretModel = editor.getCaretModel();
+    mySelectionModel = editor.getSelectionModel();
+  }
+
+  @NotNull
+  @Override
+  public CaretModel getCaretModel() {
+    return myCaretModel;
+  }
+
+  @Override
+  public boolean isValid() {
+    return true;
+  }
+
+  @Override
+  public void moveCaretRelatively(int columnShift, int lineShift, boolean withSelection, boolean scrollToCaret) {
+    myCaretModel.moveCaretRelatively(columnShift, lineShift, withSelection, false, scrollToCaret);
+  }
+
+  @Override
+  public void moveToLogicalPosition(@NotNull LogicalPosition pos) {
+    myCaretModel.moveToLogicalPosition(pos);
+  }
+
+  @Override
+  public void moveToVisualPosition(@NotNull VisualPosition pos) {
+    myCaretModel.moveToVisualPosition(pos);
+  }
+
+  @Override
+  public void moveToOffset(int offset) {
+    myCaretModel.moveToOffset(offset);
+  }
+
+  @Override
+  public void moveToOffset(int offset, boolean locateBeforeSoftWrap) {
+    myCaretModel.moveToOffset(offset, locateBeforeSoftWrap);
+  }
+
+  @Override
+  public boolean isUpToDate() {
+    return myCaretModel.isUpToDate();
+  }
+
+  @NotNull
+  @Override
+  public LogicalPosition getLogicalPosition() {
+    return myCaretModel.getLogicalPosition();
+  }
+
+  @NotNull
+  @Override
+  public VisualPosition getVisualPosition() {
+    return myCaretModel.getVisualPosition();
+  }
+
+  @Override
+  public int getOffset() {
+    return myCaretModel.getOffset();
+  }
+
+  @Override
+  public int getVisualLineStart() {
+    return myCaretModel.getVisualLineStart();
+  }
+
+  @Override
+  public int getVisualLineEnd() {
+    return myCaretModel.getVisualLineEnd();
+  }
+
+  @Override
+  public int getSelectionStart() {
+    return mySelectionModel.getSelectionStart();
+  }
+
+  @NotNull
+  @Override
+  public VisualPosition getSelectionStartPosition() {
+    return myEditor.offsetToVisualPosition(mySelectionModel.getSelectionStart());
+  }
+
+  @Override
+  public int getSelectionEnd() {
+    return mySelectionModel.getSelectionEnd();
+  }
+
+  @NotNull
+  @Override
+  public VisualPosition getSelectionEndPosition() {
+    return myEditor.offsetToVisualPosition(mySelectionModel.getSelectionEnd());
+  }
+
+  @Nullable
+  @Override
+  public String getSelectedText() {
+    return mySelectionModel.getSelectedText();
+  }
+
+  @Override
+  public int getLeadSelectionOffset() {
+    return mySelectionModel.getLeadSelectionOffset();
+  }
+
+  @NotNull
+  @Override
+  public VisualPosition getLeadSelectionPosition() {
+    return myEditor.offsetToVisualPosition(mySelectionModel.getLeadSelectionOffset());
+  }
+
+  @Override
+  public boolean hasSelection() {
+    return mySelectionModel.hasSelection();
+  }
+
+  @Override
+  public void setSelection(int startOffset, int endOffset) {
+    mySelectionModel.setSelection(startOffset, endOffset);
+  }
+
+  @Override
+  public void setSelection(int startOffset, @Nullable VisualPosition endPosition, int endOffset) {
+    mySelectionModel.setSelection(startOffset, endPosition, endOffset);
+  }
+
+  @Override
+  public void setSelection(@Nullable VisualPosition startPosition, int startOffset, @Nullable VisualPosition endPosition, int endOffset) {
+    mySelectionModel.setSelection(startPosition, startOffset, endPosition, endOffset);
+  }
+
+  @Override
+  public void removeSelection() {
+    mySelectionModel.removeSelection();
+  }
+
+  @Override
+  public void selectLineAtCaret() {
+    mySelectionModel.selectLineAtCaret();
+  }
+
+  @Override
+  public void selectWordAtCaret(boolean honorCamelWordsSettings) {
+    mySelectionModel.selectWordAtCaret(honorCamelWordsSettings);
+  }
+
+  @Nullable
+  @Override
+  public Caret clone(boolean above) {
+    return null;
+  }
+
+  @Override
+  public void dispose() {
+  }
+}
index 8f935e91ff80044c3e36e37f8eece4e8b6cdb4cf..32e8f3b66592137cde0c94dd8bac789ec056896d 100644 (file)
  */
 package com.intellij.openapi.editor.textarea;
 
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.LogicalPosition;
-import com.intellij.openapi.editor.VisualPosition;
+import com.intellij.openapi.editor.*;
 import com.intellij.openapi.editor.event.CaretListener;
 import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.util.Segment;
@@ -29,6 +26,7 @@ import javax.swing.*;
 import javax.swing.text.BadLocationException;
 import javax.swing.text.JTextComponent;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -37,10 +35,12 @@ import java.util.List;
 public class TextComponentCaretModel implements CaretModel {
   private final JTextComponent myTextComponent;
   private final TextComponentEditor myEditor;
+  private final Caret myCaret;
 
   public TextComponentCaretModel(@NotNull JTextComponent textComponent, @NotNull TextComponentEditor editor) {
     myTextComponent = textComponent;
     myEditor = editor;
+    myCaret = new TextComponentCaret(editor);
   }
 
   @Override
@@ -143,36 +143,36 @@ public class TextComponentCaretModel implements CaretModel {
   @NotNull
   @Override
   public Caret getCurrentCaret() {
-    throw new UnsupportedOperationException("Multiple carets are not supported");
+    return myCaret;
   }
 
   @NotNull
   @Override
   public Caret getPrimaryCaret() {
-    throw new UnsupportedOperationException("Multiple carets are not supported");
+    return myCaret;
   }
 
   @NotNull
   @Override
   public Collection<Caret> getAllCarets() {
-    throw new UnsupportedOperationException("Multiple carets are not supported");
+    return Collections.singleton(myCaret);
   }
 
   @Nullable
   @Override
   public Caret getCaretAt(@NotNull VisualPosition pos) {
-    throw new UnsupportedOperationException("Multiple carets are not supported");
+    return myCaret.getVisualPosition().equals(pos) ? myCaret : null;
   }
 
   @Nullable
   @Override
   public Caret addCaret(@NotNull VisualPosition pos) {
-    throw new UnsupportedOperationException("Multiple carets are not supported");
+    return null;
   }
 
   @Override
   public boolean removeCaret(@NotNull Caret caret) {
-    throw new UnsupportedOperationException("Multiple carets are not supported");
+    return false;
   }
 
   @Override
@@ -185,7 +185,12 @@ public class TextComponentCaretModel implements CaretModel {
   }
 
   @Override
-  public void runForEachCaret(@NotNull Runnable runnable) {
+  public void runForEachCaret(@NotNull CaretAction action) {
+    action.perform(myCaret);
+  }
+
+  @Override
+  public void runBatchCaretOperation(@NotNull Runnable runnable) {
     runnable.run();
   }
 }
index 1f9a42753abfc36051edf69621ce4fa03087470f..80e0cbbc128b09f4934ffc96a658c7c8bffa7ef3 100644 (file)
@@ -98,7 +98,7 @@ public class TextComponentEditor extends UserDataHolderBase implements Editor {
 
   @Override
   @NotNull
-  public SelectionModel getSelectionModel() {
+  public TextComponentSelectionModel getSelectionModel() {
     return mySelectionModel;
   }
 
index 3333bdcb0e2146d9dee2de12a7f36d0ef6e1bafb..e07a85cfbc385e901d6430ae53d58d40747d3bae 100644 (file)
@@ -220,25 +220,15 @@ public class TextEditorProvider implements FileEditorProvider, DumbAware {
   protected TextEditorState getStateImpl(final Project project, @NotNull Editor editor, @NotNull FileEditorStateLevel level){
     TextEditorState state = new TextEditorState();
     CaretModel caretModel = editor.getCaretModel();
-    if (caretModel.supportsMultipleCarets()) {
-      Collection<Caret> allCarets = caretModel.getAllCarets();
-      state.CARETS = new TextEditorState.CaretState[allCarets.size()];
-      int i = 0;
-      for (Caret caret : allCarets) {
-        state.CARETS[i] = new TextEditorState.CaretState();
-        state.CARETS[i].LINE = caret.getLogicalPosition().line;
-        state.CARETS[i].COLUMN = caret.getLogicalPosition().column;
-        state.CARETS[i].SELECTION_START = caret.getSelectionStart();
-        state.CARETS[i++].SELECTION_END = caret.getSelectionEnd();
-      }
-    }
-    else {
-      state.CARETS = new TextEditorState.CaretState[1];
-      state.CARETS[0] = new TextEditorState.CaretState();
-      state.CARETS[0].LINE = editor.getCaretModel().getLogicalPosition().line;
-      state.CARETS[0].COLUMN = editor.getCaretModel().getLogicalPosition().column;
-      state.CARETS[0].SELECTION_START = editor.getSelectionModel().getSelectionStart();
-      state.CARETS[0].SELECTION_END = editor.getSelectionModel().getSelectionEnd();
+    Collection<Caret> allCarets = caretModel.getAllCarets();
+    state.CARETS = new TextEditorState.CaretState[allCarets.size()];
+    int i = 0;
+    for (Caret caret : allCarets) {
+      state.CARETS[i] = new TextEditorState.CaretState();
+      state.CARETS[i].LINE = caret.getLogicalPosition().line;
+      state.CARETS[i].COLUMN = caret.getLogicalPosition().column;
+      state.CARETS[i].SELECTION_START = caret.getSelectionStart();
+      state.CARETS[i++].SELECTION_END = caret.getSelectionEnd();
     }
 
     // Saving scrolling proportion on UNDO may cause undesirable results of undo action fails to perform since
index 74ef6d440cb12c0d6aae0c2b803eeace9726cc79..a3e5b9c3f5a8d2ea87cef4caaa2166957f76c45e 100644 (file)
@@ -400,18 +400,12 @@ public abstract class LightPlatformCodeInsightTestCase extends LightPlatformTest
   @SuppressWarnings("ConstantConditions")
   private static void checkCaretAndSelectionPositions(EditorTestUtil.CaretsState caretState, String newFileText, String message) {
     CaretModel caretModel = myEditor.getCaretModel();
-    List<Caret> allCarets = caretModel.supportsMultipleCarets() ? new ArrayList<Caret>(caretModel.getAllCarets()) : null;
-    assertEquals("Unexpected number of carets", caretState.carets.size(), caretModel.supportsMultipleCarets() ? allCarets.size() : 1);
+    List<Caret> allCarets = new ArrayList<Caret>(caretModel.getAllCarets());
+    assertEquals("Unexpected number of carets", caretState.carets.size(), allCarets.size());
     for (int i = 0; i < caretState.carets.size(); i++) {
       String caretDescription = getCaretDescription(i, caretState.carets.size());
-      Caret currentCaret = caretModel.supportsMultipleCarets() ? allCarets.get(i) : null;
-      LogicalPosition actualCaretPosition;
-      if (caretModel.supportsMultipleCarets()) {
-        actualCaretPosition = currentCaret.getLogicalPosition();
-      }
-      else {
-        actualCaretPosition = caretModel.getLogicalPosition();
-      }
+      Caret currentCaret = allCarets.get(i);
+      LogicalPosition actualCaretPosition = currentCaret.getLogicalPosition();
       EditorTestUtil.Caret expected = caretState.carets.get(i);
       if (expected.offset != null) {
         int caretLine = StringUtil.offsetToLineNumber(newFileText, expected.offset);
@@ -433,28 +427,25 @@ public abstract class LightPlatformCodeInsightTestCase extends LightPlatformTest
         assertEquals(
             getMessage("selectionStartLine" + caretDescription, message),
             selStartLine + 1,
-            StringUtil.offsetToLineNumber(newFileText, caretModel.supportsMultipleCarets() ? currentCaret.getSelectionStart() : myEditor.getSelectionModel().getSelectionStart()) + 1);
+            StringUtil.offsetToLineNumber(newFileText, currentCaret.getSelectionStart()) + 1);
 
         assertEquals(
             getMessage("selectionStartCol" + caretDescription, message),
             selStartCol + 1,
-            (caretModel.supportsMultipleCarets() ? currentCaret.getSelectionStart() : myEditor.getSelectionModel().getSelectionStart()) -
-            StringUtil.lineColToOffset(newFileText, selStartLine, 0) +
-                                                                     1);
+            currentCaret.getSelectionStart() - StringUtil.lineColToOffset(newFileText, selStartLine, 0) + 1);
 
         assertEquals(
-            getMessage("selectionEndLine" + caretDescription, message),
+          getMessage("selectionEndLine" + caretDescription, message),
             selEndLine + 1,
-            StringUtil.offsetToLineNumber(newFileText, caretModel.supportsMultipleCarets() ? currentCaret.getSelectionEnd() : myEditor.getSelectionModel().getSelectionEnd()) + 1);
+            StringUtil.offsetToLineNumber(newFileText, currentCaret.getSelectionEnd()) + 1);
 
         assertEquals(
             getMessage("selectionEndCol" + caretDescription, message),
             selEndCol + 1,
-            (caretModel.supportsMultipleCarets() ? currentCaret.getSelectionEnd() : myEditor.getSelectionModel().getSelectionEnd()) - StringUtil.lineColToOffset(newFileText, selEndLine, 0) +
-            1);
+            currentCaret.getSelectionEnd() - StringUtil.lineColToOffset(newFileText, selEndLine, 0) + 1);
       }
       else {
-        assertFalse(getMessage("must not have selection" + caretDescription, message), caretModel.supportsMultipleCarets() ? currentCaret.hasSelection() : myEditor.getSelectionModel().hasSelection());
+        assertFalse(getMessage("must not have selection" + caretDescription, message), currentCaret.hasSelection());
       }
     }
   }
index 89ff10f437d74ad3528eff3faeef3afcef1d9ff6..82a3ad68c25e66d9cb5a6a8b6455d1aa2457483f 100644 (file)
@@ -1755,8 +1755,8 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
       return; // nothing to check, so we skip caret/selection assertions
     }
     CaretModel caretModel = editor.getCaretModel();
-    List<Caret> allCarets = caretModel.supportsMultipleCarets() ? new ArrayList<Caret>(caretModel.getAllCarets()) : null;
-    assertEquals("Unexpected number of carets", loader.caretState.carets.size(), caretModel.supportsMultipleCarets() ? allCarets.size() : 1);
+    List<Caret> allCarets = new ArrayList<Caret>(caretModel.getAllCarets());
+    assertEquals("Unexpected number of carets", loader.caretState.carets.size(), allCarets.size());
     for (int i = 0; i < loader.caretState.carets.size(); i++) {
       EditorTestUtil.Caret expected = loader.caretState.carets.get(i);
       String caretDescription = loader.caretState.carets.size() == 1 ? "" : "(" + (i + 1) + "/" + loader.caretState.carets.size() + ") ";
@@ -1766,8 +1766,8 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
         int caretLine = StringUtil.offsetToLineNumber(loader.newFileText, expected.offset);
         int caretCol = EditorUtil.calcColumnNumber(null, loader.newFileText, StringUtil.lineColToOffset(loader.newFileText, caretLine, 0), expected.offset, tabSize);
 
-        final int actualLine = caretModel.supportsMultipleCarets() ? allCarets.get(i).getLogicalPosition().line : caretModel.getLogicalPosition().line;
-        final int actualCol = caretModel.supportsMultipleCarets() ? allCarets.get(i).getLogicalPosition().column : caretModel.getLogicalPosition().column;
+        final int actualLine = allCarets.get(i).getLogicalPosition().line;
+        final int actualCol = allCarets.get(i).getLogicalPosition().column;
         boolean caretPositionEquals = caretLine == actualLine && caretCol == actualCol;
         assertTrue("Caret" + caretDescription + " position in " + expectedFile + " differs. Expected " + genCaretPositionPresentation(caretLine, caretCol)
                    + ". Actual " + genCaretPositionPresentation(actualLine, actualCol), caretPositionEquals);
@@ -1789,8 +1789,8 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
           selectionEnd = ends[ends.length-1];
         }
         else {
-          selectionStart = caretModel.supportsMultipleCarets() ? allCarets.get(i).getSelectionStart() : editor.getSelectionModel().getSelectionStart();
-          selectionEnd = caretModel.supportsMultipleCarets() ? allCarets.get(i).getSelectionEnd() : editor.getSelectionModel().getSelectionEnd();
+          selectionStart = allCarets.get(i).getSelectionStart();
+          selectionEnd = allCarets.get(i).getSelectionEnd();
         }
 
         final int selStartLineActual = StringUtil.offsetToLineNumber(loader.newFileText, selectionStart);