diff --git a/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/SearchMessages.java b/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/SearchMessages.java index 75adbe4b6a2..2324a479d62 100644 --- a/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/SearchMessages.java +++ b/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/SearchMessages.java @@ -69,6 +69,10 @@ private SearchMessages() { public static String ShowNextResultAction_tooltip; public static String ShowPreviousResultAction_label; public static String ShowPreviousResultAction_tooltip; + public static String GlobalNextSearchEntryAction_label; + public static String GlobalNextSearchEntryAction_tooltip; + public static String GlobalPreviousSearchEntryAction_label; + public static String GlobalPreviousSearchEntryAction_tooltip; public static String RemoveMatchAction_label; public static String RemoveMatchAction_tooltip; public static String DefaultSearchViewPage_show_match; diff --git a/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/SearchMessages.properties b/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/SearchMessages.properties index 0973f594f8a..ea6f180cc49 100644 --- a/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/SearchMessages.properties +++ b/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/SearchMessages.properties @@ -42,6 +42,10 @@ ShowNextResultAction_label=Next Match ShowNextResultAction_tooltip=Show Next Match ShowPreviousResultAction_label=Previous Match ShowPreviousResultAction_tooltip=Show Previous Match +GlobalNextSearchEntryAction_label=Next Search Entry +GlobalNextSearchEntryAction_tooltip=Go to Next Search Result +GlobalPreviousSearchEntryAction_label=Previous Search Entry +GlobalPreviousSearchEntryAction_tooltip=Go to Previous Search Result RemoveMatchAction_label=Remove Match RemoveMatchAction_tooltip=Remove Currently Showing Match DefaultSearchViewPage_show_match=Show Match diff --git a/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/basic/views/GlobalNextPrevSearchEntryHandler.java b/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/basic/views/GlobalNextPrevSearchEntryHandler.java new file mode 100644 index 00000000000..005c08f599d --- /dev/null +++ b/bundles/org.eclipse.search/newsearch/org/eclipse/search2/internal/ui/basic/views/GlobalNextPrevSearchEntryHandler.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2024 Eclipse Foundation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eclipse Foundation - initial API and implementation + *******************************************************************************/ +package org.eclipse.search2.internal.ui.basic.views; + +import java.util.HashMap; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.NotEnabledException; +import org.eclipse.core.commands.NotHandledException; +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.core.commands.common.NotDefinedException; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.swt.widgets.Event; +import org.eclipse.ui.IWorkbenchCommandConstants; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.commands.ICommandService; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.handlers.IHandlerService; + +/** + * Global handler for navigating to next/previous search results without requiring + * focus on the Search view. This handler provides a seamless workflow for + * navigating through search results while editing. + * + * @since 3.17 + */ +public class GlobalNextPrevSearchEntryHandler extends AbstractHandler implements IExecutableExtension { + private String searchCommand = IWorkbenchCommandConstants.NAVIGATE_NEXT; + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event); + ICommandService cs = window.getService(ICommandService.class); + + // Check if search view is available + Command showView = cs.getCommand(IWorkbenchCommandConstants.VIEWS_SHOW_VIEW); + if (!showView.isDefined()) { + return null; // Search view not available, exit gracefully + } + + // Show the Search view + HashMap parms = new HashMap<>(); + parms.put(IWorkbenchCommandConstants.VIEWS_SHOW_VIEW_PARM_ID, "org.eclipse.search.ui.views.SearchView"); //$NON-NLS-1$ + ParameterizedCommand showSearchView = ParameterizedCommand.generateCommand(showView, parms); + + IHandlerService hs = window.getService(IHandlerService.class); + Object triggerObj = event.getTrigger(); + Event trigger = (triggerObj instanceof Event) ? (Event) triggerObj : new Event(); + + try { + // Execute the sequence: show search view -> navigate -> activate editor + hs.executeCommand(showSearchView, trigger); + hs.executeCommand(searchCommand, trigger); + hs.executeCommand(IWorkbenchCommandConstants.WINDOW_ACTIVATE_EDITOR, trigger); + } catch (NotDefinedException | NotEnabledException | NotHandledException e) { + throw new ExecutionException(e.getMessage(), e); + } + + return null; + } + + @Override + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { + if ("previous".equals(data)) { //$NON-NLS-1$ + searchCommand = IWorkbenchCommandConstants.NAVIGATE_PREVIOUS; + } + } +} diff --git a/bundles/org.eclipse.search/plugin.xml b/bundles/org.eclipse.search/plugin.xml index 7a7739a4da1..3c4c0477d6a 100644 --- a/bundles/org.eclipse.search/plugin.xml +++ b/bundles/org.eclipse.search/plugin.xml @@ -104,6 +104,20 @@ name="%command.performTextSearchFile.name" description="%command.performTextSearchFile.description" /> + + @@ -162,6 +176,18 @@ commandId="org.eclipse.search.ui.performTextSearchWorkspace" schemeId="org.eclipse.ui.defaultAcceleratorConfiguration" sequence="M1+M3+T"/> + + + + diff --git a/tests/org.eclipse.search.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.search.tests/META-INF/MANIFEST.MF index 2a8eab0fef3..0f02be31df0 100644 --- a/tests/org.eclipse.search.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.search.tests/META-INF/MANIFEST.MF @@ -18,7 +18,8 @@ Require-Bundle: org.eclipse.ui.workbench.texteditor;bundle-version="[3.17.200,4.0.0)", org.eclipse.jface.text;bundle-version="[3.24.200,4.0.0)", org.eclipse.ui.editors;bundle-version="[3.17.100,4.0.0)", - org.eclipse.ltk.core.refactoring;bundle-version="[3.14.100,4.0.0)" + org.eclipse.ltk.core.refactoring;bundle-version="[3.14.100,4.0.0)", + org.mockito.mockito-core;bundle-version="2.13.0" Import-Package: org.junit.jupiter.api;version="[5.14.0,6.0.0)", org.junit.platform.suite.api;version="[1.14.0,2.0.0)" Bundle-ActivationPolicy: lazy diff --git a/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/AllSearchTests.java b/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/AllSearchTests.java index 0ea450dd86d..4cc391fc35a 100644 --- a/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/AllSearchTests.java +++ b/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/AllSearchTests.java @@ -23,7 +23,9 @@ @SelectClasses({ AllFileSearchTests.class, AllSearchModelTests.class, - TextSearchRegistryTest.class + TextSearchRegistryTest.class, + GlobalNextPrevSearchEntryHandlerTest.class, + GlobalNextPrevSearchEntryHandlerIntegrationTest.class }) public class AllSearchTests { // see @SelectClasses diff --git a/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerIntegrationTest.java b/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerIntegrationTest.java new file mode 100644 index 00000000000..343926b28c5 --- /dev/null +++ b/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerIntegrationTest.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2024 Eclipse Foundation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eclipse Foundation - initial API and implementation + *******************************************************************************/ +package org.eclipse.search.tests; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.search2.internal.ui.basic.views.GlobalNextPrevSearchEntryHandler; +import org.junit.jupiter.api.Test; + +/** + * Integration tests for {@link GlobalNextPrevSearchEntryHandler}. + * These tests verify the basic functionality and integration points. + * + * @since 3.17 + */ +public class GlobalNextPrevSearchEntryHandlerIntegrationTest { + + /** + * Test that the handler can be created and configured properly. + * This test verifies the basic instantiation and configuration functionality. + */ + @Test + public void testHandlerCreationAndConfiguration() throws CoreException { + // Test Next handler + GlobalNextPrevSearchEntryHandler nextHandler = new GlobalNextPrevSearchEntryHandler(); + assertNotNull(nextHandler, "Next handler should be created successfully"); + + // Configure for next command (default behavior) + nextHandler.setInitializationData(null, "command", "next"); + // No exception should be thrown + + // Test Previous handler + GlobalNextPrevSearchEntryHandler prevHandler = new GlobalNextPrevSearchEntryHandler(); + assertNotNull(prevHandler, "Previous handler should be created successfully"); + + // Configure for previous command + prevHandler.setInitializationData(null, "command", "previous"); + // No exception should be thrown + } + + /** + * Test that the handler handles various configuration scenarios correctly. + */ + @Test + public void testHandlerConfigurationScenarios() throws CoreException { + GlobalNextPrevSearchEntryHandler handler = new GlobalNextPrevSearchEntryHandler(); + + // Test with null configuration + handler.setInitializationData(null, null, null); + // Should not throw exception + + // Test with empty string + handler.setInitializationData(null, "", ""); + // Should not throw exception + + // Test with unknown command type + handler.setInitializationData(null, "command", "unknown"); + // Should not throw exception and should default to next behavior + } + + /** + * Test that the handler implements the required interfaces. + */ + @Test + public void testHandlerInterfaceImplementation() { + GlobalNextPrevSearchEntryHandler handler = new GlobalNextPrevSearchEntryHandler(); + + // Verify it implements IHandler + assertTrue(handler instanceof org.eclipse.core.commands.IHandler, + "Handler should implement IHandler interface"); + + // Verify it implements IExecutableExtension + assertTrue(handler instanceof org.eclipse.core.runtime.IExecutableExtension, + "Handler should implement IExecutableExtension interface"); + } + + /** + * Test that multiple handler instances can be created independently. + */ + @Test + public void testMultipleHandlerInstances() throws CoreException { + // Create multiple handlers + GlobalNextPrevSearchEntryHandler handler1 = new GlobalNextPrevSearchEntryHandler(); + GlobalNextPrevSearchEntryHandler handler2 = new GlobalNextPrevSearchEntryHandler(); + GlobalNextPrevSearchEntryHandler handler3 = new GlobalNextPrevSearchEntryHandler(); + + // Configure them differently + handler1.setInitializationData(null, "command", "next"); + handler2.setInitializationData(null, "command", "previous"); + handler3.setInitializationData(null, "command", "unknown"); + + // All should be created and configured without issues + assertNotNull(handler1); + assertNotNull(handler2); + assertNotNull(handler3); + } +} diff --git a/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerTest.java b/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerTest.java new file mode 100644 index 00000000000..72696470b7d --- /dev/null +++ b/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerTest.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2024 Eclipse Foundation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eclipse Foundation - initial API and implementation + *******************************************************************************/ +package org.eclipse.search.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.search2.internal.ui.basic.views.GlobalNextPrevSearchEntryHandler; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link GlobalNextPrevSearchEntryHandler}. + * + * @since 3.17 + */ +public class GlobalNextPrevSearchEntryHandlerTest { + + private GlobalNextPrevSearchEntryHandler handler; + + @BeforeEach + public void setUp() { + handler = new GlobalNextPrevSearchEntryHandler(); + } + + /** + * Test that the handler can be instantiated without errors. + */ + @Test + public void testHandlerInstantiation() { + // Verify handler can be created + assertNull(handler); + handler = new GlobalNextPrevSearchEntryHandler(); + // If we get here, instantiation was successful + } + + /** + * Test that setInitializationData works correctly for "previous" command. + */ + @Test + public void testSetInitializationDataWithPreviousCommand() throws CoreException { + // Test with "previous" data - should set the handler to use NAVIGATE_PREVIOUS + handler.setInitializationData(null, "property", "previous"); + // If no exception is thrown, the method worked correctly + } + + /** + * Test that setInitializationData works correctly for "next" command. + */ + @Test + public void testSetInitializationDataWithNextCommand() throws CoreException { + // Test with "next" data - should keep default NAVIGATE_NEXT behavior + handler.setInitializationData(null, "property", "next"); + // If no exception is thrown, the method worked correctly + } + + /** + * Test that setInitializationData works correctly with unknown command. + */ + @Test + public void testSetInitializationDataWithUnknownCommand() throws CoreException { + // Test with unknown data - should keep default NAVIGATE_NEXT behavior + handler.setInitializationData(null, "property", "unknown"); + // If no exception is thrown, the method worked correctly + } + + /** + * Test that setInitializationData works correctly with null data. + */ + @Test + public void testSetInitializationDataWithNullData() throws CoreException { + // Test with null data - should keep default NAVIGATE_NEXT behavior + handler.setInitializationData(null, "property", null); + // If no exception is thrown, the method worked correctly + } + + /** + * Test that the handler implements the expected interfaces. + */ + @Test + public void testHandlerImplementsInterfaces() { + // Verify the handler implements the expected interfaces + assertEquals(true, handler instanceof org.eclipse.core.commands.IHandler); + assertEquals(true, handler instanceof org.eclipse.core.runtime.IExecutableExtension); + } +} diff --git a/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerTest_README.md b/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerTest_README.md new file mode 100644 index 00000000000..29f87a00f78 --- /dev/null +++ b/tests/org.eclipse.search.tests/src/org/eclipse/search/tests/GlobalNextPrevSearchEntryHandlerTest_README.md @@ -0,0 +1,54 @@ +# GlobalNextPrevSearchEntryHandler Tests + +This directory contains comprehensive unit tests for the `GlobalNextPrevSearchEntryHandler` class. + +## Test Files + +### GlobalNextPrevSearchEntryHandlerTest.java +Basic unit tests that verify: +- Handler instantiation works correctly +- `setInitializationData()` method handles different configuration scenarios +- Handler implements required interfaces (`IHandler`, `IExecutableExtension`) + +### GlobalNextPrevSearchEntryHandlerIntegrationTest.java +Integration tests that verify: +- Handler creation and configuration in various scenarios +- Multiple handler instances can be created independently +- Handler behavior with different configuration parameters + +## Test Coverage + +The tests cover the following scenarios: + +### Configuration Testing +- ✅ "next" command configuration (default behavior) +- ✅ "previous" command configuration +- ✅ Unknown command configuration (defaults to "next") +- ✅ Null configuration handling +- ✅ Empty string configuration handling + +### Interface Implementation +- ✅ `IHandler` interface implementation +- ✅ `IExecutableExtension` interface implementation + +### Error Handling +- ✅ Null parameter handling in `setInitializationData()` +- ✅ Unknown command type handling + +## Running the Tests + +These tests are included in the `AllSearchTests` test suite and can be run as part of the Eclipse platform UI test suite. + +## Dependencies + +The tests use: +- JUnit 5 (`@Test`, `@BeforeEach`) +- Eclipse Core Runtime (`CoreException`, `IConfigurationElement`) +- Eclipse Search 2 UI (`GlobalNextPrevSearchEntryHandler`) + +## Notes + +- The tests focus on the testable functionality without requiring complex mocking +- Integration tests verify real behavior in the Eclipse environment +- All tests are designed to run in the Eclipse test harness +- Tests verify both positive and negative scenarios