diff --git a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPLanguageServer.java b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPLanguageServer.java index 3bfaec637ae..24435caa24b 100644 --- a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPLanguageServer.java +++ b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPLanguageServer.java @@ -12,10 +12,15 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.net.URI; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Platform; import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider; @@ -25,6 +30,8 @@ public class CPPLanguageServer extends ProcessStreamConnectionProvider { private static final String CLANG_LANGUAGE_SERVER = "clangd"; //$NON-NLS-1$ + private IResourceChangeListener fResourceListener; + public CPPLanguageServer() { List commands = new ArrayList<>(); File clangServerLocation = getClangServerLocation(); @@ -37,6 +44,43 @@ public class CPPLanguageServer extends ProcessStreamConnectionProvider { setCommands(commands); } + @Override + public void stop() { + super.stop(); + if (fResourceListener != null) { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(fResourceListener); + fResourceListener = null; + } + } + + @Override + public Object getInitializationOptions(URI rootPath) { + installResourceChangeListener(rootPath); + return super.getInitializationOptions(rootPath); + } + + private void installResourceChangeListener(URI rootPath) { + if (rootPath == null || fResourceListener != null) { + return; + } + + IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(rootPath); + if (containers.length == 0) { + return; + } + + for (IContainer c : containers) { + if (!(c instanceof IProject)) { + continue; + } + IProject project = (IProject) c; + fResourceListener = new CPPResourceChangeListener(project); + project.getWorkspace().addResourceChangeListener(fResourceListener); + + break; + } + } + @Override public String toString() { return "C/C++ Language Server: " + super.toString(); //$NON-NLS-1$ diff --git a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPResourceChangeListener.java b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPResourceChangeListener.java new file mode 100644 index 00000000000..9d3d23f4d4d --- /dev/null +++ b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPResourceChangeListener.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2017 Ericsson and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ + +package org.eclipse.lsp4e.cpp.language; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.lsp4e.LanguageServersRegistry; +import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; +import org.eclipse.lsp4e.LanguageServiceAccessor; +import org.eclipse.lsp4e.ProjectSpecificLanguageServerWrapper; +import org.eclipse.lsp4j.DidChangeWatchedFilesParams; +import org.eclipse.lsp4j.FileChangeType; +import org.eclipse.lsp4j.FileEvent; + +/** + * A resource listener used to generate FileEvents, as part of the LSP. This + * only listens to Added, Changed, Removed event on a specific project that as a + * C/C++ language server started. + */ +@SuppressWarnings("restriction") +final class CPPResourceChangeListener implements IResourceChangeListener { + private final IProject fProject; + + CPPResourceChangeListener(IProject project) { + fProject = project; + } + + @Override + public void resourceChanged(IResourceChangeEvent event) { + LanguageServerDefinition definition = LanguageServersRegistry.getInstance().getDefinition(CPPLanguageServer.ID); + ProjectSpecificLanguageServerWrapper wrapper = getLanguageSeverWrapper(definition); + if (event.getType() != IResourceChangeEvent.POST_CHANGE || !isRelevantDelta(event.getDelta()) + || wrapper == null) { + return; + } + + sendFileEvents(wrapper, createFileEventsFromResourceEvent(event)); + } + + private static void sendFileEvents(ProjectSpecificLanguageServerWrapper wrapper, List fileEvents) { + if (!fileEvents.isEmpty()) { + DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(fileEvents); + wrapper.getServer().getWorkspaceService().didChangeWatchedFiles(params); + } + } + + private static List createFileEventsFromResourceEvent(IResourceChangeEvent event) { + List fileEvents = new ArrayList<>(); + try { + event.getDelta().accept((delta) -> { + if (delta.getResource() instanceof IFile && isRelevantDelta(delta)) { + FileEvent fileEvent = createFileEventFromDelta(delta); + if (fileEvent != null) { + fileEvents.add(fileEvent); + } + } + return true; + }, false); + } catch (CoreException e) { + // Do nothing + } + return fileEvents; + } + + private ProjectSpecificLanguageServerWrapper getLanguageSeverWrapper(LanguageServerDefinition definition) { + try { + return LanguageServiceAccessor.getLSWrapperForConnection(fProject, definition, false); + } catch (IOException e) { + // Do nothing + return null; + } + } + + private static boolean isRelevantDelta(IResourceDelta delta) { + int kind = delta.getKind(); + int flags = delta.getFlags(); + if (delta.getResource() instanceof IFile && kind == IResourceDelta.CHANGED) { + return (flags & IResourceDelta.CONTENT) != 0; + } + + return kind == IResourceDelta.ADDED || kind == IResourceDelta.CHANGED || kind == IResourceDelta.REMOVED; + } + + private static FileEvent createFileEventFromDelta(IResourceDelta delta) { + URI locationURI = delta.getResource().getLocationURI(); + if (locationURI == null) { + return null; + } + + FileChangeType changeType = null; + if (delta.getKind() == IResourceDelta.ADDED) { + changeType = FileChangeType.Created; + } else if (delta.getKind() == IResourceDelta.CHANGED) { + changeType = FileChangeType.Changed; + } else if (delta.getKind() == IResourceDelta.REMOVED) { + changeType = FileChangeType.Deleted; + } else { + throw new IllegalStateException("Unsupported resource delta kind: " + delta.getKind()); //$NON-NLS-1$ + } + + return new FileEvent(locationURI.toString(), changeType); + } +} \ No newline at end of file