From d51dd50463943bbb9eedd3505e9bacfd132c65f5 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Fri, 4 Jul 2014 20:30:00 +0400 Subject: [PATCH] IDEA-121060 Multiple carets: comment/uncomment works incorrectly Fix for 'comment by block comment' action --- .../codeInsight/CodeInsightTestCaseUtil.java | 6 +- .../com/intellij/openapi/editor/Caret.java | 6 + .../com/intellij/psi/util/PsiUtilBase.java | 36 ++--- .../actions/MultiCaretCodeInsightAction.java | 133 ++++++++++++++++++ .../MultiCaretCodeInsightActionHandler.java | 26 ++++ .../CommentByBlockCommentHandler.java | 95 ++++++------- .../actions/CommentByBlockCommentAction.java | 13 +- .../injected/editor/InjectedCaret.java | 17 ++- .../tree/injected/InjectedLanguageUtil.java | 17 +++ .../openapi/editor/impl/CaretImpl.java | 2 + .../editor/textarea/TextComponentCaret.java | 6 + 11 files changed, 273 insertions(+), 84 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightActionHandler.java diff --git a/java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCaseUtil.java b/java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCaseUtil.java index aa77a1e03473..df90bbb727f7 100644 --- a/java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCaseUtil.java +++ b/java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCaseUtil.java @@ -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. @@ -15,13 +15,13 @@ */ 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( diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/Caret.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/Caret.java index 43b5e75e08df..ba0743b4cfb0 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/Caret.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/Caret.java @@ -25,6 +25,12 @@ import org.jetbrains.annotations.Nullable; * Provides methods to query and modify caret position and caret's associated selection. */ 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. */ diff --git a/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java b/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java index df0e0b959a23..56fe91a0dc28 100644 --- a/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java +++ b/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java @@ -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 index 000000000000..b14810147413 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java @@ -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 enabled = new Ref(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 index 000000000000..5b699cffe60a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightActionHandler.java @@ -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 diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java index ca915119388b..3704425eb54d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java @@ -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, diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/CommentByBlockCommentAction.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/CommentByBlockCommentAction.java index ce0d3b63e6e0..bd24557744f5 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/CommentByBlockCommentAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/CommentByBlockCommentAction.java @@ -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. @@ -16,11 +16,12 @@ 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; diff --git a/platform/lang-impl/src/com/intellij/injected/editor/InjectedCaret.java b/platform/lang-impl/src/com/intellij/injected/editor/InjectedCaret.java index 19651a7f68a8..e52fa66ccefc 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/InjectedCaret.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/InjectedCaret.java @@ -15,17 +15,14 @@ */ 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; @@ -34,12 +31,22 @@ class InjectedCaret implements Caret { myDelegate = delegate; } + @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(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java index f2863469d614..27089fa70e91 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java @@ -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); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java index 068f25e16565..c8eee15b0400 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java @@ -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; } 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 index 5541e61f54fb..92ef12c46558 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java @@ -27,6 +27,12 @@ public class TextComponentCaret extends UserDataHolderBase implements Caret { myEditor = editor; } + @NotNull + @Override + public Editor getEditor() { + return myEditor; + } + @NotNull @Override public CaretModel getCaretModel() {