mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 08:48:55 +00:00
[lsp] initial support for multi project builds
This commit is contained in:
parent
9c51ff724c
commit
80c7da3224
2 changed files with 148 additions and 75 deletions
|
@ -0,0 +1,90 @@
|
|||
/*******************************************************************************
|
||||
* 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.server
|
||||
|
||||
import com.google.inject.Inject
|
||||
import com.google.inject.Provider
|
||||
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.IExternalContentSupport
|
||||
import org.eclipse.xtext.resource.IExternalContentSupport.IExternalContentProvider
|
||||
import org.eclipse.xtext.resource.IResourceDescription
|
||||
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
|
||||
import java.util.Map
|
||||
import org.eclipse.xtext.build.IncrementalBuilder.Result
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
class ProjectManager {
|
||||
|
||||
@Inject protected IncrementalBuilder incrementalBuilder
|
||||
@Inject protected Provider<XtextResourceSet> resourceSetProvider
|
||||
@Inject protected IResourceServiceProvider.Registry languagesRegistry
|
||||
@Inject protected IFileSystemScanner fileSystemScanner
|
||||
@Inject protected IExternalContentSupport externalContentSupport
|
||||
|
||||
IndexState indexState = new IndexState
|
||||
URI baseDir
|
||||
(URI, Iterable<Issue>)=>void issueAcceptor
|
||||
Provider<Map<String, ResourceDescriptionsData>> indexProvider
|
||||
IExternalContentProvider openedDocumentsContentProvider
|
||||
|
||||
def Result initialize(URI baseDir, (URI, Iterable<Issue>)=>void acceptor, IExternalContentProvider openedDocumentsContentProvider, Provider<Map<String, ResourceDescriptionsData>> indexProvider) {
|
||||
val uris = newArrayList
|
||||
this.baseDir = baseDir
|
||||
this.issueAcceptor = acceptor
|
||||
this.openedDocumentsContentProvider = openedDocumentsContentProvider
|
||||
this.indexProvider = indexProvider
|
||||
this.fileSystemScanner.scan(baseDir)[uris += it]
|
||||
return doBuild(uris, emptyList)
|
||||
}
|
||||
|
||||
def Result doBuild(List<URI> dirtyFiles, List<URI> deletedFiles) {
|
||||
val result = incrementalBuilder.build(newBuildRequest(dirtyFiles, deletedFiles), [
|
||||
languagesRegistry.getResourceServiceProvider(it)
|
||||
])
|
||||
indexState = result.indexState
|
||||
return result;
|
||||
}
|
||||
|
||||
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(indexProvider.get, it)
|
||||
index.setContainer(projectDescription.name, newIndex)
|
||||
externalContentSupport.configureResourceSet(it, openedDocumentsContentProvider)
|
||||
]
|
||||
}
|
||||
}
|
|
@ -15,43 +15,48 @@ import io.typefox.lsapi.DidCloseTextDocumentParams
|
|||
import io.typefox.lsapi.DidOpenTextDocumentParams
|
||||
import io.typefox.lsapi.DidSaveTextDocumentParams
|
||||
import io.typefox.lsapi.FileEvent
|
||||
import java.util.ArrayList
|
||||
import java.util.List
|
||||
import java.util.Map
|
||||
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.IExternalContentSupport.IExternalContentProvider
|
||||
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData
|
||||
import org.eclipse.xtext.validation.Issue
|
||||
import org.eclipse.xtext.resource.IExternalContentSupport
|
||||
import org.eclipse.xtext.resource.IExternalContentSupport.IExternalContentProvider
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
class WorkspaceManager {
|
||||
|
||||
@Inject protected IncrementalBuilder incrementalBuilder
|
||||
@Inject protected Provider<XtextResourceSet> resourceSetProvider
|
||||
@Inject protected IResourceServiceProvider.Registry languagesRegistry
|
||||
@Inject protected IFileSystemScanner fileSystemScanner
|
||||
@Inject protected IExternalContentSupport externalContentSupport
|
||||
@Inject Provider<ProjectManager> projectManagerProvider
|
||||
Map<URI, ProjectManager> baseDir2ProjectManager = newHashMap()
|
||||
|
||||
IndexState indexState = new IndexState
|
||||
URI baseDir
|
||||
(URI, Iterable<Issue>)=>void issueAcceptor
|
||||
Map<URI, Document> openDocuments = newHashMap()
|
||||
val openedDocumentsContentProvider = new IExternalContentProvider() {
|
||||
|
||||
override getActualContentProvider() {
|
||||
return this
|
||||
}
|
||||
|
||||
override getContent(URI uri) {
|
||||
openDocuments.get(uri)?.contents
|
||||
}
|
||||
|
||||
override hasContent(URI uri) {
|
||||
openDocuments.containsKey(uri)
|
||||
}
|
||||
};
|
||||
Map<String, ResourceDescriptionsData> fullIndex = newHashMap()
|
||||
|
||||
def void initialize(URI baseDir, (URI, Iterable<Issue>)=>void acceptor) {
|
||||
val uris = newArrayList
|
||||
this.baseDir = baseDir
|
||||
this.issueAcceptor = acceptor
|
||||
this.fileSystemScanner.scan(baseDir)[uris += it]
|
||||
doBuild(uris, emptyList)
|
||||
// TODO support multi-projects
|
||||
// We will need to figure out how we can identify project structure, dependencies, source folders, etc...
|
||||
val projectManager = projectManagerProvider.get
|
||||
val indexResult = projectManager.initialize(baseDir, acceptor, openedDocumentsContentProvider, [fullIndex])
|
||||
baseDir2ProjectManager.put(baseDir, projectManager)
|
||||
fullIndex.put("DEFAULT", indexResult.indexState.resourceDescriptions)
|
||||
}
|
||||
|
||||
def didChangeWatchedFiles(DidChangeWatchedFilesParams fileChanges) {
|
||||
|
@ -66,14 +71,42 @@ class WorkspaceManager {
|
|||
}
|
||||
doBuild(dirtyFiles, deletedFiles)
|
||||
}
|
||||
|
||||
|
||||
def void doBuild(List<URI> dirtyFiles, List<URI> deletedFiles) {
|
||||
val result = incrementalBuilder.build(newBuildRequest(dirtyFiles, deletedFiles), [
|
||||
languagesRegistry.getResourceServiceProvider(it)
|
||||
])
|
||||
indexState = result.indexState
|
||||
//TODO sort projects by dependency
|
||||
val allDirty = new ArrayList(dirtyFiles)
|
||||
for (entry : baseDir2ProjectManager.entrySet) {
|
||||
val result = entry.value.doBuild(allDirty.filter[isPrefix(entry.key)].toList, deletedFiles.filter[isPrefix(entry.key)].toList)
|
||||
allDirty.addAll(result.affectedResources.map[uri])
|
||||
}
|
||||
}
|
||||
|
||||
def URI getProjectBaseDir(URI candidate) {
|
||||
for (projectBaseDir : baseDir2ProjectManager.keySet.sortBy[-toString.length]) {
|
||||
if (isPrefix(candidate, projectBaseDir)) {
|
||||
return projectBaseDir
|
||||
}
|
||||
}
|
||||
return baseDir
|
||||
}
|
||||
|
||||
protected def boolean isPrefix(URI candidate, URI prefix) {
|
||||
candidate.toString.startsWith(prefix.toString)
|
||||
}
|
||||
|
||||
def ProjectManager getProjectManager(URI uri) {
|
||||
val projectBaseDir = getProjectBaseDir(uri)
|
||||
return baseDir2ProjectManager.get(projectBaseDir)
|
||||
}
|
||||
|
||||
def URI toUri(String path) {
|
||||
URI.createURI(baseDir.toString + path)
|
||||
}
|
||||
|
||||
def toPath(URI uri) {
|
||||
uri.toString.substring(baseDir.toString.length)
|
||||
}
|
||||
|
||||
def didChange(DidChangeTextDocumentParams changeEvent) {
|
||||
val uri = toUri(changeEvent.textDocument.uri)
|
||||
val contents = openDocuments.get(uri)
|
||||
|
@ -87,14 +120,6 @@ class WorkspaceManager {
|
|||
doBuild(#[uri], newArrayList)
|
||||
}
|
||||
|
||||
def URI toUri(String path) {
|
||||
URI.createURI(baseDir.toString + path)
|
||||
}
|
||||
|
||||
def toPath(org.eclipse.emf.common.util.URI uri) {
|
||||
uri.toString.substring(baseDir.toString.length)
|
||||
}
|
||||
|
||||
def didClose(DidCloseTextDocumentParams changeEvent) {
|
||||
val uri = toUri(changeEvent.textDocument.uri)
|
||||
openDocuments.remove(uri)
|
||||
|
@ -105,46 +130,4 @@ class WorkspaceManager {
|
|||
// do nothing for now
|
||||
}
|
||||
|
||||
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)
|
||||
externalContentSupport.configureResourceSet(it, new IExternalContentProvider() {
|
||||
|
||||
override getActualContentProvider() {
|
||||
return this
|
||||
}
|
||||
|
||||
override getContent(URI uri) {
|
||||
openDocuments.get(uri)?.contents
|
||||
}
|
||||
|
||||
override hasContent(URI uri) {
|
||||
openDocuments.containsKey(uri)
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue