mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 00:38:56 +00:00
[lsi] Implemented list workspace symbols feature
Change-Id: I34346c5ac5dd73b2f6f12a1e34a1eb0260d216d6 Signed-off-by: akosyakov <anton.kosyakov@typefox.io>
This commit is contained in:
parent
6e16febf9b
commit
206b0d4eaf
6 changed files with 258 additions and 27 deletions
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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»
|
||||
}
|
||||
'''
|
||||
|
||||
}
|
||||
|
|
|
@ -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»
|
||||
}
|
||||
'''
|
||||
|
||||
}
|
||||
|
|
|
@ -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 = ''
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue