[lsi] Implemented list workspace symbols feature

Change-Id: I34346c5ac5dd73b2f6f12a1e34a1eb0260d216d6
Signed-off-by: akosyakov <anton.kosyakov@typefox.io>
This commit is contained in:
akosyakov 2016-06-01 09:06:41 +02:00
parent 6e16febf9b
commit 206b0d4eaf
6 changed files with 258 additions and 27 deletions

View file

@ -61,6 +61,7 @@ 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
import org.eclipse.xtext.ide.server.symbol.WorkspaceSymbolService
import org.eclipse.xtext.resource.IResourceServiceProvider
import org.eclipse.xtext.validation.Issue
@ -78,6 +79,9 @@ import org.eclipse.xtext.ide.editor.syntaxcoloring.IEditorHighlightingConfigurat
@Inject
RequestManager requestManager
@Inject
WorkspaceSymbolService workspaceSymbolService
InitializeParams params
@Inject Provider<WorkspaceManager> workspaceManagerProvider
@ -98,6 +102,7 @@ import org.eclipse.xtext.ide.editor.syntaxcoloring.IEditorHighlightingConfigurat
definitionProvider = true
referencesProvider = true
documentSymbolProvider = true
workspaceSymbolProvider = true
textDocumentSync = ServerCapabilities.SYNC_INCREMENTAL
completionProvider = new CompletionOptionsImpl => [
resolveProvider = false
@ -247,7 +252,7 @@ import org.eclipse.xtext.ide.editor.syntaxcoloring.IEditorHighlightingConfigurat
val result = new CompletionListImpl
if (contentAssistService === null)
return result
val entries = workspaceManager.doRead(uri) [ document, resource |
val offset = document.getOffSet(params.position)
return contentAssistService.createProposals(document.contents, offset, resource, cancelIndicator)
@ -321,11 +326,15 @@ import org.eclipse.xtext.ide.editor.syntaxcoloring.IEditorHighlightingConfigurat
]
}
// end symbols
override symbol(WorkspaceSymbolParams params) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
return requestManager.<List<? extends SymbolInformation>>runRead [ cancelIndicator |
val indexData = workspaceManager.index
return workspaceSymbolService.getSymbols(params.query, resourceAccess, indexData, cancelIndicator)
]
}
// end symbols
override didChangeConfiguraton(DidChangeConfigurationParams params) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}

View file

@ -14,6 +14,8 @@ import io.typefox.lsapi.Location
import io.typefox.lsapi.SymbolInformation
import io.typefox.lsapi.SymbolInformationImpl
import java.util.List
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.findReferences.IReferenceFinder
import org.eclipse.xtext.findReferences.IReferenceFinder.IResourceAccess
@ -23,7 +25,10 @@ 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.naming.QualifiedName
import org.eclipse.xtext.resource.EObjectAtOffsetHelper
import org.eclipse.xtext.resource.IEObjectDescription
import org.eclipse.xtext.resource.IResourceDescription
import org.eclipse.xtext.resource.IResourceDescriptions
import org.eclipse.xtext.resource.IResourceServiceProvider
import org.eclipse.xtext.resource.XtextResource
@ -55,7 +60,7 @@ class DocumentSymbolService {
@Inject
Provider<TargetURIs> targetURIProvider
@Inject
OperationCanceledManager operationCanceledManager
@ -77,11 +82,8 @@ class DocumentSymbolService {
for (targetURI : targetURIs) {
operationCanceledManager.checkCanceled(cancelIndicator)
resourceAccess.readOnly(targetURI) [ resourceSet |
val object = resourceSet.getEObject(targetURI, true)
if (object !== null)
locations += object.newLocation
return null
resourceAccess.doRead(targetURI) [ obj |
locations += obj.newLocation
]
}
return locations
@ -105,11 +107,8 @@ class DocumentSymbolService {
resourceAccess,
indexData,
new ReferenceAcceptor(resourceServiceProviderRegistry) [ reference |
resourceAccess.readOnly(reference.sourceEObjectUri) [ resourceSet |
val targetObject = resourceSet.getEObject(reference.sourceEObjectUri, true)
if (targetObject !== null)
locations += targetObject.newLocation(reference.EReference, reference.indexInList)
return null
resourceAccess.doRead(reference.sourceEObjectUri) [ obj |
locations += obj.newLocation(reference.EReference, reference.indexInList)
]
],
new CancelIndicatorProgressMonitor(cancelIndicator)
@ -129,7 +128,7 @@ class DocumentSymbolService {
while (contents.hasNext) {
operationCanceledManager.checkCanceled(cancelIndicator)
val obj = contents.next
val obj = contents.next as EObject
val symbol = obj.createSymbol
if (symbol !== null) {
symbols.put(obj, symbol)
@ -158,11 +157,74 @@ class DocumentSymbolService {
}
protected def String getSymbolName(EObject object) {
return object.fullyQualifiedName?.toString
return object.fullyQualifiedName.symbolName
}
protected def int getSymbolKind(EObject object) {
return object.eClass.symbolKind
}
def List<? extends SymbolInformation> getSymbols(
IResourceDescription resourceDescription,
String query,
IResourceAccess resourceAccess,
CancelIndicator cancelIndicator
) {
val symbols = newLinkedList
for (description : resourceDescription.exportedObjects) {
operationCanceledManager.checkCanceled(cancelIndicator)
if (description.filter(query)) {
val symbol = description.createSymbol
if (symbol !== null) {
symbols += symbol
resourceAccess.doRead(description.EObjectURI) [ obj |
symbol.location = obj.newLocation
]
}
}
}
return symbols
}
protected def boolean filter(IEObjectDescription description, String query) {
return description.qualifiedName.toLowerCase.toString.contains(query.toLowerCase)
}
protected def SymbolInformationImpl createSymbol(IEObjectDescription description) {
val symbolName = description.symbolName
if(symbolName === null) return null
val symbol = new SymbolInformationImpl
symbol.name = symbolName
symbol.kind = description.symbolKind
return symbol
}
protected def String getSymbolName(IEObjectDescription description) {
return description.qualifiedName.symbolName
}
protected def int getSymbolKind(IEObjectDescription description) {
return description.EClass.symbolKind
}
protected def String getSymbolName(QualifiedName qualifiedName) {
return qualifiedName?.toString
}
protected def int getSymbolKind(EClass type) {
return 0
// return SymbolInformation.KIND_PROPERTY
}
protected def void doRead(IResourceAccess resourceAccess, URI objectURI, (EObject)=>void acceptor) {
resourceAccess.readOnly(objectURI) [ resourceSet |
val object = resourceSet.getEObject(objectURI, true)
if (object !== null) {
acceptor.apply(object)
}
return 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.server.symbol
import com.google.inject.Inject
import com.google.inject.Singleton
import io.typefox.lsapi.SymbolInformation
import java.util.List
import org.eclipse.xtext.findReferences.IReferenceFinder.IResourceAccess
import org.eclipse.xtext.resource.IResourceDescriptions
import org.eclipse.xtext.resource.IResourceServiceProvider
import org.eclipse.xtext.service.OperationCanceledManager
import org.eclipse.xtext.util.CancelIndicator
/**
* @author kosyakov - Initial contribution and API
*/
@Singleton
class WorkspaceSymbolService {
@Inject
extension IResourceServiceProvider.Registry
@Inject
OperationCanceledManager operationCanceledManager
def List<? extends SymbolInformation> getSymbols(
String query,
IResourceAccess resourceAccess,
IResourceDescriptions indexData,
CancelIndicator cancelIndicator
) {
val result = newLinkedList
for (resourceDescription : indexData.allResourceDescriptions) {
operationCanceledManager.checkCanceled(cancelIndicator)
val resourceServiceProvider = resourceDescription.URI.resourceServiceProvider
val documentSymbolService = resourceServiceProvider?.get(DocumentSymbolService)
if (documentSymbolService !== null) {
result += documentSymbolService.getSymbols(resourceDescription, query, resourceAccess, cancelIndicator)
}
}
return result
}
}

View file

@ -17,6 +17,7 @@ import io.typefox.lsapi.Location
import io.typefox.lsapi.Position
import io.typefox.lsapi.PublishDiagnosticsParams
import io.typefox.lsapi.Range
import io.typefox.lsapi.SymbolInformation
import java.io.File
import java.io.FileWriter
import java.net.URI
@ -137,4 +138,14 @@ class AbstractLanguageServerTest implements Consumer<PublishDiagnosticsParams> {
protected def dispatch String toExpectation(Position it) '''[«line», «character»]'''
protected def dispatch String toExpectation(SymbolInformation it) '''
symbol "«name»" {
kind: «kind»
location: «location.toExpectation»
«IF !container.nullOrEmpty»
container: "«container»"
«ENDIF»
}
'''
}

View file

@ -7,7 +7,6 @@
*******************************************************************************/
package org.eclipse.xtext.ide.tests.server
import io.typefox.lsapi.SymbolInformation
import org.eclipse.xtend.lib.annotations.Accessors
import org.junit.Test
@ -79,14 +78,4 @@ class DocumentSymbolTest extends AbstractLanguageServerTest {
String expectedSymbols = ''
}
protected def dispatch String toExpectation(SymbolInformation it) '''
symbol "«name»" {
kind: «kind»
location: «location.toExpectation»
«IF !container.nullOrEmpty»
container: "«container»"
«ENDIF»
}
'''
}

View file

@ -0,0 +1,110 @@
/*******************************************************************************
* 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 org.eclipse.xtend.lib.annotations.Accessors
import org.junit.Test
import static org.junit.Assert.*
import static extension io.typefox.lsapi.util.LsapiFactories.*
/**
* @author kosyakov - Initial contribution and API
*/
class WorkspaceSymbolTest extends AbstractLanguageServerTest {
@Test
def void testDocumentSymbol_01() {
testDocumentSymbol[
model = '''
type Foo {
int bar
}
type Bar {
Foo foo
}
'''
query = 'F'
expectedSymbols = '''
symbol "Foo" {
kind: 0
location: MyModel.testlang [[0, 5] .. [0, 8]]
}
symbol "Foo.bar" {
kind: 0
location: MyModel.testlang [[1, 5] .. [1, 8]]
}
symbol "Foo.bar.int" {
kind: 0
location: MyModel.testlang [[1, 1] .. [1, 4]]
}
symbol "Bar.foo" {
kind: 0
location: MyModel.testlang [[4, 5] .. [4, 8]]
}
'''
]
}
@Test
def void testDocumentSymbol_02() {
testDocumentSymbol[
model = '''
type Foo {
int bar
}
type Bar {
Foo foo
}
'''
query = 'oO'
expectedSymbols = '''
symbol "Foo" {
kind: 0
location: MyModel.testlang [[0, 5] .. [0, 8]]
}
symbol "Foo.bar" {
kind: 0
location: MyModel.testlang [[1, 5] .. [1, 8]]
}
symbol "Foo.bar.int" {
kind: 0
location: MyModel.testlang [[1, 1] .. [1, 4]]
}
symbol "Bar.foo" {
kind: 0
location: MyModel.testlang [[4, 5] .. [4, 8]]
}
'''
]
}
protected def void testDocumentSymbol((WorkspaceSymbolConfiguraiton)=>void configurator) {
val extension configuration = new WorkspaceSymbolConfiguraiton
configurator.apply(configuration)
val fileUri = filePath -> model
initialize
open(fileUri, model)
val symbols = languageServer.symbol(query.newWorkspaceSymbolParams).get
val String actualSymbols = symbols.toExpectation
assertEquals(expectedSymbols, actualSymbols)
}
@Accessors
static class WorkspaceSymbolConfiguraiton {
String model = ''
String filePath = 'MyModel.testlang'
String query = ''
String expectedSymbols = ''
}
}