From 718ee80dd44730fc0cca4f2de360eaac58d1c451 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 19 Feb 2014 18:54:27 +0400 Subject: [PATCH] IDEA-80056 Column selection mode improvement implementation of select/unselect next occurrence actions --- .../actionSystem/LastActionTracker.java | 38 ++++ .../editorActions/SelectWordUtil.java | 4 +- .../actions/SelectNextOccurrenceAction.java | 156 ++++++++++++++ .../actions/UnselectLastOccurrenceAction.java | 48 +++++ .../openapi/actionSystem/IdeActions.java | 4 +- .../codeInsight/hint/HintManagerImpl.java | 3 +- .../impl/LastActionTrackerImpl.java | 116 +++++++++++ .../editor/actions/EditorActionUtil.java | 17 ++ .../src/messages/ActionsBundle.properties | 2 + .../src/messages/FindBundle.properties | 1 + .../src/componentSets/UICore.xml | 4 + .../src/idea/Keymap_Default.xml | 6 + .../src/idea/PlatformActions.xml | 2 + .../SelectUnselectOccurrenceActionsTest.java | 194 ++++++++++++++++++ 14 files changed, 591 insertions(+), 4 deletions(-) create mode 100644 platform/editor-ui-api/src/com/intellij/openapi/actionSystem/LastActionTracker.java create mode 100644 platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectNextOccurrenceAction.java create mode 100644 platform/lang-impl/src/com/intellij/openapi/editor/actions/UnselectLastOccurrenceAction.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/LastActionTrackerImpl.java create mode 100644 platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/SelectUnselectOccurrenceActionsTest.java diff --git a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/LastActionTracker.java b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/LastActionTracker.java new file mode 100644 index 000000000000..b3f18c926e4c --- /dev/null +++ b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/LastActionTracker.java @@ -0,0 +1,38 @@ +/* + * 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.actionSystem; + +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. + *

+ * It's supposed to be used from EDT only. + */ +public abstract class LastActionTracker { + public static LastActionTracker getInstance() { + return ApplicationManager.getApplication().getComponent(LastActionTracker.class); + } + + /** + * Returns the id of the previously invoked action or null, if no history exists yet, or last user activity was of + * non-action type, like mouse clicking in editor or text typing. + */ + @Nullable + public abstract String getLastActionId(); +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SelectWordUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SelectWordUtil.java index 07923f11d5c5..8253873421fe 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SelectWordUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SelectWordUtil.java @@ -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. @@ -65,7 +65,7 @@ public class SelectWordUtil { return SELECTIONERS; } - private static final CharCondition JAVA_IDENTIFIER_PART_CONDITION = new CharCondition() { + public static final CharCondition JAVA_IDENTIFIER_PART_CONDITION = new CharCondition() { @Override public boolean value(char ch) { return Character.isJavaIdentifierPart(ch); diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectNextOccurrenceAction.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectNextOccurrenceAction.java new file mode 100644 index 000000000000..d8870484c380 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectNextOccurrenceAction.java @@ -0,0 +1,156 @@ +/* + * 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.actions; + +import com.intellij.codeInsight.editorActions.SelectWordUtil; +import com.intellij.codeInsight.hint.HintManager; +import com.intellij.codeInsight.hint.HintManagerImpl; +import com.intellij.codeInsight.hint.HintUtil; +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.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.editor.Caret; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.actionSystem.EditorAction; +import com.intellij.openapi.editor.actionSystem.EditorActionHandler; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.TextRange; +import com.intellij.ui.LightweightHint; +import org.jetbrains.annotations.Nullable; + +public class SelectNextOccurrenceAction extends EditorAction { + private static final Key NOT_FOUND = Key.create("select.next.occurence.not.found"); + private static final Key WHOLE_WORDS = Key.create("select.next.occurence.whole.words"); + + protected SelectNextOccurrenceAction() { + super(new Handler()); + } + + private static class Handler extends EditorActionHandler { + @Override + public boolean isEnabled(Editor editor, DataContext dataContext) { + return super.isEnabled(editor, dataContext) && editor.getProject() != null && editor.getCaretModel().supportsMultipleCarets(); + } + + @Override + public void execute(Editor editor, @Nullable Caret c, DataContext dataContext) { + Caret caret = c == null ? editor.getCaretModel().getPrimaryCaret() : c; + TextRange wordSelectionRange = SelectWordUtil.getWordSelectionRange(editor.getDocument().getCharsSequence(), + caret.getOffset(), + SelectWordUtil.JAVA_IDENTIFIER_PART_CONDITION); + boolean notFoundPreviously = getAndResetNotFoundStatus(editor); + Boolean wholeWordSearch = isWholeWordSearch(editor); + if (caret.hasSelection()) { + Project project = editor.getProject(); + String selectedText = caret.getSelectedText(); + if (project == null || selectedText == null) { + return; + } + FindManager findManager = FindManager.getInstance(project); + + if (wholeWordSearch == null) { + wholeWordSearch = wordSelectionRange != null + && wordSelectionRange.getStartOffset() == caret.getSelectionStart() + && wordSelectionRange.getEndOffset() == caret.getSelectionEnd(); + setWholeWordSearch(editor, wholeWordSearch); + } + FindModel model = new FindModel(); + model.setStringToFind(caret.getSelectedText()); + model.setCaseSensitive(true); + model.setWholeWordsOnly(wholeWordSearch); + + int searchStartOffset = notFoundPreviously ? 0 : caret.getSelectionEnd(); + FindResult findResult = findManager.findString(editor.getDocument().getCharsSequence(), searchStartOffset, model); + if (findResult.isStringFound()) { + int newCaretOffset = caret.getOffset() - caret.getSelectionStart() + findResult.getStartOffset(); + EditorActionUtil.makePositionVisible(editor, newCaretOffset); + Caret newCaret = editor.getCaretModel().addCaret(editor.offsetToVisualPosition(newCaretOffset)); + if (newCaret == null) { + // this means that the found occurence is already selected + if (notFoundPreviously) { + setNotFoundStatus(editor); // to make sure we won't show hint anymore if there are no more occurrences + } + } + else { + setSelection(editor, newCaret, findResult); + } + } + else { + setNotFoundStatus(editor); + showHint(editor); + } + } + else { + if (wordSelectionRange == null) { + return; + } + setSelection(editor, caret, wordSelectionRange); + setWholeWordSearch(editor, true); + } + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + + private static void setSelection(Editor editor, Caret caret, TextRange selectionRange) { + EditorActionUtil.makePositionVisible(editor, selectionRange.getStartOffset()); + EditorActionUtil.makePositionVisible(editor, selectionRange.getEndOffset()); + caret.setSelection(selectionRange.getStartOffset(), selectionRange.getEndOffset()); + } + + + private static void showHint(final Editor editor) { + String message = FindBundle.message("select.next.occurence.not.found.message"); + final LightweightHint hint = new LightweightHint(HintUtil.createInformationLabel(message)); + HintManagerImpl.getInstanceImpl().showEditorHint(hint, + editor, + HintManager.UNDER, + HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, + 0, + false); + } + + private static boolean getAndResetNotFoundStatus(Editor editor) { + boolean status = editor.getUserData(NOT_FOUND) != null; + editor.putUserData(NOT_FOUND, null); + return status && isRepeatedActionInvocation(); + } + + private static void setNotFoundStatus(Editor editor) { + editor.putUserData(NOT_FOUND, Boolean.TRUE); + } + + private static Boolean isWholeWordSearch(Editor editor) { + if (!isRepeatedActionInvocation()) { + editor.putUserData(WHOLE_WORDS, null); + } + return editor.getUserData(WHOLE_WORDS); + } + + private static void setWholeWordSearch(Editor editor, boolean isWholeWordSearch) { + editor.putUserData(WHOLE_WORDS, isWholeWordSearch); + } + + private static boolean isRepeatedActionInvocation() { + String lastActionId = LastActionTracker.getInstance().getLastActionId(); + return IdeActions.ACTION_SELECT_NEXT_OCCURENCE.equals(lastActionId) || IdeActions.ACTION_UNSELECT_LAST_OCCURENCE.equals(lastActionId); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/UnselectLastOccurrenceAction.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/UnselectLastOccurrenceAction.java new file mode 100644 index 000000000000..37b6c56a5c4a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/UnselectLastOccurrenceAction.java @@ -0,0 +1,48 @@ +/* + * 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.actions; + +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Caret; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.actionSystem.EditorAction; +import com.intellij.openapi.editor.actionSystem.EditorActionHandler; +import org.jetbrains.annotations.Nullable; + +public class UnselectLastOccurrenceAction extends EditorAction { + protected UnselectLastOccurrenceAction() { + super(new Handler()); + } + + private static class Handler extends EditorActionHandler { + @Override + public boolean isEnabled(Editor editor, DataContext dataContext) { + return super.isEnabled(editor, dataContext) && editor.getCaretModel().supportsMultipleCarets(); + } + + @Override + public void execute(Editor editor, @Nullable Caret caret, DataContext dataContext) { + if (editor.getCaretModel().getAllCarets().size() > 1) { + editor.getCaretModel().removeCaret(editor.getCaretModel().getPrimaryCaret()); + } + else { + editor.getSelectionModel().removeSelection(); + } + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index 7f9e40550a6d..b515b84e8f3f 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -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. @@ -114,6 +114,8 @@ public interface IdeActions { @NonNls String ACTION_FIND = "Find"; @NonNls String ACTION_FIND_NEXT = "FindNext"; @NonNls String ACTION_FIND_PREVIOUS = "FindPrevious"; + @NonNls String ACTION_SELECT_NEXT_OCCURENCE = "SelectNextOccurrence"; + @NonNls String ACTION_UNSELECT_LAST_OCCURENCE = "UnselectLastOccurrence"; @NonNls String ACTION_COMPILE = "Compile"; @NonNls String ACTION_COMPILE_PROJECT = "CompileProject"; @NonNls String ACTION_MAKE_MODULE = "MakeModule"; diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java index d8e4246c47ac..caecb4c31d7d 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -587,6 +587,7 @@ public class HintManagerImpl extends HintManager implements Disposable { @NotNull LogicalPosition pos2, @PositionFlags short constraint, boolean showByBalloon) { + if (ApplicationManager.getApplication().isUnitTestMode()) return new Point(); Point p = _getHintPosition(hint, editor, pos1, pos2, constraint, showByBalloon); JLayeredPane layeredPane = editor.getComponent().getRootPane().getLayeredPane(); Dimension hintSize = hint.getComponent().getPreferredSize(); diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/LastActionTrackerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/LastActionTrackerImpl.java new file mode 100644 index 000000000000..7f003654d1f2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/LastActionTrackerImpl.java @@ -0,0 +1,116 @@ +/* + * 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.actionSystem.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.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 { + private final ActionManager myActionManager; + private final EditorEventMulticaster myEditorEventMulticaster; + + private String myLastActionId; + private Editor myLastEditor; + + LastActionTrackerImpl(ActionManager actionManager, EditorFactory editorFactory) { + myActionManager = actionManager; + myEditorEventMulticaster = editorFactory.getEventMulticaster(); + } + + @Override + public void initComponent() { + myActionManager.addAnActionListener(this); + myEditorEventMulticaster.addEditorMouseListener(this); + } + + @Override + public void disposeComponent() { + myEditorEventMulticaster.removeEditorMouseListener(this); + myActionManager.removeAnActionListener(this); + } + + @NotNull + @Override + public String getComponentName() { + return "LastActionTracker"; + } + + @Override + @Nullable + public String getLastActionId() { + return myLastActionId; + } + + @Override + public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { + if (CommonDataKeys.EDITOR.getData(dataContext) != myLastEditor) { + resetLastAction(); + } + } + + @Override + public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { + myLastActionId = getActionId(action); + myLastEditor = CommonDataKeys.EDITOR.getData(dataContext); + } + + @Override + public void beforeEditorTyping(char c, DataContext dataContext) { + resetLastAction(); + } + + @Override + public void mousePressed(EditorMouseEvent e) { + resetLastAction(); + } + + @Override + public void mouseClicked(EditorMouseEvent e) { + resetLastAction(); + } + + @Override + public void mouseReleased(EditorMouseEvent e) { + resetLastAction(); + } + + @Override + public void mouseEntered(EditorMouseEvent e) { + + } + + @Override + public void mouseExited(EditorMouseEvent e) { + + } + + private String getActionId(AnAction action) { + return action instanceof ActionStub ? ((ActionStub)action).getId() : myActionManager.getId(action); + } + + private void resetLastAction() { + myLastActionId = null; + myLastEditor = null; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java index e86bc16cd437..e0abbf65d3a4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java @@ -784,4 +784,21 @@ public class EditorActionUtil { public static void moveCaretToLineStartIgnoringSoftWraps(@NotNull Editor editor) { editor.getCaretModel().moveToLogicalPosition(EditorUtil.calcCaretLineRange(editor).first); } + + /** + * This method will make required expansions of collapsed region to make given offset 'visible'. + */ + public static void makePositionVisible(@NotNull final Editor editor, final int offset) { + FoldingModel foldingModel = editor.getFoldingModel(); + FoldRegion collapsedRegionAtOffset; + while ((collapsedRegionAtOffset = foldingModel.getCollapsedRegionAtOffset(offset)) != null) { + final FoldRegion region = collapsedRegionAtOffset; + foldingModel.runBatchFoldingOperation(new Runnable() { + @Override + public void run() { + region.setExpanded(true); + } + }); + } + } } diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index ac04bd213af6..fa38ddeae330 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -107,6 +107,8 @@ action.EditorMoveUpAndScrollWithSelection.text=Move Up and Scroll with Selection action.EditorMoveDownAndScrollWithSelection.text=Move Down and Scroll with Selection action.EditorCloneCaretBelow.text=Clone Caret Below action.EditorCloneCaretAbove.text=Clone Caret Above +action.SelectNextOccurence.text=Select Next Occurence +action.UnselectLastOccurence.text=Unselect Last Occurence action.EditorToggleStickySelection.text=Toggle Sticky Selection action.EditorSwapSelectionBoundaries.text=Swap selection boundaries action.EditorLineStart.text=Move Caret to Line Start diff --git a/platform/platform-resources-en/src/messages/FindBundle.properties b/platform/platform-resources-en/src/messages/FindBundle.properties index 20ec7830472d..26959ba14d4b 100644 --- a/platform/platform-resources-en/src/messages/FindBundle.properties +++ b/platform/platform-resources-en/src/messages/FindBundle.properties @@ -7,6 +7,7 @@ find.search.again.from.top.hotkey.message={0}, press {1} to search from the top find.search.again.from.top.action.message={0}, perform \"Find Next\" again to search from the top find.search.again.from.bottom.hotkey.message={0}, press {1} to search from the bottom find.search.again.from.bottom.action.message={0}, perform \"Find Previous\" again to search from the bottom +select.next.occurence.not.found.message=Not found, try again to search from top find.no.usages.at.cursor.error=Cannot search for usages. \nPosition to an element to find usages for, and try again. find.what.group=Find diff --git a/platform/platform-resources/src/componentSets/UICore.xml b/platform/platform-resources/src/componentSets/UICore.xml index 8d7d5d63e055..042b053f68d4 100644 --- a/platform/platform-resources/src/componentSets/UICore.xml +++ b/platform/platform-resources/src/componentSets/UICore.xml @@ -21,6 +21,10 @@ com.intellij.openapi.actionSystem.ActionManager com.intellij.openapi.actionSystem.impl.ActionManagerImpl + + com.intellij.openapi.actionSystem.LastActionTracker + com.intellij.openapi.actionSystem.impl.LastActionTrackerImpl + com.intellij.openapi.keymap.KeymapManager com.intellij.openapi.keymap.impl.KeymapManagerImpl diff --git a/platform/platform-resources/src/idea/Keymap_Default.xml b/platform/platform-resources/src/idea/Keymap_Default.xml index 2e91d14985ab..2abce4dfd297 100644 --- a/platform/platform-resources/src/idea/Keymap_Default.xml +++ b/platform/platform-resources/src/idea/Keymap_Default.xml @@ -112,6 +112,12 @@ + + + + + + diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index 3fe320b77ffc..9902476aab7f 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -225,6 +225,8 @@ + + diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/SelectUnselectOccurrenceActionsTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/SelectUnselectOccurrenceActionsTest.java new file mode 100644 index 000000000000..58d36578ee37 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/SelectUnselectOccurrenceActionsTest.java @@ -0,0 +1,194 @@ +/* + * 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.actions; + +import com.intellij.codeInsight.hint.EditorHintListener; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.FoldRegion; +import com.intellij.openapi.editor.FoldingModel; +import com.intellij.openapi.fileTypes.FileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.testFramework.EditorTestUtil; +import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; +import com.intellij.ui.LightweightHint; + +public class SelectUnselectOccurrenceActionsTest extends LightPlatformCodeInsightFixtureTestCase { + private int hintCount; + + @Override + public void setUp() throws Exception { + super.setUp(); + EditorTestUtil.enableMultipleCarets(); + EditorHintListener listener = new EditorHintListener() { + @Override + public void hintShown(Project project, LightweightHint hint, int flags) { + hintCount++; + } + }; + ApplicationManager.getApplication().getMessageBus().connect(myTestRootDisposable).subscribe(EditorHintListener.TOPIC, listener); + } + + @Override + public void tearDown() throws Exception { + EditorTestUtil.disableMultipleCarets(); + super.tearDown(); + } + + public void testNoInitialSelection() throws Exception { + init("some text\n" + + "some texts\n" + + "another text here" + ); + executeAction(); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + assertEquals(0, hintCount); + } + + public void testInitialWholeWordSelection() throws Exception { + init("some text\n" + + "some texts\n" + + "another text here"); + executeAction(); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + assertEquals(0, hintCount); + } + + public void testShowingHint() throws Exception { + init("some text\n" + + "some texts\n" + + "another text here"); + executeAction(); + assertEquals(1, hintCount); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + executeAction(); + assertEquals(1, hintCount); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + } + + public void testRevert() throws Exception { + init("some text\n" + + "some texts\n" + + "another text here"); + executeReverseAction(); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + assertEquals(0, hintCount); + } + + public void testRevertSingleSelection() throws Exception { + init("some text\n" + + "some texts\n" + + "another text here"); + executeReverseAction(); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + assertEquals(0, hintCount); + } + + public void testSelectAfterHint() throws Exception { + init("some text\n" + + "some texts\n" + + "another text here"); + executeAction(); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + assertEquals(1, hintCount); + executeAction(); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + assertEquals(1, hintCount); + } + + public void testInitialNonWholeWordSelection() throws Exception { + init("some text\n" + + "some texts\n" + + "another text here"); + executeAction(); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + executeAction(); + checkResult("some text\n" + + "some texts\n" + + "another text here"); + assertEquals(0, hintCount); + } + + public void testWholeWordinessIsRememberedAtStart() throws Exception { + init("some texts\n" + + "another text\n" + + "some other texts"); + executeAction(); + checkResult("some texts\n" + + "another text\n" + + "some other texts"); + executeAction(); + checkResult("some texts\n" + + "another text\n" + + "some other texts"); + assertEquals(0, hintCount); + } + + public void testOccurrenceInCollapsedRegion() throws Exception { + init("normal line\n" + + "collapsed line"); + final FoldingModel foldingModel = myFixture.getEditor().getFoldingModel(); + final Document document = myFixture.getEditor().getDocument(); + foldingModel.runBatchFoldingOperation(new Runnable() { + @Override + public void run() { + FoldRegion foldRegion = foldingModel.addFoldRegion(document.getLineStartOffset(1), document.getLineEndOffset(1), "..."); + assertNotNull(foldRegion); + foldRegion.setExpanded(false); + } + }); + executeAction(); + checkResult("normal line\n" + + "collapsed line"); + FoldRegion[] foldRegions = foldingModel.getAllFoldRegions(); + assertEquals(1, foldRegions.length); + assertTrue(foldRegions[0].isExpanded()); + } + + private void init(String text) { + myFixture.configureByText(FileTypes.PLAIN_TEXT, text); + } + + private void checkResult(String text) { + myFixture.checkResult(text); + } + + private void executeAction() { + myFixture.performEditorAction("SelectNextOccurrence"); + } + + private void executeReverseAction() { + myFixture.performEditorAction("UnselectLastOccurrence"); + } +}