IDEA-121060 Multiple carets: comment/uncomment works incorrectly
authorDmitry Batrak <Dmitry.Batrak@jetbrains.com>
Fri, 4 Jul 2014 16:30:00 +0000 (20:30 +0400)
committerDmitry Batrak <Dmitry.Batrak@jetbrains.com>
Wed, 9 Jul 2014 07:35:20 +0000 (11:35 +0400)
Fix for 'comment by block comment' action

java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCaseUtil.java
platform/editor-ui-api/src/com/intellij/openapi/editor/Caret.java
platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java
platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightActionHandler.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java
platform/lang-impl/src/com/intellij/codeInsight/generation/actions/CommentByBlockCommentAction.java
platform/lang-impl/src/com/intellij/injected/editor/InjectedCaret.java
platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java

index aa77a1e034735dadaa9472a47908e9fab7ba5b11..df90bbb727f78176f075478791ede3748a45fc6d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * 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.
  */
 package com.intellij.codeInsight;
 
-import com.intellij.codeInsight.actions.CodeInsightAction;
 import com.intellij.ide.DataManager;
 import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.AnActionEvent;
 
 public class CodeInsightTestCaseUtil {
-  public static void doAction(CodeInsightTestCase testCase, CodeInsightAction action, String testName, String ext) throws Exception {
+  public static void doAction(CodeInsightTestCase testCase, AnAction action, String testName, String ext) throws Exception {
     testCase.configureByFile(testName + "." + ext);
 
     action.actionPerformed(new AnActionEvent(
index 43b5e75e08df4afb3d7052248d5d02f42476c15e..ba0743b4cfb090cf6be173b7cf67478718d56b36 100644 (file)
@@ -26,6 +26,12 @@ import org.jetbrains.annotations.Nullable;
  */
 public interface Caret extends UserDataHolderEx, Disposable {
   /**
+   * Returns an instance of Editor, current caret belongs to.
+   */
+  @NotNull
+  Editor getEditor();
+
+  /**
    * Returns an instance of CaretModel, current caret is associated with.
    */
   @NotNull
index df0e0b959a23715555845e760c0609ea11a84b9b..56fe91a0dc2840a0c168254bc35ad0a6b80eaac2 100644 (file)
@@ -23,9 +23,9 @@ import com.intellij.lang.injection.InjectedLanguageManager;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.SelectionModel;
 import com.intellij.openapi.fileEditor.FileEditor;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.fileEditor.TextEditor;
@@ -83,20 +83,22 @@ public class PsiUtilBase extends PsiUtilCore implements PsiEditorUtil {
 
   @Nullable
   public static Language getLanguageInEditor(@NotNull final Editor editor, @NotNull final Project project) {
+    return getLanguageInEditor(editor.getCaretModel().getCurrentCaret(), project);
+  }
+
+  @Nullable
+  public static Language getLanguageInEditor(@NotNull Caret caret, @NotNull final Project project) {
+    Editor editor = caret.getEditor();
     PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
     if (file == null) return null;
 
-    final SelectionModel selectionModel = editor.getSelectionModel();
-    int caretOffset = editor.getCaretModel().getOffset();
-    int mostProbablyCorrectLanguageOffset = caretOffset == selectionModel.getSelectionStart() ||
-                                            caretOffset == selectionModel.getSelectionEnd()
-                                            ? selectionModel.getSelectionStart()
-                                            : caretOffset;
+    int caretOffset = caret.getOffset();
+    int mostProbablyCorrectLanguageOffset = caretOffset == caret.getSelectionEnd() ? caret.getSelectionStart() : caretOffset;
     PsiElement elt = getElementAtOffset(file, mostProbablyCorrectLanguageOffset);
     Language lang = findLanguageFromElement(elt);
 
-    if (selectionModel.hasSelection()) {
-      final Language rangeLanguage = evaluateLanguageInRange(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd(), file);
+    if (caret.hasSelection()) {
+      final Language rangeLanguage = evaluateLanguageInRange(caret.getSelectionStart(), caret.getSelectionEnd(), file);
       if (rangeLanguage == null) return file.getLanguage();
 
       lang = rangeLanguage;
@@ -115,22 +117,24 @@ public class PsiUtilBase extends PsiUtilCore implements PsiEditorUtil {
 
   @Nullable
   public static PsiFile getPsiFileInEditor(@NotNull final Editor editor, @NotNull final Project project) {
+    return getPsiFileInEditor(editor.getCaretModel().getCurrentCaret(), project);
+  }
+
+  @Nullable
+  public static PsiFile getPsiFileInEditor(@NotNull Caret caret, @NotNull final Project project) {
+    Editor editor = caret.getEditor();
     final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
     if (file == null) return null;
 
     PsiUtilCore.ensureValid(file);
 
-    final Language language = getLanguageInEditor(editor, project);
+    final Language language = getLanguageInEditor(caret, project);
     if (language == null) return file;
 
     if (language == file.getLanguage()) return file;
 
-    final SelectionModel selectionModel = editor.getSelectionModel();
-    int caretOffset = editor.getCaretModel().getOffset();
-    int mostProbablyCorrectLanguageOffset = caretOffset == selectionModel.getSelectionStart() ||
-                                            caretOffset == selectionModel.getSelectionEnd()
-                                            ? selectionModel.getSelectionStart()
-                                            : caretOffset;
+    int caretOffset = caret.getOffset();
+    int mostProbablyCorrectLanguageOffset = caretOffset == caret.getSelectionEnd() ? caret.getSelectionStart() : caretOffset;
     return getPsiFileAtOffset(file, mostProbablyCorrectLanguageOffset);
   }
 
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java
new file mode 100644 (file)
index 0000000..b148101
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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.codeInsight.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretAction;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.ScrollType;
+import com.intellij.openapi.editor.actionSystem.DocCommandGroupId;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Ref;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
+import com.intellij.psi.util.PsiUtilBase;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class MultiCaretCodeInsightAction extends AnAction {
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    final Project project = e.getProject();
+    if (project == null) {
+      return;
+    }
+    final Editor hostEditor = CommonDataKeys.EDITOR.getData(e.getDataContext());
+    if (hostEditor == null) {
+      return;
+    }
+
+    actionPerformedImpl(project, hostEditor);
+  }
+
+  public void actionPerformedImpl(final Project project, final Editor hostEditor) {
+    CommandProcessor.getInstance().executeCommand(project, new Runnable() {
+      @Override
+      public void run() {
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          @Override
+          public void run() {
+            iterateOverCarets(project, hostEditor, getHandler());
+          }
+        });
+      }
+    }, getCommandName(), DocCommandGroupId.noneGroupId(hostEditor.getDocument()));
+
+    hostEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
+  }
+
+  @Override
+  public void update(AnActionEvent e) {
+    final Presentation presentation = e.getPresentation();
+
+    Project project = e.getProject();
+    if (project == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    Editor hostEditor = CommonDataKeys.EDITOR.getData(e.getDataContext());
+    if (hostEditor == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    final Ref<Boolean> enabled  = new Ref<Boolean>(Boolean.FALSE);
+    iterateOverCarets(project, hostEditor, new MultiCaretCodeInsightActionHandler() {
+      @Override
+      public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) {
+        if (isValidFor(project, editor, caret, file)) {
+          enabled.set(Boolean.TRUE);
+        }
+      }
+    });
+    presentation.setEnabled(enabled.get());
+  }
+
+  private static void iterateOverCarets(@NotNull final Project project,
+                                 @NotNull final Editor hostEditor,
+                                 @NotNull final MultiCaretCodeInsightActionHandler handler) {
+    PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
+    final PsiFile psiFile = documentManager.getCachedPsiFile(hostEditor.getDocument());
+    documentManager.commitAllDocuments();
+
+    hostEditor.getCaretModel().runForEachCaret(new CaretAction() {
+      @Override
+      public void perform(Caret caret) {
+        Editor editor = hostEditor;
+        if (psiFile != null) {
+          Caret injectedCaret = InjectedLanguageUtil.getCaretForInjectedLanguageNoCommit(caret, psiFile);
+          if (injectedCaret != null) {
+            caret = injectedCaret;
+            editor = caret.getEditor();
+          }
+        }
+        final PsiFile file = PsiUtilBase.getPsiFileInEditor(caret, project);
+        if (file != null) {
+          handler.invoke(project, editor, caret, file);
+        }
+      }
+    }, true);
+  }
+
+  protected boolean isValidFor(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) {
+    return true;
+  }
+
+  @NotNull
+  protected abstract MultiCaretCodeInsightActionHandler getHandler();
+
+  protected String getCommandName() {
+    String text = getTemplatePresentation().getText();
+    return text == null ? "" : text;
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightActionHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightActionHandler.java
new file mode 100644 (file)
index 0000000..5b699cf
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.codeInsight.actions;
+
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class MultiCaretCodeInsightActionHandler {
+  public abstract void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file);
+}
\ No newline at end of file
index ca915119388b32555b686f6dbf4423b557834f2b..3704425eb54de213e3dd42a2aeffd17624e631db 100644 (file)
@@ -16,9 +16,9 @@
 
 package com.intellij.codeInsight.generation;
 
-import com.intellij.codeInsight.CodeInsightActionHandler;
 import com.intellij.codeInsight.CodeInsightUtilBase;
 import com.intellij.codeInsight.CommentUtil;
+import com.intellij.codeInsight.actions.MultiCaretCodeInsightActionHandler;
 import com.intellij.featureStatistics.FeatureUsageTracker;
 import com.intellij.ide.highlighter.custom.CustomFileTypeLexer;
 import com.intellij.lang.Commenter;
@@ -52,18 +52,20 @@ import org.jetbrains.annotations.Nullable;
 import java.util.ArrayList;
 import java.util.List;
 
-public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
+public class CommentByBlockCommentHandler extends MultiCaretCodeInsightActionHandler {
   private Project myProject;
   private Editor myEditor;
+  private Caret myCaret;
   private @NotNull PsiFile myFile;
   private Document myDocument;
   private CommenterDataHolder mySelfManagedCommenterData;
 
   @Override
-  public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
+  public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) {
     if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return;
     myProject = project;
     myEditor = editor;
+    myCaret = caret;
     myFile = file;
 
     myDocument = editor.getDocument();
@@ -72,19 +74,17 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
       return;
     }
     FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.block");
-    final Commenter commenter = findCommenter(myFile, myEditor);
+    final Commenter commenter = findCommenter(myFile, myEditor, caret);
     if (commenter == null) return;
 
-    final SelectionModel selectionModel = myEditor.getSelectionModel();
-
     final String prefix;
     final String suffix;
 
     if (commenter instanceof SelfManagingCommenter) {
       final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter)commenter;
       mySelfManagedCommenterData = selfManagingCommenter.createBlockCommentingState(
-        selectionModel.getSelectionStart(),
-        selectionModel.getSelectionEnd(),
+        caret.getSelectionStart(),
+        caret.getSelectionEnd(),
         myDocument,
         myFile
       );
@@ -94,12 +94,12 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
       }
 
       prefix = selfManagingCommenter.getBlockCommentPrefix(
-        selectionModel.getSelectionStart(),
+        caret.getSelectionStart(),
         myDocument,
         mySelfManagedCommenterData
       );
       suffix = selfManagingCommenter.getBlockCommentSuffix(
-        selectionModel.getSelectionEnd(),
+        caret.getSelectionEnd(),
         myDocument,
         mySelfManagedCommenterData
       );
@@ -117,9 +117,9 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
       final int commentEnd = commentedRange.getEndOffset();
       int selectionStart = commentStart;
       int selectionEnd = commentEnd;
-      if (selectionModel.hasSelection()) {
-        selectionStart = selectionModel.getSelectionStart();
-        selectionEnd = selectionModel.getSelectionEnd();
+      if (myCaret.hasSelection()) {
+        selectionStart = myCaret.getSelectionStart();
+        selectionEnd = myCaret.getSelectionEnd();
       }
       if ((commentStart < selectionStart || commentStart >= selectionEnd) && (commentEnd <= selectionStart || commentEnd > selectionEnd)) {
         commentRange(selectionStart, selectionEnd, prefix, suffix, commenter);
@@ -129,9 +129,9 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
       }
     }
     else {
-      if (selectionModel.hasBlockSelection()) {
-        final LogicalPosition start = selectionModel.getBlockStart();
-        final LogicalPosition end = selectionModel.getBlockEnd();
+      if (myEditor.getSelectionModel().hasBlockSelection()) {
+        final LogicalPosition start = myEditor.getSelectionModel().getBlockStart();
+        final LogicalPosition end = myEditor.getSelectionModel().getBlockEnd();
 
         assert start != null;
         assert end != null;
@@ -151,9 +151,9 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
           EditorModificationUtil.insertStringAtCaret(editor, prefix, true, true);
         }
       }
-      else if (selectionModel.hasSelection()) {
-        int selectionStart = selectionModel.getSelectionStart();
-        int selectionEnd = selectionModel.getSelectionEnd();
+      else if (myCaret.hasSelection()) {
+        int selectionStart = myCaret.getSelectionStart();
+        int selectionEnd = myCaret.getSelectionEnd();
         if (commenter instanceof IndentedCommenter) {
           final Boolean value = ((IndentedCommenter)commenter).forceIndentedLineComment();
           if (value != null && value == Boolean.TRUE) {
@@ -165,7 +165,7 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
       }
       else {
         EditorUtil.fillVirtualSpaceUntilCaret(editor);
-        int caretOffset = myEditor.getCaretModel().getOffset();
+        int caretOffset = myCaret.getOffset();
         if (commenter instanceof IndentedCommenter) {
           final Boolean value = ((IndentedCommenter)commenter).forceIndentedLineComment();
           if (value != null && value == Boolean.TRUE) {
@@ -177,7 +177,7 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
           }
         }
         myDocument.insertString(caretOffset, prefix + suffix);
-        myEditor.getCaretModel().moveToOffset(caretOffset + prefix.length());
+        myCaret.moveToOffset(caretOffset + prefix.length());
       }
     }
   }
@@ -188,12 +188,11 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
   }
 
   private boolean testSelectionForNonComments() {
-    SelectionModel model = myEditor.getSelectionModel();
-    if (!model.hasSelection()) {
+    if (!myCaret.hasSelection()) {
       return true;
     }
     TextRange range
-      = new TextRange(model.getSelectionStart(), model.getSelectionEnd() - 1);
+      = new TextRange(myCaret.getSelectionStart(), myCaret.getSelectionEnd() - 1);
     for (PsiElement element = myFile.findElementAt(range.getStartOffset()); element != null && range.intersects(element.getTextRange());
          element = element.getNextSibling()) {
       if (element instanceof OuterLanguageElement) {
@@ -247,7 +246,7 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
     final FileType fileType = myFile.getFileType();
     if (fileType instanceof CustomSyntaxTableFileType) {
       Lexer lexer = new CustomFileTypeLexer(((CustomSyntaxTableFileType)fileType).getSyntaxTable());
-      final int caretOffset = myEditor.getCaretModel().getOffset();
+      final int caretOffset = myCaret.getOffset();
       int commentStart = CharArrayUtil.lastIndexOf(text, commenter.getBlockCommentPrefix(), caretOffset);
       if (commentStart == -1) return null;
 
@@ -261,17 +260,16 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
     final String prefix;
     final String suffix;
 
-    final SelectionModel selectionModel = myEditor.getSelectionModel();
     if (commenter instanceof SelfManagingCommenter) {
       SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter)commenter;
 
       prefix = selfManagingCommenter.getBlockCommentPrefix(
-        selectionModel.getSelectionStart(),
+        myCaret.getSelectionStart(),
         myDocument,
         mySelfManagedCommenterData
       );
       suffix = selfManagingCommenter.getBlockCommentSuffix(
-        selectionModel.getSelectionEnd(),
+        myCaret.getSelectionEnd(),
         myDocument,
         mySelfManagedCommenterData
       );
@@ -286,8 +284,8 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
 
     if (commenter instanceof SelfManagingCommenter) {
       commentedRange = ((SelfManagingCommenter)commenter).getBlockCommentRange(
-        selectionModel.getSelectionStart(),
-        selectionModel.getSelectionEnd(),
+        myCaret.getSelectionStart(),
+        myCaret.getSelectionEnd(),
         myDocument,
         mySelfManagedCommenterData
       );
@@ -316,11 +314,10 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
   @Nullable
   private TextRange getSelectedComments(CharSequence text, String prefix, String suffix) {
     TextRange commentedRange = null;
-    final SelectionModel selectionModel = myEditor.getSelectionModel();
-    if (selectionModel.hasSelection()) {
-      int selectionStart = selectionModel.getSelectionStart();
+    if (myCaret.hasSelection()) {
+      int selectionStart = myCaret.getSelectionStart();
       selectionStart = CharArrayUtil.shiftForward(text, selectionStart, " \t\n");
-      int selectionEnd = selectionModel.getSelectionEnd() - 1;
+      int selectionEnd = myCaret.getSelectionEnd() - 1;
       selectionEnd = CharArrayUtil.shiftBackward(text, selectionEnd, " \t\n") + 1;
       if (selectionEnd - selectionStart >= prefix.length() + suffix.length() &&
           CharArrayUtil.regionMatches(text, selectionStart, prefix) &&
@@ -332,13 +329,13 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
   }
 
   @Nullable
-  private static Commenter findCommenter(PsiFile file, Editor editor) {
+  private static Commenter findCommenter(PsiFile file, Editor editor, Caret caret) {
     final FileType fileType = file.getFileType();
     if (fileType instanceof AbstractFileType) {
       return ((AbstractFileType)fileType).getCommenter();
     }
 
-    Language lang = PsiUtilBase.getLanguageInEditor(editor, file.getProject());
+    Language lang = PsiUtilBase.getLanguageInEditor(caret, file.getProject());
 
     return getCommenter(file, editor, lang, lang);
   }
@@ -371,9 +368,8 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
 
   @Nullable
   private PsiElement findCommentAtCaret() {
-    int offset = myEditor.getCaretModel().getOffset();
-    SelectionModel selectionModel = myEditor.getSelectionModel();
-    TextRange range = new TextRange(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
+    int offset = myCaret.getOffset();
+    TextRange range = new TextRange(myCaret.getSelectionStart(), myCaret.getSelectionEnd());
     if (offset == range.getEndOffset()) {
       offset--;
     }
@@ -383,21 +379,16 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
     PsiElement elt = myFile.getViewProvider().findElementAt(offset);
     if (elt == null) return null;
     PsiElement comment = PsiTreeUtil.getParentOfType(elt, PsiComment.class, false);
-    if (comment == null || selectionModel.hasSelection() && !range.contains(comment.getTextRange())) {
+    if (comment == null || myCaret.hasSelection() && !range.contains(comment.getTextRange())) {
       return null;
     }
 
     return comment;
   }
 
-  @Override
-  public boolean startInWriteAction() {
-    return true;
-  }
-
   public void commentRange(int startOffset, int endOffset, String commentPrefix, String commentSuffix, Commenter commenter) {
     CharSequence chars = myDocument.getCharsSequence();
-    LogicalPosition caretPosition = myEditor.getCaretModel().getLogicalPosition();
+    LogicalPosition caretPosition = myCaret.getLogicalPosition();
 
     if (startOffset == 0 || chars.charAt(startOffset - 1) == '\n') {
       if (endOffset == myDocument.getTextLength() || endOffset > 0 && chars.charAt(endOffset - 1) == '\n') {
@@ -426,21 +417,17 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
         nestingSuffix.append("\n");
         TextRange range =
           insertNestedComments(chars, startOffset, endOffset, nestingPrefix.toString(), nestingSuffix.toString(), commenter);
-        myEditor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
-        //myEditor.getSelectionModel().removeSelection();
+        myCaret.setSelection(range.getStartOffset(), range.getEndOffset());
         LogicalPosition pos = new LogicalPosition(caretPosition.line + 1, caretPosition.column);
-        myEditor.getCaretModel().moveToLogicalPosition(pos);
-        myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
+        myCaret.moveToLogicalPosition(pos);
         return;
       }
     }
 
     TextRange range = insertNestedComments(chars, startOffset, endOffset, commentPrefix, commentSuffix, commenter);
-    myEditor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
-    //myEditor.getSelectionModel().removeSelection();
+    myCaret.setSelection(range.getStartOffset(), range.getEndOffset());
     LogicalPosition pos = new LogicalPosition(caretPosition.line, caretPosition.column + commentPrefix.length());
-    myEditor.getCaretModel().moveToLogicalPosition(pos);
-    myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
+    myCaret.moveToLogicalPosition(pos);
   }
 
   private int doBoundCommentingAndGetShift(int offset,
index ce0d3b63e6e0dc3a1808bed540b9bd15f669338f..bd24557744f56bcc0369a9efbdff083ee7a38995 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * 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.
 
 package com.intellij.codeInsight.generation.actions;
 
-import com.intellij.codeInsight.CodeInsightActionHandler;
-import com.intellij.codeInsight.actions.BaseCodeInsightAction;
+import com.intellij.codeInsight.actions.MultiCaretCodeInsightAction;
+import com.intellij.codeInsight.actions.MultiCaretCodeInsightActionHandler;
 import com.intellij.codeInsight.generation.CommentByBlockCommentHandler;
 import com.intellij.lang.Commenter;
 import com.intellij.lang.LanguageCommenters;
+import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.fileTypes.impl.AbstractFileType;
@@ -29,19 +30,19 @@ import com.intellij.openapi.project.Project;
 import com.intellij.psi.PsiFile;
 import org.jetbrains.annotations.NotNull;
 
-public class CommentByBlockCommentAction extends BaseCodeInsightAction implements DumbAware {
+public class CommentByBlockCommentAction extends MultiCaretCodeInsightAction implements DumbAware {
   public CommentByBlockCommentAction() {
     setEnabledInModalContext(true);
   }
 
   @NotNull
   @Override
-  protected CodeInsightActionHandler getHandler() {
+  protected MultiCaretCodeInsightActionHandler getHandler() {
     return new CommentByBlockCommentHandler();
   }
 
   @Override
-  protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull final PsiFile file) {
+  protected boolean isValidFor(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull final PsiFile file) {
     final FileType fileType = file.getFileType();
     if (fileType instanceof AbstractFileType) {
       return ((AbstractFileType)fileType).getCommenter() != null;
index 19651a7f68a87a53331feba8058147555b5c6161..e52fa66ccefc177f5762d7103ba8f4edf7d514ab 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.util.Key;
 import com.intellij.openapi.util.ProperTextRange;
 import com.intellij.openapi.util.TextRange;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-class InjectedCaret implements Caret {
+public class InjectedCaret implements Caret {
   private final EditorWindow myEditorWindow;
   final Caret myDelegate;
 
@@ -36,10 +33,20 @@ class InjectedCaret implements Caret {
 
   @NotNull
   @Override
+  public Editor getEditor() {
+    return myEditorWindow;
+  }
+
+  @NotNull
+  @Override
   public CaretModel getCaretModel() {
     return myEditorWindow.getCaretModel();
   }
 
+  public Caret getDelegate() {
+    return myDelegate;
+  }
+
   @Override
   public boolean isValid() {
     return myDelegate.isValid();
index f2863469d61441bdb97b4ab06673f0109719c188..27089fa70e91ae050014bd2a5ce257be8213acc8 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.injected.editor.*;
 import com.intellij.lang.Language;
 import com.intellij.lang.LanguageUtil;
 import com.intellij.lang.injection.InjectedLanguageManager;
+import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.SelectionModel;
@@ -158,6 +159,22 @@ public class InjectedLanguageUtil {
     return getEditorForInjectedLanguageNoCommit(editor, file, offset);
   }
 
+  public static Caret getCaretForInjectedLanguageNoCommit(@Nullable Caret caret, @Nullable PsiFile file) {
+    if (caret == null || file == null || caret instanceof InjectedCaret) return caret;
+
+    PsiFile injectedFile = findInjectedPsiNoCommit(file, caret.getOffset());
+    Editor injectedEditor = getInjectedEditorForInjectedFile(caret.getEditor(), injectedFile);
+    if (!(injectedEditor instanceof EditorWindow)) {
+      return caret;
+    }
+    for (Caret injectedCaret : injectedEditor.getCaretModel().getAllCarets()) {
+      if (((InjectedCaret)injectedCaret).getDelegate() == caret) {
+        return injectedCaret;
+      }
+    }
+    return null;
+  }
+
   public static Editor getEditorForInjectedLanguageNoCommit(@Nullable Editor editor, @Nullable PsiFile file, final int offset) {
     if (editor == null || file == null || editor instanceof EditorWindow) return editor;
     PsiFile injectedFile = findInjectedPsiNoCommit(file, offset);
index 068f25e16565cb06eb2eda358eb830436c95d4e2..c8eee15b04006a254f03c2a6b11785b50df1f220 100644 (file)
@@ -1453,6 +1453,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
     return marker != null && marker.isValid() && isVirtualSelectionEnabled() && myEndVirtualOffset > myStartVirtualOffset;
   }
 
+  @Override
+  @NotNull
   public EditorImpl getEditor() {
     return myEditor;
   }
index 5541e61f54fb997bbba67e5c77c9879e9879fba4..92ef12c46558590743de64cda156f257a8c26236 100644 (file)
@@ -29,6 +29,12 @@ public class TextComponentCaret extends UserDataHolderBase implements Caret {
 
   @NotNull
   @Override
+  public Editor getEditor() {
+    return myEditor;
+  }
+
+  @NotNull
+  @Override
   public CaretModel getCaretModel() {
     return myEditor.getCaretModel();
   }