From 5c30041e1d7cce89dbb48e3e7443de1ab683e36c Mon Sep 17 00:00:00 2001 From: Gesa HENTSCHKE Date: Tue, 5 Mar 2024 09:24:00 +0100 Subject: [PATCH 1/5] [#242] Make ClangdConfigurationFileManager public API since its needed by some vendors to overwrite certain methods it should be made available. part of #276 --- .../META-INF/MANIFEST.MF | 4 +- ...clangd.ClangdConfigurationFileManager.xml} | 4 +- .../ClangdConfigurationFileManager.java | 232 ++++++++++++++++++ .../ClangdConfigurationFileManagerTest.java | 2 +- .../tests/ClangdConfigFileCheckerTest.java | 2 +- 5 files changed, 238 insertions(+), 6 deletions(-) rename bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/{org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml => org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml} (69%) create mode 100644 bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java diff --git a/bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF b/bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF index 94c8c1c0..cff9bd41 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF @@ -28,9 +28,9 @@ Require-Bundle: org.eclipse.cdt.lsp;bundle-version="0.0.0", org.eclipse.ui.workbench.texteditor;bundle-version="0.0.0", org.eclipse.core.variables;bundle-version="0.0.0", org.yaml.snakeyaml;bundle-version="0.0.0" -Service-Component: OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.BuiltinClangdOptionsDefaults.xml, +Service-Component: OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml, + OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.BuiltinClangdOptionsDefaults.xml, OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationAccess.xml, - OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml, OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdFallbackManager.xml, OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdMetadataDefaults.xml Bundle-Activator: org.eclipse.cdt.lsp.clangd.plugin.ClangdPlugin diff --git a/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml b/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml similarity index 69% rename from bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml rename to bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml index 34b4c21b..e327dfa5 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml +++ b/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml @@ -1,9 +1,9 @@ - + - + \ No newline at end of file diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java new file mode 100644 index 00000000..46829823 --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java @@ -0,0 +1,232 @@ +/******************************************************************************* + * Copyright (c) 2024 Bachmann electronic 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: + * Gesa Hentschke (Bachmann electronic GmbH) - initial implementation + *******************************************************************************/ + +package org.eclipse.cdt.lsp.clangd; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.cdt.core.build.CBuildConfiguration; +import org.eclipse.cdt.core.build.ICBuildConfiguration; +import org.eclipse.cdt.core.build.ICBuildConfigurationManager; +import org.eclipse.cdt.core.cdtvariables.CdtVariableException; +import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.lsp.plugin.LspPlugin; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.scanner.ScannerException; + +/** + * Default implementation of the {@link ClangdCProjectDescriptionListener}. + * Can be extended by vendors if needed. This implementation sets the path to + * the compile_commands.json in the .clangd file in the projects root directory. + * This is needed by CDT projects since the compile_commands.json is generated in the build folder. + * When the active build configuration changes in managed build projects, this manager updates the path to the database in + * the .clangd file to ensure that clangd uses the compile_commads.json of the active build configuration. + * + * This class can be extended by vendors. + */ +@Component(property = { "service.ranking:Integer=0" }) +public class ClangdConfigurationFileManager implements ClangdCProjectDescriptionListener { + public static final String CLANGD_CONFIG_FILE_NAME = ".clangd"; //$NON-NLS-1$ + private static final String COMPILE_FLAGS = "CompileFlags"; //$NON-NLS-1$ + private static final String COMPILATTION_DATABASE = "CompilationDatabase"; //$NON-NLS-1$ + private static final String SET_COMPILATION_DB = COMPILE_FLAGS + ": {" + COMPILATTION_DATABASE + ": %s}"; //$NON-NLS-1$ //$NON-NLS-2$ + private static final String EMPTY = ""; //$NON-NLS-1$ + + @Reference + private ICBuildConfigurationManager build; + + @Override + public void handleEvent(CProjectDescriptionEvent event, MacroResolver macroResolver) { + setCompilationDatabasePath(event.getProject(), event.getNewCProjectDescription(), macroResolver); + } + + /** + * Set the CompilationDatabase entry in the .clangd file which is located in the project root, + * if the yaml file syntax can be parsed. + * The .clangd file will be created, if it's not existing. + * The CompilationDatabase points to the build folder of the active build configuration + * (in case project is a managed C/C++ project). + * + * In the following example clangd uses the compile_commands.json file in the Debug folder: + *
CompileFlags: {CompilationDatabase: Debug}
+ * + * @param project C/C++ project + * @param newCProjectDescription new CProject description + * @param macroResolver helper to resolve macros in the CWD path of the builder + */ + protected void setCompilationDatabasePath(IProject project, ICProjectDescription newCProjectDescription, + MacroResolver macroResolver) { + if (project != null && newCProjectDescription != null) { + if (enableSetCompilationDatabasePath(project)) { + var relativeDatabasePath = getRelativeDatabasePath(project, newCProjectDescription, macroResolver); + if (!relativeDatabasePath.isEmpty()) { + setCompilationDatabase(project, relativeDatabasePath); + } else { + Platform.getLog(getClass()).error("Cannot determine path to compile_commands.json"); //$NON-NLS-1$ + } + } + } + } + + /** + * Enabler for {@link setCompilationDatabasePath}. Can be overriden for customization. + * @param project + * @return true if the database path should be written to .clangd file in the project root. + */ + protected boolean enableSetCompilationDatabasePath(IProject project) { + return Optional.ofNullable(LspPlugin.getDefault()).map(LspPlugin::getCLanguageServerProvider) + .map(provider -> provider.isEnabledFor(project)).orElse(Boolean.FALSE); + } + + /** + * Get project relative path to compile_commands.json file. + * By de + * @param project + * @param newCProjectDescription + * @param macroResolver + * @return project relative path to active build folder or empty String + */ + private String getRelativeDatabasePath(IProject project, ICProjectDescription newCProjectDescription, + MacroResolver macroResolver) { + if (project != null && newCProjectDescription != null) { + ICConfigurationDescription config = newCProjectDescription.getDefaultSettingConfiguration(); + var cwdBuilder = config.getBuildSetting().getBuilderCWD(); + var projectLocation = project.getLocation().addTrailingSeparator().toOSString(); + if (cwdBuilder != null) { + try { + var cwdString = macroResolver.resolveValue(cwdBuilder.toOSString(), EMPTY, null, config); + return cwdString.replace(projectLocation, EMPTY); + } catch (CdtVariableException e) { + Platform.getLog(getClass()).log(e.getStatus()); + } + } else { + //it is probably a cmake project: + return buildConfiguration(project)// + .filter(CBuildConfiguration.class::isInstance)// + .map(bc -> { + try { + return ((CBuildConfiguration) bc).getBuildContainer(); + } catch (CoreException e) { + Platform.getLog(getClass()).log(e.getStatus()); + } + return null; + })// + .map(c -> c.getLocation())// + .map(l -> l.toOSString().replace(projectLocation, EMPTY)).orElse(EMPTY); + } + } + return EMPTY; + } + + private Optional buildConfiguration(IResource initial) { + try { + var active = initial.getProject().getActiveBuildConfig(); + if (active != null && build != null) { + return Optional.ofNullable(build.getBuildConfiguration(active)); + } + } catch (CoreException e) { + Platform.getLog(getClass()).error(e.getMessage(), e); + } + return Optional.empty(); + } + + /** + * Set the CompilationDatabase entry in the .clangd file in the given project root. + * The file will be created, if it's not existing. + * A ScannerException will be thrown if the configuration file contains invalid yaml syntax. + * + * @param project to write the .clangd file + * @param databasePath project relative path to .clangd file + * @throws IOException + * @throws ScannerException + * @throws CoreException + */ + @SuppressWarnings("unchecked") + public void setCompilationDatabase(IProject project, String databasePath) { + var configFile = project.getFile(CLANGD_CONFIG_FILE_NAME); + try { + if (createClangdConfigFile(configFile, project.getDefaultCharset(), databasePath, false)) { + return; + } + Map data = null; + Yaml yaml = new Yaml(); + try (var inputStream = configFile.getContents()) { + //throws ScannerException and ParserException: + try { + data = yaml.load(inputStream); + } catch (Exception e) { + Platform.getLog(getClass()).error(e.getMessage(), e); + // return, since the file syntax is corrupted. The user has to fix it first: + return; + } + } + if (data == null) { + //empty file: (re)create .clangd file: + createClangdConfigFile(configFile, project.getDefaultCharset(), databasePath, true); + return; + } + Map map = (Map) data.get(COMPILE_FLAGS); + if (map != null) { + var cdb = map.get(COMPILATTION_DATABASE); + if (cdb != null && cdb instanceof String) { + if (cdb.equals(databasePath)) { + return; + } + } + map.put(COMPILATTION_DATABASE, databasePath); + data.put(COMPILE_FLAGS, map); + try (var yamlWriter = new PrintWriter(configFile.getLocation().toFile())) { + yaml.dump(data, yamlWriter); + } + } + } catch (CoreException e) { + Platform.getLog(getClass()).log(e.getStatus()); + } catch (IOException e) { + Platform.getLog(getClass()).error(e.getMessage(), e); + } + } + + private boolean createClangdConfigFile(IFile configFile, String charset, String databasePath, + boolean overwriteContent) { + if (!configFile.exists() || overwriteContent) { + try (final var data = new ByteArrayInputStream( + String.format(SET_COMPILATION_DB, databasePath).getBytes(charset))) { + if (overwriteContent) { + configFile.setContents(data, IResource.KEEP_HISTORY, new NullProgressMonitor()); + } else { + configFile.create(data, false, new NullProgressMonitor()); + } + return true; + } catch (CoreException e) { + Platform.getLog(getClass()).log(e.getStatus()); + } catch (IOException e) { + Platform.getLog(getClass()).error(e.getMessage(), e); + } + } + return false; + } +} diff --git a/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/clangd/tests/ClangdConfigurationFileManagerTest.java b/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/clangd/tests/ClangdConfigurationFileManagerTest.java index bd415fe1..7aaa2808 100644 --- a/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/clangd/tests/ClangdConfigurationFileManagerTest.java +++ b/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/clangd/tests/ClangdConfigurationFileManagerTest.java @@ -33,7 +33,7 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.internal.core.settings.model.CConfigurationDescriptionCache; import org.eclipse.cdt.lsp.clangd.ClangdCProjectDescriptionListener; -import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager; +import org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; diff --git a/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/internal/clangd/tests/ClangdConfigFileCheckerTest.java b/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/internal/clangd/tests/ClangdConfigFileCheckerTest.java index cf4bb34c..6ac1be7e 100644 --- a/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/internal/clangd/tests/ClangdConfigFileCheckerTest.java +++ b/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/internal/clangd/tests/ClangdConfigFileCheckerTest.java @@ -20,9 +20,9 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager; import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigFileChecker; import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigFileMonitor; -import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager; import org.eclipse.cdt.lsp.clangd.tests.TestUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; From 0e3e89b04fc723bb7d5f264825aa70e9f4dc2a47 Mon Sep 17 00:00:00 2001 From: Gesa Hentschke Date: Tue, 30 Apr 2024 13:22:54 +0200 Subject: [PATCH 2/5] Rebase --- .../ClangdConfigurationFileManager.java | 17 +- .../ClangdConfigurationFileManager.java | 229 ------------------ 2 files changed, 7 insertions(+), 239 deletions(-) delete mode 100644 bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java index 46829823..5d1c5ba9 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java +++ b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java @@ -26,6 +26,7 @@ import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.lsp.clangd.internal.config.MacroResolver; import org.eclipse.cdt.lsp.plugin.LspPlugin; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; @@ -60,8 +61,8 @@ public class ClangdConfigurationFileManager implements ClangdCProjectDescription private ICBuildConfigurationManager build; @Override - public void handleEvent(CProjectDescriptionEvent event, MacroResolver macroResolver) { - setCompilationDatabasePath(event.getProject(), event.getNewCProjectDescription(), macroResolver); + public void handleEvent(CProjectDescriptionEvent event) { + setCompilationDatabasePath(event.getProject(), event.getNewCProjectDescription()); } /** @@ -76,13 +77,11 @@ public void handleEvent(CProjectDescriptionEvent event, MacroResolver macroResol * * @param project C/C++ project * @param newCProjectDescription new CProject description - * @param macroResolver helper to resolve macros in the CWD path of the builder */ - protected void setCompilationDatabasePath(IProject project, ICProjectDescription newCProjectDescription, - MacroResolver macroResolver) { + protected void setCompilationDatabasePath(IProject project, ICProjectDescription newCProjectDescription) { if (project != null && newCProjectDescription != null) { if (enableSetCompilationDatabasePath(project)) { - var relativeDatabasePath = getRelativeDatabasePath(project, newCProjectDescription, macroResolver); + var relativeDatabasePath = getRelativeDatabasePath(project, newCProjectDescription); if (!relativeDatabasePath.isEmpty()) { setCompilationDatabase(project, relativeDatabasePath); } else { @@ -107,18 +106,16 @@ protected boolean enableSetCompilationDatabasePath(IProject project) { * By de * @param project * @param newCProjectDescription - * @param macroResolver * @return project relative path to active build folder or empty String */ - private String getRelativeDatabasePath(IProject project, ICProjectDescription newCProjectDescription, - MacroResolver macroResolver) { + private String getRelativeDatabasePath(IProject project, ICProjectDescription newCProjectDescription) { if (project != null && newCProjectDescription != null) { ICConfigurationDescription config = newCProjectDescription.getDefaultSettingConfiguration(); var cwdBuilder = config.getBuildSetting().getBuilderCWD(); var projectLocation = project.getLocation().addTrailingSeparator().toOSString(); if (cwdBuilder != null) { try { - var cwdString = macroResolver.resolveValue(cwdBuilder.toOSString(), EMPTY, null, config); + var cwdString = new MacroResolver().resolveValue(cwdBuilder.toOSString(), EMPTY, null, config); return cwdString.replace(projectLocation, EMPTY); } catch (CdtVariableException e) { Platform.getLog(getClass()).log(e.getStatus()); diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java deleted file mode 100644 index ccde8f5c..00000000 --- a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java +++ /dev/null @@ -1,229 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 Bachmann electronic 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: - * Gesa Hentschke (Bachmann electronic GmbH) - initial implementation - *******************************************************************************/ - -package org.eclipse.cdt.lsp.clangd.internal.config; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Map; -import java.util.Optional; - -import org.eclipse.cdt.core.build.CBuildConfiguration; -import org.eclipse.cdt.core.build.ICBuildConfiguration; -import org.eclipse.cdt.core.build.ICBuildConfigurationManager; -import org.eclipse.cdt.core.cdtvariables.CdtVariableException; -import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; -import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; -import org.eclipse.cdt.core.settings.model.ICProjectDescription; -import org.eclipse.cdt.lsp.clangd.ClangdCProjectDescriptionListener; -import org.eclipse.cdt.lsp.plugin.LspPlugin; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.Platform; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.scanner.ScannerException; - -/** - * Default implementation of the {@link ClangdCProjectDescriptionListener}. - * Can be extended by vendors if needed. This implementation sets the path to - * the compile_commands.json in the .clangd file in the projects root directory. - * This is needed by CDT projects since the compile_commands.json is generated in the build folder. - * When the active build configuration changes in managed build projects, this manager updates the path to the database in - * the .clangd file to ensure that clangd uses the compile_commads.json of the active build configuration. - * - * This class can be extended by vendors. - */ -@Component(property = { "service.ranking:Integer=0" }) -public class ClangdConfigurationFileManager implements ClangdCProjectDescriptionListener { - public static final String CLANGD_CONFIG_FILE_NAME = ".clangd"; //$NON-NLS-1$ - private static final String COMPILE_FLAGS = "CompileFlags"; //$NON-NLS-1$ - private static final String COMPILATTION_DATABASE = "CompilationDatabase"; //$NON-NLS-1$ - private static final String SET_COMPILATION_DB = COMPILE_FLAGS + ": {" + COMPILATTION_DATABASE + ": %s}"; //$NON-NLS-1$ //$NON-NLS-2$ - private static final String EMPTY = ""; //$NON-NLS-1$ - - @Reference - private ICBuildConfigurationManager build; - - @Override - public void handleEvent(CProjectDescriptionEvent event) { - setCompilationDatabasePath(event.getProject(), event.getNewCProjectDescription()); - } - - /** - * Set the CompilationDatabase entry in the .clangd file which is located in the project root, - * if the yaml file syntax can be parsed. - * The .clangd file will be created, if it's not existing. - * The CompilationDatabase points to the build folder of the active build configuration - * (in case project is a managed C/C++ project). - * - * In the following example clangd uses the compile_commands.json file in the Debug folder: - *
CompileFlags: {CompilationDatabase: Debug}
- * - * @param project C/C++ project - * @param newCProjectDescription new CProject description - */ - protected void setCompilationDatabasePath(IProject project, ICProjectDescription newCProjectDescription) { - if (project != null && newCProjectDescription != null) { - if (enableSetCompilationDatabasePath(project)) { - var relativeDatabasePath = getRelativeDatabasePath(project, newCProjectDescription); - if (!relativeDatabasePath.isEmpty()) { - setCompilationDatabase(project, relativeDatabasePath); - } else { - Platform.getLog(getClass()).error("Cannot determine path to compile_commands.json"); //$NON-NLS-1$ - } - } - } - } - - /** - * Enabler for {@link setCompilationDatabasePath}. Can be overriden for customization. - * @param project - * @return true if the database path should be written to .clangd file in the project root. - */ - protected boolean enableSetCompilationDatabasePath(IProject project) { - return Optional.ofNullable(LspPlugin.getDefault()).map(LspPlugin::getCLanguageServerProvider) - .map(provider -> provider.isEnabledFor(project)).orElse(Boolean.FALSE); - } - - /** - * Get project relative path to compile_commands.json file. - * By de - * @param project - * @param newCProjectDescription - * @return project relative path to active build folder or empty String - */ - private String getRelativeDatabasePath(IProject project, ICProjectDescription newCProjectDescription) { - if (project != null && newCProjectDescription != null) { - ICConfigurationDescription config = newCProjectDescription.getDefaultSettingConfiguration(); - var cwdBuilder = config.getBuildSetting().getBuilderCWD(); - var projectLocation = project.getLocation().addTrailingSeparator().toOSString(); - if (cwdBuilder != null) { - try { - var cwdString = new MacroResolver().resolveValue(cwdBuilder.toOSString(), EMPTY, null, config); - return cwdString.replace(projectLocation, EMPTY); - } catch (CdtVariableException e) { - Platform.getLog(getClass()).log(e.getStatus()); - } - } else { - //it is probably a cmake project: - return buildConfiguration(project)// - .filter(CBuildConfiguration.class::isInstance)// - .map(bc -> { - try { - return ((CBuildConfiguration) bc).getBuildContainer(); - } catch (CoreException e) { - Platform.getLog(getClass()).log(e.getStatus()); - } - return null; - })// - .map(c -> c.getLocation())// - .map(l -> l.toOSString().replace(projectLocation, EMPTY)).orElse(EMPTY); - } - } - return EMPTY; - } - - private Optional buildConfiguration(IResource initial) { - try { - var active = initial.getProject().getActiveBuildConfig(); - if (active != null && build != null) { - return Optional.ofNullable(build.getBuildConfiguration(active)); - } - } catch (CoreException e) { - Platform.getLog(getClass()).error(e.getMessage(), e); - } - return Optional.empty(); - } - - /** - * Set the CompilationDatabase entry in the .clangd file in the given project root. - * The file will be created, if it's not existing. - * A ScannerException will be thrown if the configuration file contains invalid yaml syntax. - * - * @param project to write the .clangd file - * @param databasePath project relative path to .clangd file - * @throws IOException - * @throws ScannerException - * @throws CoreException - */ - @SuppressWarnings("unchecked") - public void setCompilationDatabase(IProject project, String databasePath) { - var configFile = project.getFile(CLANGD_CONFIG_FILE_NAME); - try { - if (createClangdConfigFile(configFile, project.getDefaultCharset(), databasePath, false)) { - return; - } - Map data = null; - Yaml yaml = new Yaml(); - try (var inputStream = configFile.getContents()) { - //throws ScannerException and ParserException: - try { - data = yaml.load(inputStream); - } catch (Exception e) { - Platform.getLog(getClass()).error(e.getMessage(), e); - // return, since the file syntax is corrupted. The user has to fix it first: - return; - } - } - if (data == null) { - //empty file: (re)create .clangd file: - createClangdConfigFile(configFile, project.getDefaultCharset(), databasePath, true); - return; - } - Map map = (Map) data.get(COMPILE_FLAGS); - if (map != null) { - var cdb = map.get(COMPILATTION_DATABASE); - if (cdb != null && cdb instanceof String) { - if (cdb.equals(databasePath)) { - return; - } - } - map.put(COMPILATTION_DATABASE, databasePath); - data.put(COMPILE_FLAGS, map); - try (var yamlWriter = new PrintWriter(configFile.getLocation().toFile())) { - yaml.dump(data, yamlWriter); - } - } - } catch (CoreException e) { - Platform.getLog(getClass()).log(e.getStatus()); - } catch (IOException e) { - Platform.getLog(getClass()).error(e.getMessage(), e); - } - } - - private boolean createClangdConfigFile(IFile configFile, String charset, String databasePath, - boolean overwriteContent) { - if (!configFile.exists() || overwriteContent) { - try (final var data = new ByteArrayInputStream( - String.format(SET_COMPILATION_DB, databasePath).getBytes(charset))) { - if (overwriteContent) { - configFile.setContents(data, IResource.KEEP_HISTORY, new NullProgressMonitor()); - } else { - configFile.create(data, false, new NullProgressMonitor()); - } - return true; - } catch (CoreException e) { - Platform.getLog(getClass()).log(e.getStatus()); - } catch (IOException e) { - Platform.getLog(getClass()).error(e.getMessage(), e); - } - } - return false; - } -} From aceeab9643f8a5b950033d150ce3dde8ad07d785 Mon Sep 17 00:00:00 2001 From: Gesa Hentschke Date: Tue, 30 Apr 2024 13:51:21 +0200 Subject: [PATCH 3/5] Add service to provide enabler to set database path in .clangd file --- .../META-INF/MANIFEST.MF | 7 ++++--- ...config.ClangdConfigurationFileManager.xml} | 5 +++-- ...faultClangdCompilationDatabaseSettings.xml | 8 +++++++ .../ClangdCompilationDatabaseSettings.java | 15 +++++++++++++ .../ClangdConfigurationFileManager.java | 21 +++++++------------ ...aultClangdCompilationDatabaseSettings.java | 19 +++++++++++++++++ .../ClangdConfigurationFileManagerTest.java | 2 +- .../tests/ClangdConfigFileCheckerTest.java | 2 +- 8 files changed, 58 insertions(+), 21 deletions(-) rename bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/{org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml => org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml} (56%) create mode 100644 bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.DefaultClangdCompilationDatabaseSettings.xml create mode 100644 bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdCompilationDatabaseSettings.java rename bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/{ => internal/config}/ClangdConfigurationFileManager.java (92%) create mode 100644 bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/DefaultClangdCompilationDatabaseSettings.java diff --git a/bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF b/bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF index cff9bd41..3c2281a1 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF @@ -28,9 +28,10 @@ Require-Bundle: org.eclipse.cdt.lsp;bundle-version="0.0.0", org.eclipse.ui.workbench.texteditor;bundle-version="0.0.0", org.eclipse.core.variables;bundle-version="0.0.0", org.yaml.snakeyaml;bundle-version="0.0.0" -Service-Component: OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml, - OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.BuiltinClangdOptionsDefaults.xml, +Service-Component: OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.BuiltinClangdOptionsDefaults.xml, OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationAccess.xml, + OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml, OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdFallbackManager.xml, - OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdMetadataDefaults.xml + OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdMetadataDefaults.xml, + OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.DefaultClangdCompilationDatabaseSettings.xml Bundle-Activator: org.eclipse.cdt.lsp.clangd.plugin.ClangdPlugin diff --git a/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml b/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml similarity index 56% rename from bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml rename to bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml index e327dfa5..51492590 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml +++ b/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager.xml @@ -1,9 +1,10 @@ - + - + + \ No newline at end of file diff --git a/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.DefaultClangdCompilationDatabaseSettings.xml b/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.DefaultClangdCompilationDatabaseSettings.xml new file mode 100644 index 00000000..853e3a55 --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.clangd/OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.DefaultClangdCompilationDatabaseSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdCompilationDatabaseSettings.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdCompilationDatabaseSettings.java new file mode 100644 index 00000000..4246d77f --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdCompilationDatabaseSettings.java @@ -0,0 +1,15 @@ +package org.eclipse.cdt.lsp.clangd; + +import org.eclipse.core.resources.IProject; + +public interface ClangdCompilationDatabaseSettings { + + /** + * Enabler for {@link org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager#setCompilationDatabase(IProject, String)}. + * Can be overriden for customization. + * @param project + * @return true if the database path should be written to .clangd file in the project root. + */ + boolean enableSetCompilationDatabasePath(IProject project); + +} \ No newline at end of file diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java similarity index 92% rename from bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java rename to bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java index 5d1c5ba9..794fcb6d 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdConfigurationFileManager.java +++ b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java @@ -11,7 +11,7 @@ * Gesa Hentschke (Bachmann electronic GmbH) - initial implementation *******************************************************************************/ -package org.eclipse.cdt.lsp.clangd; +package org.eclipse.cdt.lsp.clangd.internal.config; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -26,8 +26,8 @@ import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; -import org.eclipse.cdt.lsp.clangd.internal.config.MacroResolver; -import org.eclipse.cdt.lsp.plugin.LspPlugin; +import org.eclipse.cdt.lsp.clangd.ClangdCProjectDescriptionListener; +import org.eclipse.cdt.lsp.clangd.ClangdCompilationDatabaseSettings; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -60,6 +60,9 @@ public class ClangdConfigurationFileManager implements ClangdCProjectDescription @Reference private ICBuildConfigurationManager build; + @Reference + ClangdCompilationDatabaseSettings settings; + @Override public void handleEvent(CProjectDescriptionEvent event) { setCompilationDatabasePath(event.getProject(), event.getNewCProjectDescription()); @@ -80,7 +83,7 @@ public void handleEvent(CProjectDescriptionEvent event) { */ protected void setCompilationDatabasePath(IProject project, ICProjectDescription newCProjectDescription) { if (project != null && newCProjectDescription != null) { - if (enableSetCompilationDatabasePath(project)) { + if (settings.enableSetCompilationDatabasePath(project)) { var relativeDatabasePath = getRelativeDatabasePath(project, newCProjectDescription); if (!relativeDatabasePath.isEmpty()) { setCompilationDatabase(project, relativeDatabasePath); @@ -91,16 +94,6 @@ protected void setCompilationDatabasePath(IProject project, ICProjectDescription } } - /** - * Enabler for {@link setCompilationDatabasePath}. Can be overriden for customization. - * @param project - * @return true if the database path should be written to .clangd file in the project root. - */ - protected boolean enableSetCompilationDatabasePath(IProject project) { - return Optional.ofNullable(LspPlugin.getDefault()).map(LspPlugin::getCLanguageServerProvider) - .map(provider -> provider.isEnabledFor(project)).orElse(Boolean.FALSE); - } - /** * Get project relative path to compile_commands.json file. * By de diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/DefaultClangdCompilationDatabaseSettings.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/DefaultClangdCompilationDatabaseSettings.java new file mode 100644 index 00000000..b2edc6f7 --- /dev/null +++ b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/DefaultClangdCompilationDatabaseSettings.java @@ -0,0 +1,19 @@ +package org.eclipse.cdt.lsp.clangd.internal.config; + +import java.util.Optional; + +import org.eclipse.cdt.lsp.clangd.ClangdCompilationDatabaseSettings; +import org.eclipse.cdt.lsp.plugin.LspPlugin; +import org.eclipse.core.resources.IProject; +import org.osgi.service.component.annotations.Component; + +@Component(property = { "service.ranking:Integer=0" }) +public class DefaultClangdCompilationDatabaseSettings implements ClangdCompilationDatabaseSettings { + + @Override + public boolean enableSetCompilationDatabasePath(IProject project) { + return Optional.ofNullable(LspPlugin.getDefault()).map(LspPlugin::getCLanguageServerProvider) + .map(provider -> provider.isEnabledFor(project)).orElse(Boolean.FALSE); + } + +} diff --git a/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/clangd/tests/ClangdConfigurationFileManagerTest.java b/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/clangd/tests/ClangdConfigurationFileManagerTest.java index 7aaa2808..bd415fe1 100644 --- a/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/clangd/tests/ClangdConfigurationFileManagerTest.java +++ b/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/clangd/tests/ClangdConfigurationFileManagerTest.java @@ -33,7 +33,7 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.internal.core.settings.model.CConfigurationDescriptionCache; import org.eclipse.cdt.lsp.clangd.ClangdCProjectDescriptionListener; -import org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager; +import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; diff --git a/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/internal/clangd/tests/ClangdConfigFileCheckerTest.java b/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/internal/clangd/tests/ClangdConfigFileCheckerTest.java index 6ac1be7e..cf4bb34c 100644 --- a/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/internal/clangd/tests/ClangdConfigFileCheckerTest.java +++ b/tests/org.eclipse.cdt.lsp.clangd.tests/src/org/eclipse/cdt/lsp/internal/clangd/tests/ClangdConfigFileCheckerTest.java @@ -20,9 +20,9 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; -import org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager; import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigFileChecker; import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigFileMonitor; +import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigurationFileManager; import org.eclipse.cdt.lsp.clangd.tests.TestUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; From 681070cdae01991fecda32671d4524cddd8c6db2 Mon Sep 17 00:00:00 2001 From: Gesa Hentschke Date: Tue, 30 Apr 2024 14:01:18 +0200 Subject: [PATCH 4/5] Make settings field private --- .../clangd/internal/config/ClangdConfigurationFileManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java index 794fcb6d..833ad7f3 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java +++ b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/ClangdConfigurationFileManager.java @@ -61,7 +61,7 @@ public class ClangdConfigurationFileManager implements ClangdCProjectDescription private ICBuildConfigurationManager build; @Reference - ClangdCompilationDatabaseSettings settings; + private ClangdCompilationDatabaseSettings settings; @Override public void handleEvent(CProjectDescriptionEvent event) { From d4293de605b710f230eff380e9a720661904716e Mon Sep 17 00:00:00 2001 From: Gesa Hentschke Date: Tue, 30 Apr 2024 14:18:47 +0200 Subject: [PATCH 5/5] Add license header --- .../clangd/ClangdCompilationDatabaseSettings.java | 13 +++++++++++++ .../DefaultClangdCompilationDatabaseSettings.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdCompilationDatabaseSettings.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdCompilationDatabaseSettings.java index 4246d77f..645371f0 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdCompilationDatabaseSettings.java +++ b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/ClangdCompilationDatabaseSettings.java @@ -1,3 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2024 Contributors to the Eclipse Foundation. + * + * 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: + * See git history + *******************************************************************************/ + package org.eclipse.cdt.lsp.clangd; import org.eclipse.core.resources.IProject; diff --git a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/DefaultClangdCompilationDatabaseSettings.java b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/DefaultClangdCompilationDatabaseSettings.java index b2edc6f7..048b355d 100644 --- a/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/DefaultClangdCompilationDatabaseSettings.java +++ b/bundles/org.eclipse.cdt.lsp.clangd/src/org/eclipse/cdt/lsp/clangd/internal/config/DefaultClangdCompilationDatabaseSettings.java @@ -1,3 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2024 Contributors to the Eclipse Foundation. + * + * 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: + * See git history + *******************************************************************************/ + package org.eclipse.cdt.lsp.clangd.internal.config; import java.util.Optional;