Skip to content
This repository has been archived by the owner on Apr 21, 2023. It is now read-only.

Commit

Permalink
[LS] support multiple workspace root folders
Browse files Browse the repository at this point in the history
Fixes #1238
  • Loading branch information
JanKoehnlein committed Feb 19, 2020
1 parent c059268 commit 52d115d
Show file tree
Hide file tree
Showing 11 changed files with 518 additions and 8 deletions.
@@ -0,0 +1,123 @@
/*******************************************************************************
* 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.Files
import org.eclipse.xtext.util.Modules2
import org.junit.Test

import static org.junit.Assert.assertEquals

/**
* @author Jan Koehnlein - Initial contribution and API
*/
class WorkspaceFoldersTest extends AbstractTestLangLanguageServerTest {

@Inject extension UriExtensions

@Inject WorkspaceManager workspaceManager

@Test
def void testInitialize() {
val rootFolder1 = getRoot('root1')
val rootFolder2 = getRoot('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
}

protected def getRoot(String path) {
val root = new File(path)
if (!root.mkdirs) {
Files.cleanFolder(root, null, true, false)
}
root.deleteOnExit
root
}

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)
}

}
@@ -0,0 +1,182 @@
/**
* 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.Files;
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.Test;

/**
* @author Jan Koehnlein - Initial contribution and API
*/
@SuppressWarnings("all")
public class WorkspaceFoldersTest extends AbstractTestLangLanguageServerTest {
@Inject
@Extension
private UriExtensions _uriExtensions;

@Inject
private WorkspaceManager workspaceManager;

@Test
public void testInitialize() {
final File rootFolder1 = this.getRoot("root1");
final File rootFolder2 = this.getRoot("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());
}

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);
}
}

protected File getRoot(final String path) {
try {
File _xblockexpression = null;
{
final File root = new File(path);
boolean _mkdirs = root.mkdirs();
boolean _not = (!_mkdirs);
if (_not) {
Files.cleanFolder(root, null, true, false);
}
root.deleteOnExit();
_xblockexpression = root;
}
return _xblockexpression;
} 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);
}
}
@@ -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.11
*/
public interface IMultiRootWorkspaceConfigFactory {

/**
* 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);

}

0 comments on commit 52d115d

Please sign in to comment.