ChangeModifierIntention: prevent flicker for fields and classes (unfortunately not for methods yet)

+ some other cosmetic changes (IDEA-CR-48063)

GitOrigin-RevId: 9631635dd51c691555b668e4ff790ed273d815fb
This commit is contained in:
Tagir Valeev
2019-06-11 18:00:13 +03:00
committed by intellij-monorepo-bot
parent 4018b8bad1
commit 0186a7142b
@@ -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<PsiMember> 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<PsiElement, String> 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) {