[ide server] Added support for #didChangeWatchedFiles()

This commit is contained in:
Sven Efftinge 2016-05-13 15:59:33 +02:00
parent c866c7e061
commit 6a5537990e
17 changed files with 189 additions and 96 deletions

View file

@ -11,15 +11,20 @@ import com.google.inject.Inject
import com.google.inject.Provider
import io.typefox.lsapi.DidChangeConfigurationParams
import io.typefox.lsapi.DidChangeWatchedFilesParams
import io.typefox.lsapi.FileEvent
import io.typefox.lsapi.InitializeParams
import io.typefox.lsapi.WorkspaceService
import io.typefox.lsapi.WorkspaceSymbolParams
import java.util.List
import org.eclipse.emf.common.util.URI
import org.eclipse.xtext.build.BuildRequest
import org.eclipse.xtext.build.IncrementalBuilder
import org.eclipse.xtext.build.IndexState
import org.eclipse.xtext.resource.IResourceServiceProvider
import org.eclipse.xtext.resource.XtextResourceSet
import org.eclipse.xtext.resource.impl.ChunkedResourceDescriptions
import org.eclipse.xtext.resource.impl.ProjectDescription
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData
import org.eclipse.xtext.validation.Issue
/**
@ -30,43 +35,75 @@ class WorkspaceServiceImpl implements WorkspaceService {
@Inject
protected IncrementalBuilder incrementalBuilder
@Inject
protected IndexState indexState
@Inject
protected Provider<XtextResourceSet> resourceSetProvider
@Inject
protected IResourceServiceProvider.Registry languagesRegistry
protected IndexState indexState = new IndexState
@Inject IFileSystemScanner fileSystemScanner
URI baseDir
(URI, Iterable<Issue>)=>void issueAcceptor
public def void initialize(InitializeParams params, (URI, Iterable<Issue>)=>void acceptor) {
val uris = newArrayList
this.baseDir = URI.createFileURI(params.rootPath)
this.issueAcceptor = acceptor
fileSystemScanner.scan(URI.createFileURI(params.rootPath)) [uris += it]
incrementalBuilder.build(new BuildRequest => [
it.baseDir = URI.createFileURI(params.rootPath)
it.resourceSet = resourceSetProvider.get
dirtyFiles = uris
afterValidate = [ uri, issues|
if (!issues.empty)
acceptor.apply(uri, issues)
return true
]
],[languagesRegistry.getResourceServiceProvider(it)])
val result = incrementalBuilder.build(newBuildRequest(uris, emptyList),[languagesRegistry.getResourceServiceProvider(it)])
indexState = result.indexState
}
override didChangeConfiguraton(DidChangeConfigurationParams param) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
override didChangeWatchedFiles(DidChangeWatchedFilesParams param) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
override didChangeWatchedFiles(DidChangeWatchedFilesParams fileChanges) {
val dirtyFiles = newArrayList
val deletedFiles = newArrayList
for (fileEvent : fileChanges.changes) {
if (fileEvent.type === FileEvent.TYPE_DELETED) {
deletedFiles += URI.createFileURI(fileEvent.uri)
} else {
dirtyFiles += URI.createFileURI(fileEvent.uri)
}
}
val result = incrementalBuilder.build(newBuildRequest(dirtyFiles, deletedFiles),[languagesRegistry.getResourceServiceProvider(it)])
indexState = result.indexState
}
override symbol(WorkspaceSymbolParams param) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
protected def BuildRequest newBuildRequest(List<URI> changedFiles, List<URI> deletedFiles) {
new BuildRequest => [
it.baseDir = baseDir
it.resourceSet = createFreshResourceSet(new ResourceDescriptionsData(emptyList))
it.state = new IndexState(indexState.resourceDescriptions.copy, indexState.fileMappings.copy)
it.resourceSet = createFreshResourceSet(state.resourceDescriptions)
it. dirtyFiles = changedFiles
it.deletedFiles = deletedFiles
afterValidate = [ uri, issues|
issueAcceptor.apply(uri, issues)
return true
]
]
}
protected def XtextResourceSet createFreshResourceSet(ResourceDescriptionsData newIndex) {
resourceSetProvider.get => [
val projectDescription = new ProjectDescription => [
name = 'test-project'
]
projectDescription.attachToEmfObject(it)
val index = new ChunkedResourceDescriptions(emptyMap, it)
index.setContainer(projectDescription.name, newIndex)
]
}
}

View file

@ -20,7 +20,7 @@
upperBound="-1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="TypeReference" eSuperTypes="#//Type">
<eStructuralFeatures xsi:type="ecore:EReference" name="typeRef" eType="#//Type"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="typeRef" eType="#//TypeDeclaration"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="PrimitiveType" eSuperTypes="#//Type">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>

View file

@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (c) 2016 TypeFox GmbH (http://www.typefox.io) 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.xtext.ide.tests.server
import com.google.inject.Guice
import com.google.inject.Inject
import io.typefox.lsapi.Diagnostic
import io.typefox.lsapi.NotificationCallback
import io.typefox.lsapi.PublishDiagnosticsParams
import java.io.File
import java.io.FileWriter
import java.util.List
import java.util.Map
import org.eclipse.xtext.ide.server.LanguageServerImpl
import org.eclipse.xtext.ide.server.ServerModule
import org.eclipse.xtext.util.Files
import org.junit.Before
/**
* @author efftinge - Initial contribution and API
*/
class AbstractLanguageServerTest implements NotificationCallback<PublishDiagnosticsParams> {
@Before
def void setup() {
val injector = Guice.createInjector(new ServerModule())
injector.injectMembers(this)
// register notification callbacks
languageServer.getTextDocumentService.onPublishDiagnostics(this)
// create workingdir
root = new File("./test-data/test-project")
if (!root.mkdirs) {
Files.cleanFolder(root, null, true, false)
}
root.deleteOnExit
}
@Inject
protected LanguageServerImpl languageServer
protected Map<String, List<? extends Diagnostic>> diagnostics = newHashMap()
protected File root
def String ->(String path, CharSequence contents) {
val file = new File(root, path)
file.parentFile.mkdirs
file.createNewFile
new FileWriter(file) => [
write(contents.toString)
close
]
return file.absolutePath
}
override onNotification(PublishDiagnosticsParams t) {
diagnostics.put(t.uri, t.diagnostics)
}
}

View file

@ -7,21 +7,14 @@
*******************************************************************************/
package org.eclipse.xtext.ide.tests.server
import com.google.inject.Guice
import com.google.inject.Inject
import io.typefox.lsapi.DidChangeWatchedFilesParamsImpl
import io.typefox.lsapi.FileEvent
import io.typefox.lsapi.FileEventImpl
import io.typefox.lsapi.InitializeParamsImpl
import io.typefox.lsapi.NotificationCallback
import io.typefox.lsapi.PublishDiagnosticsParams
import java.io.File
import java.io.FileWriter
import java.util.List
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtext.ide.server.LanguageServerImpl
import org.eclipse.xtext.ide.server.ServerModule
import org.eclipse.xtext.ide.tests.testlanguage.TestLanguageStandaloneSetup
import org.eclipse.xtext.resource.FileExtensionProvider
import org.eclipse.xtext.resource.IResourceServiceProvider
import org.junit.Before
import org.junit.Test
import static org.junit.Assert.*
@ -29,21 +22,13 @@ import static org.junit.Assert.*
/**
* @author Sven Efftinge - Initial contribution and API
*/
class ServerTest implements NotificationCallback<PublishDiagnosticsParams> {
@Before
def void setup() {
val injector = Guice.createInjector(new ServerModule())
injector.injectMembers(this)
}
class ServerTest extends AbstractLanguageServerTest {
@Inject def voidRegisterTestLanguage(IResourceServiceProvider.Registry registry) {
val injector = new TestLanguageStandaloneSetup().createInjectorAndDoEMFRegistration
registry.extensionToFactoryMap.put(injector.getInstance(FileExtensionProvider).primaryFileExtension, injector.getInstance(IResourceServiceProvider))
}
@Inject LanguageServerImpl languageServer
@Test
def void testInitializeBuild() {
'MyType1.testlang' -> '''
@ -51,11 +36,10 @@ class ServerTest implements NotificationCallback<PublishDiagnosticsParams> {
string foo
}
'''
languageServer.getTextDocumentService.onPublishDiagnostics(this)
languageServer.initialize(new InitializeParamsImpl => [
rootPath = root.absolutePath
])
assertTrue(diagnosticNotifications.join(','), diagnosticNotifications.isEmpty)
assertTrue(diagnostics.entrySet.join(','), diagnostics.values.head.empty)
}
@Test
@ -65,34 +49,36 @@ class ServerTest implements NotificationCallback<PublishDiagnosticsParams> {
NonExisting foo
}
'''
languageServer.getTextDocumentService.onPublishDiagnostics(this)
languageServer.initialize(new InitializeParamsImpl => [
rootPath = root.absolutePath
])
assertEquals("Couldn't resolve reference to Type 'NonExisting'.", diagnosticNotifications.head.diagnostics.head.message)
assertEquals("Couldn't resolve reference to TypeDeclaration 'NonExisting'.", diagnostics.values.head.head?.message)
}
// utilities
val File root = new File("./test-data/test-project") => [
mkdirs
deleteOnExit
]
def void ->(String path, CharSequence contents) {
val file = new File(root, path)
file.parentFile.mkdirs
file.createNewFile
new FileWriter(file) => [
write(contents.toString)
close
]
}
List<PublishDiagnosticsParams> diagnosticNotifications = newArrayList()
override onNotification(PublishDiagnosticsParams t) {
diagnosticNotifications += t
@Test
def void testIncrementalBuildWithError() {
'MyType1.testlang' -> '''
type Test {
NonExisting foo
}
'''
languageServer.initialize(new InitializeParamsImpl => [
rootPath = root.absolutePath
])
assertEquals("Couldn't resolve reference to TypeDeclaration 'NonExisting'.", diagnostics.values.head.head.message)
val path = 'MyType2.testlang' -> '''
type NonExisting {
}
'''
languageServer.getWorkspaceService.didChangeWatchedFiles(new DidChangeWatchedFilesParamsImpl => [
changes = newArrayList(new FileEventImpl => [
uri = path
type = FileEvent.TYPE_CREATED
])
])
assertNotNull(diagnostics.get(path))
assertTrue(diagnostics.values.join(','), diagnostics.values.forall[empty])
}
}

View file

@ -0,0 +1,3 @@
type Test {
NonExisting foo
}

View file

@ -0,0 +1,2 @@
type NonExisting {
}

View file

@ -648,13 +648,13 @@ rule__TypeReference__TypeRefAssignment
}
:
(
{ before(grammarAccess.getTypeReferenceAccess().getTypeRefTypeCrossReference_0()); }
{ before(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationCrossReference_0()); }
(
{ before(grammarAccess.getTypeReferenceAccess().getTypeRefTypeIDTerminalRuleCall_0_1()); }
{ before(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationIDTerminalRuleCall_0_1()); }
RULE_ID
{ after(grammarAccess.getTypeReferenceAccess().getTypeRefTypeIDTerminalRuleCall_0_1()); }
{ after(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationIDTerminalRuleCall_0_1()); }
)
{ after(grammarAccess.getTypeReferenceAccess().getTypeRefTypeCrossReference_0()); }
{ after(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationCrossReference_0()); }
)
;
finally {

View file

@ -1896,17 +1896,17 @@ public class InternalTestLanguageParser extends AbstractInternalContentAssistPar
// InternalTestLanguage.g:650:2: ( ( RULE_ID ) )
// InternalTestLanguage.g:651:3: ( RULE_ID )
{
before(grammarAccess.getTypeReferenceAccess().getTypeRefTypeCrossReference_0());
before(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationCrossReference_0());
// InternalTestLanguage.g:652:3: ( RULE_ID )
// InternalTestLanguage.g:653:4: RULE_ID
{
before(grammarAccess.getTypeReferenceAccess().getTypeRefTypeIDTerminalRuleCall_0_1());
before(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationIDTerminalRuleCall_0_1());
match(input,RULE_ID,FOLLOW_2);
after(grammarAccess.getTypeReferenceAccess().getTypeRefTypeIDTerminalRuleCall_0_1());
after(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationIDTerminalRuleCall_0_1());
}
after(grammarAccess.getTypeReferenceAccess().getTypeRefTypeCrossReference_0());
after(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationCrossReference_0());
}

View file

@ -306,7 +306,7 @@ ruleTypeReference returns [EObject current=null]
}
otherlv_0=RULE_ID
{
newLeafNode(otherlv_0, grammarAccess.getTypeReferenceAccess().getTypeRefTypeCrossReference_0());
newLeafNode(otherlv_0, grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationCrossReference_0());
}
)
)

View file

@ -748,7 +748,7 @@ public class InternalTestLanguageParser extends AbstractInternalAntlrParser {
otherlv_0=(Token)match(input,RULE_ID,FOLLOW_2);
newLeafNode(otherlv_0, grammarAccess.getTypeReferenceAccess().getTypeRefTypeCrossReference_0());
newLeafNode(otherlv_0, grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationCrossReference_0());
}

View file

@ -148,7 +148,7 @@ public class TestLanguageSemanticSequencer extends AbstractDelegatingSemanticSeq
* TypeReference returns TypeReference
*
* Constraint:
* typeRef=[Type|ID]
* typeRef=[TypeDeclaration|ID]
*/
protected void sequence_TypeReference(ISerializationContext context, TypeReference semanticObject) {
if (errorAcceptor != null) {
@ -156,7 +156,7 @@ public class TestLanguageSemanticSequencer extends AbstractDelegatingSemanticSeq
errorAcceptor.accept(diagnosticProvider.createFeatureValueMissing(semanticObject, TestLanguagePackage.Literals.TYPE_REFERENCE__TYPE_REF));
}
SequenceFeeder feeder = createSequencerFeeder(context, semanticObject);
feeder.accept(grammarAccess.getTypeReferenceAccess().getTypeRefTypeIDTerminalRuleCall_0_1(), semanticObject.getTypeRef());
feeder.accept(grammarAccess.getTypeReferenceAccess().getTypeRefTypeDeclarationIDTerminalRuleCall_0_1(), semanticObject.getTypeRef());
feeder.finish();
}
@ -166,7 +166,7 @@ public class TestLanguageSemanticSequencer extends AbstractDelegatingSemanticSeq
* Type returns TypeReference
*
* Constraint:
* (typeRef=[Type|ID] arrayDiemensions+='['*)
* (typeRef=[TypeDeclaration|ID] arrayDiemensions+='['*)
*/
protected void sequence_Type_TypeReference(ISerializationContext context, TypeReference semanticObject) {
genericSequencer.createSequence(context, semanticObject);

View file

@ -152,21 +152,21 @@ public class TestLanguageGrammarAccess extends AbstractGrammarElementFinder {
public class TypeReferenceElements extends AbstractParserRuleElementFinder {
private final ParserRule rule = (ParserRule) GrammarUtil.findRuleForName(getGrammar(), "org.eclipse.xtext.ide.tests.testlanguage.TestLanguage.TypeReference");
private final Assignment cTypeRefAssignment = (Assignment)rule.eContents().get(1);
private final CrossReference cTypeRefTypeCrossReference_0 = (CrossReference)cTypeRefAssignment.eContents().get(0);
private final RuleCall cTypeRefTypeIDTerminalRuleCall_0_1 = (RuleCall)cTypeRefTypeCrossReference_0.eContents().get(1);
private final CrossReference cTypeRefTypeDeclarationCrossReference_0 = (CrossReference)cTypeRefAssignment.eContents().get(0);
private final RuleCall cTypeRefTypeDeclarationIDTerminalRuleCall_0_1 = (RuleCall)cTypeRefTypeDeclarationCrossReference_0.eContents().get(1);
//TypeReference:
// typeRef=[Type];
// typeRef=[TypeDeclaration];
@Override public ParserRule getRule() { return rule; }
//typeRef=[Type]
//typeRef=[TypeDeclaration]
public Assignment getTypeRefAssignment() { return cTypeRefAssignment; }
//[Type]
public CrossReference getTypeRefTypeCrossReference_0() { return cTypeRefTypeCrossReference_0; }
//[TypeDeclaration]
public CrossReference getTypeRefTypeDeclarationCrossReference_0() { return cTypeRefTypeDeclarationCrossReference_0; }
//ID
public RuleCall getTypeRefTypeIDTerminalRuleCall_0_1() { return cTypeRefTypeIDTerminalRuleCall_0_1; }
public RuleCall getTypeRefTypeDeclarationIDTerminalRuleCall_0_1() { return cTypeRefTypeDeclarationIDTerminalRuleCall_0_1; }
}
public class PrimitiveTypeElements extends AbstractParserRuleElementFinder {
private final ParserRule rule = (ParserRule) GrammarUtil.findRuleForName(getGrammar(), "org.eclipse.xtext.ide.tests.testlanguage.TestLanguage.PrimitiveType");
@ -291,7 +291,7 @@ public class TestLanguageGrammarAccess extends AbstractGrammarElementFinder {
}
//TypeReference:
// typeRef=[Type];
// typeRef=[TypeDeclaration];
public TypeReferenceElements getTypeReferenceAccess() {
return pTypeReference;
}

View file

@ -35,12 +35,12 @@ public interface TypeReference extends Type
* </p>
* <!-- end-user-doc -->
* @return the value of the '<em>Type Ref</em>' reference.
* @see #setTypeRef(Type)
* @see #setTypeRef(TypeDeclaration)
* @see org.eclipse.xtext.ide.tests.testlanguage.testLanguage.TestLanguagePackage#getTypeReference_TypeRef()
* @model
* @generated
*/
Type getTypeRef();
TypeDeclaration getTypeRef();
/**
* Sets the value of the '{@link org.eclipse.xtext.ide.tests.testlanguage.testLanguage.TypeReference#getTypeRef <em>Type Ref</em>}' reference.
@ -50,6 +50,6 @@ public interface TypeReference extends Type
* @see #getTypeRef()
* @generated
*/
void setTypeRef(Type value);
void setTypeRef(TypeDeclaration value);
} // TypeReference

View file

@ -375,7 +375,7 @@ public class TestLanguagePackageImpl extends EPackageImpl implements TestLanguag
initEAttribute(getType_ArrayDiemensions(), ecorePackage.getEString(), "arrayDiemensions", null, 0, -1, Type.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, !IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
initEClass(typeReferenceEClass, TypeReference.class, "TypeReference", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
initEReference(getTypeReference_TypeRef(), this.getType(), null, "typeRef", null, 0, 1, TypeReference.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
initEReference(getTypeReference_TypeRef(), this.getTypeDeclaration(), null, "typeRef", null, 0, 1, TypeReference.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
initEClass(primitiveTypeEClass, PrimitiveType.class, "PrimitiveType", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
initEAttribute(getPrimitiveType_Name(), ecorePackage.getEString(), "name", null, 0, 1, PrimitiveType.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);

View file

@ -15,7 +15,7 @@ import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.TestLanguagePackage;
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.Type;
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.TypeDeclaration;
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.TypeReference;
/**
@ -41,7 +41,7 @@ public class TypeReferenceImpl extends TypeImpl implements TypeReference
* @generated
* @ordered
*/
protected Type typeRef;
protected TypeDeclaration typeRef;
/**
* <!-- begin-user-doc -->
@ -69,12 +69,12 @@ public class TypeReferenceImpl extends TypeImpl implements TypeReference
* <!-- end-user-doc -->
* @generated
*/
public Type getTypeRef()
public TypeDeclaration getTypeRef()
{
if (typeRef != null && typeRef.eIsProxy())
{
InternalEObject oldTypeRef = (InternalEObject)typeRef;
typeRef = (Type)eResolveProxy(oldTypeRef);
typeRef = (TypeDeclaration)eResolveProxy(oldTypeRef);
if (typeRef != oldTypeRef)
{
if (eNotificationRequired())
@ -89,7 +89,7 @@ public class TypeReferenceImpl extends TypeImpl implements TypeReference
* <!-- end-user-doc -->
* @generated
*/
public Type basicGetTypeRef()
public TypeDeclaration basicGetTypeRef()
{
return typeRef;
}
@ -99,9 +99,9 @@ public class TypeReferenceImpl extends TypeImpl implements TypeReference
* <!-- end-user-doc -->
* @generated
*/
public void setTypeRef(Type newTypeRef)
public void setTypeRef(TypeDeclaration newTypeRef)
{
Type oldTypeRef = typeRef;
TypeDeclaration oldTypeRef = typeRef;
typeRef = newTypeRef;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, TestLanguagePackage.TYPE_REFERENCE__TYPE_REF, oldTypeRef, typeRef));
@ -135,7 +135,7 @@ public class TypeReferenceImpl extends TypeImpl implements TypeReference
switch (featureID)
{
case TestLanguagePackage.TYPE_REFERENCE__TYPE_REF:
setTypeRef((Type)newValue);
setTypeRef((TypeDeclaration)newValue);
return;
}
super.eSet(featureID, newValue);
@ -152,7 +152,7 @@ public class TypeReferenceImpl extends TypeImpl implements TypeReference
switch (featureID)
{
case TestLanguagePackage.TYPE_REFERENCE__TYPE_REF:
setTypeRef((Type)null);
setTypeRef((TypeDeclaration)null);
return;
}
super.eUnset(featureID);

View file

@ -27,7 +27,7 @@ Type :
;
TypeReference:
typeRef=[Type]
typeRef=[TypeDeclaration]
;
PrimitiveType: