IDEA-80056 Column selection mode improvement

* fix issue with Select/Unselect next occurence actions
* fix regression (IDEA-121011)
This commit is contained in:
Dmitry Batrak
2014-02-20 13:38:15 +04:00
parent 14d837e464
commit abd4c25a0a
9 changed files with 118 additions and 24 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* 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.
@@ -35,6 +35,9 @@ import com.intellij.openapi.actionSystem.DataContext;
public interface AnActionListener {
void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event);
/**
* Note that using <code>dataContext</code> in implementing methods is unsafe - it could have been invalidated by the performed action.
*/
void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event);
void beforeEditorTyping(char c, DataContext dataContext);
@@ -13,25 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.actionSystem;
package com.intellij.openapi.editor;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.annotations.Nullable;
/**
* This component provides the notion of last action. Its purpose is to be able to determine whether some action was performed right after
* another specific action.
* This component provides the notion of last editor action.
* Its purpose is to be able to determine whether some action was performed right after another specific action.
* <p>
* It's supposed to be used from EDT only.
*/
public abstract class LastActionTracker {
public static LastActionTracker getInstance() {
return ApplicationManager.getApplication().getComponent(LastActionTracker.class);
public abstract class EditorLastActionTracker {
public static EditorLastActionTracker getInstance() {
return ApplicationManager.getApplication().getComponent(EditorLastActionTracker.class);
}
/**
* Returns the id of the previously invoked action or <code>null</code>, if no history exists yet, or last user activity was of
* non-action type, like mouse clicking in editor or text typing.
* non-action type, like mouse clicking in editor or text typing, or previous action was invoked for a different editor.
*/
@Nullable
public abstract String getLastActionId();
@@ -23,7 +23,7 @@ import com.intellij.find.FindBundle;
import com.intellij.find.FindManager;
import com.intellij.find.FindModel;
import com.intellij.find.FindResult;
import com.intellij.openapi.actionSystem.LastActionTracker;
import com.intellij.openapi.editor.EditorLastActionTracker;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.Caret;
@@ -45,7 +45,7 @@ public class SelectNextOccurrenceAction extends EditorAction {
super(new Handler());
}
private static class Handler extends EditorActionHandler {
static class Handler extends EditorActionHandler {
@Override
public boolean isEnabled(Editor editor, DataContext dataContext) {
return super.isEnabled(editor, dataContext) && editor.getProject() != null && editor.getCaretModel().supportsMultipleCarets();
@@ -127,7 +127,7 @@ public class SelectNextOccurrenceAction extends EditorAction {
false);
}
private static boolean getAndResetNotFoundStatus(Editor editor) {
static boolean getAndResetNotFoundStatus(Editor editor) {
boolean status = editor.getUserData(NOT_FOUND) != null;
editor.putUserData(NOT_FOUND, null);
return status && isRepeatedActionInvocation();
@@ -149,7 +149,7 @@ public class SelectNextOccurrenceAction extends EditorAction {
}
private static boolean isRepeatedActionInvocation() {
String lastActionId = LastActionTracker.getInstance().getLastActionId();
String lastActionId = EditorLastActionTracker.getInstance().getLastActionId();
return IdeActions.ACTION_SELECT_NEXT_OCCURENCE.equals(lastActionId) || IdeActions.ACTION_UNSELECT_LAST_OCCURENCE.equals(lastActionId);
}
}
@@ -42,6 +42,7 @@ public class UnselectLastOccurrenceAction extends EditorAction {
else {
editor.getSelectionModel().removeSelection();
}
SelectNextOccurrenceAction.Handler.getAndResetNotFoundStatus(editor);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
}
}
@@ -13,27 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.actionSystem.impl;
package com.intellij.openapi.editor.impl;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.AnActionListener;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.EditorLastActionTracker;
import com.intellij.openapi.editor.event.EditorEventMulticaster;
import com.intellij.openapi.editor.event.EditorMouseEvent;
import com.intellij.openapi.editor.event.EditorMouseListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class LastActionTrackerImpl extends LastActionTracker implements ApplicationComponent, AnActionListener, EditorMouseListener {
public class EditorLastActionTrackerImpl extends EditorLastActionTracker implements ApplicationComponent, AnActionListener, EditorMouseListener {
private final ActionManager myActionManager;
private final EditorEventMulticaster myEditorEventMulticaster;
private String myLastActionId;
private Editor myCurrentEditor;
private Editor myLastEditor;
LastActionTrackerImpl(ActionManager actionManager, EditorFactory editorFactory) {
EditorLastActionTrackerImpl(ActionManager actionManager, EditorFactory editorFactory) {
myActionManager = actionManager;
myEditorEventMulticaster = editorFactory.getEventMulticaster();
}
@@ -53,7 +55,7 @@ public class LastActionTrackerImpl extends LastActionTracker implements Applicat
@NotNull
@Override
public String getComponentName() {
return "LastActionTracker";
return "EditorLastActionTracker";
}
@Override
@@ -64,7 +66,8 @@ public class LastActionTrackerImpl extends LastActionTracker implements Applicat
@Override
public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
if (CommonDataKeys.EDITOR.getData(dataContext) != myLastEditor) {
myCurrentEditor = CommonDataKeys.EDITOR.getData(dataContext);
if (myCurrentEditor != myLastEditor) {
resetLastAction();
}
}
@@ -72,7 +75,8 @@ public class LastActionTrackerImpl extends LastActionTracker implements Applicat
@Override
public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
myLastActionId = getActionId(action);
myLastEditor = CommonDataKeys.EDITOR.getData(dataContext);
myLastEditor = myCurrentEditor;
myCurrentEditor = null;
}
@Override
@@ -145,6 +145,11 @@
<component>
<implementation-class>com.intellij.diagnostic.DebugLogManager</implementation-class>
</component>
<component>
<interface-class>com.intellij.openapi.editor.EditorLastActionTracker</interface-class>
<implementation-class>com.intellij.openapi.editor.impl.EditorLastActionTrackerImpl</implementation-class>
</component>
</application-components>
<project-components>
@@ -21,10 +21,6 @@
<interface-class>com.intellij.openapi.actionSystem.ActionManager</interface-class>
<implementation-class>com.intellij.openapi.actionSystem.impl.ActionManagerImpl</implementation-class>
</component>
<component>
<interface-class>com.intellij.openapi.actionSystem.LastActionTracker</interface-class>
<implementation-class>com.intellij.openapi.actionSystem.impl.LastActionTrackerImpl</implementation-class>
</component>
<component>
<interface-class>com.intellij.openapi.keymap.KeymapManager</interface-class>
<implementation-class>com.intellij.openapi.keymap.impl.KeymapManagerImpl</implementation-class>
@@ -16,6 +16,7 @@
package com.intellij.openapi.editor.actions;
import com.intellij.codeInsight.hint.EditorHintListener;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.FoldRegion;
@@ -176,6 +177,14 @@ public class SelectUnselectOccurrenceActionsTest extends LightPlatformCodeInsigh
assertTrue(foldRegions[0].isExpanded());
}
public void testSelectAfterNotFoundAndUnselect() throws Exception {
init("text <selection><caret>text</selection> <selection><caret>text</selection>");
executeAction();
executeReverseAction();
executeAction();
checkResult("text <selection><caret>text</selection> <selection><caret>text</selection>");
}
private void init(String text) {
myFixture.configureByText(FileTypes.PLAIN_TEXT, text);
}
@@ -185,10 +194,10 @@ public class SelectUnselectOccurrenceActionsTest extends LightPlatformCodeInsigh
}
private void executeAction() {
myFixture.performEditorAction("SelectNextOccurrence");
myFixture.performEditorAction(IdeActions.ACTION_SELECT_NEXT_OCCURENCE);
}
private void executeReverseAction() {
myFixture.performEditorAction("UnselectLastOccurrence");
myFixture.performEditorAction(IdeActions.ACTION_UNSELECT_LAST_OCCURENCE);
}
}
@@ -0,0 +1,76 @@
/*
* 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.openapi.editor.impl;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorLastActionTracker;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.testFramework.fixtures.EditorMouseFixture;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
import org.jetbrains.annotations.Nullable;
public class EditorLastActionTrackerTest extends LightPlatformCodeInsightFixtureTestCase {
public static final String SAMPLE_ACTION = "EditorDelete";
private final EditorActionHandler myActionHandler = new MyActionHandler();
private EditorLastActionTracker myTracker;
private EditorActionHandler mySavedHandler;
@Override
public void setUp() throws Exception {
super.setUp();
myTracker = EditorLastActionTracker.getInstance();
mySavedHandler = EditorActionManager.getInstance().setActionHandler(SAMPLE_ACTION, myActionHandler);
myFixture.configureByText(getTestName(true) + ".txt", "doesn't matter");
myFixture.performEditorAction(SAMPLE_ACTION);
}
@Override
public void tearDown() throws Exception {
EditorActionManager.getInstance().setActionHandler(SAMPLE_ACTION, mySavedHandler);
super.tearDown();
}
public void testLastActionIsAvailable() throws Exception {
assertEquals(SAMPLE_ACTION, myTracker.getLastActionId());
}
public void testMouseClickClearsLastAction() throws Exception {
new EditorMouseFixture((EditorImpl)myFixture.getEditor()).clickAt(0, 1);
assertNull(myTracker.getLastActionId());
}
public void testTypingClearsLastAction() throws Exception {
myFixture.type('A');
assertNull(myTracker.getLastActionId());
}
public void testTwoEditors() throws Exception {
myFixture.configureByText(getTestName(true) + "-other.txt", "doesn't matter as well");
myFixture.performEditorAction(SAMPLE_ACTION);
}
private class MyActionHandler extends EditorActionHandler {
@Override
public void execute(Editor editor, @Nullable Caret caret, DataContext dataContext) {
assertNull(myTracker.getLastActionId());
}
}
}