diff --git a/platform/analysis-api/src/com/intellij/openapi/command/undo/BasicUndoableAction.java b/platform/analysis-api/src/com/intellij/openapi/command/undo/BasicUndoableAction.java index 157b488221c8..39de2b6a7fb8 100644 --- a/platform/analysis-api/src/com/intellij/openapi/command/undo/BasicUndoableAction.java +++ b/platform/analysis-api/src/com/intellij/openapi/command/undo/BasicUndoableAction.java @@ -9,6 +9,7 @@ import org.jetbrains.annotations.Nullable; public abstract class BasicUndoableAction implements UndoableAction { private final DocumentReference[] myRefs; + private long myPerformedTimestamp = -1L; public BasicUndoableAction() { myRefs = null; @@ -41,4 +42,14 @@ public abstract class BasicUndoableAction implements UndoableAction { public boolean isGlobal() { return false; } + + @Override + public long getPerformedNanoTime() { + return myPerformedTimestamp; + } + + @Override + public void setPerformedNanoTime(long l) { + myPerformedTimestamp = l; + } } diff --git a/platform/analysis-api/src/com/intellij/openapi/command/undo/UndoManager.java b/platform/analysis-api/src/com/intellij/openapi/command/undo/UndoManager.java index cee6a44a04a8..d1bd44c6f683 100644 --- a/platform/analysis-api/src/com/intellij/openapi/command/undo/UndoManager.java +++ b/platform/analysis-api/src/com/intellij/openapi/command/undo/UndoManager.java @@ -8,6 +8,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NlsActions; import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -48,4 +49,16 @@ public abstract class UndoManager { public abstract @NotNull Pair<@NlsActions.ActionText String, @NlsActions.ActionDescription String> getUndoActionNameAndDescription(FileEditor editor); public abstract @NotNull Pair<@NlsActions.ActionText String, @NlsActions.ActionDescription String> getRedoActionNameAndDescription(FileEditor editor); + + @ApiStatus.Experimental + public abstract long getNextUndoNanoTime(@NotNull FileEditor editor); + + @ApiStatus.Experimental + public abstract long getNextRedoNanoTime(@NotNull FileEditor editor); + + @ApiStatus.Experimental + public abstract boolean isNextUndoAskConfirmation(@NotNull FileEditor editor); + + @ApiStatus.Experimental + public abstract boolean isNextRedoAskConfirmation(@NotNull FileEditor editor); } diff --git a/platform/analysis-api/src/com/intellij/openapi/command/undo/UndoableAction.java b/platform/analysis-api/src/com/intellij/openapi/command/undo/UndoableAction.kt similarity index 57% rename from platform/analysis-api/src/com/intellij/openapi/command/undo/UndoableAction.java rename to platform/analysis-api/src/com/intellij/openapi/command/undo/UndoableAction.kt index 101486d9ae3d..73f03b7be4e0 100644 --- a/platform/analysis-api/src/com/intellij/openapi/command/undo/UndoableAction.java +++ b/platform/analysis-api/src/com/intellij/openapi/command/undo/UndoableAction.kt @@ -1,14 +1,21 @@ // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.openapi.command.undo; +package com.intellij.openapi.command.undo -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.ApiStatus /** - * @see UndoManager#undoableActionPerformed(UndoableAction) + * @see UndoManager.undoableActionPerformed */ -public interface UndoableAction { - void undo() throws UnexpectedUndoException; - void redo() throws UnexpectedUndoException; +interface UndoableAction { + @get:ApiStatus.Experimental + @set:ApiStatus.Experimental + var performedNanoTime: Long + + @Throws(UnexpectedUndoException::class) + fun undo() + + @Throws(UnexpectedUndoException::class) + fun redo() /** * Returns the documents, affected by this action. @@ -16,10 +23,10 @@ public interface UndoableAction { * The action can be undone if all of its affected documents are either * not affected by any of further actions or all of such actions are undone. */ - DocumentReference @Nullable [] getAffectedDocuments(); + val affectedDocuments: Array? /** * Global actions are those, that can be undone not only from the document of the file, but also from the project tree view. */ - boolean isGlobal(); + val isGlobal: Boolean } diff --git a/platform/lang-impl/src/com/intellij/refactoring/suggested/SuggestedRefactoringUndoableAction.kt b/platform/lang-impl/src/com/intellij/refactoring/suggested/SuggestedRefactoringUndoableAction.kt index 06e530902dd1..d9b327df7e15 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/suggested/SuggestedRefactoringUndoableAction.kt +++ b/platform/lang-impl/src/com/intellij/refactoring/suggested/SuggestedRefactoringUndoableAction.kt @@ -33,9 +33,11 @@ class SuggestedRefactoringUndoableAction private constructor( private val documentReference = DocumentReferenceManager.getInstance().create(document) - override fun getAffectedDocuments(): Array = arrayOf(documentReference) + override var performedNanoTime: Long = -1 - override fun isGlobal(): Boolean = false + override val affectedDocuments: Array = arrayOf(documentReference) + + override val isGlobal: Boolean = false override fun undo() { val document = documentReference.document ?: return diff --git a/platform/platform-impl/src/com/intellij/ide/actions/UndoRedoAction.java b/platform/platform-impl/src/com/intellij/ide/actions/UndoRedoAction.java index 00bf7c05f6d2..44b990121d93 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/UndoRedoAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/UndoRedoAction.java @@ -178,6 +178,26 @@ public abstract class UndoRedoAction extends DumbAwareAction implements LightEdi return getUndoOrRedoActionNameAndDescription( false); } + @Override + public long getNextUndoNanoTime(@NotNull FileEditor editor) { + return -1L; + } + + @Override + public long getNextRedoNanoTime(@NotNull FileEditor editor) { + return -1L; + } + + @Override + public boolean isNextUndoAskConfirmation(@NotNull FileEditor editor) { + return false; + } + + @Override + public boolean isNextRedoAskConfirmation(@NotNull FileEditor editor) { + return false; + } + private static @NotNull Pair getUndoOrRedoActionNameAndDescription(boolean undo) { if (undo) { return Pair.create( diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandMerger.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandMerger.java index a639dc311358..f45f8e352e5f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandMerger.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandMerger.java @@ -16,6 +16,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.concurrency.ThreadingAssertions; import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/MentionOnlyUndoableAction.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/MentionOnlyUndoableAction.java index 1055ac462c7a..ae743be401e4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/MentionOnlyUndoableAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/MentionOnlyUndoableAction.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; * Undo action can be available even if this Document is ReadOnly. */ final class MentionOnlyUndoableAction implements UndoableAction { + private long myPerformedTimestamp = -1L; private final DocumentReference[] myRefs; MentionOnlyUndoableAction(DocumentReference @NotNull [] refs) { @@ -33,4 +34,14 @@ final class MentionOnlyUndoableAction implements UndoableAction { public boolean isGlobal() { return false; } + + @Override + public long getPerformedNanoTime() { + return myPerformedTimestamp; + } + + @Override + public void setPerformedNanoTime(long l) { + myPerformedTimestamp = l; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/NonUndoableAction.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/NonUndoableAction.java index 7d3e5588093e..16e697c37486 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/NonUndoableAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/NonUndoableAction.java @@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull; final class NonUndoableAction implements UndoableAction { private static final Logger LOG = Logger.getInstance(NonUndoableAction.class); - + private long myPerformedTimestamp = -1; private final DocumentReference[] myRefs; private final boolean myGlobal; @@ -39,4 +39,14 @@ final class NonUndoableAction implements UndoableAction { public boolean isGlobal() { return myGlobal; } + + @Override + public long getPerformedNanoTime() { + return myPerformedTimestamp; + } + + @Override + public void setPerformedNanoTime(long l) { + myPerformedTimestamp = l; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java index c06849262a87..645bbd7622a0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java @@ -332,6 +332,7 @@ public final class UndoManagerImpl extends UndoManager { return; } + action.setPerformedNanoTime(System.nanoTime()); if (state.myCommandLevel == 0) { LOG.assertTrue(action instanceof NonUndoableAction, "Undoable actions allowed inside commands only (see com.intellij.openapi.command.CommandProcessor.executeCommand())"); @@ -631,6 +632,58 @@ public final class UndoManagerImpl extends UndoManager { return getUndoOrRedoActionNameAndDescription(editor, false); } + @Override +public long getNextUndoNanoTime(@NotNull FileEditor editor) { + return getNextNanoTime(editor, true); +} + +@Override +public long getNextRedoNanoTime(@NotNull FileEditor editor) { + return getNextNanoTime(editor, false); +} + +@Override +public boolean isNextUndoAskConfirmation(@NotNull FileEditor editor) { + return isNextAskConfirmation(editor, true); +} + +@Override +public boolean isNextRedoAskConfirmation(@NotNull FileEditor editor) { + return isNextAskConfirmation(editor, false); +} + +private long getNextNanoTime(@NotNull FileEditor editor, boolean isUndo) { + ClientState clientState = getClientState(editor); + Collection references = getDocRefs(editor); + if (clientState == null || references == null) { + return -1L; + } + + if (isUndo) { + clientState.myMerger.flushCurrentCommand(); + } + + @NotNull UndoRedoStacksHolder stack = getStackHolder(clientState, isUndo); + UndoableGroup lastAction = stack.getLastAction(references); + return lastAction == null ? -1L : lastAction.getGroupStartPerformedTimestamp(); +} + +private boolean isNextAskConfirmation(@NotNull FileEditor editor, boolean isUndo) { + ClientState clientState = getClientState(editor); + Collection references = getDocRefs(editor); + if (clientState == null || references == null) { + return false; + } + + if (isUndo) { + clientState.myMerger.flushCurrentCommand(); + } + + @NotNull UndoRedoStacksHolder stack = getStackHolder(clientState, isUndo); + UndoableGroup lastAction = stack.getLastAction(references); + return lastAction != null && lastAction.shouldAskConfirmation(!isUndo); +} + private @NotNull Pair<@NlsActions.ActionText String, @NlsActions.ActionDescription String> getUndoOrRedoActionNameAndDescription(@Nullable FileEditor editor, boolean undo) { String desc = isUndoOrRedoAvailable(editor, undo) ? doFormatAvailableUndoRedoAction(editor, undo) : null; if (desc == null) desc = ""; diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoRedoStacksHolder.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoRedoStacksHolder.java index ce0518cb69ae..f77ea01ce740 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoRedoStacksHolder.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoRedoStacksHolder.java @@ -7,6 +7,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import java.util.*; @@ -26,7 +27,7 @@ final class UndoRedoStacksHolder extends UndoRedoStacksHolderBase return false; } - @NotNull + @Nullable UndoableGroup getLastAction(@NotNull Collection refs) { if (refs.isEmpty()) return myGlobalStack.getLast(); diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoableGroup.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoableGroup.java index fcd9c322a815..173e1900e796 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoableGroup.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoableGroup.java @@ -18,6 +18,7 @@ import com.intellij.openapi.util.NlsContexts; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -352,12 +353,18 @@ final class UndoableGroup implements Dumpable { return null; } + @ApiStatus.Experimental boolean shouldAskConfirmation(boolean redo) { if (shouldAskConfirmationForStartFinishGroup(redo)) return true; return myConfirmationPolicy == UndoConfirmationPolicy.REQUEST_CONFIRMATION || myConfirmationPolicy != UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION && myGlobal; } + long getGroupStartPerformedTimestamp() { + if (myActions.isEmpty()) return -1L; + return Math.min(myActions.get(0).getPerformedNanoTime(), myActions.get(myActions.size() - 1).getPerformedNanoTime()); + } + void invalidateChangeRanges() { for (UndoableAction action : myActions) { if (action instanceof AdjustableUndoableAction) { diff --git a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/folding/EditPropertyValueAction.java b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/folding/EditPropertyValueAction.java index 36dc1411fc9a..553aea0b0bf8 100644 --- a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/folding/EditPropertyValueAction.java +++ b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/folding/EditPropertyValueAction.java @@ -338,6 +338,8 @@ public final class EditPropertyValueAction extends BaseRefactoringAction { new VisualPosition(regionStartPosition.line, regionStartPosition.column + placeholderColumn)); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); UndoManager.getInstance(project).undoableActionPerformed(new UndoableAction() { + private long myPerformedTimestamp = -1L; + @Override public void undo() { if (foldRegion.isValid()) { @@ -361,6 +363,16 @@ public final class EditPropertyValueAction extends BaseRefactoringAction { public boolean isGlobal() { return false; } + + @Override + public long getPerformedNanoTime() { + return myPerformedTimestamp; + } + + @Override + public void setPerformedNanoTime(long performedTimestamp) { + myPerformedTimestamp = performedTimestamp; + } }); }, targetPsiFile); editorComponent.paintImmediately(new Rectangle(editorComponent.getSize()));