diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui.test/src/org/eclipse/cdt/lsp/editor/ui/test/preference/LspEditorPreferencesTesterTest.java b/bundles/org.eclipse.cdt.lsp.editor.ui.test/src/org/eclipse/cdt/lsp/editor/ui/test/preference/LspEditorPreferencesTesterTest.java index bff74847..5d06339d 100644 --- a/bundles/org.eclipse.cdt.lsp.editor.ui.test/src/org/eclipse/cdt/lsp/editor/ui/test/preference/LspEditorPreferencesTesterTest.java +++ b/bundles/org.eclipse.cdt.lsp.editor.ui.test/src/org/eclipse/cdt/lsp/editor/ui/test/preference/LspEditorPreferencesTesterTest.java @@ -6,6 +6,8 @@ import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.file.Files; + import org.eclipse.cdt.lsp.LspPlugin; import org.eclipse.cdt.lsp.editor.ui.test.TestUtils; import org.eclipse.cdt.lsp.server.ICLanguageServerProvider; @@ -15,18 +17,24 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.io.TempDir; public class LspEditorPreferencesTesterTest { private static final String FILE_CONTENT = "// sample file content"; private static final String MAIN_CPP = "main.cpp"; - private static final String EXTERNAL_HEADER_HPP = "ExternalHeader.hpp"; + private static final String HEADER_HPP = "header.hpp"; + private static final String MAIN_C = "main.c"; + private static final String HEADER_H = "header.h"; + //private static final String EXTERNAL_HEADER_HPP = "ExternalHeader.hpp"; private IProject project; - @TempDir - private File tempDir; + // @TempDir -> does not work with org.junit.jupiter.api. Needs junit-jupiter-api and junit-jupiter-params. + // These packages are not accessible on the CI build server because we build with Eclipse 2022-06 + // Path tempDir = Files.createTempFile("ExternalHeader", ".hpp", null); + private File createTempHppHeaderfile() throws IOException { + return Files.createTempFile("ExternalHeader", ".hpp").toFile(); + } @BeforeEach public void setUp(TestInfo testInfo) throws CoreException { @@ -81,11 +89,11 @@ public void testLsEnableByUriTest_WITHOUT_LsEditorPreferred() throws CoreExcepti } /** - * Tests whether the C/C++ Editor is used for a resource to open whose project has "Prefer C/C++ Editor (LSP)" disabled. + * Tests whether the C/C++ Editor is used for a C++ source file to open whose project has "Prefer C/C++ Editor (LSP)" disabled. * @throws UnsupportedEncodingException */ @Test - public void testEditorUsedToOpenFile_WITHOUT_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { + public void testEditorUsedToOpenCppFile_WITHOUT_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { //GIVEN is a project with DISABLED "Prefer C/C++ Editor (LSP)" in the preferences: TestUtils.setLspPreferred(project, false); //AND a file exits in the given project: @@ -94,14 +102,15 @@ public void testEditorUsedToOpenFile_WITHOUT_LsEditorPreferred() throws CoreExce var editorPart = TestUtils.openInEditor(file); //THEN it will be opened in the C/C++ Editor: assertEquals(LspPlugin.C_EDITOR_ID, editorPart.getEditorSite().getId()); + TestUtils.closeEditor(editorPart, false); } /** - * Tests whether the C/C++ Editor (LSP) is used for a resource to open whose project has "Prefer C/C++ Editor (LSP)" enabled. + * Tests whether the C/C++ Editor (LSP) is used for a C++ source file to open whose project has "Prefer C/C++ Editor (LSP)" enabled. * @throws UnsupportedEncodingException */ @Test - public void testEditorUsedToOpenFile_WITH_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { + public void testEditorUsedToOpenCppFile_WITH_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { //GIVEN is a project with ENABLED "Prefer C/C++ Editor (LSP)" in the preferences: TestUtils.setLspPreferred(project, true); //AND a file exits in the given project: @@ -110,6 +119,109 @@ public void testEditorUsedToOpenFile_WITH_LsEditorPreferred() throws CoreExcepti var editorPart = TestUtils.openInEditor(file); //THEN it will be opened in the C/C++ Editor (LSP): assertEquals(LspPlugin.LSP_C_EDITOR_ID, editorPart.getEditorSite().getId()); + TestUtils.closeEditor(editorPart, false); + } + + /** + * Tests whether the C/C++ Editor is used for a C++ header file to open whose project has "Prefer C/C++ Editor (LSP)" disabled. + * @throws UnsupportedEncodingException + */ + @Test + public void testEditorUsedToOpenCppHeaderFile_WITHOUT_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { + //GIVEN is a project with DISABLED "Prefer C/C++ Editor (LSP)" in the preferences: + TestUtils.setLspPreferred(project, false); + //AND a file exits in the given project: + var file = TestUtils.createFile(project, HEADER_HPP, FILE_CONTENT); + //WHEN this file will be opened: + var editorPart = TestUtils.openInEditor(file); + //THEN it will be opened in the C/C++ Editor: + assertEquals(LspPlugin.C_EDITOR_ID, editorPart.getEditorSite().getId()); + TestUtils.closeEditor(editorPart, false); + } + + /** + * Tests whether the C/C++ Editor (LSP) is used for a C++ header file to open whose project has "Prefer C/C++ Editor (LSP)" enabled. + * @throws UnsupportedEncodingException + */ + @Test + public void testEditorUsedToOpenCppHeaderFile_WITH_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { + //GIVEN is a project with ENABLED "Prefer C/C++ Editor (LSP)" in the preferences: + TestUtils.setLspPreferred(project, true); + //AND a file exits in the given project: + var file = TestUtils.createFile(project, HEADER_HPP, FILE_CONTENT); + //WHEN this file will be opened: + var editorPart = TestUtils.openInEditor(file); + //THEN it will be opened in the C/C++ Editor (LSP): + assertEquals(LspPlugin.LSP_C_EDITOR_ID, editorPart.getEditorSite().getId()); + TestUtils.closeEditor(editorPart, false); + } + + /** + * Tests whether the C/C++ Editor is used for a C source file to open whose project has "Prefer C/C++ Editor (LSP)" disabled. + * @throws UnsupportedEncodingException + */ + @Test + public void testEditorUsedToOpenCFile_WITHOUT_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { + //GIVEN is a project with DISABLED "Prefer C/C++ Editor (LSP)" in the preferences: + TestUtils.setLspPreferred(project, false); + //AND a file exits in the given project: + var file = TestUtils.createFile(project, MAIN_C, FILE_CONTENT); + //WHEN this file will be opened: + var editorPart = TestUtils.openInEditor(file); + //THEN it will be opened in the C/C++ Editor: + assertEquals(LspPlugin.C_EDITOR_ID, editorPart.getEditorSite().getId()); + TestUtils.closeEditor(editorPart, false); + } + + /** + * Tests whether the C/C++ Editor (LSP) is used for a C source file to open whose project has "Prefer C/C++ Editor (LSP)" enabled. + * @throws UnsupportedEncodingException + */ + @Test + public void testEditorUsedToOpenCFile_WITH_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { + //GIVEN is a project with ENABLED "Prefer C/C++ Editor (LSP)" in the preferences: + TestUtils.setLspPreferred(project, true); + //AND a file exits in the given project: + var file = TestUtils.createFile(project, MAIN_C, FILE_CONTENT); + //WHEN this file will be opened: + var editorPart = TestUtils.openInEditor(file); + //THEN it will be opened in the C/C++ Editor (LSP): + assertEquals(LspPlugin.LSP_C_EDITOR_ID, editorPart.getEditorSite().getId()); + TestUtils.closeEditor(editorPart, false); + } + + /** + * Tests whether the C/C++ Editor is used for a C header file to open whose project has "Prefer C/C++ Editor (LSP)" disabled. + * @throws UnsupportedEncodingException + */ + @Test + public void testEditorUsedToOpenCHeaderFile_WITHOUT_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { + //GIVEN is a project with DISABLED "Prefer C/C++ Editor (LSP)" in the preferences: + TestUtils.setLspPreferred(project, false); + //AND a file exits in the given project: + var file = TestUtils.createFile(project, HEADER_HPP, FILE_CONTENT); + //WHEN this file will be opened: + var editorPart = TestUtils.openInEditor(file); + //THEN it will be opened in the C/C++ Editor: + assertEquals(LspPlugin.C_EDITOR_ID, editorPart.getEditorSite().getId()); + TestUtils.closeEditor(editorPart, false); + } + + /** + * Tests whether the C/C++ Editor (LSP) is used for a C header file to open whose project has "Prefer C/C++ Editor (LSP)" enabled. + * @throws UnsupportedEncodingException + */ + @Test + public void testEditorUsedToOpenCHeaderFile_WITH_LsEditorPreferred() throws CoreException, UnsupportedEncodingException { + //GIVEN is a project with ENABLED "Prefer C/C++ Editor (LSP)" in the preferences: + TestUtils.setLspPreferred(project, true); + //AND a file exits in the given project: + var file = TestUtils.createFile(project, HEADER_H, FILE_CONTENT); + //WHEN this file will be opened: + var editorPart = TestUtils.openInEditor(file); + //THEN it will be opened in the C/C++ Editor (LSP): + assertEquals(LspPlugin.LSP_C_EDITOR_ID, editorPart.getEditorSite().getId()); + TestUtils.closeEditor(editorPart, false); } /** @@ -119,12 +231,14 @@ public void testEditorUsedToOpenFile_WITH_LsEditorPreferred() throws CoreExcepti @Test public void testLsEnableByExternalUriTest_NoEditorOpen() throws CoreException, IOException { //GIVEN is an external file which does not exists in the given project and is not opened: - File externalFile = new File(tempDir, EXTERNAL_HEADER_HPP); + File externalFile = createTempHppHeaderfile(); //AND a ICLanguageServerProvider which uses LspEditorPreferencesTester as enabledWhen tester: ICLanguageServerProvider cLanguageServerProvider = LspPlugin.getDefault().getCLanguageServerProvider(); //WHEN the LspEditorPreferencesTester gets called by the property tester in the enabledWhen element of the serverProvider extension point, //THEN the LspEditorPreferencesTester.test returns FALSE for the given file URI: assertTrue(!cLanguageServerProvider.isEnabledFor(externalFile.toURI())); + //ensure clean up + externalFile.delete(); } /** @@ -133,7 +247,7 @@ public void testLsEnableByExternalUriTest_NoEditorOpen() throws CoreException, I @Test public void testLsEnableByExternalUriTest_OpenedInLspCEditor() throws CoreException, IOException { //GIVEN is an existing external file: - File externalFile = new File(tempDir, EXTERNAL_HEADER_HPP); + File externalFile = createTempHppHeaderfile(); externalFile.createNewFile(); //AND it's opened in the LSP based C/C++ Editor: var editor = TestUtils.openInEditor(externalFile.toURI(), LspPlugin.LSP_C_EDITOR_ID); @@ -143,6 +257,8 @@ public void testLsEnableByExternalUriTest_OpenedInLspCEditor() throws CoreExcept //THEN the LspEditorPreferencesTester.test returns TRUE for the given file URI: assertTrue(cLanguageServerProvider.isEnabledFor(externalFile.toURI())); TestUtils.closeEditor(editor, false); + //ensure clean up + externalFile.delete(); } /** @@ -151,7 +267,7 @@ public void testLsEnableByExternalUriTest_OpenedInLspCEditor() throws CoreExcept @Test public void testLsEnableByExternalUriTest_OpenedInCEditor() throws CoreException, IOException { //GIVEN is an existing external file: - File externalFile = new File(tempDir, EXTERNAL_HEADER_HPP); + File externalFile = createTempHppHeaderfile(); externalFile.createNewFile(); //AND it's opened in the C/C++ Editor: var editor = TestUtils.openInEditor(externalFile.toURI(), LspPlugin.C_EDITOR_ID); @@ -161,6 +277,8 @@ public void testLsEnableByExternalUriTest_OpenedInCEditor() throws CoreException //THEN the LspEditorPreferencesTester.test returns FALSE for the given file URI: assertTrue(!cLanguageServerProvider.isEnabledFor(externalFile.toURI())); TestUtils.closeEditor(editor, false); + //ensure clean up + externalFile.delete(); } } diff --git a/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/clangd/CompileCommandsMonitor.java b/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/clangd/CompileCommandsMonitor.java index 633011ca..6879d9a4 100644 --- a/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/clangd/CompileCommandsMonitor.java +++ b/bundles/org.eclipse.cdt.lsp.editor.ui/src/org/eclipse/cdt/lsp/editor/ui/clangd/CompileCommandsMonitor.java @@ -18,6 +18,7 @@ import java.util.Set; import java.util.stream.Stream; +import org.eclipse.cdt.lsp.LspUtils; import org.eclipse.cdt.lsp.editor.ui.LspEditorUiPlugin; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; @@ -71,8 +72,7 @@ private boolean isCppFile(IResource resource) { if (resource instanceof IFile) { var contentTypes = Platform.getContentTypeManager().findContentTypesFor(((IFile) resource).getName()); return Arrays.stream(contentTypes).anyMatch(contentType -> { - var id = contentType.getId(); - return id.startsWith("org.eclipse.cdt.core.c") && (id.endsWith("Source") || id.endsWith("Header")); + return LspUtils.isCContentType(contentType.getId()); }); } return false; diff --git a/bundles/org.eclipse.cdt.lsp.test/src/org/eclipse/cdt/lsp/test/LspUtilsTest.java b/bundles/org.eclipse.cdt.lsp.test/src/org/eclipse/cdt/lsp/test/LspUtilsTest.java new file mode 100644 index 00000000..7b3a3b16 --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.test/src/org/eclipse/cdt/lsp/test/LspUtilsTest.java @@ -0,0 +1,41 @@ +package org.eclipse.cdt.lsp.test; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.lsp.LspUtils; +import org.junit.jupiter.api.Test; + +class LspUtilsTest { + + @Test + void testIsCContentType_EmptyId() { + assertTrue(!LspUtils.isCContentType("")); + } + + @Test + void testIsCContentType_CppContentTypeFromTM4E() { + assertTrue(LspUtils.isCContentType("lng.cpp")); + } + + @Test + void testIsCContentType_CONTENT_TYPE_CSOURCE() { + assertTrue(LspUtils.isCContentType(CCorePlugin.CONTENT_TYPE_CSOURCE)); + } + + @Test + void testIsCContentType_CONTENT_TYPE_CHEADER() { + assertTrue(LspUtils.isCContentType(CCorePlugin.CONTENT_TYPE_CHEADER)); + } + + @Test + void testIsCContentType_CONTENT_TYPE_CXXSOURCE() { + assertTrue(LspUtils.isCContentType(CCorePlugin.CONTENT_TYPE_CXXSOURCE)); + } + + @Test + void testIsCContentType_CONTENT_TYPE_CXXHEADER() { + assertTrue(LspUtils.isCContentType(CCorePlugin.CONTENT_TYPE_CXXHEADER)); + } + +} diff --git a/bundles/org.eclipse.cdt.lsp/plugin.xml b/bundles/org.eclipse.cdt.lsp/plugin.xml index 763364e0..a67b9c92 100644 --- a/bundles/org.eclipse.cdt.lsp/plugin.xml +++ b/bundles/org.eclipse.cdt.lsp/plugin.xml @@ -17,7 +17,16 @@ + + + + diff --git a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/LspUtils.java b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/LspUtils.java new file mode 100644 index 00000000..0fb4af98 --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/LspUtils.java @@ -0,0 +1,17 @@ +package org.eclipse.cdt.lsp; + +public class LspUtils { + + /** + * Checks if given ContentType id matches the content types for C/C++ files. + * + * @param id ContentType id + * @return {@code true} if C/C++ content type + */ + public static boolean isCContentType(String id) { + // TODO: The content type definition from TM4E "lng.cpp" can be omitted if either https://github.com/eclipse-cdt/cdt/pull/310 or + // https://github.com/eclipse/tm4e/pull/500 has been merged. + return ( id.startsWith("org.eclipse.cdt.core.c") && (id.endsWith("Source") || id.endsWith("Header")) ) || "lng.cpp".equals(id); + } + +} diff --git a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/editor/CEditorAssociationOverride.java b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/editor/CEditorAssociationOverride.java index 8e9fc3b3..12899d4c 100644 --- a/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/editor/CEditorAssociationOverride.java +++ b/bundles/org.eclipse.cdt.lsp/src/org/eclipse/cdt/lsp/editor/CEditorAssociationOverride.java @@ -12,8 +12,8 @@ package org.eclipse.cdt.lsp.editor; -import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.lsp.LspPlugin; +import org.eclipse.cdt.lsp.LspUtils; import org.eclipse.cdt.lsp.server.ICLanguageServerProvider; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; @@ -72,12 +72,10 @@ public IEditorDescriptor overrideDefaultEditor(String fileName, IContentType con } private boolean isNoCElement(IContentType contentType) { - if (contentType == null || !(CCorePlugin.CONTENT_TYPE_CHEADER.equals(contentType.getId()) || - CCorePlugin.CONTENT_TYPE_CSOURCE.equals(contentType.getId()) || - CCorePlugin.CONTENT_TYPE_CXXHEADER.equals(contentType.getId()) || - CCorePlugin.CONTENT_TYPE_CXXSOURCE.equals(contentType.getId()))) + if (contentType == null) { return true; - return false; + } + return !LspUtils.isCContentType(contentType.getId()); } private IEditorDescriptor[] editorFilter(String editorId, IEditorDescriptor[] editorDescriptors) { @@ -100,15 +98,17 @@ private IEditorDescriptor getEditorDescriptor(IEditorInput editorInput, IContent return null; if (cLanguageServerProvider.isEnabledFor(editorInput)) { - return getLspCEditor(editorInput, contentType); - } - return null; + return getEditorDescriptorById(editorInput.getName(), LspPlugin.LSP_C_EDITOR_ID, contentType); // return LSP based C/C++ Editor + } + // TODO: return null; when either https://github.com/eclipse-cdt/cdt/pull/310 or + // https://github.com/eclipse/tm4e/pull/500 has been merged. + return getEditorDescriptorById(editorInput.getName(), LspPlugin.C_EDITOR_ID, contentType); // return C/C++ Editor } - private IEditorDescriptor getLspCEditor(IEditorInput editorInput, IContentType contentType) { + private IEditorDescriptor getEditorDescriptorById(String fileName, String editorId, IContentType contentType) { IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry(); - for (IEditorDescriptor descriptor : registry.getEditors(editorInput.getName(), contentType)) { - if (LspPlugin.LSP_C_EDITOR_ID.equals(descriptor.getId())) { + for (IEditorDescriptor descriptor : registry.getEditors(fileName, contentType)) { + if (editorId.equals(descriptor.getId())) { return descriptor; } }