diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui.test/META-INF/MANIFEST.MF b/bundles/org.eclipse.cdt.lsp.editor.ui.test/META-INF/MANIFEST.MF index 24a4866..d0456d6 100644 --- a/bundles/org.eclipse.cdt.lsp.editor.ui.test/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.cdt.lsp.editor.ui.test/META-INF/MANIFEST.MF @@ -7,6 +7,10 @@ Bundle-Vendor: Eclipse.org Fragment-Host: org.eclipse.cdt.lsp.editor.ui;bundle-version="0.0.0" Require-Bundle: org.eclipse.cdt.lsp, junit-jupiter-api, - org.yaml.snakeyaml + org.yaml.snakeyaml, + org.objenesis, + net.bytebuddy.byte-buddy, + net.bytebuddy.byte-buddy-agent, + org.mockito Automatic-Module-Name: org.eclipse.cdt.lsp.editor.ui.test Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui.test/src/org/eclipse/cdt/lsp/editor/ui/test/commands/LspEditorActiveTesterTest.java b/bundles/org.eclipse.cdt.lsp.editor.ui.test/src/org/eclipse/cdt/lsp/editor/ui/test/commands/LspEditorActiveTesterTest.java new file mode 100644 index 0000000..1b83e5a --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.editor.ui.test/src/org/eclipse/cdt/lsp/editor/ui/test/commands/LspEditorActiveTesterTest.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2023 COSEDA Technologies GmbH and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Dominic Scharfe (COSEDA Technologies GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.lsp.editor.ui.test.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.eclipse.cdt.lsp.LspPlugin; +import org.eclipse.cdt.lsp.editor.ui.commands.LspEditorActiveTester; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.texteditor.ITextEditor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class LspEditorActiveTesterTest { + + LspEditorActiveTester tut; + + IEditorPart editorPart; + IEditorSite editorSite; + + @BeforeEach + public void setup() { + tut = new LspEditorActiveTester(); + + editorPart = mock(IEditorPart.class); + editorSite = mock(IEditorSite.class); + + when(editorPart.getSite()).thenReturn(editorSite); + } + + /** + * Assert that a receiver which is not an editor part is not detected as c + * editor. + */ + @Test + public void nonEditorPart() { + assertFalse(tut.test(new String("not an edit part"), LspEditorActiveTester.IS_LSP_CEDITOR_PROPERTY, null, tut)); + } + + /** + * Assert that tester only tests for the correct property, even if the given + * receiver is an {@link IEditorPart}. + */ + @Test + public void editorPartNonMatchingProperty() { + assertFalse(tut.test(editorPart, "someOtherProperty", null, tut)); + } + + /** + * Assert that an {@link IEditorPart} with some other id is not detected as c editor. + */ + @Test + public void matchingEditorPartWithNonMatchingId() { + when(editorSite.getId()).thenReturn("someId"); + assertTestResult(false); + } + + /** + * Assert that an {@link IEditorPart} with the c editor id is detected as c editor. + */ + @Test + public void matchingEditorPartWithMatchingId() { + when(editorSite.getId()).thenReturn(LspPlugin.LSP_C_EDITOR_ID); + assertTestResult(true); + } + + /** + * Assert that an {@link IEditorPart} with an "inner editor" (part adapts to + * {@link ITextEditor.class}) is not detected as c editor when the inner editor + * doesn't have the c editor id. + */ + @Test + public void editorPartWithInnerNonMatchingEditor() { + ITextEditor innerEditor = mock(ITextEditor.class); + IEditorSite innerEditorSite = mock(IEditorSite.class); + when(innerEditor.getSite()).thenReturn(innerEditorSite); + when(innerEditorSite.getId()).thenReturn("someId"); + when(editorPart.getAdapter(ITextEditor.class)).thenReturn(innerEditor); + when(editorSite.getId()).thenReturn("someOtherId"); + + assertTestResult(false); + } + + /** + * Assert that an {@link IEditorPart} with an "inner editor" (part adapts to + * {@link ITextEditor.class}) is detected as c editor when the inner editor + * has the c editor id. + */ + @Test + public void editorPartWithInnerMatchingEditor() { + ITextEditor innerEditor = mock(ITextEditor.class); + IEditorSite innerEditorSite = mock(IEditorSite.class); + when(innerEditor.getSite()).thenReturn(innerEditorSite); + when(innerEditorSite.getId()).thenReturn(LspPlugin.LSP_C_EDITOR_ID); + when(editorPart.getAdapter(ITextEditor.class)).thenReturn(innerEditor); + when(editorSite.getId()).thenReturn("someOtherId"); + + assertTestResult(true); + } + + /** + * Helper method. + */ + private void assertTestResult(boolean expectedResult) { + assertEquals(expectedResult, tut.test(editorPart, LspEditorActiveTester.IS_LSP_CEDITOR_PROPERTY, null, tut)); + } + +} diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.cdt.lsp.editor.ui/META-INF/MANIFEST.MF index cfd2bfd..d47f95f 100644 --- a/bundles/org.eclipse.cdt.lsp.editor.ui/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.cdt.lsp.editor.ui/META-INF/MANIFEST.MF @@ -5,6 +5,7 @@ Bundle-SymbolicName: org.eclipse.cdt.lsp.editor.ui;singleton:=true Bundle-Version: 1.0.0.qualifier Bundle-Activator: org.eclipse.cdt.lsp.editor.ui.LspEditorUiPlugin Bundle-Vendor: Eclipse.org +Bundle-Localization: plugin Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.cdt.lsp, @@ -15,7 +16,10 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ui.editors, org.eclipse.cdt.core, org.eclipse.jface.text, - org.yaml.snakeyaml + org.yaml.snakeyaml, + org.eclipse.lsp4j, + org.eclipse.lsp4e, + org.eclipse.lsp4j.jsonrpc Bundle-RequiredExecutionEnvironment: JavaSE-17 Automatic-Module-Name: org.eclipse.cdt.lsp.editor.ui Bundle-ActivationPolicy: lazy diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui/build.properties b/bundles/org.eclipse.cdt.lsp.editor.ui/build.properties index e9863e2..0dc34f7 100644 --- a/bundles/org.eclipse.cdt.lsp.editor.ui/build.properties +++ b/bundles/org.eclipse.cdt.lsp.editor.ui/build.properties @@ -2,4 +2,5 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ .,\ - plugin.xml + plugin.xml,\ + plugin.properties diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui/plugin.properties b/bundles/org.eclipse.cdt.lsp.editor.ui/plugin.properties new file mode 100644 index 0000000..da72f0d --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.editor.ui/plugin.properties @@ -0,0 +1,2 @@ +Commands.ToggleSourceAndHeader.name=Toggle Source/Header +PopupMenu.ToggleSourceAndHeader.label=Toggle Source/Header \ No newline at end of file diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui/plugin.xml b/bundles/org.eclipse.cdt.lsp.editor.ui/plugin.xml index 496f9fa..00ccc98 100644 --- a/bundles/org.eclipse.cdt.lsp.editor.ui/plugin.xml +++ b/bundles/org.eclipse.cdt.lsp.editor.ui/plugin.xml @@ -37,6 +37,13 @@ properties="LspEditorPreference" type="java.lang.Object"> + + @@ -52,4 +59,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/commands/LspEditorActiveTester.java b/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/commands/LspEditorActiveTester.java new file mode 100644 index 0000000..60d903a --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/commands/LspEditorActiveTester.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2023 COSEDA Technologies GmbH and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Dominic Scharfe (COSEDA Technologies GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.lsp.editor.ui.commands; + +import java.util.Optional; + +import org.eclipse.cdt.lsp.LspPlugin; +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.core.runtime.Adapters; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.texteditor.ITextEditor; + +/** + * Property tester for checking if a receiver is a c editor. Will evaluate + * {@code true} if the receiver can adapt to {@link ITextEditor} and has the c + * editor id. + */ +public class LspEditorActiveTester extends PropertyTester { + public final static String IS_LSP_CEDITOR_PROPERTY = "isLspCEditor"; + + @Override + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + if (receiver instanceof IEditorPart && IS_LSP_CEDITOR_PROPERTY.equals(property)) { + IEditorPart innerEditor = Optional.ofNullable((IEditorPart) Adapters.adapt(receiver, ITextEditor.class)) + .orElse((IEditorPart) receiver); + + return LspPlugin.LSP_C_EDITOR_ID.equals(innerEditor.getSite().getId()); + } + return false; + } +} diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/commands/ToggleSourceAndHeaderCommandHandler.java b/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/commands/ToggleSourceAndHeaderCommandHandler.java new file mode 100644 index 0000000..fd0e7ac --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/commands/ToggleSourceAndHeaderCommandHandler.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2023 COSEDA Technologies GmbH and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Dominic Scharfe (COSEDA Technologies GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.lsp.editor.ui.commands; + +import java.net.URI; +import java.util.Optional; + +import org.eclipse.cdt.lsp.LspPlugin; +import org.eclipse.cdt.lsp.editor.ui.LspEditorUiPlugin; +import org.eclipse.cdt.lsp.services.ClangdLanguageServer; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.runtime.Adapters; +import org.eclipse.jface.text.IDocument; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IURIEditorInput; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.statushandlers.StatusManager; +import org.eclipse.ui.texteditor.ITextEditor; + +public class ToggleSourceAndHeaderCommandHandler extends AbstractHandler implements IHandler { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + return execute(HandlerUtil.getActiveEditor(event)); + } + + @SuppressWarnings("restriction") + private Object execute(IEditorPart activeEditor) { + // Try to adapt to ITextEditor (e.g. to support editors embedded in + // MultiPageEditorParts), otherwise use activeEditor. + IEditorPart innerEditor = Optional.ofNullable((IEditorPart) Adapters.adapt(activeEditor, ITextEditor.class)) + .orElse(activeEditor); + + getUri(innerEditor).ifPresent(fileUri -> { + IDocument document = org.eclipse.lsp4e.LSPEclipseUtils.getDocument(innerEditor.getEditorInput()); + org.eclipse.lsp4e.LanguageServers.forDocument(document) + .computeFirst( + server -> server instanceof ClangdLanguageServer + ? ((ClangdLanguageServer) server) + .switchSourceHeader(new TextDocumentIdentifier(fileUri.toString())) + : null) + .thenAccept(otherFileUri -> otherFileUri + .ifPresent(uri -> openEditor(innerEditor.getEditorSite().getPage(), URI.create(uri)))); + }); + + return null; + } + + /** + * Returns the URI of the given editor depending on the type of its + * {@link IEditorPart#getEditorInput()}. + * + * @return the URI or an empty {@link Optional} if the URI couldn't be + * determined. + */ + private static Optional getUri(IEditorPart editor) { + IEditorInput editorInput = editor.getEditorInput(); + + if (editorInput instanceof IFileEditorInput) { + return Optional.of(((IFileEditorInput) editor.getEditorInput()).getFile().getLocationURI()); + } else if (editorInput instanceof IURIEditorInput) { + return Optional.of(((IURIEditorInput) editorInput).getURI()); + } else { + return Optional.empty(); + } + } + + private static void openEditor(IWorkbenchPage page, URI fileUri) { + page.getWorkbenchWindow().getShell().getDisplay().asyncExec(() -> { + try { + IDE.openEditor(page, fileUri, LspPlugin.LSP_C_EDITOR_ID, true); + } catch (PartInitException e) { + StatusManager.getManager().handle(e, LspEditorUiPlugin.PLUGIN_ID); + } + }); + } + +} \ No newline at end of file diff --git a/bundles/org.eclipse.cdt.lsp/META-INF/MANIFEST.MF b/bundles/org.eclipse.cdt.lsp/META-INF/MANIFEST.MF index 0442cba..5455ab0 100644 --- a/bundles/org.eclipse.cdt.lsp/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.cdt.lsp/META-INF/MANIFEST.MF @@ -4,7 +4,8 @@ Bundle-Name: Language Server based C/C++ Editor Bundle-SymbolicName: org.eclipse.cdt.lsp;singleton:=true Bundle-Version: 1.0.0.qualifier Export-Package: org.eclipse.cdt.lsp, - org.eclipse.cdt.lsp.server + org.eclipse.cdt.lsp.server, + org.eclipse.cdt.lsp.services Bundle-Activator: org.eclipse.cdt.lsp.LspPlugin Bundle-Vendor: Eclipse.org Require-Bundle: org.eclipse.ui, @@ -22,7 +23,9 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ui.workbench, org.eclipse.core.expressions, org.eclipse.jface.text, - org.eclipse.debug.ui + org.eclipse.debug.ui, + org.eclipse.lsp4j, + org.eclipse.lsp4j.jsonrpc Bundle-RequiredExecutionEnvironment: JavaSE-17 Automatic-Module-Name: org.eclipse.cdt.lsp Bundle-ActivationPolicy: lazy diff --git a/bundles/org.eclipse.cdt.lsp/plugin.xml b/bundles/org.eclipse.cdt.lsp/plugin.xml index 280c738..30cc1d4 100644 --- a/bundles/org.eclipse.cdt.lsp/plugin.xml +++ b/bundles/org.eclipse.cdt.lsp/plugin.xml @@ -34,6 +34,7 @@ class="org.eclipse.cdt.lsp.internal.server.CLanguageServerStreamConnectionProvider" id="org.eclipse.cdt.lsp.server" label="C/C++ Language Server" + serverInterface="org.eclipse.cdt.lsp.services.ClangdLanguageServer" singleton="true"> + *
  • get the corresponding header if a source file was provided
  • + *
  • get the source file if a header was provided
  • + * + * + * @param textDocument open file + * @return URI of the corresponding header/source file + * + * @see https://clangd.llvm.org/extensions#switch-between-sourceheader + */ + @JsonRequest(value = "textDocument/switchSourceHeader") + CompletableFuture switchSourceHeader(TextDocumentIdentifier textDocument); +} diff --git a/releng/org.eclipse.cdt.lsp.target/org.eclipse.cdt.lsp.target.target b/releng/org.eclipse.cdt.lsp.target/org.eclipse.cdt.lsp.target.target index 75ffb2a..f496ed5 100644 --- a/releng/org.eclipse.cdt.lsp.target/org.eclipse.cdt.lsp.target.target +++ b/releng/org.eclipse.cdt.lsp.target/org.eclipse.cdt.lsp.target.target @@ -45,6 +45,8 @@ + + \ No newline at end of file