summaryrefslogtreecommitdiff
path: root/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java
diff options
context:
space:
mode:
Diffstat (limited to 'platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java')
-rw-r--r--platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java153
1 files changed, 153 insertions, 0 deletions
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..568e17b58bd6
--- /dev/null
+++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java
@@ -0,0 +1,153 @@
+/*
+ * 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;
+
+/**
+ * Base class for PSI-aware editor actions that need to support multiple carets.
+ * Recognizes multi-root PSI and injected fragments, so different carets might be processed in context of different
+ * {@link com.intellij.openapi.editor.Editor} and {@link com.intellij.psi.PsiFile} instances.
+ * <p>
+ * Implementations should implement {@link #getHandler()} method, and might override {@link
+ * #isValidFor(com.intellij.openapi.project.Project, com.intellij.openapi.editor.Editor, com.intellij.openapi.editor.Caret, com.intellij.psi.PsiFile)} method.
+ *
+ * @see com.intellij.codeInsight.actions.MultiCaretCodeInsightActionHandler
+ */
+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() {
+ MultiCaretCodeInsightActionHandler handler = getHandler();
+ try {
+ iterateOverCarets(project, hostEditor, handler);
+ }
+ finally {
+ handler.postInvoke();
+ }
+ }
+ });
+ }
+ }, 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);
+ }
+
+ /**
+ * During action status update this method is invoked for each caret in editor. If at least for a single caret it returns
+ * <code>true</code>, action is considered enabled.
+ */
+ 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;
+ }
+}