Skip to content

Commit

Permalink
add URIEnableCache class
Browse files Browse the repository at this point in the history
  • Loading branch information
ghentschke committed Feb 6, 2025
1 parent 674b488 commit e1cb948
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Contributors to the Eclipse Foundation.
* Copyright (c) 2023, 2025 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/
Expand All @@ -12,43 +12,65 @@

package org.eclipse.cdt.lsp.editor;

import org.eclipse.cdt.lsp.config.ConfigurationMetadata;
import org.eclipse.cdt.lsp.internal.messages.LspUiMessages;
import org.eclipse.core.runtime.preferences.PreferenceMetadata;

public interface EditorMetadata {
public interface EditorMetadata extends ConfigurationMetadata {
/**
* @since 3.0
*/
public static final String PREFER_LSP_KEY = "prefer_lsp"; //$NON-NLS-1$

/**
* Returns the metadata for the "Prefer C/C++ Editor (LSP)" option, must not return <code>null</code>.
*
* @return the metadata for the "Prefer C/C++ Editor (LSP)" option
* The predefined metadata for the "Prefer C/C++ Editor (LSP)" option
*
* @see EditorOptions#preferLspEditor()
*
* @since 3.0
*/
PreferenceMetadata<Boolean> preferLspEditor();
PreferenceMetadata<Boolean> preferLspEditor = new PreferenceMetadata<>(Boolean.class, //
PREFER_LSP_KEY, false, //
LspUiMessages.LspEditorConfigurationPage_preferLspEditor,
LspUiMessages.LspEditorConfigurationPage_preferLspEditor_description);

/**
* Returns the metadata for the "Format source code" option, must not return <code>null</code>.
*
* @return the metadata for the "Format source code" option
* The predefined metadata for the "Format source code" option
*
* @see EditorOptions#formatOnSave()
*
* @since 3.0
*/
PreferenceMetadata<Boolean> formatOnSave();
PreferenceMetadata<Boolean> formatOnSave = new PreferenceMetadata<>(Boolean.class, //
"format_source", //$NON-NLS-1$
false, //
LspUiMessages.SaveActionsConfigurationPage_FormatSourceCode,
LspUiMessages.SaveActionsConfigurationPage_FormatSourceCode_description);

/**
* Returns the metadata for the "Format all lines" option, must not return <code>null</code>.
*
* @return the metadata for the "Format all lines" option
* The predefined metadata for the "Format all lines" option.
*
* @see EditorOptions#formatAllLines()
*
* @since 3.0
*/
PreferenceMetadata<Boolean> formatAllLines();
PreferenceMetadata<Boolean> formatAllLines = new PreferenceMetadata<>(Boolean.class, //
"format_all_lines", //$NON-NLS-1$
true, //
LspUiMessages.SaveActionsConfigurationPage_FormatAllLines,
LspUiMessages.SaveActionsConfigurationPage_FormatAllLines_description);

/**
* Returns the metadata for the "Format edited lines" option, must not return <code>null</code>.
*
* @return the metadata for the "Format edited lines" option
* Returns the metadata for the "Format edited lines" option.
*
* @see EditorOptions#formatEditedLines()
*
* @since 3.0
*/
PreferenceMetadata<Boolean> formatEditedLines();
PreferenceMetadata<Boolean> formatEditedLines = new PreferenceMetadata<>(Boolean.class, //
"format_edited_lines", //$NON-NLS-1$
false, //
LspUiMessages.SaveActionsConfigurationPage_FormatEditedLines,
LspUiMessages.SaveActionsConfigurationPage_FormatEditedLines_description);

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

package org.eclipse.cdt.lsp.editor;

import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;

public interface EditorOptions {

/**
Expand Down Expand Up @@ -42,4 +44,14 @@ public interface EditorOptions {
*/
boolean formatEditedLines();

/**
* @since 3.0
*/
void addPreferenceChangedListener(IPreferenceChangeListener listener);

/**
* @since 3.0
*/
void removePreferenceChangedListener(IPreferenceChangeListener listener);

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Contributors to the Eclipse Foundation.
* Copyright (c) 2023, 2025 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/
Expand All @@ -12,52 +12,67 @@

package org.eclipse.cdt.lsp.internal.editor;

import java.util.Objects;
import java.util.Optional;

import org.eclipse.cdt.lsp.PreferredOptions;
import org.eclipse.cdt.lsp.editor.EditorMetadata;
import org.eclipse.cdt.lsp.editor.EditorOptions;
import org.eclipse.cdt.lsp.editor.LanguageServerEnable;
import org.eclipse.cdt.lsp.internal.server.URIEnableCache;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IScopeContext;

public class EditorPreferredOptions extends PreferredOptions implements EditorOptions, LanguageServerEnable {
private final EditorMetadata metadata;
public final class EditorPreferredOptions extends PreferredOptions implements EditorOptions, LanguageServerEnable {
private final LanguageServerEnable enable;

public EditorPreferredOptions(String qualifier, IScopeContext[] scopes, EditorMetadata metadata,
public EditorPreferredOptions(EditorMetadata metadata, String qualifier, IScopeContext[] scopes,
LanguageServerEnable enable) {
super(qualifier, scopes);
this.metadata = Objects.requireNonNull(metadata);
super(metadata, qualifier, scopes);
this.enable = enable;
this.addPreferenceChangedListener(URIEnableCache.getInstance());
}

@Override
public boolean preferLspEditor() {
return booleanValue(metadata.preferLspEditor());
return booleanValue(EditorMetadata.preferLspEditor);
}

@Override
public boolean formatOnSave() {
return booleanValue(metadata.formatOnSave());
return booleanValue(EditorMetadata.formatOnSave);
}

@Override
public boolean formatAllLines() {
return booleanValue(metadata.formatAllLines());
return booleanValue(EditorMetadata.formatAllLines);
}

@Override
public boolean formatEditedLines() {
return booleanValue(metadata.formatEditedLines());
return booleanValue(EditorMetadata.formatEditedLines);
}

@Override
public boolean isEnabledFor(IProject project) {
if (enable != null) {
return enable.isEnabledFor(project);
}
return booleanValue(metadata.preferLspEditor());
return booleanValue(EditorMetadata.preferLspEditor);
}

@Override
public void addPreferenceChangedListener(IPreferenceChangeListener listener) {
for (var scope : scopes) {
Optional.ofNullable(scope.getNode(qualifier)).ifPresent(n -> n.addPreferenceChangeListener(listener));
}
}

@Override
public void removePreferenceChangedListener(IPreferenceChangeListener listener) {
for (var scope : scopes) {
Optional.ofNullable(scope.getNode(qualifier)).ifPresent(n -> n.removePreferenceChangeListener(listener));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

import java.io.File;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.eclipse.cdt.core.model.ICProject;
Expand All @@ -39,8 +37,8 @@ public class HasLanguageServerPropertyTester extends PropertyTester {
private final ICLanguageServerProvider cLanguageServerProvider;
private final ServiceCaller<InitialUri> initial;
private final ServiceCaller<IWorkspace> workspace;
private final URIEnableCache cache = URIEnableCache.getInstance();
private Optional<IProject> project;
private final Map<URI, Boolean> cache = new HashMap<>();

public HasLanguageServerPropertyTester() {
this.cLanguageServerProvider = LspPlugin.getDefault().getCLanguageServerProvider();
Expand All @@ -64,10 +62,10 @@ public boolean test(Object receiver, String property, Object[] args, Object expe
}
// when getProject is empty, it's an external file: Check if the file is already opened, if not check the active editor:
var isEnabled = enabledFor(uri);
cache.put(uri, isEnabled);
if (isEnabled) {
initial.call(iu -> iu.register(uri));
}
cache.put(uri, isEnabled);
return isEnabled;
} else if (receiver instanceof ITranslationUnit) {
// called to enable the LS based CSymbolsContentProvider:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*******************************************************************************
* Copyright (c) 2025 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.internal.server;

import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;

import org.eclipse.cdt.internal.core.LRUCache;
import org.eclipse.cdt.lsp.editor.EditorMetadata;
import org.eclipse.core.internal.content.ContentTypeManager;
import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
import org.eclipse.core.runtime.content.IContentTypeManager.IContentTypeChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.genericeditor.ExtensionBasedTextEditor;

/**
* Caches the enable status for a given resource URI. Used by {@link HasLanguageServerPropertyTester#test(Object, String, Object[], Object)}
* The cache is getting cleared: on changes in the C/C++ content types or the prefer LSP editor option has been changed (workspace or project level).
* A resource URI shall be removed from the cache if it's getting closed in the editor.
*/
public final class URIEnableCache
implements IPreferenceChangeListener, IContentTypeChangeListener, IPartListener, IWindowListener {
private static final String C_SOURCE = "org.eclipse.cdt.core.cSource"; //$NON-NLS-1$
private static final String CXX_SOURCE = "org.eclipse.cdt.core.cxxSource"; //$NON-NLS-1$
private static final String C_HEADER = "org.eclipse.cdt.core.cHeader"; //$NON-NLS-1$
private static final String CXX_HEADER = "org.eclipse.cdt.core.cxxHeader"; //$NON-NLS-1$
private static final Map<URI, Boolean> cache = Collections.synchronizedMap(new LRUCache<>(100));
private static URIEnableCache instance = null;

private URIEnableCache() {
ContentTypeManager.getInstance().addContentTypeChangeListener(this);
if (PlatformUI.isWorkbenchRunning()) {
var workbench = PlatformUI.getWorkbench();
workbench.addWindowListener(this);
Arrays.stream(workbench.getWorkbenchWindows()).map(IWorkbenchWindow::getPages).flatMap(Arrays::stream)
.forEach(p -> p.addPartListener(this));
}
}

public static void stop() {
if (instance != null) {
var workbench = PlatformUI.getWorkbench();
workbench.removeWindowListener(instance);
Arrays.stream(workbench.getWorkbenchWindows()).map(IWorkbenchWindow::getPages).flatMap(Arrays::stream)
.forEach(p -> p.removePartListener(instance));
cache.clear();
}
}

public static synchronized URIEnableCache getInstance() {
if (instance == null) {
instance = new URIEnableCache();
}
return instance;
}

public Boolean get(URI uri) {
return cache.get(uri);
}

public void put(URI uri, Boolean value) {
cache.put(uri, value);
}

@Override
public void preferenceChange(PreferenceChangeEvent event) {
if (EditorMetadata.PREFER_LSP_KEY.contentEquals(event.getKey())) {
cache.clear();
}
}

@Override
public void contentTypeChanged(ContentTypeChangeEvent event) {
var id = event.getContentType().getId();
if (C_SOURCE.contentEquals(id) || CXX_SOURCE.contentEquals(id) || C_HEADER.contentEquals(id)
|| CXX_HEADER.contentEquals(id)) {
cache.clear();
}
}

@Override
public void partActivated(IWorkbenchPart part) {
// do nothing
}

@Override
public void partBroughtToTop(IWorkbenchPart part) {
// do nothing
}

@Override
public void partClosed(IWorkbenchPart part) {
if (part instanceof ExtensionBasedTextEditor editor) {
Optional.ofNullable(LSPEclipseUtils.toUri(editor.getEditorInput())).ifPresent(uri -> cache.remove(uri));
}
}

@Override
public void partDeactivated(IWorkbenchPart part) {
// do nothing
}

@Override
public void partOpened(IWorkbenchPart part) {
// do nothing
}

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

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

@Override
public void windowClosed(IWorkbenchWindow window) {
Arrays.stream(window.getPages()).forEach(p -> p.removePartListener(this));
}

@Override
public void windowOpened(IWorkbenchWindow window) {
Arrays.stream(window.getPages()).forEach(p -> p.addPartListener(this));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.logging.Logger;

import org.eclipse.cdt.lsp.internal.server.CLanguageServerRegistry;
import org.eclipse.cdt.lsp.internal.server.URIEnableCache;
import org.eclipse.cdt.lsp.server.ICLanguageServerProvider;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
Expand Down Expand Up @@ -58,6 +59,7 @@ public void start(BundleContext context) throws Exception {

@Override
public void stop(BundleContext context) throws Exception {
URIEnableCache.stop();
plugin = null;
super.stop(context);
}
Expand Down

0 comments on commit e1cb948

Please sign in to comment.