mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 00:38:56 +00:00
[lsi] Support of (cancellable) read/write requests
Change-Id: If294db6305bf836f0f7d75e47683e39730a975c0 Signed-off-by: akosyakov <anton.kosyakov@typefox.io>
This commit is contained in:
parent
9d3ab92088
commit
4193f4aaae
13 changed files with 413 additions and 123 deletions
|
@ -27,4 +27,8 @@ Export-Package: org.eclipse.xtext.ide;x-friends:="org.eclipse.xtend.ide",
|
|||
org.eclipse.xtext.ide.editor.partialEditing,
|
||||
org.eclipse.xtext.ide.editor.syntaxcoloring,
|
||||
org.eclipse.xtext.ide.labels;x-friends:="org.eclipse.xtext.web",
|
||||
org.eclipse.xtext.ide.server
|
||||
org.eclipse.xtext.ide.server,
|
||||
org.eclipse.xtext.ide.server.concurrent,
|
||||
org.eclipse.xtext.ide.server.contentassist,
|
||||
org.eclipse.xtext.ide.server.findReferences,
|
||||
org.eclipse.xtext.ide.server.symbol
|
||||
|
|
|
@ -32,6 +32,7 @@ import io.typefox.lsapi.InitializeParams
|
|||
import io.typefox.lsapi.InitializeResult
|
||||
import io.typefox.lsapi.InitializeResultImpl
|
||||
import io.typefox.lsapi.LanguageServer
|
||||
import io.typefox.lsapi.Location
|
||||
import io.typefox.lsapi.MessageParams
|
||||
import io.typefox.lsapi.NotificationCallback
|
||||
import io.typefox.lsapi.PublishDiagnosticsParams
|
||||
|
@ -42,6 +43,7 @@ import io.typefox.lsapi.RenameParams
|
|||
import io.typefox.lsapi.ServerCapabilities
|
||||
import io.typefox.lsapi.ServerCapabilitiesImpl
|
||||
import io.typefox.lsapi.ShowMessageRequestParams
|
||||
import io.typefox.lsapi.SymbolInformation
|
||||
import io.typefox.lsapi.TextDocumentPositionParams
|
||||
import io.typefox.lsapi.TextDocumentService
|
||||
import io.typefox.lsapi.WindowService
|
||||
|
@ -51,6 +53,8 @@ import java.util.List
|
|||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.xtend.lib.annotations.Accessors
|
||||
import org.eclipse.xtext.ide.editor.contentassist.ContentAssistEntry
|
||||
import org.eclipse.xtext.ide.server.concurrent.CancellableIndicator
|
||||
import org.eclipse.xtext.ide.server.concurrent.RequestManager
|
||||
import org.eclipse.xtext.ide.server.contentassist.ContentAssistService
|
||||
import org.eclipse.xtext.ide.server.findReferences.WorkspaceResourceAccess
|
||||
import org.eclipse.xtext.ide.server.symbol.DocumentSymbolService
|
||||
|
@ -65,6 +69,9 @@ import static io.typefox.lsapi.util.LsapiFactories.*
|
|||
*/
|
||||
@Accessors class LanguageServerImpl implements LanguageServer, WorkspaceService, WindowService, TextDocumentService {
|
||||
|
||||
@Inject
|
||||
RequestManager requestManager
|
||||
|
||||
InitializeParams params
|
||||
@Inject Provider<WorkspaceManager> workspaceManagerProvider
|
||||
WorkspaceManager workspaceManager
|
||||
|
@ -72,26 +79,31 @@ import static io.typefox.lsapi.util.LsapiFactories.*
|
|||
@Inject extension IResourceServiceProvider.Registry languagesRegistry
|
||||
|
||||
override InitializeResult initialize(InitializeParams params) {
|
||||
if (params.rootPath === null) {
|
||||
throw new IllegalArgumentException("Bad initialization request. rootPath must not be null.")
|
||||
}
|
||||
this.params = params
|
||||
workspaceManager = workspaceManagerProvider.get
|
||||
if (params.rootPath === null) {
|
||||
throw new IllegalArgumentException("Bad initialization request. rootPath must not be null.")
|
||||
}
|
||||
val rootURI = URI.createFileURI(params.rootPath)
|
||||
resourceAccess = new WorkspaceResourceAccess(workspaceManager)
|
||||
workspaceManager.initialize(rootURI)[this.publishDiagnostics($0, $1)]
|
||||
return new InitializeResultImpl => [
|
||||
capabilities = new ServerCapabilitiesImpl => [
|
||||
definitionProvider = true
|
||||
referencesProvider = true
|
||||
documentSymbolProvider = true
|
||||
textDocumentSync = ServerCapabilities.SYNC_INCREMENTAL
|
||||
completionProvider = new CompletionOptionsImpl => [
|
||||
resolveProvider = false
|
||||
triggerCharacters = #["."]
|
||||
]
|
||||
|
||||
val result = new InitializeResultImpl
|
||||
result.capabilities = new ServerCapabilitiesImpl => [
|
||||
definitionProvider = true
|
||||
referencesProvider = true
|
||||
documentSymbolProvider = true
|
||||
textDocumentSync = ServerCapabilities.SYNC_INCREMENTAL
|
||||
completionProvider = new CompletionOptionsImpl => [
|
||||
resolveProvider = false
|
||||
triggerCharacters = #["."]
|
||||
]
|
||||
]
|
||||
|
||||
requestManager.runWrite([ cancelIndicator |
|
||||
val rootURI = URI.createFileURI(params.rootPath)
|
||||
workspaceManager.initialize(rootURI, [this.publishDiagnostics($0, $1)], cancelIndicator)
|
||||
], CancellableIndicator.NullImpl)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override exit() {
|
||||
|
@ -128,34 +140,44 @@ import static io.typefox.lsapi.util.LsapiFactories.*
|
|||
// end notification callbacks
|
||||
// file/content change events
|
||||
override didOpen(DidOpenTextDocumentParams params) {
|
||||
workspaceManager.didOpen(params.textDocument.uri.toUri, params.textDocument.version, params.textDocument.text)
|
||||
requestManager.runWrite [ cancelIndicator |
|
||||
workspaceManager.didOpen(params.textDocument.uri.toUri, params.textDocument.version, params.textDocument.text, cancelIndicator)
|
||||
]
|
||||
}
|
||||
|
||||
override didChange(DidChangeTextDocumentParams params) {
|
||||
workspaceManager.didChange(params.textDocument.uri.toUri, params.textDocument.version, params.contentChanges.map [ event |
|
||||
newTextEdit(event.range as RangeImpl, event.text)
|
||||
])
|
||||
requestManager.runWrite [ cancelIndicator |
|
||||
workspaceManager.didChange(params.textDocument.uri.toUri, params.textDocument.version, params.contentChanges.map [ event |
|
||||
newTextEdit(event.range as RangeImpl, event.text)
|
||||
], cancelIndicator)
|
||||
]
|
||||
}
|
||||
|
||||
override didClose(DidCloseTextDocumentParams params) {
|
||||
workspaceManager.didClose(params.textDocument.uri.toUri)
|
||||
requestManager.runWrite [ cancelIndicator |
|
||||
workspaceManager.didClose(params.textDocument.uri.toUri, cancelIndicator)
|
||||
]
|
||||
}
|
||||
|
||||
override didSave(DidSaveTextDocumentParams params) {
|
||||
workspaceManager.didSave(params.textDocument.uri.toUri)
|
||||
requestManager.runWrite [ cancelIndicator |
|
||||
workspaceManager.didSave(params.textDocument.uri.toUri, cancelIndicator)
|
||||
]
|
||||
}
|
||||
|
||||
override didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
|
||||
val dirtyFiles = newArrayList
|
||||
val deletedFiles = newArrayList
|
||||
for (fileEvent : params.changes) {
|
||||
if (fileEvent.type === FileEvent.TYPE_DELETED) {
|
||||
deletedFiles += toUri(fileEvent.uri)
|
||||
} else {
|
||||
dirtyFiles += toUri(fileEvent.uri)
|
||||
requestManager.runWrite [ cancelIndicator |
|
||||
val dirtyFiles = newArrayList
|
||||
val deletedFiles = newArrayList
|
||||
for (fileEvent : params.changes) {
|
||||
if (fileEvent.type === FileEvent.TYPE_DELETED) {
|
||||
deletedFiles += toUri(fileEvent.uri)
|
||||
} else {
|
||||
dirtyFiles += toUri(fileEvent.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
workspaceManager.doBuild(dirtyFiles, deletedFiles)
|
||||
workspaceManager.doBuild(dirtyFiles, deletedFiles, cancelIndicator)
|
||||
]
|
||||
}
|
||||
|
||||
// end file/content change events
|
||||
|
@ -198,17 +220,19 @@ import static io.typefox.lsapi.util.LsapiFactories.*
|
|||
// end validation stuff
|
||||
// completion stuff
|
||||
override completion(TextDocumentPositionParams params) {
|
||||
val uri = params.textDocument.uri.toUri
|
||||
val resourceServiceProvider = uri.resourceServiceProvider
|
||||
val contentAssistService = resourceServiceProvider?.get(ContentAssistService)
|
||||
if (contentAssistService === null)
|
||||
return emptyList
|
||||
|
||||
val entries = workspaceManager.doRead(uri) [ document, resource |
|
||||
val offset = document.getOffSet(params.position)
|
||||
return contentAssistService.createProposals(document.contents, offset, resource)
|
||||
]
|
||||
return entries.map[toCompletionItem].toList
|
||||
return requestManager.runRead[ cancelIndicator |
|
||||
val uri = params.textDocument.uri.toUri
|
||||
val resourceServiceProvider = uri.resourceServiceProvider
|
||||
val contentAssistService = resourceServiceProvider?.get(ContentAssistService)
|
||||
if (contentAssistService === null)
|
||||
return emptyList
|
||||
|
||||
val entries = workspaceManager.doRead(uri) [ document, resource |
|
||||
val offset = document.getOffSet(params.position)
|
||||
return contentAssistService.createProposals(document.contents, offset, resource, cancelIndicator)
|
||||
]
|
||||
return entries.map[toCompletionItem].toList
|
||||
].get
|
||||
}
|
||||
|
||||
protected def CompletionItem toCompletionItem(ContentAssistEntry entry) {
|
||||
|
@ -222,50 +246,57 @@ import static io.typefox.lsapi.util.LsapiFactories.*
|
|||
// end completion stuff
|
||||
// symbols
|
||||
override definition(TextDocumentPositionParams params) {
|
||||
val uri = params.textDocument.uri.toUri
|
||||
val resourceServiceProvider = uri.resourceServiceProvider
|
||||
val documentSymbolService = resourceServiceProvider?.get(DocumentSymbolService)
|
||||
if (documentSymbolService === null)
|
||||
return emptyList
|
||||
|
||||
return workspaceManager.doRead(uri) [ document, resource |
|
||||
val offset = document.getOffSet(params.position)
|
||||
return documentSymbolService.getDefinitions(resource, offset, resourceAccess)
|
||||
]
|
||||
return requestManager.<List<? extends Location>>runRead[ cancelIndicator |
|
||||
val uri = params.textDocument.uri.toUri
|
||||
val resourceServiceProvider = uri.resourceServiceProvider
|
||||
val documentSymbolService = resourceServiceProvider?.get(DocumentSymbolService)
|
||||
if (documentSymbolService === null)
|
||||
return emptyList
|
||||
|
||||
val definitions = workspaceManager.doRead(uri) [ document, resource |
|
||||
val offset = document.getOffSet(params.position)
|
||||
return documentSymbolService.getDefinitions(resource, offset, resourceAccess, cancelIndicator)
|
||||
]
|
||||
return definitions
|
||||
].get
|
||||
}
|
||||
|
||||
override references(ReferenceParams params) {
|
||||
val uri = params.textDocument.uri.toUri
|
||||
val resourceServiceProvider = uri.resourceServiceProvider
|
||||
val documentSymbolService = resourceServiceProvider?.get(DocumentSymbolService)
|
||||
if (documentSymbolService === null)
|
||||
return emptyList
|
||||
|
||||
return workspaceManager.doRead(uri) [ document, resource |
|
||||
val offset = document.getOffSet(params.position)
|
||||
|
||||
val definitions = if (params.context.includeDeclaration)
|
||||
documentSymbolService.getDefinitions(resource, offset, resourceAccess)
|
||||
else
|
||||
emptyList
|
||||
|
||||
val indexData = workspaceManager.index
|
||||
val references = documentSymbolService.getReferences(resource, offset, resourceAccess, indexData)
|
||||
val result = definitions + references
|
||||
return result.toList
|
||||
]
|
||||
return requestManager.<List<? extends Location>>runRead[ cancelIndicator |
|
||||
val uri = params.textDocument.uri.toUri
|
||||
val resourceServiceProvider = uri.resourceServiceProvider
|
||||
val documentSymbolService = resourceServiceProvider?.get(DocumentSymbolService)
|
||||
if (documentSymbolService === null)
|
||||
return emptyList
|
||||
|
||||
return workspaceManager.doRead(uri) [ document, resource |
|
||||
val offset = document.getOffSet(params.position)
|
||||
|
||||
val definitions = if (params.context.includeDeclaration)
|
||||
documentSymbolService.getDefinitions(resource, offset, resourceAccess, cancelIndicator)
|
||||
else
|
||||
emptyList
|
||||
|
||||
val indexData = workspaceManager.index
|
||||
val references = documentSymbolService.getReferences(resource, offset, resourceAccess, indexData, cancelIndicator)
|
||||
val result = definitions + references
|
||||
return result.toList
|
||||
]
|
||||
].get
|
||||
}
|
||||
|
||||
override documentSymbol(DocumentSymbolParams params) {
|
||||
val uri = params.textDocument.uri.toUri
|
||||
val resourceServiceProvider = uri.resourceServiceProvider
|
||||
val documentSymbolService = resourceServiceProvider?.get(DocumentSymbolService)
|
||||
if (documentSymbolService === null)
|
||||
return emptyList
|
||||
|
||||
return workspaceManager.doRead(uri) [ document, resource |
|
||||
return documentSymbolService.getSymbols(resource)
|
||||
]
|
||||
return requestManager.<List<? extends SymbolInformation>>runRead[ cancelIndicator |
|
||||
val uri = params.textDocument.uri.toUri
|
||||
val resourceServiceProvider = uri.resourceServiceProvider
|
||||
val documentSymbolService = resourceServiceProvider?.get(DocumentSymbolService)
|
||||
if (documentSymbolService === null)
|
||||
return emptyList
|
||||
|
||||
return workspaceManager.doRead(uri) [ document, resource |
|
||||
return documentSymbolService.getSymbols(resource, cancelIndicator)
|
||||
]
|
||||
].get
|
||||
}
|
||||
|
||||
// end symbols
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.eclipse.xtext.resource.impl.ProjectDescription
|
|||
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData
|
||||
import org.eclipse.xtext.util.IFileSystemScanner
|
||||
import org.eclipse.xtext.validation.Issue
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
|
@ -49,18 +50,18 @@ class ProjectManager {
|
|||
@Accessors(PUBLIC_GETTER)
|
||||
XtextResourceSet resourceSet
|
||||
|
||||
def Result initialize(URI baseDir, (URI, Iterable<Issue>)=>void acceptor, IExternalContentProvider openedDocumentsContentProvider, Provider<Map<String, ResourceDescriptionsData>> indexProvider) {
|
||||
def Result initialize(URI baseDir, (URI, Iterable<Issue>)=>void acceptor, IExternalContentProvider openedDocumentsContentProvider, Provider<Map<String, ResourceDescriptionsData>> indexProvider, CancelIndicator cancelIndicator) {
|
||||
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)
|
||||
return doBuild(uris, emptyList, cancelIndicator)
|
||||
}
|
||||
|
||||
def Result doBuild(List<URI> dirtyFiles, List<URI> deletedFiles) {
|
||||
val request = newBuildRequest(dirtyFiles, deletedFiles)
|
||||
def Result doBuild(List<URI> dirtyFiles, List<URI> deletedFiles, CancelIndicator cancelIndicator) {
|
||||
val request = newBuildRequest(dirtyFiles, deletedFiles, cancelIndicator)
|
||||
val result = incrementalBuilder.build(request, [
|
||||
languagesRegistry.getResourceServiceProvider(it)
|
||||
])
|
||||
|
@ -69,7 +70,7 @@ class ProjectManager {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected def BuildRequest newBuildRequest(List<URI> changedFiles, List<URI> deletedFiles) {
|
||||
protected def BuildRequest newBuildRequest(List<URI> changedFiles, List<URI> deletedFiles, CancelIndicator cancelIndicator) {
|
||||
new BuildRequest => [
|
||||
it.baseDir = baseDir
|
||||
it.resourceSet = createFreshResourceSet(new ResourceDescriptionsData(emptyList))
|
||||
|
@ -81,6 +82,7 @@ class ProjectManager {
|
|||
issueAcceptor.apply(uri, issues)
|
||||
return true
|
||||
]
|
||||
it.cancelIndicator = cancelIndicator
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
package org.eclipse.xtext.ide.server
|
||||
|
||||
import com.google.inject.AbstractModule
|
||||
import com.google.inject.name.Names
|
||||
import io.typefox.lsapi.LanguageServer
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import org.eclipse.xtext.ide.server.concurrent.RequestManager
|
||||
import org.eclipse.xtext.resource.IResourceServiceProvider
|
||||
import org.eclipse.xtext.resource.ResourceServiceProviderServiceLoader
|
||||
|
||||
|
@ -18,6 +22,12 @@ import org.eclipse.xtext.resource.ResourceServiceProviderServiceLoader
|
|||
class ServerModule extends AbstractModule {
|
||||
|
||||
override protected configure() {
|
||||
val readExecutorService = Executors.newCachedThreadPool
|
||||
bind(ExecutorService).annotatedWith(Names.named(RequestManager.READ_EXECUTOR_SERVICE)).toInstance(readExecutorService)
|
||||
|
||||
val writeExecutorService = Executors.newSingleThreadExecutor
|
||||
bind(ExecutorService).annotatedWith(Names.named(RequestManager.WRITE_EXECUTOR_SERVICE)).toInstance(writeExecutorService)
|
||||
|
||||
bind(LanguageServer).to(LanguageServerImpl)
|
||||
bind(IResourceServiceProvider.Registry).toProvider(ResourceServiceProviderServiceLoader)
|
||||
}
|
||||
|
|
|
@ -11,15 +11,18 @@ import com.google.inject.Inject
|
|||
import com.google.inject.Provider
|
||||
import io.typefox.lsapi.TextEdit
|
||||
import java.util.ArrayList
|
||||
import java.util.Collection
|
||||
import java.util.List
|
||||
import java.util.Map
|
||||
import java.util.Set
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.xtext.resource.IExternalContentSupport.IExternalContentProvider
|
||||
import org.eclipse.xtext.resource.IResourceDescriptions
|
||||
import org.eclipse.xtext.resource.XtextResource
|
||||
import org.eclipse.xtext.resource.impl.ChunkedResourceDescriptions
|
||||
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
import org.eclipse.xtext.validation.Issue
|
||||
import org.eclipse.xtext.resource.IResourceDescriptions
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
|
@ -46,25 +49,43 @@ class WorkspaceManager {
|
|||
}
|
||||
};
|
||||
Map<String, ResourceDescriptionsData> fullIndex = newHashMap()
|
||||
|
||||
val dirtyFiles = <URI>newLinkedHashSet
|
||||
val deletedFiles = <URI>newLinkedHashSet
|
||||
|
||||
protected def void queue(Set<URI> files, Collection<URI> toRemove, Collection<URI> toAdd) {
|
||||
files -= toRemove
|
||||
files += toAdd
|
||||
}
|
||||
|
||||
def void initialize(URI baseDir, (URI, Iterable<Issue>)=>void acceptor) {
|
||||
def void initialize(URI baseDir, (URI, Iterable<Issue>)=>void acceptor, CancelIndicator cancelIndicator) {
|
||||
this.baseDir = baseDir
|
||||
// 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])
|
||||
val indexResult = projectManager.initialize(baseDir, acceptor, openedDocumentsContentProvider, [fullIndex], cancelIndicator)
|
||||
baseDir2ProjectManager.put(baseDir, projectManager)
|
||||
fullIndex.put("DEFAULT", indexResult.indexState.resourceDescriptions)
|
||||
}
|
||||
|
||||
def void doBuild(List<URI> dirtyFiles, List<URI> deletedFiles) {
|
||||
//TODO sort projects by dependency
|
||||
def void doBuild(List<URI> dirtyFiles, List<URI> deletedFiles, CancelIndicator cancelIndicator) {
|
||||
queue(this.dirtyFiles, deletedFiles, dirtyFiles)
|
||||
queue(this.deletedFiles, dirtyFiles, deletedFiles)
|
||||
internalBuild(cancelIndicator)
|
||||
}
|
||||
|
||||
protected def void internalBuild(CancelIndicator cancelIndicator) {
|
||||
//TODO sort projects by dependency
|
||||
val allDirty = new ArrayList(dirtyFiles)
|
||||
for (entry : baseDir2ProjectManager.entrySet) {
|
||||
val projectDirtyFiles = allDirty.filter[isPrefix(entry.key)].toList
|
||||
val projectDirtyFiles = allDirty.filter[isPrefix(entry.key)].toList
|
||||
val projectDeletedFiles = deletedFiles.filter[isPrefix(entry.key)].toList
|
||||
val result = entry.value.doBuild(projectDirtyFiles, projectDeletedFiles)
|
||||
allDirty.addAll(result.affectedResources.map[uri])
|
||||
|
||||
val result = entry.value.doBuild(projectDirtyFiles, projectDeletedFiles, cancelIndicator)
|
||||
allDirty.addAll(result.affectedResources.map[uri])
|
||||
|
||||
this.dirtyFiles -= projectDirtyFiles
|
||||
this.deletedFiles -= projectDeletedFiles
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,23 +111,23 @@ class WorkspaceManager {
|
|||
return baseDir2ProjectManager.get(projectBaseDir)
|
||||
}
|
||||
|
||||
def didChange(URI uri, int version, Iterable<TextEdit> changes) {
|
||||
def didChange(URI uri, int version, Iterable<TextEdit> changes, CancelIndicator cancelIndicator) {
|
||||
val contents = openDocuments.get(uri)
|
||||
openDocuments.put(uri, contents.applyChanges(changes))
|
||||
doBuild(#[uri], newArrayList)
|
||||
doBuild(#[uri], newArrayList, cancelIndicator)
|
||||
}
|
||||
|
||||
def didOpen(URI uri, int version, String contents) {
|
||||
def didOpen(URI uri, int version, String contents, CancelIndicator cancelIndicator) {
|
||||
openDocuments.put(uri, new Document(version, contents))
|
||||
doBuild(#[uri], newArrayList)
|
||||
doBuild(#[uri], newArrayList, cancelIndicator)
|
||||
}
|
||||
|
||||
def didClose(URI uri) {
|
||||
def didClose(URI uri, CancelIndicator cancelIndicator) {
|
||||
openDocuments.remove(uri)
|
||||
doBuild(#[uri], newArrayList)
|
||||
doBuild(#[uri], newArrayList, cancelIndicator)
|
||||
}
|
||||
|
||||
def didSave(URI uri) {
|
||||
def didSave(URI uri, CancelIndicator cancelIndicator) {
|
||||
// do nothing for now
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*******************************************************************************
|
||||
* 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.concurrent
|
||||
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
|
||||
/**
|
||||
* @author kosyakov - Initial contribution and API
|
||||
*/
|
||||
interface CancellableIndicator extends CancelIndicator {
|
||||
def void cancel()
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*******************************************************************************
|
||||
* 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.concurrent
|
||||
|
||||
import org.eclipse.xtend.lib.annotations.Accessors
|
||||
|
||||
/**
|
||||
* @author kosyakov - Initial contribution and API
|
||||
*/
|
||||
class RequestCancelIndicator implements CancellableIndicator {
|
||||
|
||||
@Accessors(PUBLIC_GETTER)
|
||||
boolean canceled
|
||||
|
||||
override cancel() {
|
||||
canceled = true
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*******************************************************************************
|
||||
* 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.concurrent
|
||||
|
||||
import com.google.inject.Inject
|
||||
import com.google.inject.Singleton
|
||||
import com.google.inject.name.Named
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Future
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.Semaphore
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
|
||||
/**
|
||||
* @author kosyakov - Initial contribution and API
|
||||
*/
|
||||
@Singleton
|
||||
class RequestManager {
|
||||
|
||||
public static val READ_EXECUTOR_SERVICE = 'org.eclipse.xtext.ide.server.concurrent.RequestManager.readExecutorService'
|
||||
public static val WRITE_EXECUTOR_SERVICE = 'org.eclipse.xtext.ide.server.concurrent.RequestManager.writeExecutorService'
|
||||
|
||||
val final MAX_PERMITS = Integer.MAX_VALUE
|
||||
|
||||
@Inject
|
||||
@Named(READ_EXECUTOR_SERVICE)
|
||||
ExecutorService readExecutorService
|
||||
|
||||
@Inject
|
||||
@Named(WRITE_EXECUTOR_SERVICE)
|
||||
ExecutorService writeExecutorService
|
||||
|
||||
val cancelIndicators = new LinkedBlockingQueue<CancellableIndicator>
|
||||
|
||||
val semaphore = new Semaphore(MAX_PERMITS)
|
||||
|
||||
def Future<Void> runWrite((CancelIndicator)=>void writeRequest) {
|
||||
runWrite(writeRequest, new RequestCancelIndicator)
|
||||
}
|
||||
|
||||
def Future<Void> runWrite((CancelIndicator)=>void writeRequest, CancelIndicator cancelIndicator) {
|
||||
cancelIndicators.forEach[cancel]
|
||||
writeExecutorService.run([
|
||||
writeRequest.apply(it)
|
||||
return null
|
||||
], MAX_PERMITS, cancelIndicator)
|
||||
}
|
||||
|
||||
def <V> Future<V> runRead((CancelIndicator)=>V readRequest) {
|
||||
runRead(readRequest, new RequestCancelIndicator)
|
||||
}
|
||||
|
||||
def <V> Future<V> runRead((CancelIndicator)=>V readRequest, CancelIndicator cancelIndicator) {
|
||||
readExecutorService.run(readRequest, 1, cancelIndicator)
|
||||
}
|
||||
|
||||
protected def <V> Future<V> run(
|
||||
ExecutorService executorService,
|
||||
(CancelIndicator)=>V request,
|
||||
int permits,
|
||||
CancelIndicator cancelIndicator
|
||||
) {
|
||||
return executorService.submit [
|
||||
semaphore.acquire(permits)
|
||||
try {
|
||||
if (cancelIndicator instanceof CancellableIndicator)
|
||||
cancelIndicators += cancelIndicator
|
||||
|
||||
return request.apply(cancelIndicator)
|
||||
} finally {
|
||||
if (cancelIndicator instanceof CancellableIndicator)
|
||||
cancelIndicators -= cancelIndicator
|
||||
|
||||
semaphore.release(permits)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
}
|
|
@ -18,6 +18,8 @@ import org.eclipse.xtext.ide.editor.contentassist.IdeContentProposalProvider
|
|||
import org.eclipse.xtext.ide.editor.contentassist.antlr.ContentAssistContextFactory
|
||||
import org.eclipse.xtext.resource.XtextResource
|
||||
import org.eclipse.xtext.util.TextRegion
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
import org.eclipse.xtext.service.OperationCanceledManager
|
||||
|
||||
/**
|
||||
* @author kosyakov - Initial contribution and API
|
||||
|
@ -32,23 +34,17 @@ class ContentAssistService {
|
|||
@Inject ExecutorService executorService
|
||||
|
||||
@Inject IdeContentProposalProvider proposalProvider
|
||||
|
||||
@Inject OperationCanceledManager operationCanceledManager
|
||||
|
||||
def Iterable<ContentAssistEntry> createProposals(
|
||||
String document,
|
||||
int caretOffset,
|
||||
XtextResource resource
|
||||
XtextResource resource,
|
||||
CancelIndicator cancelIndicator
|
||||
) {
|
||||
val selection = new TextRegion(caretOffset, 0)
|
||||
return createProposals(document, selection, caretOffset, resource)
|
||||
}
|
||||
|
||||
def Iterable<ContentAssistEntry> createProposals(
|
||||
String document,
|
||||
TextRegion selection,
|
||||
int caretOffset,
|
||||
XtextResource resource
|
||||
) {
|
||||
return createProposals(document, selection, caretOffset, resource, DEFAULT_PROPOSALS_LIMIT)
|
||||
return createProposals(document, selection, caretOffset, resource, cancelIndicator)
|
||||
}
|
||||
|
||||
def Iterable<ContentAssistEntry> createProposals(
|
||||
|
@ -56,7 +52,18 @@ class ContentAssistService {
|
|||
TextRegion selection,
|
||||
int caretOffset,
|
||||
XtextResource resource,
|
||||
int proposalsLimit
|
||||
CancelIndicator cancelIndicator
|
||||
) {
|
||||
return createProposals(document, selection, caretOffset, resource, DEFAULT_PROPOSALS_LIMIT, cancelIndicator)
|
||||
}
|
||||
|
||||
def Iterable<ContentAssistEntry> createProposals(
|
||||
String document,
|
||||
TextRegion selection,
|
||||
int caretOffset,
|
||||
XtextResource resource,
|
||||
int proposalsLimit,
|
||||
CancelIndicator cancelIndicator
|
||||
) {
|
||||
val entries = new TreeSet<Pair<Integer, ContentAssistEntry>> [ p1, p2 |
|
||||
val prioResult = p2.key.compareTo(p1.key)
|
||||
|
@ -74,6 +81,7 @@ class ContentAssistService {
|
|||
throw new IllegalArgumentException('proposal must not be null.')
|
||||
entries.add(priority -> entry)
|
||||
}
|
||||
operationCanceledManager.checkCanceled(cancelIndicator)
|
||||
}
|
||||
|
||||
override canAcceptMoreProposals() {
|
||||
|
|
|
@ -21,11 +21,14 @@ import org.eclipse.xtext.findReferences.ReferenceAcceptor
|
|||
import org.eclipse.xtext.findReferences.TargetURICollector
|
||||
import org.eclipse.xtext.findReferences.TargetURIs
|
||||
import org.eclipse.xtext.ide.server.DocumentExtensions
|
||||
import org.eclipse.xtext.ide.util.CancelIndicatorProgressMonitor
|
||||
import org.eclipse.xtext.naming.IQualifiedNameProvider
|
||||
import org.eclipse.xtext.resource.EObjectAtOffsetHelper
|
||||
import org.eclipse.xtext.resource.IResourceDescriptions
|
||||
import org.eclipse.xtext.resource.IResourceServiceProvider
|
||||
import org.eclipse.xtext.resource.XtextResource
|
||||
import org.eclipse.xtext.service.OperationCanceledManager
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
|
||||
import static extension org.eclipse.emf.ecore.util.EcoreUtil.*
|
||||
|
||||
|
@ -52,11 +55,19 @@ class DocumentSymbolService {
|
|||
|
||||
@Inject
|
||||
Provider<TargetURIs> targetURIProvider
|
||||
|
||||
@Inject
|
||||
OperationCanceledManager operationCanceledManager
|
||||
|
||||
@Inject
|
||||
IResourceServiceProvider.Registry resourceServiceProviderRegistry
|
||||
|
||||
def List<? extends Location> getDefinitions(XtextResource resource, int offset, IResourceAccess resourceAccess) {
|
||||
def List<? extends Location> getDefinitions(
|
||||
XtextResource resource,
|
||||
int offset,
|
||||
IResourceAccess resourceAccess,
|
||||
CancelIndicator cancelIndicator
|
||||
) {
|
||||
val element = resource.resolveElementAt(offset)
|
||||
if (element === null)
|
||||
return emptyList
|
||||
|
@ -64,8 +75,13 @@ class DocumentSymbolService {
|
|||
val locations = newArrayList
|
||||
val targetURIs = element.collectTargetURIs
|
||||
for (targetURI : targetURIs) {
|
||||
operationCanceledManager.checkCanceled(cancelIndicator)
|
||||
|
||||
resourceAccess.readOnly(targetURI) [ resourceSet |
|
||||
locations += resourceSet.getEObject(targetURI, true).newLocation
|
||||
val object = resourceSet.getEObject(targetURI, true)
|
||||
if (object !== null)
|
||||
locations += object.newLocation
|
||||
return null
|
||||
]
|
||||
}
|
||||
return locations
|
||||
|
@ -75,7 +91,8 @@ class DocumentSymbolService {
|
|||
XtextResource resource,
|
||||
int offset,
|
||||
IResourceAccess resourceAccess,
|
||||
IResourceDescriptions indexData
|
||||
IResourceDescriptions indexData,
|
||||
CancelIndicator cancelIndicator
|
||||
) {
|
||||
val element = resource.resolveElementAt(offset)
|
||||
if (element === null)
|
||||
|
@ -90,12 +107,13 @@ class DocumentSymbolService {
|
|||
new ReferenceAcceptor(resourceServiceProviderRegistry) [ reference |
|
||||
resourceAccess.readOnly(reference.sourceEObjectUri) [ resourceSet |
|
||||
val targetObject = resourceSet.getEObject(reference.sourceEObjectUri, true)
|
||||
locations += targetObject.newLocation(reference.EReference, reference.indexInList)
|
||||
if (targetObject !== null)
|
||||
locations += targetObject.newLocation(reference.EReference, reference.indexInList)
|
||||
return null
|
||||
]
|
||||
],
|
||||
null
|
||||
new CancelIndicatorProgressMonitor(cancelIndicator)
|
||||
)
|
||||
|
||||
return locations
|
||||
}
|
||||
|
||||
|
@ -105,10 +123,12 @@ class DocumentSymbolService {
|
|||
return targetURIs
|
||||
}
|
||||
|
||||
def List<? extends SymbolInformation> getSymbols(XtextResource resource) {
|
||||
def List<? extends SymbolInformation> getSymbols(XtextResource resource, CancelIndicator cancelIndicator) {
|
||||
val symbols = newLinkedHashMap
|
||||
val contents = resource.getAllProperContents(true)
|
||||
while (contents.hasNext) {
|
||||
operationCanceledManager.checkCanceled(cancelIndicator)
|
||||
|
||||
val obj = contents.next
|
||||
val symbol = obj.createSymbol
|
||||
if (symbol !== null) {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*******************************************************************************
|
||||
* 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.util
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor
|
||||
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
|
||||
/**
|
||||
* @author kosyakov - Initial contribution and API
|
||||
*/
|
||||
@FinalFieldsConstructor
|
||||
class CancelIndicatorProgressMonitor implements IProgressMonitor {
|
||||
|
||||
val CancelIndicator delegate
|
||||
|
||||
boolean canceled
|
||||
|
||||
override isCanceled() {
|
||||
canceled || delegate.isCanceled
|
||||
}
|
||||
|
||||
override setCanceled(boolean value) {
|
||||
canceled = value
|
||||
}
|
||||
|
||||
override beginTask(String name, int totalWork) {
|
||||
}
|
||||
|
||||
override setTaskName(String name) {
|
||||
}
|
||||
|
||||
override subTask(String name) {
|
||||
}
|
||||
|
||||
override internalWorked(double work) {
|
||||
}
|
||||
|
||||
override worked(int work) {
|
||||
}
|
||||
|
||||
override done() {
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.ide.tests.server
|
||||
|
||||
import com.google.inject.AbstractModule
|
||||
import com.google.inject.Guice
|
||||
import com.google.inject.Inject
|
||||
import io.typefox.lsapi.Diagnostic
|
||||
|
@ -28,13 +29,18 @@ import java.nio.file.Path
|
|||
import java.nio.file.Paths
|
||||
import java.util.List
|
||||
import java.util.Map
|
||||
import java.util.concurrent.ExecutorService
|
||||
import org.eclipse.xtext.ide.server.LanguageServerImpl
|
||||
import org.eclipse.xtext.ide.server.ServerModule
|
||||
import org.eclipse.xtext.ide.server.UriExtensions
|
||||
import org.eclipse.xtext.ide.server.concurrent.RequestManager
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
import org.eclipse.xtext.util.Files
|
||||
import org.eclipse.xtext.util.Modules2
|
||||
import org.junit.Before
|
||||
|
||||
import static io.typefox.lsapi.util.LsapiFactories.*
|
||||
import com.google.common.util.concurrent.Futures
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
|
@ -43,7 +49,20 @@ class AbstractLanguageServerTest implements NotificationCallback<PublishDiagnost
|
|||
|
||||
@Before
|
||||
def void setup() {
|
||||
val injector = Guice.createInjector(new ServerModule())
|
||||
val module = Modules2.mixin(new ServerModule, new AbstractModule() {
|
||||
|
||||
override protected configure() {
|
||||
bind(RequestManager).toInstance(new RequestManager() {
|
||||
|
||||
override protected <V> run(ExecutorService executorService, (CancelIndicator)=>V request, int permits, CancelIndicator cancelIndicator) {
|
||||
Futures.immediateCheckedFuture(request.apply(cancelIndicator))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
val injector = Guice.createInjector(module)
|
||||
injector.injectMembers(this)
|
||||
// register notification callbacks
|
||||
languageServer.getTextDocumentService.onPublishDiagnostics(this)
|
||||
|
|
|
@ -34,7 +34,7 @@ class WorkspaceManagerTest {
|
|||
}
|
||||
'''
|
||||
|
||||
workspaceManger.doBuild(#[path], emptyList)
|
||||
workspaceManger.doBuild(#[path], emptyList, null)
|
||||
|
||||
val String inMemContents = '''
|
||||
type Test {
|
||||
|
@ -42,7 +42,7 @@ class WorkspaceManagerTest {
|
|||
}
|
||||
'''
|
||||
|
||||
workspaceManger.didOpen(path, 1, inMemContents)
|
||||
workspaceManger.didOpen(path, 1, inMemContents, null)
|
||||
|
||||
Assert.assertEquals(inMemContents, workspaceManger.doRead(path, [$0.contents]))
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class WorkspaceManagerTest {
|
|||
Files.cleanFolder(root, null, true, false)
|
||||
}
|
||||
root.deleteOnExit
|
||||
workspaceManger.initialize(URI.createFileURI(root.absolutePath), [diagnostics.put($0, $1.toList)])
|
||||
workspaceManger.initialize(URI.createFileURI(root.absolutePath), [diagnostics.put($0, $1.toList)], null)
|
||||
}
|
||||
|
||||
protected Map<URI, List<Issue>> diagnostics = newHashMap()
|
||||
|
|
Loading…
Reference in a new issue