Classpath change events now contain classpath info

This commit is contained in:
nsingh
2018-03-16 12:07:58 -07:00
parent 39ffe531e6
commit e16cb6a9c9
14 changed files with 279 additions and 314 deletions

View File

@@ -1,34 +0,0 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* 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
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver;
public class ClasspathParams {
private String resourceUri;
public ClasspathParams(String resourceUri) {
super();
this.resourceUri = resourceUri;
}
public String getResourceUri() {
return resourceUri;
}
public void setResourceUri(String resourceUri) {
this.resourceUri = resourceUri;
}
@Override
public String toString() {
return "ClasspathParams [resourceUri=" + resourceUri + "]";
}
}

View File

@@ -35,9 +35,6 @@ public interface STS4LanguageClient extends LanguageClient {
@JsonRequest("sts/moveCursor")
CompletableFuture<Object> moveCursor(CursorMovement cursorMovement);
@JsonRequest("sts/classpath")
CompletableFuture<ClasspathResponse> classpath(ClasspathParams classpathParams);
@JsonRequest("sts/project")
CompletableFuture<ProjectResponse> project(String uri);

View File

@@ -1,36 +1,28 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* 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
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver;
package org.springframework.ide.vscode.commons.languageserver.jdt.ls;
import java.util.List;
public class ClasspathResponse {
public class Classpath {
public static final String ENTRY_KIND_SOURCE = "source";
public static final String ENTRY_KIND_BINARY = "binary";
public static final String OUTPUT_LOCATION = "output_location";
private List<Entry> entries;
private List<CPE> entries;
private String defaultOutputFolder;
public ClasspathResponse(List<Entry> entries, String defaultOutputFolder) {
public Classpath(List<CPE> entries, String defaultOutputFolder) {
super();
this.entries = entries;
this.defaultOutputFolder = defaultOutputFolder;
}
public List<Entry> getEntries() {
public List<CPE> getEntries() {
return entries;
}
public void setEntries(List<Entry> entries) {
public void setEntries(List<CPE> entries) {
this.entries = entries;
}
@@ -47,12 +39,11 @@ public class ClasspathResponse {
return "Classpath [entries=" + entries + ", defaultOutputFolder=" + defaultOutputFolder + "]";
}
public static class Entry {
public static class CPE {
private String kind;
private String path;
public Entry(String kind, String path) {
public CPE(String kind, String path) {
super();
this.kind = kind;
this.path = path;
@@ -78,5 +69,7 @@ public class ClasspathResponse {
public String toString() {
return "CPE [kind=" + kind + ", path=" + path + "]\n";
}
}
}

View File

@@ -12,7 +12,23 @@ package org.springframework.ide.vscode.commons.languageserver.jdt.ls;
public interface ClasspathListener {
void changed(String projectUri, boolean deleted);
void changed(Event event);
static class Event {
public final String projectUri;
public final String name;
public final boolean deleted;
public final Classpath classpath;
public Event(String projectUri, String name, boolean deleted, Classpath classpath) {
super();
this.projectUri = projectUri;
this.name = name;
this.deleted = deleted;
this.classpath = classpath;
}
}
}

View File

@@ -10,6 +10,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.jdt.ls;
import static org.springframework.ide.vscode.commons.languageserver.util.AsyncRunner.thenLog;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -27,12 +29,11 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import reactor.core.Disposable;
import static org.springframework.ide.vscode.commons.languageserver.util.AsyncRunner.*;
public class ClasspathListenerManager {
private static Logger log = LoggerFactory.getLogger(ClasspathListenerManager.class);
@@ -41,6 +42,8 @@ public class ClasspathListenerManager {
private SimpleLanguageServer server;
private AsyncRunner async;
private static final Gson gson = new Gson();
public ClasspathListenerManager(SimpleLanguageServer server) {
this.server = server;
this.async = server.getAsync();
@@ -55,8 +58,12 @@ public class ClasspathListenerManager {
//Note: not sure... but args might be deserialized as com.google.gson.JsonElement's.
//If so the code below is not correct (casts will fail).
String projectUri = ((JsonElement) args.get(0)).getAsString();
boolean deleted = args.size()>=2 && ((JsonElement)args.get(1)).getAsBoolean();
classpathListener.changed(projectUri, deleted);
String name = ((JsonElement) args.get(1)).getAsString();
boolean deleted = ((JsonElement)args.get(2)).getAsBoolean();
Classpath classpath = gson.fromJson((JsonElement)args.get(3), Classpath.class);
classpathListener.changed(new ClasspathListener.Event(projectUri, name, deleted, classpath));
return "done";
}));

View File

@@ -90,8 +90,6 @@ import org.eclipse.lsp4j.WorkspaceClientCapabilities;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.LanguageClientAware;
import org.springframework.ide.vscode.commons.languageserver.ClasspathParams;
import org.springframework.ide.vscode.commons.languageserver.ClasspathResponse;
import org.springframework.ide.vscode.commons.languageserver.HighlightParams;
import org.springframework.ide.vscode.commons.languageserver.ProgressParams;
import org.springframework.ide.vscode.commons.languageserver.ProjectResponse;
@@ -296,11 +294,6 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
}
@Override
public CompletableFuture<ClasspathResponse> classpath(ClasspathParams classpathEvent) {
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<ProjectResponse> project(String uri) {
return CompletableFuture.completedFuture(null);

View File

@@ -1,11 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension point="org.eclipse.jdt.ls.core.delegateCommandHandler">
<delegateCommandHandler class="org.springframework.tooling.jdt.ls.extension.ResolveClasspathHandler">
<command id="sts.java.resolveClasspath"/>
</delegateCommandHandler>
</extension>
<extension point="org.eclipse.jdt.ls.core.delegateCommandHandler">
<delegateCommandHandler class="org.springframework.tooling.jdt.ls.extension.ResolveProjectHandler">
<command id="sts.java.resolveProject"/>

View File

@@ -0,0 +1,74 @@
package org.springframework.tooling.jdt.ls.extension;
import java.util.List;
public class Classpath {
public static final String ENTRY_KIND_SOURCE = "source";
public static final String ENTRY_KIND_BINARY = "binary";
public static final String OUTPUT_LOCATION = "output_location";
private List<CPE> entries;
private String defaultOutputFolder;
public Classpath(List<CPE> entries, String defaultOutputFolder) {
super();
this.entries = entries;
this.defaultOutputFolder = defaultOutputFolder;
}
public List<CPE> getEntries() {
return entries;
}
public void setEntries(List<CPE> entries) {
this.entries = entries;
}
public String getDefaultOutputFolder() {
return defaultOutputFolder;
}
public void setDefaultOutputFolder(String defaultOutputFolder) {
this.defaultOutputFolder = defaultOutputFolder;
}
@Override
public String toString() {
return "Classpath [entries=" + entries + ", defaultOutputFolder=" + defaultOutputFolder + "]";
}
public static class CPE {
private String kind;
private String path;
public CPE(String kind, String path) {
super();
this.kind = kind;
this.path = path;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return "CPE [kind=" + kind + ", path=" + path + "]\n";
}
}
}

View File

@@ -16,72 +16,82 @@ import org.springframework.tooling.jdt.ls.extension.ClasspathListenerManager.Cla
public class ClasspathListenerHandler implements IDelegateCommandHandler {
static class MyClasspathListener implements ClasspathListener {
private ClasspathListenerManager manager = null;
private List<String> subscribers = new ArrayList<>(1);
public synchronized void subscribe(String callbackCommandId) {
Logger.log("subscribing to classpath changes: "+callbackCommandId);
if (manager==null) {
Logger.log("subscribing to classpath changes: " + callbackCommandId);
if (manager == null) {
this.manager = new ClasspathListenerManager(this);
}
subscribers.add(callbackCommandId);
Logger.log("subsribers = "+subscribers);
Logger.log("subsribers = " + subscribers);
}
@Override
public void classpathChanged(IJavaProject jp) {
log("Classpath changed "+jp.getElementName());
log("Classpath changed " + jp.getElementName());
String project = jp.getProject().getLocationURI().toString();
boolean deleted = !jp.exists();
JavaClientConnection conn = JavaLanguageServerPlugin.getInstance().getClientConnection();
String projectName = jp.getElementName();
for (String callbackCommandId : subscribers) {
conn.executeCommand(callbackCommandId, project, deleted);
Classpath classpath = null;
if (!deleted) {
try {
classpath = ClasspathUtil.resolve(jp);
} catch (Exception e) {
Logger.log(e);
}
}
conn.executeCommand(callbackCommandId, project, projectName, deleted, classpath);
}
}
public synchronized void unsubscribe(String callbackCommandId) {
Logger.log("unsubscribing from classpath changes: "+callbackCommandId);
if (subscribers!=null) {
Logger.log("unsubscribing from classpath changes: " + callbackCommandId);
if (subscribers != null) {
subscribers.remove(callbackCommandId);
if (subscribers.isEmpty()) {
subscribers = null;
if (manager!=null) {
if (manager != null) {
manager.dispose();
manager = null;
}
}
}
Logger.log("subsribers = "+subscribers);
Logger.log("subsribers = " + subscribers);
}
}
private static MyClasspathListener classpathListener = new MyClasspathListener();
@Override
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor monitor) throws Exception {
log("ClasspathListenerHandler executeCommand "+commandId+ ", "+arguments);
log("ClasspathListenerHandler executeCommand " + commandId + ", " + arguments);
switch (commandId) {
case "sts.java.addClasspathListener":
return addClasspathListener((String)arguments.get(0));
return addClasspathListener((String) arguments.get(0));
case "sts.java.removeClasspathListener":
return removeClasspathListener((String)arguments.get(0));
return removeClasspathListener((String) arguments.get(0));
default:
throw new IllegalArgumentException("Unknown command id: "+commandId);
throw new IllegalArgumentException("Unknown command id: " + commandId);
}
}
private Object removeClasspathListener(String callbackCommandId) {
log("ClasspathListenerHandler addClasspathListener "+callbackCommandId);
log("ClasspathListenerHandler addClasspathListener " + callbackCommandId);
classpathListener.unsubscribe(callbackCommandId);
log("ClasspathListenerHandler addClasspathListener "+callbackCommandId+ " => OK");
log("ClasspathListenerHandler addClasspathListener " + callbackCommandId + " => OK");
return "ok";
}
private Object addClasspathListener(String callbackCommandId) {
log("ClasspathListenerHandler addClasspathListener "+callbackCommandId);
log("ClasspathListenerHandler addClasspathListener " + callbackCommandId);
classpathListener.subscribe(callbackCommandId);
log("ClasspathListenerHandler addClasspathListener "+callbackCommandId+ " => OK");
log("ClasspathListenerHandler addClasspathListener " + callbackCommandId + " => OK");
return "ok";
}

View File

@@ -0,0 +1,43 @@
package org.springframework.tooling.jdt.ls.extension;
import static org.springframework.tooling.jdt.ls.extension.Logger.log;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.springframework.tooling.jdt.ls.extension.Classpath.CPE;
import static org.springframework.tooling.jdt.ls.extension.Classpath.*;
public class ClasspathUtil {
public static Classpath resolve(IJavaProject javaProject) throws Exception {
List<CPE> cpEntries = new ArrayList<>();
IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
if (entries != null) {
for (IClasspathEntry entry : entries) {
String kind = toContentKind(entry);
String path = entry.getPath().toString();
cpEntries.add(new CPE(kind, path));
}
}
Classpath classpath = new Classpath(cpEntries, javaProject.getOutputLocation().toString());
log("classpath=" + classpath.getEntries().size() + " entries");
return classpath;
}
private static String toContentKind(IClasspathEntry entry) {
switch (entry.getContentKind()) {
case IPackageFragmentRoot.K_BINARY:
return ENTRY_KIND_BINARY;
case IPackageFragmentRoot.K_SOURCE:
return ENTRY_KIND_SOURCE;
default:
return "unknown: " + entry.getContentKind();
}
}
}

View File

@@ -1,147 +0,0 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* 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
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.jdt.ls.extension;
import static org.springframework.tooling.jdt.ls.extension.Logger.log;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler;
import org.eclipse.jdt.ls.core.internal.JavaClientConnection;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
@SuppressWarnings("restriction")
public class ResolveClasspathHandler implements IDelegateCommandHandler {
public static final String ENTRY_KIND_SOURCE = "source";
public static final String ENTRY_KIND_BINARY = "binary";
public static final String OUTPUT_LOCATION = "output_location";
@Override
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor monitor) throws Exception {
log("ResolveClasspathHandler=" + commandId);
try {
URI resourceUri = ResourceUtils.getResourceUri(arguments);
IJavaProject javaProject = ResourceUtils.getJavaProject(resourceUri);
return resolveClasspathHandler(javaProject);
} catch (Exception e) {
log(e);
throw e;
}
}
private Classpath resolveClasspathHandler(IJavaProject javaProject) throws Exception {
List<CPE> cpEntries = new ArrayList<>();
IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
if (entries != null) {
for (IClasspathEntry entry : entries) {
String kind = toContentKind(entry);
String path = entry.getPath().toString();
cpEntries.add(new CPE(kind, path));
}
}
Classpath classpath = new Classpath(cpEntries, javaProject.getOutputLocation().toString());
log("classpath=" + classpath.getEntries().size()+ " entries");
return classpath;
}
private String toContentKind(IClasspathEntry entry) {
switch (entry.getContentKind()) {
case IPackageFragmentRoot.K_BINARY:
return ENTRY_KIND_BINARY;
case IPackageFragmentRoot.K_SOURCE:
return ENTRY_KIND_SOURCE;
default:
return "unknown: " + entry.getContentKind();
}
}
public static class CPE {
private String kind;
private String path;
public CPE(String kind, String path) {
super();
this.kind = kind;
this.path = path;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return "CPE [kind=" + kind + ", path=" + path + "]\n";
}
}
public static class Classpath {
private List<CPE> entries;
private String defaultOutputFolder;
public Classpath(List<CPE> entries, String defaultOutputFolder) {
super();
this.entries = entries;
this.defaultOutputFolder = defaultOutputFolder;
}
public List<CPE> getEntries() {
return entries;
}
public void setEntries(List<CPE> entries) {
this.entries = entries;
}
public String getDefaultOutputFolder() {
return defaultOutputFolder;
}
public void setDefaultOutputFolder(String defaultOutputFolder) {
this.defaultOutputFolder = defaultOutputFolder;
}
@Override
public String toString() {
return "Classpath [entries=" + entries + ", defaultOutputFolder=" + defaultOutputFolder + "]";
}
}
}

View File

@@ -0,0 +1,75 @@
package org.springframework.ide.vscode.boot.jdt.ls;
import java.util.List;
public class Classpath {
public static final String ENTRY_KIND_SOURCE = "source";
public static final String ENTRY_KIND_BINARY = "binary";
public static final String OUTPUT_LOCATION = "output_location";
private List<CPE> entries;
private String defaultOutputFolder;
public Classpath(List<CPE> entries, String defaultOutputFolder) {
super();
this.entries = entries;
this.defaultOutputFolder = defaultOutputFolder;
}
public List<CPE> getEntries() {
return entries;
}
public void setEntries(List<CPE> entries) {
this.entries = entries;
}
public String getDefaultOutputFolder() {
return defaultOutputFolder;
}
public void setDefaultOutputFolder(String defaultOutputFolder) {
this.defaultOutputFolder = defaultOutputFolder;
}
@Override
public String toString() {
return "Classpath [entries=" + entries + ", defaultOutputFolder=" + defaultOutputFolder + "]";
}
public static class CPE {
private String kind;
private String path;
public CPE(String kind, String path) {
super();
this.kind = kind;
this.path = path;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return "CPE [kind=" + kind + ", path=" + path + "]\n";
}
}
}

View File

@@ -27,18 +27,12 @@ import org.springframework.ide.vscode.commons.java.ClasspathData;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.IJavadocProvider;
import org.springframework.ide.vscode.commons.languageserver.ClasspathParams;
import org.springframework.ide.vscode.commons.languageserver.ClasspathResponse;
import org.springframework.ide.vscode.commons.languageserver.ProjectResponse;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListener;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import reactor.core.Disposable;
@@ -55,8 +49,10 @@ public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver {
this.server.onInitialized(() ->
disposable.complete(server.addClasspathListener(new ClasspathListener() {
@Override
public void changed(String projectUri, boolean deleted) {
log.info("Classpath changed: "+projectUri);
public void changed(Event event) {
log.info("Classpath changed: "+ event.projectUri);
}
}))
);
@@ -77,66 +73,37 @@ public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver {
}
private synchronized IJavaProject project(ProjectResponse project) {
if (project == null) {
return null;
}
return table.computeIfAbsent(project.getUri(), uri ->
new JdtLsProject(project)
);
}
@Override
public Optional<IJavaProject> find(TextDocumentIdentifier doc) {
try {
ProjectResponse projectUri = getClient().project(doc.getUri()).get();
return Optional.of(project(projectUri));
} catch (Exception e) {
log.error("Problems finding project for {}", doc.getUri(), e);
}
return Optional.empty();
}
private STS4LanguageClient getClient() {
return ((SimpleLanguageServer) server).getClient();
}
private class JdtLsProject implements IJavaProject {
private Supplier<IClasspath> classpath = Suppliers.memoize(this::computeClasspath);
private ProjectResponse projectResponse;
private final IClasspath classpath;
public JdtLsProject(ProjectResponse projectResponse) {
this.projectResponse = projectResponse;
public JdtLsProject(String name, String uri, Classpath jdtClasspath) {
this.classpath = new JdtClasspath(name, uri, jdtClasspath);
}
@Override
public IClasspath getClasspath() {
return classpath.get();
}
private IClasspath computeClasspath() {
try {
ClasspathResponse response = getClient().classpath(new ClasspathParams(projectResponse.getUri())).get();
return new JdtClasspath(response, projectResponse);
} catch (Exception e) {
log.error("", e);
}
return null;
return classpath;
}
}
private class JdtClasspath extends JandexClasspath {
private ClasspathResponse response;
private Classpath classpath;
private String name;
private String projectUri;
public JdtClasspath(ClasspathResponse response, ProjectResponse projectResponse) {
this.response = response;
this.name = projectResponse.getName();
this.projectUri = projectResponse.getUri();
public JdtClasspath(String name, String uri, Classpath classpath) {
this.name = name;
this.projectUri = uri;
this.classpath = classpath;
}
@Override
@@ -151,15 +118,15 @@ public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver {
@Override
public Path getOutputFolder() {
return Paths.get(response.getDefaultOutputFolder());
return Paths.get(classpath.getDefaultOutputFolder());
}
@Override
public ImmutableList<Path> getClasspathEntries() throws Exception {
return response
return classpath
.getEntries()
.stream()
.filter(cpe -> cpe.getKind().equals(ClasspathResponse.ENTRY_KIND_BINARY))
.filter(cpe -> cpe.getKind().equals(Classpath.ENTRY_KIND_BINARY))
.map(cpe -> Paths.get(cpe.getPath()))
.collect(CollectorUtil.toImmutableList());
}

View File

@@ -5,11 +5,6 @@ import * as VSCode from 'vscode';
import { LanguageClient, RequestType } from 'vscode-languageclient';
export function registerClasspathService(client : LanguageClient) : void {
let classpathRequest = new RequestType<ClasspathParams, ClasspathResponse, void, void>("sts/classpath");
client.onRequest(classpathRequest, async (params: ClasspathParams) => {
return await executeClasspathCommand(params.resourceUri);
});
let classpathListenerRequest = new RequestType<ClasspathListenerParams, ClasspathListenerResponse, void, void>("sts/addClasspathListener");
client.onRequest(classpathListenerRequest, async (params: ClasspathListenerParams) => {
@@ -17,28 +12,9 @@ export function registerClasspathService(client : LanguageClient) : void {
});
}
async function executeClasspathCommand(resourceUri : string) : Promise<ClasspathResponse> {
return <ClasspathResponse> (await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.resolveClasspath", resourceUri));
}
interface ClasspathListenerParams {
callbackCommandId: string
}
interface ClasspathListenerResponse {
}
interface ClasspathResponse {
entries: ClasspathEntry[],
defaultOutputFolder : string
}
interface ClasspathParams {
resourceUri: string
}
interface ClasspathEntry {
kind : string,
path : string
}