From 9dbf2af399f944a081af7960de8bb3ba71b134b9 Mon Sep 17 00:00:00 2001 From: Marc-Andre Laperle Date: Sat, 23 Sep 2017 21:44:05 -0400 Subject: lsp4e-cpp: Generate LSP FileEvents from ResourceChangeEvents FileEvents are useful for indexing and lsp4e (so far) does not send them. It is not clear whether or not this will be implemented in lsp4e. In the mean time, lsp4e-cpp can have its own mechanism to generate the events. Change-Id: I09aac4fda7755260f47b73fd683ca6d2ad317f81 Signed-off-by: Marc-Andre Laperle --- .../lsp4e/cpp/language/CPPLanguageServer.java | 44 ++++++++ .../cpp/language/CPPResourceChangeListener.java | 118 +++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPResourceChangeListener.java (limited to 'lsp4e-cpp/org.eclipse.lsp4e.cpp.language') 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 -- cgit v1.2.3