[LS] support multiple workspace root folders

Fixes #1238
This commit is contained in:
Jan Koehnlein 2020-02-19 11:26:36 +01:00
parent c059268a49
commit e2cd8edd53
11 changed files with 509 additions and 6 deletions

View file

@ -0,0 +1,118 @@
/*******************************************************************************
* Copyright (c) 2020 TypeFox GmbH (http://www.typefox.io) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.ide.tests.server
import com.google.inject.AbstractModule
import com.google.inject.Inject
import com.google.inject.Module
import com.google.inject.Scopes
import java.io.File
import java.io.FileWriter
import java.util.concurrent.CompletableFuture
import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams
import org.eclipse.lsp4j.WorkspaceFolder
import org.eclipse.lsp4j.WorkspaceFoldersChangeEvent
import org.eclipse.xtext.ide.server.UriExtensions
import org.eclipse.xtext.ide.server.WorkspaceManager
import org.eclipse.xtext.util.Modules2
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import static org.junit.Assert.assertEquals
/**
* @author Jan Koehnlein - Initial contribution and API
*/
class WorkspaceFoldersTest extends AbstractTestLangLanguageServerTest {
@Inject extension UriExtensions
@Inject WorkspaceManager workspaceManager
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder()
@Test
def void testInitialize() {
val rootFolder1 = temporaryFolder.newFolder('root1')
val rootFolder2 = temporaryFolder.newFolder('root2')
writeFile(rootFolder1, "one.testlang", '''
type Foo {
Bar bar
}
''')
val twoUri = writeFile(rootFolder2, "two.testlang", '''
type Bar {
Foo foo
}
''')
initialize[
workspaceFolders = #[
new WorkspaceFolder(rootFolder1.toURI.toUriString, 'root1'),
new WorkspaceFolder(rootFolder2.toURI.toUriString, 'root2')
]
]
assertEquals(2, diagnostics.size)
assertEquals(1, diagnostics.get(twoUri).size)
withBuild [
languageServer.didChangeWorkspaceFolders(new DidChangeWorkspaceFoldersParams => [
event = new WorkspaceFoldersChangeEvent => [
removed = #[
new WorkspaceFolder(rootFolder2.toURI.toUriString, 'root2')
]
]
])
]
assertEquals(0, diagnostics.get(twoUri).size)
withBuild [
languageServer.didChangeWorkspaceFolders(new DidChangeWorkspaceFoldersParams => [
event = new WorkspaceFoldersChangeEvent => [
added = #[
new WorkspaceFolder(rootFolder2.toURI.toUriString, 'root2')
]
]
])
]
assertEquals(1, diagnostics.get(twoUri).size)
}
protected def void withBuild(()=>void lambda) {
val future = new CompletableFuture<Void>()
workspaceManager.addBuildListener[
workspaceManager.removeBuildListener(self)
future.complete(null)
]
lambda.apply
future.get
}
def String writeFile(File root, String path, CharSequence contents) {
val file = new File(root, path)
file.parentFile.mkdirs
file.createNewFile
val writer = new FileWriter(file)
writer.write(contents.toString)
writer.close
return file.toURI.normalize.toUriString
}
override protected getServerModule() {
val defaultModule = super.getServerModule()
val Module customModule = new AbstractModule() {
override protected configure() {
bind(WorkspaceManager).in(Scopes.SINGLETON);
}
}
return Modules2.mixin(defaultModule, customModule)
}
}

View file

@ -0,0 +1,171 @@
/**
* Copyright (c) 2020 TypeFox GmbH (http://www.typefox.io) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.xtext.ide.tests.server;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Scopes;
import java.io.File;
import java.io.FileWriter;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.lsp4j.WorkspaceFoldersChangeEvent;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.ide.server.ILanguageServerAccess;
import org.eclipse.xtext.ide.server.UriExtensions;
import org.eclipse.xtext.ide.server.WorkspaceManager;
import org.eclipse.xtext.ide.tests.server.AbstractTestLangLanguageServerTest;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.util.Modules2;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure0;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
/**
* @author Jan Koehnlein - Initial contribution and API
*/
@SuppressWarnings("all")
public class WorkspaceFoldersTest extends AbstractTestLangLanguageServerTest {
@Inject
@Extension
private UriExtensions _uriExtensions;
@Inject
private WorkspaceManager workspaceManager;
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void testInitialize() {
try {
final File rootFolder1 = this.temporaryFolder.newFolder("root1");
final File rootFolder2 = this.temporaryFolder.newFolder("root2");
StringConcatenation _builder = new StringConcatenation();
_builder.append("type Foo {");
_builder.newLine();
_builder.append("\t");
_builder.append("Bar bar");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this.writeFile(rootFolder1, "one.testlang", _builder);
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("type Bar {");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("Foo foo");
_builder_1.newLine();
_builder_1.append("}");
_builder_1.newLine();
final String twoUri = this.writeFile(rootFolder2, "two.testlang", _builder_1);
final Procedure1<InitializeParams> _function = (InitializeParams it) -> {
String _uriString = this._uriExtensions.toUriString(rootFolder1.toURI());
WorkspaceFolder _workspaceFolder = new WorkspaceFolder(_uriString, "root1");
String _uriString_1 = this._uriExtensions.toUriString(rootFolder2.toURI());
WorkspaceFolder _workspaceFolder_1 = new WorkspaceFolder(_uriString_1, "root2");
it.setWorkspaceFolders(Collections.<WorkspaceFolder>unmodifiableList(CollectionLiterals.<WorkspaceFolder>newArrayList(_workspaceFolder, _workspaceFolder_1)));
};
this.initialize(_function);
Assert.assertEquals(2, this.getDiagnostics().size());
Assert.assertEquals(1, this.getDiagnostics().get(twoUri).size());
final Procedure0 _function_1 = () -> {
DidChangeWorkspaceFoldersParams _didChangeWorkspaceFoldersParams = new DidChangeWorkspaceFoldersParams();
final Procedure1<DidChangeWorkspaceFoldersParams> _function_2 = (DidChangeWorkspaceFoldersParams it) -> {
WorkspaceFoldersChangeEvent _workspaceFoldersChangeEvent = new WorkspaceFoldersChangeEvent();
final Procedure1<WorkspaceFoldersChangeEvent> _function_3 = (WorkspaceFoldersChangeEvent it_1) -> {
String _uriString = this._uriExtensions.toUriString(rootFolder2.toURI());
WorkspaceFolder _workspaceFolder = new WorkspaceFolder(_uriString, "root2");
it_1.setRemoved(Collections.<WorkspaceFolder>unmodifiableList(CollectionLiterals.<WorkspaceFolder>newArrayList(_workspaceFolder)));
};
WorkspaceFoldersChangeEvent _doubleArrow = ObjectExtensions.<WorkspaceFoldersChangeEvent>operator_doubleArrow(_workspaceFoldersChangeEvent, _function_3);
it.setEvent(_doubleArrow);
};
DidChangeWorkspaceFoldersParams _doubleArrow = ObjectExtensions.<DidChangeWorkspaceFoldersParams>operator_doubleArrow(_didChangeWorkspaceFoldersParams, _function_2);
this.languageServer.didChangeWorkspaceFolders(_doubleArrow);
};
this.withBuild(_function_1);
Assert.assertEquals(0, this.getDiagnostics().get(twoUri).size());
final Procedure0 _function_2 = () -> {
DidChangeWorkspaceFoldersParams _didChangeWorkspaceFoldersParams = new DidChangeWorkspaceFoldersParams();
final Procedure1<DidChangeWorkspaceFoldersParams> _function_3 = (DidChangeWorkspaceFoldersParams it) -> {
WorkspaceFoldersChangeEvent _workspaceFoldersChangeEvent = new WorkspaceFoldersChangeEvent();
final Procedure1<WorkspaceFoldersChangeEvent> _function_4 = (WorkspaceFoldersChangeEvent it_1) -> {
String _uriString = this._uriExtensions.toUriString(rootFolder2.toURI());
WorkspaceFolder _workspaceFolder = new WorkspaceFolder(_uriString, "root2");
it_1.setAdded(Collections.<WorkspaceFolder>unmodifiableList(CollectionLiterals.<WorkspaceFolder>newArrayList(_workspaceFolder)));
};
WorkspaceFoldersChangeEvent _doubleArrow = ObjectExtensions.<WorkspaceFoldersChangeEvent>operator_doubleArrow(_workspaceFoldersChangeEvent, _function_4);
it.setEvent(_doubleArrow);
};
DidChangeWorkspaceFoldersParams _doubleArrow = ObjectExtensions.<DidChangeWorkspaceFoldersParams>operator_doubleArrow(_didChangeWorkspaceFoldersParams, _function_3);
this.languageServer.didChangeWorkspaceFolders(_doubleArrow);
};
this.withBuild(_function_2);
Assert.assertEquals(1, this.getDiagnostics().get(twoUri).size());
} catch (Throwable _e) {
throw Exceptions.sneakyThrow(_e);
}
}
protected void withBuild(final Procedure0 lambda) {
try {
final CompletableFuture<Void> future = new CompletableFuture<Void>();
final ILanguageServerAccess.IBuildListener _function = new ILanguageServerAccess.IBuildListener() {
@Override
public void afterBuild(final List<IResourceDescription.Delta> it) {
WorkspaceFoldersTest.this.workspaceManager.removeBuildListener(this);
future.complete(null);
}
};
this.workspaceManager.addBuildListener(_function);
lambda.apply();
future.get();
} catch (Throwable _e) {
throw Exceptions.sneakyThrow(_e);
}
}
public String writeFile(final File root, final String path, final CharSequence contents) {
try {
final File file = new File(root, path);
file.getParentFile().mkdirs();
file.createNewFile();
final FileWriter writer = new FileWriter(file);
writer.write(contents.toString());
writer.close();
return this._uriExtensions.toUriString(file.toURI().normalize());
} catch (Throwable _e) {
throw Exceptions.sneakyThrow(_e);
}
}
@Override
protected com.google.inject.Module getServerModule() {
final com.google.inject.Module defaultModule = super.getServerModule();
final com.google.inject.Module customModule = new AbstractModule() {
@Override
protected void configure() {
this.<WorkspaceManager>bind(WorkspaceManager.class).in(Scopes.SINGLETON);
}
};
return Modules2.mixin(defaultModule, customModule);
}
}

View file

@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2020 TypeFox GmbH (http://www.typefox.io) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.ide.server;
import java.util.List;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.xtext.workspace.IWorkspaceConfig;
/**
* @author Jan Koehnlein - Initial contribution and API
* @since 2.21
*/
public interface IMultiRootWorkspaceConfigFactory extends IWorkspaceConfigFactory {
/**
* Create a workspace config at the given location.
*
* @param workspaceFolders the list of workspace root folders
* @return the workspace configuration.
*/
IWorkspaceConfig getWorkspaceConfig(List<WorkspaceFolder> workspaceFolders);
}

View file

@ -41,6 +41,7 @@ import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
@ -84,6 +85,9 @@ import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceClientCapabilities;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.lsp4j.WorkspaceFoldersOptions;
import org.eclipse.lsp4j.WorkspaceServerCapabilities;
import org.eclipse.lsp4j.WorkspaceSymbolParams;
import org.eclipse.lsp4j.jsonrpc.Endpoint;
import org.eclipse.lsp4j.jsonrpc.json.JsonRpcMethod;
@ -219,7 +223,14 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex
result.setCapabilities(createServerCapabilities(params));
access.addBuildListener(this);
return requestManager.runWrite(() -> {
workspaceManager.initialize(baseDir, this::publishDiagnostics, CancelIndicator.NullImpl);
if (workspaceManager.isSupportsWorkspaceFolders()) {
List<WorkspaceFolder> workspaceFolders = params.getWorkspaceFolders();
if (workspaceFolders == null)
workspaceFolders = Collections.emptyList();
workspaceManager.initialize(workspaceFolders, this::publishDiagnostics, CancelIndicator.NullImpl);
} else {
workspaceManager.initialize(baseDir, this::publishDiagnostics, CancelIndicator.NullImpl);
}
return result;
}, (cancelIndicator, it) -> it).thenApply(it -> initializeResult = it);
}
@ -293,6 +304,12 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex
ExecuteCommandCapabilities executeCommand = null;
if (workspace != null) {
executeCommand = workspace.getExecuteCommand();
if (workspace.getWorkspaceFolders() == Boolean.TRUE && workspaceManager.isSupportsWorkspaceFolders()) {
WorkspaceFoldersOptions workspaceFoldersOptions = new WorkspaceFoldersOptions();
workspaceFoldersOptions.setSupported(true);
workspaceFoldersOptions.setChangeNotifications(true);
serverCapabilities.setWorkspace(new WorkspaceServerCapabilities(workspaceFoldersOptions));
}
}
if (executeCommand != null) {
commandRegistry.initialize(allLanguages, clientCapabilities, client);
@ -451,6 +468,17 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex
}, (a, b) -> null);
}
/**
* @since 2.21
*/
@Override
public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) {
requestManager.runWrite(() -> {
workspaceManager.didChangeWorkspaceFolders(params, CancelIndicator.NullImpl);
return null;
}, (a, b) -> null);
}
private void publishDiagnostics(URI uri, Iterable<? extends Issue> issues) {
initialized.thenAccept((initParams) -> {
PublishDiagnosticsParams publishDiagnosticsParams = new PublishDiagnosticsParams();

View file

@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (c) 2020 TypeFox GmbH (http://www.typefox.io) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.ide.server;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.URI;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.xtext.workspace.FileProjectConfig;
import org.eclipse.xtext.workspace.IWorkspaceConfig;
import org.eclipse.xtext.workspace.WorkspaceConfig;
import com.google.inject.Inject;
/**
* Creates one project per workspace root folder.
*
* @author Jan Koehnlein - Initial contribution and API
* @since 2.21
*/
public class MultiRootWorkspaceConfigFactory extends ProjectWorkspaceConfigFactory implements IWorkspaceConfigFactory, IMultiRootWorkspaceConfigFactory {
@Inject UriExtensions uriExtensions;
@Override
public IWorkspaceConfig getWorkspaceConfig(List<WorkspaceFolder> workspaceFolders) {
WorkspaceConfig workspaceConfig = new WorkspaceConfig();
Set<String> existingProjectNames = new HashSet<>();
for(WorkspaceFolder workspaceFolder: workspaceFolders)
addProjectsForWorkspaceFolder(workspaceConfig, workspaceFolder, existingProjectNames);
return workspaceConfig;
}
@Override
public IWorkspaceConfig getWorkspaceConfig(URI workspaceBaseURI) {
WorkspaceConfig workspaceConfig = new WorkspaceConfig();
addProjectsForWorkspaceFolder(workspaceConfig, new WorkspaceFolder(uriExtensions.toUriString(workspaceBaseURI), "workspace"), new HashSet<>());
return workspaceConfig;
}
protected void addProjectsForWorkspaceFolder(WorkspaceConfig workspaceConfig, WorkspaceFolder workspaceFolder, Set<String> existingNames) {
if (workspaceFolder != null && workspaceFolder.getUri() != null) {
FileProjectConfig project = new FileProjectConfig(uriExtensions.toUri(workspaceFolder.getUri()), getUniqueProjectName(workspaceFolder.getName(), existingNames));
project.addSourceFolder(".");
workspaceConfig.addProject(project);
}
}
/**
* Project names have to be unique, as the WorkspaceManager uses them as keys in a map.
*/
protected String getUniqueProjectName(String proposal, Set<String> existingNames){
if (existingNames.add(proposal)) {
return proposal;
} else {
for(int count = 1; true; ++count) {
if (existingNames.add(proposal + count))
return proposal + count;
}
}
}
}

View file

@ -215,4 +215,15 @@ public class ProjectManager {
public IProjectConfig getProjectConfig() {
return projectConfig;
}
/**
* @since 2.21
*/
public void aboutToRemoveFromWorkspace() {
for (ISourceFolder srcFolder : projectConfig.getSourceFolders()) {
for (URI resourceURI: srcFolder.getAllResources(fileSystemScanner)) {
issueAcceptor.apply(resourceURI, Collections.emptyList());
}
}
}
}

View file

@ -29,8 +29,8 @@ class ServerModule extends AbstractModule {
bind(LanguageServer).to(LanguageServerImpl)
bind(IResourceServiceProvider.Registry).toProvider(ResourceServiceProviderServiceLoader)
bind(IWorkspaceConfigFactory).to(ProjectWorkspaceConfigFactory)
bind(ProjectWorkspaceConfigFactory).to(MultiRootWorkspaceConfigFactory)
bind(IProjectDescriptionFactory).to(DefaultProjectDescriptionFactory)
bind(IContainer.Manager).to(ProjectDescriptionBasedContainerManager)
}
}

View file

@ -19,8 +19,11 @@ import java.util.Set;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.URI;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams;
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.lsp4j.WorkspaceFoldersChangeEvent;
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseError;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode;
@ -60,10 +63,13 @@ public class WorkspaceManager {
@Inject
private IProjectDescriptionFactory projectDescriptionFactory;
@Inject
private UriExtensions uriExtensions;
private BuildManager buildManager;
private URI baseDir;
private List<WorkspaceFolder> workspaceFolders = Collections.emptyList();
private Procedure2<? super URI, ? super Iterable<Issue>> issueAcceptor;
@ -141,16 +147,73 @@ public class WorkspaceManager {
*/
public void initialize(URI baseDir, Procedure2<? super URI, ? super Iterable<Issue>> issueAcceptor,
CancelIndicator cancelIndicator) {
this.baseDir = baseDir;
WorkspaceFolder workspaceFolder = new WorkspaceFolder(uriExtensions.toUriString(baseDir), "");
initialize(Collections.singletonList(workspaceFolder), issueAcceptor, cancelIndicator);
}
/**
* Initialize a workspace with the given workspace folders.
*
* @param workspaceFolders
* the list of workspace root folders
* @param issueAcceptor
* the issue acceptor
* @param cancelIndicator
* allows to cancel the initialization
* @since 2.21
*/
public void initialize(List<WorkspaceFolder> workspaceFolders, Procedure2<? super URI, ? super Iterable<Issue>> issueAcceptor,
CancelIndicator cancelIndicator) {
this.workspaceFolders = new ArrayList<>(workspaceFolders);
this.issueAcceptor = issueAcceptor;
refreshWorkspaceConfig(cancelIndicator);
}
/**
* @return whether this workspace manager supports multiple workspace root folders.
* @since 2.21
*/
public boolean isSupportsWorkspaceFolders() {
return workspaceConfigFactory instanceof IMultiRootWorkspaceConfigFactory;
}
protected List<WorkspaceFolder> getWorkspaceFolders() {
return workspaceFolders;
}
/**
* Updates the workspace folders and refreshes the workspace.
*
* @since 2.21
*/
public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params, CancelIndicator cancelIndicator) {
WorkspaceFoldersChangeEvent event = params.getEvent();
Map<String, WorkspaceFolder> uri2workspaceFolder = new HashMap<>();
workspaceFolders.forEach(it -> uri2workspaceFolder.put(it.getUri(), it));
event.getRemoved().forEach(it -> uri2workspaceFolder.remove(it.getUri()));
event.getAdded().forEach(it -> {
if (!uri2workspaceFolder.containsKey(it.getUri()))
uri2workspaceFolder.put(it.getUri(), it);
});
this.workspaceFolders = new ArrayList<>(uri2workspaceFolder.values());
refreshWorkspaceConfig(cancelIndicator);
}
protected IWorkspaceConfig createWorkspaceConfig() {
if (isSupportsWorkspaceFolders())
return ((IMultiRootWorkspaceConfigFactory) workspaceConfigFactory).getWorkspaceConfig(workspaceFolders);
URI workspaceUri = (!workspaceFolders.isEmpty())
? uriExtensions.toUri(workspaceFolders.get(0).getUri())
: null;
return workspaceConfigFactory.getWorkspaceConfig(workspaceUri);
}
/**
* Refresh the workspace.
*/
protected void refreshWorkspaceConfig(CancelIndicator cancelIndicator) {
setWorkspaceConfig(workspaceConfigFactory.getWorkspaceConfig(baseDir));
IWorkspaceConfig newWorkspaceConfig = createWorkspaceConfig();
setWorkspaceConfig(newWorkspaceConfig);
List<ProjectDescription> newProjects = new ArrayList<>();
Set<String> projectNames = projectName2ProjectManager.keySet();
Set<String> remainingProjectNames = new HashSet<>(projectNames);
@ -167,7 +230,8 @@ public class WorkspaceManager {
}
}
for (String deletedProject : remainingProjectNames) {
projectName2ProjectManager.remove(deletedProject);
ProjectManager projectManager = projectName2ProjectManager.remove(deletedProject);
projectManager.aboutToRemoveFromWorkspace();
fullIndex.remove(deletedProject);
}
afterBuild(buildManager.doInitialBuild(newProjects, cancelIndicator));

View file

@ -16,6 +16,7 @@ import org.eclipse.xtext.ide.server.DefaultProjectDescriptionFactory;
import org.eclipse.xtext.ide.server.IProjectDescriptionFactory;
import org.eclipse.xtext.ide.server.IWorkspaceConfigFactory;
import org.eclipse.xtext.ide.server.LanguageServerImpl;
import org.eclipse.xtext.ide.server.MultiRootWorkspaceConfigFactory;
import org.eclipse.xtext.ide.server.ProjectWorkspaceConfigFactory;
import org.eclipse.xtext.resource.IContainer;
import org.eclipse.xtext.resource.IResourceServiceProvider;
@ -34,6 +35,7 @@ public class ServerModule extends AbstractModule {
this.<LanguageServer>bind(LanguageServer.class).to(LanguageServerImpl.class);
this.<IResourceServiceProvider.Registry>bind(IResourceServiceProvider.Registry.class).toProvider(ResourceServiceProviderServiceLoader.class);
this.<IWorkspaceConfigFactory>bind(IWorkspaceConfigFactory.class).to(ProjectWorkspaceConfigFactory.class);
this.<ProjectWorkspaceConfigFactory>bind(ProjectWorkspaceConfigFactory.class).to(MultiRootWorkspaceConfigFactory.class);
this.<IProjectDescriptionFactory>bind(IProjectDescriptionFactory.class).to(DefaultProjectDescriptionFactory.class);
this.<IContainer.Manager>bind(IContainer.Manager.class).to(ProjectDescriptionBasedContainerManager.class);
}

View file

@ -87,6 +87,7 @@ import static extension org.eclipse.lsp4j.util.Ranges.containsRange
import static extension org.eclipse.xtext.util.Strings.*
import org.eclipse.lsp4j.SignatureHelpParams
import org.eclipse.lsp4j.MarkupContent
import org.eclipse.lsp4j.WorkspaceFolder
/**
* @author Sven Efftinge - Initial contribution and API
@ -190,6 +191,9 @@ abstract class AbstractLanguageServerTest implements Endpoint {
val params = new InitializeParams => [
processId = 1
rootUri = root.toURI.normalize.toUriString
workspaceFolders = #[
new WorkspaceFolder(rootUri, '')
]
]
initializer?.apply(params)
hierarchicalDocumentSymbolSupport = params.capabilities?.textDocument?.documentSymbol?.

View file

@ -21,6 +21,7 @@ import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -77,6 +78,7 @@ import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.lsp4j.WorkspaceSymbolParams;
import org.eclipse.lsp4j.jsonrpc.Endpoint;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@ -325,6 +327,9 @@ public abstract class AbstractLanguageServerTest implements Endpoint {
final Procedure1<InitializeParams> _function = (InitializeParams it) -> {
it.setProcessId(Integer.valueOf(1));
it.setRootUri(this._uriExtensions.toUriString(this.root.toURI().normalize()));
String _rootUri = it.getRootUri();
WorkspaceFolder _workspaceFolder = new WorkspaceFolder(_rootUri, "");
it.setWorkspaceFolders(Collections.<WorkspaceFolder>unmodifiableList(CollectionLiterals.<WorkspaceFolder>newArrayList(_workspaceFolder)));
};
final InitializeParams params = ObjectExtensions.<InitializeParams>operator_doubleArrow(_initializeParams, _function);
if (initializer!=null) {