[lsi] Support of (cancellable) read/write requests

Change-Id: If294db6305bf836f0f7d75e47683e39730a975c0
Signed-off-by: akosyakov <anton.kosyakov@typefox.io>
This commit is contained in:
akosyakov 2016-05-27 15:19:35 +02:00
parent 9d3ab92088
commit 4193f4aaae
13 changed files with 413 additions and 123 deletions

View file

@ -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

View file

@ -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

View file

@ -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
]
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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() {

View file

@ -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) {

View file

@ -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() {
}
}

View file

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

View file

@ -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()