From 0186a7142be7647b985f600a1b032f11b716a084 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 11 Jun 2019 16:55:37 +0700 Subject: [PATCH] ChangeModifierIntention: prevent flicker for fields and classes (unfortunately not for methods yet) + some other cosmetic changes (IDEA-CR-48063) GitOrigin-RevId: 9631635dd51c691555b668e4ff790ed273d815fb --- .../modifiers/ChangeModifierIntention.java | 140 ++++++++++++------ 1 file changed, 91 insertions(+), 49 deletions(-) diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/modifiers/ChangeModifierIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/modifiers/ChangeModifierIntention.java index 40b387bb5f98..d30396a71bde 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/modifiers/ChangeModifierIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/modifiers/ChangeModifierIntention.java @@ -21,8 +21,11 @@ import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.JBPopupListener; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; @@ -152,28 +155,25 @@ public class ChangeModifierIntention extends BaseElementAtCaretIntentionAction { } range = TextRange.from(pos, 0); } + editor.getCaretModel().moveToOffset(range.getStartOffset()); + StartMarkAction markAction; + try { + markAction = StartMarkAction.start(editor, project, getFamilyName()); + } + catch (StartMarkAction.AlreadyStartedException e) { + Messages.showErrorDialog(project, e.getMessage(), StringUtil.toTitleCase(getFamilyName())); + return; + } + TextAttributes lvAttr = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.LIVE_TEMPLATE_ATTRIBUTES); RangeHighlighter highlighter = editor.getMarkupModel() .addRangeHighlighter(range.getStartOffset(), range.getEndOffset(), HighlighterLayer.LAST + 1, lvAttr, HighlighterTargetArea.EXACT_RANGE); highlighter.setGreedyToRight(true); highlighter.setGreedyToLeft(true); - boolean extendLeft = range.getStartOffset() > 0 && !StringUtil.isWhiteSpace(sequence.charAt(range.getStartOffset() - 1)); - boolean extendRight = range.getEndOffset() < sequence.length() && !StringUtil.isWhiteSpace(sequence.charAt(range.getEndOffset())); - String originalText = sequence.subSequence(range.getStartOffset(), range.getEndOffset()).toString(); + ModifierUpdater updater = new ModifierUpdater(file, document, range, getFamilyName()); AccessModifier current = ContainerUtil.find(modifiers, m -> m.hasModifier(member)); - RangeMarker marker = document.createRangeMarker(range); - marker.setGreedyToRight(true); - marker.setGreedyToLeft(true); - editor.getCaretModel().moveToOffset(range.getStartOffset()); SmartPsiElementPointer memberPointer = SmartPointerManager.createPointer(member); - StartMarkAction markAction; - try { - markAction = StartMarkAction.start(editor, project, getFamilyName()); - } - catch (StartMarkAction.AlreadyStartedException e) { - return; - } JBPopup popup = JBPopupFactory.getInstance().createPopupChooserBuilder(modifiers) .setSelectionMode(ListSelectionModel.SINGLE_SELECTION) .setSelectedValue(current, true) @@ -182,52 +182,94 @@ public class ChangeModifierIntention extends BaseElementAtCaretIntentionAction { .setResizable(false) .setRequestFocus(true) .setFont(editor.getColorsScheme().getFont(EditorFontType.PLAIN)) - .setItemSelectedCallback(t -> { - if (t == null) return; - String updatedText; - if (t == AccessModifier.PACKAGE_LOCAL) { - updatedText = " "; + .setItemSelectedCallback(updater::setModifier) + .addListener(new JBPopupListener() { + @Override + public void onClosed(@NotNull LightweightWindowEvent event) { + highlighter.dispose(); + if (!event.isOk()) { + FinishMarkAction.finish(project, editor, markAction); + updater.undoChange(true); + } } - else { - updatedText = extendLeft ? extendRight ? " " + t + " " : " " + t - : extendRight ? t + " " : t.toString(); - } - WriteCommandAction.writeCommandAction(project, file) - .withName(getFamilyName()) - .run(() -> document.replaceString(marker.getStartOffset(), marker.getEndOffset(), updatedText)); - }) - .setCancelCallback(() -> { - highlighter.dispose(); - FinishMarkAction.finish(project, editor, markAction); - undoChange(project, file, document, originalText, marker); - return true; }) .setItemChosenCallback(t -> { + updater.undoChange(false); + PsiDocumentManager.getInstance(project).commitDocument(document); + updater.setModifier(t); + // do not commit document now: checkForConflicts should have original content + // while the editor should display the updated content to prevent flicker PsiMember m = memberPointer.getElement(); - if (m != null) { - setModifier(m, t); + if (m == null) return; + PsiModifierList modifierList = m.getModifierList(); + if (modifierList == null) return; + final MultiMap conflicts = checkForConflicts(m, t); + if (conflicts == null || + !conflicts.isEmpty() && !new ConflictsDialog(project, conflicts, () -> changeModifier(modifierList, t)).showAndGet()) { + //canceled by user + FinishMarkAction.finish(project, editor, markAction); + updater.undoChange(true); + return; } + updater.undoChange(false); + PsiDocumentManager.getInstance(project).commitDocument(document); + changeModifier(modifierList, t); + FinishMarkAction.finish(project, editor, markAction); }) .createPopup(); popup.showInBestPositionFor(editor); } - private void undoChange(@NotNull Project project, - PsiFile file, - Document document, - String originalText, - RangeMarker marker) { - FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); - FileEditor fileEditor = fileEditorManager.getSelectedEditor(file.getVirtualFile()); - UndoManager manager = UndoManager.getInstance(project); - if (manager.isUndoAvailable(fileEditor)) { - manager.undo(fileEditor); - } else { - WriteCommandAction.writeCommandAction(project, file) - .withName(getFamilyName()) - .run(() -> document.replaceString(marker.getStartOffset(), marker.getEndOffset(), originalText)); + private static class ModifierUpdater { + private final Document myDocument; + private final boolean myExtendLeft, myExtendRight; + private final String myOriginalText; + private final RangeMarker myMarker; + private final String myActionName; + private final PsiFile myFile; + + ModifierUpdater(@NotNull PsiFile file, @NotNull Document document, @NotNull TextRange range, @NotNull String actionName) { + myDocument = document; + myFile = file; + myActionName = actionName; + CharSequence sequence = document.getCharsSequence(); + myExtendLeft = range.getStartOffset() > 0 && !StringUtil.isWhiteSpace(sequence.charAt(range.getStartOffset() - 1)); + myExtendRight = range.getEndOffset() < sequence.length() && !StringUtil.isWhiteSpace(sequence.charAt(range.getEndOffset())); + myOriginalText = sequence.subSequence(range.getStartOffset(), range.getEndOffset()).toString(); + myMarker = document.createRangeMarker(range); + myMarker.setGreedyToRight(true); + myMarker.setGreedyToLeft(true); + } + + void undoChange(boolean viaUndoManager) { + Project project = myFile.getProject(); + FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); + FileEditor fileEditor = fileEditorManager.getSelectedEditor(myFile.getVirtualFile()); + UndoManager manager = UndoManager.getInstance(project); + if (viaUndoManager && manager.isUndoAvailable(fileEditor)) { + manager.undo(fileEditor); + } + else { + WriteCommandAction.writeCommandAction(project, myFile) + .withName(myActionName) + .run(() -> myDocument.replaceString(myMarker.getStartOffset(), myMarker.getEndOffset(), myOriginalText)); + } + } + + void setModifier(@Nullable AccessModifier target) { + if (target == null) return; + String updatedText; + if (target == AccessModifier.PACKAGE_LOCAL) { + updatedText = " "; + } + else { + updatedText = myExtendLeft ? myExtendRight ? " " + target + " " : " " + target + : myExtendRight ? target + " " : target.toString(); + } + WriteCommandAction.writeCommandAction(myFile.getProject(), myFile) + .withName(myActionName) + .run(() -> myDocument.replaceString(myMarker.getStartOffset(), myMarker.getEndOffset(), updatedText)); } - PsiDocumentManager.getInstance(project).commitDocument(document); } private static TextRange getRange(PsiMember member) {