undo should restore primary caret properly (IDEA-80056)

This commit is contained in:
Dmitry Batrak
2014-03-13 14:39:14 +04:00
parent e32ac9d174
commit dfe4e01082
6 changed files with 97 additions and 15 deletions
@@ -224,9 +224,25 @@ public interface CaretModel {
* If multiple carets are not supported, the behaviour is unspecified.
*
* @see #supportsMultipleCarets()
* @see #getCaretsAndSelections()
*/
void setCaretsAndSelections(@NotNull List<CaretState> caretStates);
/**
* Returns the current positions of all carets and their selections. The order of entries in the returned list does not necessarily
* correspond to the order of {@link #getAllCarets()} method results. Passing the result of this method to
* {@link #setCaretsAndSelections(java.util.List)} will restore the state of carets, including the internal caret order, in particular,
* the caret, that was primary when this method was called, will be the primary one after corresponding
* {@link #setCaretsAndSelections(java.util.List)} invocation.
* <p>
* If multiple carets are not supported, the behaviour is unspecified.
*
* @see #supportsMultipleCarets()
* @see #setCaretsAndSelections(java.util.List)
*/
@NotNull
List<CaretState> getCaretsAndSelections();
/**
* Executes the given task for each existing caret. Carets are iterated in their position order. Set of carets to iterate over is
* determined in the beginning and is not affected by the potential carets addition or removal by the task being executed.
@@ -221,6 +221,19 @@ public class CaretModelWindow implements CaretModel {
myDelegate.setCaretsAndSelections(convertedStates);
}
@NotNull
@Override
public List<CaretState> getCaretsAndSelections() {
List<CaretState> caretsAndSelections = myDelegate.getCaretsAndSelections();
List<CaretState> convertedStates = new ArrayList<CaretState>(caretsAndSelections.size());
for (CaretState state : caretsAndSelections) {
convertedStates.add(new CaretState(state.getCaretPosition() == null ? null : myEditorWindow.hostToInjected(state.getCaretPosition()),
state.getSelectionStart() == null ? null : myEditorWindow.hostToInjected(state.getSelectionStart()),
state.getSelectionEnd() == null ? null : myEditorWindow.hostToInjected(state.getSelectionEnd())));
}
return convertedStates;
}
private InjectedCaret createInjectedCaret(Caret caret) {
if (caret == null) {
return null;
@@ -488,6 +488,20 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
});
}
@NotNull
@Override
public List<CaretState> getCaretsAndSelections() {
synchronized (myCarets) {
List<CaretState> states = new ArrayList<CaretState>(myCarets.size());
for (CaretImpl caret : myCarets) {
states.add(new CaretState(caret.getLogicalPosition(),
myEditor.visualToLogicalPosition(caret.getSelectionStartPosition()),
myEditor.visualToLogicalPosition(caret.getSelectionEndPosition())));
}
return states;
}
}
void fireCaretPositionChanged(CaretEvent caretEvent) {
myCaretListeners.getMulticaster().caretPositionChanged(caretEvent);
}
@@ -187,6 +187,12 @@ public class TextComponentCaretModel implements CaretModel {
throw new UnsupportedOperationException("Multiple carets are not supported");
}
@NotNull
@Override
public List<CaretState> getCaretsAndSelections() {
throw new UnsupportedOperationException("Multiple carets are not supported");
}
@Override
public void runForEachCaret(@NotNull CaretAction action) {
action.perform(myCaret);
@@ -29,7 +29,9 @@ import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
@@ -42,7 +44,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
@@ -226,19 +227,23 @@ public class TextEditorProvider implements FileEditorProvider, DumbAware {
protected TextEditorState getStateImpl(final Project project, @NotNull Editor editor, @NotNull FileEditorStateLevel level){
TextEditorState state = new TextEditorState();
CaretModel caretModel = editor.getCaretModel();
Collection<Caret> allCarets = caretModel.getAllCarets();
state.CARETS = new TextEditorState.CaretState[allCarets.size()];
int i = 0;
for (Caret caret : allCarets) {
state.CARETS[i] = new TextEditorState.CaretState();
state.CARETS[i].LINE = caret.getLogicalPosition().line;
state.CARETS[i].COLUMN = caret.getLogicalPosition().column;
LogicalPosition selectionStartPosition = editor.visualToLogicalPosition(caret.getSelectionStartPosition());
LogicalPosition selectionEndPosition = editor.visualToLogicalPosition(caret.getSelectionEndPosition());
state.CARETS[i].SELECTION_START_LINE = selectionStartPosition.line;
state.CARETS[i].SELECTION_START_COLUMN = selectionStartPosition.column;
state.CARETS[i].SELECTION_END_LINE = selectionEndPosition.line;
state.CARETS[i++].SELECTION_END_COLUMN = selectionEndPosition.column;
if (caretModel.supportsMultipleCarets()) {
List<CaretState> caretsAndSelections = caretModel.getCaretsAndSelections();
state.CARETS = new TextEditorState.CaretState[caretsAndSelections.size()];
for (int i = 0; i < caretsAndSelections.size(); i++) {
CaretState caretState = caretsAndSelections.get(i);
LogicalPosition caretPosition = caretState.getCaretPosition();
LogicalPosition selectionStartPosition = caretState.getSelectionStart();
LogicalPosition selectionEndPosition = caretState.getSelectionEnd();
state.CARETS[i] = createCaretState(caretPosition, selectionStartPosition, selectionEndPosition);
}
}
else {
LogicalPosition caretPosition = caretModel.getLogicalPosition();
LogicalPosition selectionStartPosition = editor.offsetToLogicalPosition(editor.getSelectionModel().getSelectionStart());
LogicalPosition selectionEndPosition = editor.offsetToLogicalPosition(editor.getSelectionModel().getSelectionEnd());
state.CARETS = new TextEditorState.CaretState[1];
state.CARETS[0] = createCaretState(caretPosition, selectionStartPosition, selectionEndPosition);
}
// Saving scrolling proportion on UNDO may cause undesirable results of undo action fails to perform since
@@ -253,6 +258,25 @@ public class TextEditorProvider implements FileEditorProvider, DumbAware {
return state;
}
private static TextEditorState.CaretState createCaretState(LogicalPosition caretPosition, LogicalPosition selectionStartPosition, LogicalPosition selectionEndPosition) {
TextEditorState.CaretState caretState = new TextEditorState.CaretState();
caretState.LINE = getLine(caretPosition);
caretState.COLUMN = getColumn(caretPosition);
caretState.SELECTION_START_LINE = getLine(selectionStartPosition);
caretState.SELECTION_START_COLUMN = getColumn(selectionStartPosition);
caretState.SELECTION_END_LINE = getLine(selectionEndPosition);
caretState.SELECTION_END_COLUMN = getColumn(selectionEndPosition);
return caretState;
}
private static int getLine(@Nullable LogicalPosition pos) {
return pos == null ? 0 : pos.line;
}
private static int getColumn(@Nullable LogicalPosition pos) {
return pos == null ? 0 : pos.column;
}
protected void setStateImpl(final Project project, final Editor editor, final TextEditorState state){
if (editor.getCaretModel().supportsMultipleCarets()) {
CaretModel caretModel = editor.getCaretModel();
@@ -97,6 +97,15 @@ public class EditorMultiCaretUndoRedoTest extends AbstractEditorTest {
verifyCaretsAndSelections(0, 2, 0, 0, 0, 2);
}
public void testPrimaryCaretPositionAfterUndo() throws Exception {
init("line1\n" +
"line2");
mouse().alt().clickAt(1, 1).dragTo(0, 0).release();
type(' ');
undo();
assertEquals(new LogicalPosition(0, 0), myEditor.getCaretModel().getPrimaryCaret().getLogicalPosition());
}
private void checkResult(final String text) {
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@Override