Skip to content

Commit

Permalink
refactor: .clang-format file handling (eclipse-cdt#364)
Browse files Browse the repository at this point in the history
refactor: .clang-format file handling

- Mark ICLanguageServerProvider2 as deprecated since it's not a good way
to do things here.

- Creates the .clang-format file when a C/C++ source file gets opened or
edited in the LSP based editor.

- Use a creation-and-open job for the .clang-format file when it gets
accessed via the project properties UI. This solves the problem that the
file creation has to be finished before the file can be opened.

- Create interface ClangFormatFile to allow vendors to provide their own default .clang-format file

TODO: enable automatic creation of the .clang-format file
via project properties setting.
  • Loading branch information
ghentschke authored Aug 7, 2024
1 parent 160adf6 commit 1e6f3d0
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 55 deletions.
1 change: 1 addition & 0 deletions bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Require-Bundle: org.eclipse.cdt.lsp;bundle-version="0.0.0",
org.yaml.snakeyaml;bundle-version="0.0.0",
org.eclipse.tm4e.language_pack;bundle-version="0.0.0"
Service-Component: OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.BuiltinClangdOptionsDefaults.xml,
OSGI-INF/org.eclipse.cdt.lsp.clangd.internal.config.ClangFormatFileHandler.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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.cdt.lsp.clangd.internal.config.ClangFormatFileHandler">
<property name="service.ranking" type="Integer" value="0"/>
<service>
<provide interface="org.eclipse.cdt.lsp.clangd.ClangFormatFile"/>
</service>
<implementation class="org.eclipse.cdt.lsp.clangd.internal.config.ClangFormatFileHandler"/>
</scr:component>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* 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;

/**
* @since 2.1
*/
public interface ClangFormatFile {

/**
* Opens the .clang-format file in the given project. Creates a file with default values, if not yet existing prior to the opening.
* @param formatFile
*/
void openClangFormatFile(IProject project);

/**
* Creates a new .clang-format file with default settings in the project root directory if not yet existing.
* @param project
*/
void createClangFormatFile(IProject project);

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,61 @@
* See git history
*******************************************************************************/

package org.eclipse.cdt.lsp.clangd.utils;
package org.eclipse.cdt.lsp.clangd.internal.config;

import java.io.IOException;
import java.util.Optional;

import org.eclipse.cdt.lsp.clangd.ClangFormatFile;
import org.eclipse.cdt.lsp.clangd.plugin.ClangdPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.ui.progress.WorkbenchJob;
import org.osgi.service.component.annotations.Component;

public final class ClangFormatUtils {
@Component(property = { "service.ranking:Integer=0" })
public final class ClangFormatFileHandler implements ClangFormatFile {
public static final String format_file = ".clang-format"; //$NON-NLS-1$

/**
* Checks if the formatFile exists. Creates it if not.
* Opens the .clang-format file in the given project. Creates a file with default values, if not yet existing prior to the opening.
* @param formatFile
*/
public Optional<IFile> getClangFormatFile(IProject project) {
var formatFile = project.getFile(format_file);
var status = createFileFromResource(formatFile);
return status.isOK() ? Optional.of(formatFile) : Optional.empty();
@Override
public void openClangFormatFile(IProject project) {
runClangFormatFileCreatorJob(project, true);
}

/**
* Creates a new .clang-format file with default settings in the project root directory if not yet existing.
* @param project
*/
@Override
public void createClangFormatFile(IProject project) {
runClangFormatFileCreatorJob(project, false);
}

private void runClangFormatFileCreatorJob(IProject project, boolean openFile) {
var formatFile = project.getFile(format_file);
WorkspaceJob job = new WorkspaceJob("Create " + format_file + " file") { //$NON-NLS-1$ //$NON-NLS-2$
WorkbenchJob job = new WorkbenchJob("Create " + format_file + " file") { //$NON-NLS-1$ //$NON-NLS-2$
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
public IStatus runInUIThread(IProgressMonitor monitor) {
return createFileFromResource(formatFile);
}

@Override
public void performDone(IJobChangeEvent event) {
if (openFile) {
LSPEclipseUtils.open(formatFile.getLocationURI().toString(), null);
}
}
};
job.setSystem(true);
job.setRule(formatFile.getWorkspace().getRuleFactory().createRule(formatFile));
Expand All @@ -58,7 +77,7 @@ private IStatus createFileFromResource(IFile formatFile) {
formatFile.create(source, true, new NullProgressMonitor());
} catch (IOException | CoreException e) {
Platform.getLog(getClass()).error(e.getMessage(), e);
return new Status(IStatus.ERROR, ClangdPlugin.PLUGIN_ID, "Cannot create " + format_file, e); //$NON-NLS-1$
return new Status(IStatus.ERROR, ClangdPlugin.PLUGIN_ID, "Cannot create " + format_file + " file", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return Status.OK_STATUS;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*******************************************************************************
* 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 org.eclipse.cdt.lsp.clangd.ClangFormatFile;
import org.eclipse.cdt.lsp.plugin.LspPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Platform;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

public final class ClangFormatMonitor implements IPartListener2, IWindowListener {
private ClangFormatFile clangFormat;

public ClangFormatMonitor start() {
if (PlatformUI.isWorkbenchRunning()) {
var workbench = PlatformUI.getWorkbench();
clangFormat = workbench.getService(ClangFormatFile.class);
if (clangFormat == null) {
Platform.getLog(getClass()).error("Cannot get ClangFormatFile service."); //$NON-NLS-1$
return this;
}
workbench.addWindowListener(this);

// Ensure existing windows get connected
IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
for (int i = 0, length = windows.length; i < length; i++) {
windows[i].getPartService().addPartListener(this);
}
}
return this;
}

public void stop() {
PlatformUI.getWorkbench().removeWindowListener(this);
}

@Override
public void partActivated(IWorkbenchPartReference partRef) {
createClangFormatFile(partRef);
}

@Override
public void windowActivated(IWorkbenchWindow window) {
// do nothing
}

@Override
public void windowDeactivated(IWorkbenchWindow window) {
// do nothing
}

@Override
public void windowClosed(IWorkbenchWindow window) {
window.getPartService().removePartListener(this);
}

@Override
public void windowOpened(IWorkbenchWindow window) {
window.getPartService().addPartListener(this);
}

private void createClangFormatFile(IWorkbenchPartReference partRef) {
if (isLspCEditor(partRef)) {
var file = partRef.getPage().getActiveEditor().getEditorInput().getAdapter(IFile.class);

if (file == null) {
return;
}
clangFormat.createClangFormatFile(file.getProject());
}
}

private boolean isLspCEditor(IWorkbenchPartReference partRef) {
return partRef != null ? LspPlugin.LSP_C_EDITOR_ID.equals(partRef.getId()) : false;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Bachmann electronic GmbH and others.
* Copyright (c) 2023, 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
Expand All @@ -23,25 +23,22 @@

import org.eclipse.cdt.lsp.clangd.ClangdConfiguration;
import org.eclipse.cdt.lsp.clangd.ClangdFallbackFlags;
import org.eclipse.cdt.lsp.clangd.utils.ClangFormatUtils;
import org.eclipse.cdt.lsp.config.Configuration;
import org.eclipse.cdt.lsp.editor.LanguageServerEnable;
import org.eclipse.cdt.lsp.server.ICLanguageServerProvider2;
import org.eclipse.cdt.lsp.server.ICLanguageServerProvider;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ServiceCaller;
import org.eclipse.core.variables.VariablesPlugin;

public final class ClangdLanguageServerProvider implements ICLanguageServerProvider2 {
public final class ClangdLanguageServerProvider implements ICLanguageServerProvider {

private final ServiceCaller<ClangdConfiguration> configuration = new ServiceCaller<>(getClass(),
ClangdConfiguration.class);

private final ServiceCaller<Configuration> editorConfiguration = new ServiceCaller<>(getClass(),
Configuration.class);

private final ClangFormatUtils utils = new ClangFormatUtils();

@Override
public Object getInitializationOptions(URI rootUri) {
List<Object> result = new ArrayList<>();
Expand Down Expand Up @@ -73,9 +70,4 @@ public boolean isEnabledFor(IProject project) {
return enabled[0];
}

@Override
public void preFileOpening(IProject project) {
utils.createClangFormatFile(project);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@

package org.eclipse.cdt.lsp.clangd.internal.ui;

import org.eclipse.cdt.lsp.clangd.utils.ClangFormatUtils;
import org.eclipse.cdt.lsp.clangd.ClangFormatFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.preference.IPreferencePageContainer;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
Expand All @@ -31,22 +29,22 @@
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PropertyPage;

public class ClangFormatConfigurationPage extends PropertyPage implements IWorkbenchPreferencePage {
public final class ClangFormatConfigurationPage extends PropertyPage implements IWorkbenchPreferencePage {
private IProject project;
private IWorkspace workspace;
private ClangFormatUtils utils = new ClangFormatUtils();
private final ClangFormatFile formatFile;

public ClangFormatConfigurationPage() {
formatFile = PlatformUI.getWorkbench().getService(ClangFormatFile.class);
}

@Override
public void init(IWorkbench workbench) {
workspace = workbench.getService(IWorkspace.class);
// do nothing
}

@Override
public void setContainer(IPreferencePageContainer container) {
super.setContainer(container);
if (workspace == null) {
workspace = PlatformUI.getWorkbench().getService(IWorkspace.class);
}
project = (IProject) getElement();
}

Expand Down Expand Up @@ -76,19 +74,11 @@ private Button createButton(Composite composite) {
@Override
public void widgetSelected(SelectionEvent event) {
if (project != null) {
var formatFile = utils.getClangFormatFile(project);
if (formatFile.isPresent()) {
openFile(formatFile.get().getLocationURI().toString());
}
formatFile.openClangFormatFile(project);
getShell().close();
}
}
});
return button;
}

private void openFile(String path) {
LSPEclipseUtils.open(path, null);
// close preference page:
getShell().close();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Bachmann electronic GmbH and others.
* Copyright (c) 2023, 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
Expand All @@ -15,6 +15,7 @@
package org.eclipse.cdt.lsp.clangd.plugin;

import org.eclipse.cdt.lsp.clangd.internal.config.CProjectChangeMonitor;
import org.eclipse.cdt.lsp.clangd.internal.config.ClangFormatMonitor;
import org.eclipse.cdt.lsp.clangd.internal.config.ClangdConfigFileMonitor;
import org.eclipse.cdt.lsp.clangd.internal.config.CompileCommandsMonitor;
import org.eclipse.core.resources.IWorkspace;
Expand All @@ -26,10 +27,12 @@
* The activator class controls the plug-in life cycle
*/
public class ClangdPlugin extends AbstractUIPlugin {
private ServiceTracker<IWorkspace, IWorkspace> workspaceTracker;
private IWorkspace workspace;
private CompileCommandsMonitor compileCommandsMonitor;
private CProjectChangeMonitor cProjectChangeMonitor;
private ClangdConfigFileMonitor configFileMonitor;
private ClangFormatMonitor formatMonitor;

// The plug-in ID
public static final String PLUGIN_ID = "org.eclipse.cdt.lsp.clangd"; //$NON-NLS-1$
Expand All @@ -47,12 +50,13 @@ public ClangdPlugin() {
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
ServiceTracker<IWorkspace, IWorkspace> workspaceTracker = new ServiceTracker<>(context, IWorkspace.class, null);
workspaceTracker = new ServiceTracker<>(context, IWorkspace.class, null);
workspaceTracker.open();
workspace = workspaceTracker.getService();
compileCommandsMonitor = new CompileCommandsMonitor(workspace).start();
cProjectChangeMonitor = new CProjectChangeMonitor().start();
configFileMonitor = new ClangdConfigFileMonitor(workspace).start();
formatMonitor = new ClangFormatMonitor().start();
}

@Override
Expand All @@ -61,6 +65,8 @@ public void stop(BundleContext context) throws Exception {
compileCommandsMonitor.stop();
cProjectChangeMonitor.stop();
configFileMonitor.stop();
formatMonitor.stop();
workspaceTracker.close();
super.stop(context);
}

Expand Down
Loading

0 comments on commit 1e6f3d0

Please sign in to comment.