diff --git a/org.eclipse.xtext.ide.tests/src/org/eclipse/xtext/ide/tests/server/CodeLensTest.xtend b/org.eclipse.xtext.ide.tests/src/org/eclipse/xtext/ide/tests/server/CodeLensTest.xtend new file mode 100644 index 000000000..2449ff338 --- /dev/null +++ b/org.eclipse.xtext.ide.tests/src/org/eclipse/xtext/ide/tests/server/CodeLensTest.xtend @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2017 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.lsp4j.Position +import org.junit.Assert +import org.junit.Test + +/** + * @author Sven Efftinge - Initial contribution and API + */ +class CodeLensTest extends AbstractTestLangLanguageServerTest { + + @Test def void testCodeLens() { + testCodeLens [ + model = ''' + type Foo {} + type Bar { + Foo foo + } + ''' + assertCodeLenses = [ + assertEquals("Do Awesome Stuff(RESOLVED)", head.command.title) + Assert.assertEquals(1, (head.data as Position).line) + ] + ] + } +} \ No newline at end of file diff --git a/org.eclipse.xtext.ide.tests/testlang-src/org/eclipse/xtext/ide/tests/testlanguage/ide/TestLanguageIdeModule.xtend b/org.eclipse.xtext.ide.tests/testlang-src/org/eclipse/xtext/ide/tests/testlanguage/ide/TestLanguageIdeModule.xtend index 7f5adb8dd..d53b091e7 100644 --- a/org.eclipse.xtext.ide.tests/testlang-src/org/eclipse/xtext/ide/tests/testlanguage/ide/TestLanguageIdeModule.xtend +++ b/org.eclipse.xtext.ide.tests/testlang-src/org/eclipse/xtext/ide/tests/testlanguage/ide/TestLanguageIdeModule.xtend @@ -8,6 +8,9 @@ package org.eclipse.xtext.ide.tests.testlanguage.ide import org.eclipse.xtext.ide.server.ILanguageServerExtension +import org.eclipse.xtext.ide.server.codelens.ICodeLensResolver +import org.eclipse.xtext.ide.tests.testlanguage.scoping.CodeLensProvider +import org.eclipse.xtext.ide.server.codelens.ICodeLensService /** * Use this class to register ide components. @@ -18,4 +21,12 @@ class TestLanguageIdeModule extends AbstractTestLanguageIdeModule { TestLangLSPExtension } + def Class bindICodeLensResolver() { + CodeLensProvider + } + + def Class bindICodeLensService() { + CodeLensProvider + } + } diff --git a/org.eclipse.xtext.ide.tests/testlang-src/org/eclipse/xtext/ide/tests/testlanguage/scoping/CodeLensProvider.xtend b/org.eclipse.xtext.ide.tests/testlang-src/org/eclipse/xtext/ide/tests/testlanguage/scoping/CodeLensProvider.xtend new file mode 100644 index 000000000..40df1633c --- /dev/null +++ b/org.eclipse.xtext.ide.tests/testlang-src/org/eclipse/xtext/ide/tests/testlanguage/scoping/CodeLensProvider.xtend @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2017 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.testlanguage.scoping + +import org.eclipse.xtext.ide.server.codelens.ICodeLensService +import org.eclipse.xtext.ide.server.codelens.ICodeLensResolver +import org.eclipse.xtext.ide.server.Document +import org.eclipse.xtext.resource.XtextResource +import org.eclipse.lsp4j.CodeLensParams +import org.eclipse.xtext.util.CancelIndicator +import org.eclipse.lsp4j.CodeLens +import org.eclipse.lsp4j.Command +import org.eclipse.lsp4j.Position + +/** + * @author Sven Efftinge - Initial contribution and API + */ +class CodeLensProvider implements ICodeLensService, ICodeLensResolver { + + override computeCodeLenses(Document document, XtextResource resource, CodeLensParams params, CancelIndicator indicator) { + return #[new CodeLens() => [ + command = new Command() => [ + command = "do.this" + title = "Do Awesome Stuff" + arguments = #[ + 'foo', + 1, + true + ] + ] + data = new Position(1,2) + ]] + } + + override resolveCodeLens(Document document, XtextResource resource, CodeLens codeLens, CancelIndicator indicator) { + codeLens.command.title = codeLens.command.title + "(RESOLVED)" + return codeLens + } + +} \ No newline at end of file diff --git a/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/server/CodeLensTest.java b/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/server/CodeLensTest.java new file mode 100644 index 000000000..9a1869c3d --- /dev/null +++ b/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/server/CodeLensTest.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2017 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 java.util.List; +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.Position; +import org.eclipse.xtend2.lib.StringConcatenation; +import org.eclipse.xtext.ide.tests.server.AbstractTestLangLanguageServerTest; +import org.eclipse.xtext.testing.AbstractLanguageServerTest; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Sven Efftinge - Initial contribution and API + */ +@SuppressWarnings("all") +public class CodeLensTest extends AbstractTestLangLanguageServerTest { + @Test + public void testCodeLens() { + final Procedure1 _function = (AbstractLanguageServerTest.TestCodeLensConfiguration it) -> { + StringConcatenation _builder = new StringConcatenation(); + _builder.append("type Foo {}"); + _builder.newLine(); + _builder.append("type Bar {"); + _builder.newLine(); + _builder.append("\t"); + _builder.append("Foo foo"); + _builder.newLine(); + _builder.append("}"); + _builder.newLine(); + it.setModel(_builder.toString()); + final Procedure1> _function_1 = (List it_1) -> { + this.assertEquals("Do Awesome Stuff(RESOLVED)", IterableExtensions.head(it_1).getCommand().getTitle()); + Object _data = IterableExtensions.head(it_1).getData(); + Assert.assertEquals(1, ((Position) _data).getLine()); + }; + it.setAssertCodeLenses(_function_1); + }; + this.testCodeLens(_function); + } +} diff --git a/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/testlanguage/ide/TestLanguageIdeModule.java b/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/testlanguage/ide/TestLanguageIdeModule.java index 01b1f3408..a0930eecb 100644 --- a/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/testlanguage/ide/TestLanguageIdeModule.java +++ b/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/testlanguage/ide/TestLanguageIdeModule.java @@ -8,8 +8,11 @@ package org.eclipse.xtext.ide.tests.testlanguage.ide; import org.eclipse.xtext.ide.server.ILanguageServerExtension; +import org.eclipse.xtext.ide.server.codelens.ICodeLensResolver; +import org.eclipse.xtext.ide.server.codelens.ICodeLensService; import org.eclipse.xtext.ide.tests.testlanguage.ide.AbstractTestLanguageIdeModule; import org.eclipse.xtext.ide.tests.testlanguage.ide.TestLangLSPExtension; +import org.eclipse.xtext.ide.tests.testlanguage.scoping.CodeLensProvider; /** * Use this class to register ide components. @@ -19,4 +22,12 @@ public class TestLanguageIdeModule extends AbstractTestLanguageIdeModule { public Class bindLanguageServerExtension() { return TestLangLSPExtension.class; } + + public Class bindICodeLensResolver() { + return CodeLensProvider.class; + } + + public Class bindICodeLensService() { + return CodeLensProvider.class; + } } diff --git a/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/testlanguage/scoping/CodeLensProvider.java b/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/testlanguage/scoping/CodeLensProvider.java new file mode 100644 index 000000000..6218e8de8 --- /dev/null +++ b/org.eclipse.xtext.ide.tests/xtend-gen/org/eclipse/xtext/ide/tests/testlanguage/scoping/CodeLensProvider.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2017 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.testlanguage.scoping; + +import java.util.Collections; +import java.util.List; +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.CodeLensParams; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.Position; +import org.eclipse.xtext.ide.server.Document; +import org.eclipse.xtext.ide.server.codelens.ICodeLensResolver; +import org.eclipse.xtext.ide.server.codelens.ICodeLensService; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.CancelIndicator; +import org.eclipse.xtext.xbase.lib.CollectionLiterals; +import org.eclipse.xtext.xbase.lib.ObjectExtensions; +import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; + +/** + * @author Sven Efftinge - Initial contribution and API + */ +@SuppressWarnings("all") +public class CodeLensProvider implements ICodeLensService, ICodeLensResolver { + @Override + public List computeCodeLenses(final Document document, final XtextResource resource, final CodeLensParams params, final CancelIndicator indicator) { + CodeLens _codeLens = new CodeLens(); + final Procedure1 _function = (CodeLens it) -> { + Command _command = new Command(); + final Procedure1 _function_1 = (Command it_1) -> { + it_1.setCommand("do.this"); + it_1.setTitle("Do Awesome Stuff"); + it_1.setArguments(Collections.unmodifiableList(CollectionLiterals.newArrayList("foo", Integer.valueOf(1), Boolean.valueOf(true)))); + }; + Command _doubleArrow = ObjectExtensions.operator_doubleArrow(_command, _function_1); + it.setCommand(_doubleArrow); + Position _position = new Position(1, 2); + it.setData(_position); + }; + CodeLens _doubleArrow = ObjectExtensions.operator_doubleArrow(_codeLens, _function); + return Collections.unmodifiableList(CollectionLiterals.newArrayList(_doubleArrow)); + } + + @Override + public CodeLens resolveCodeLens(final Document document, final XtextResource resource, final CodeLens codeLens, final CancelIndicator indicator) { + Command _command = codeLens.getCommand(); + String _title = codeLens.getCommand().getTitle(); + String _plus = (_title + "(RESOLVED)"); + _command.setTitle(_plus); + return codeLens; + } +} diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/ICapabilitiesContributor.xtend b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/ICapabilitiesContributor.xtend new file mode 100644 index 000000000..abf923caa --- /dev/null +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/ICapabilitiesContributor.xtend @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.ide.server + +import org.eclipse.lsp4j.ServerCapabilities +import org.eclipse.lsp4j.InitializeParams + +/** + * @author Sven Efftinge - Initial contribution and API + */ +interface ICapabilitiesContributor { + + /** + * Allows an individual language to contribute to and overwrite properties in the server's capabilities. + */ + def ServerCapabilities contribute(ServerCapabilities capabilities, InitializeParams params); +} \ No newline at end of file diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/LanguageServerImpl.xtend b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/LanguageServerImpl.xtend index d0d38b970..91dcc7a1c 100644 --- a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/LanguageServerImpl.xtend +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/LanguageServerImpl.xtend @@ -17,6 +17,7 @@ import java.util.function.Function import org.eclipse.emf.common.util.URI import org.eclipse.lsp4j.CodeActionParams import org.eclipse.lsp4j.CodeLens +import org.eclipse.lsp4j.CodeLensOptions import org.eclipse.lsp4j.CodeLensParams import org.eclipse.lsp4j.ColoringParams import org.eclipse.lsp4j.CompletionItem @@ -63,12 +64,14 @@ import org.eclipse.lsp4j.services.TextDocumentService import org.eclipse.lsp4j.services.WorkspaceService import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor import org.eclipse.xtext.ide.server.ILanguageServerAccess.IBuildListener +import org.eclipse.xtext.ide.server.codelens.ICodeLensResolver +import org.eclipse.xtext.ide.server.codelens.ICodeLensService import org.eclipse.xtext.ide.server.coloring.IColoringService 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.formatting.FormattingService -import org.eclipse.xtext.ide.server.hover.HoverService +import org.eclipse.xtext.ide.server.hover.IHoverService import org.eclipse.xtext.ide.server.occurrences.IDocumentHighlightService import org.eclipse.xtext.ide.server.signatureHelp.ISignatureHelpService import org.eclipse.xtext.ide.server.symbol.DocumentSymbolService @@ -100,6 +103,13 @@ import org.eclipse.xtext.validation.Issue this.workspaceManager = manager resourceAccess = new WorkspaceResourceAccess(workspaceManager) } + + private def Iterable getAllLanguages() { + this.languagesRegistry.extensionToFactoryMap.keySet.toList.sort.map[ext| + val synthUri = URI.createURI("synth:///file."+ext) + return synthUri.resourceServiceProvider + ] + } override CompletableFuture initialize(InitializeParams params) { if (this.params !== null) { @@ -111,13 +121,20 @@ import org.eclipse.xtext.validation.Issue } this.params = params val result = new InitializeResult - result.capabilities = new ServerCapabilities => [ + var capabilities = new ServerCapabilities => [ hoverProvider = true definitionProvider = true referencesProvider = true documentSymbolProvider = true workspaceSymbolProvider = true - //TODO make this language specific + + // check if a language with code lens capability exists + if (allLanguages.exists[get(ICodeLensService)!==null]) { + codeLensProvider = new CodeLensOptions => [ + resolveProvider = allLanguages.exists[get(ICodeLensResolver)!==null] + ] + } + signatureHelpProvider = new SignatureHelpOptions(#['(', ',']) textDocumentSync = TextDocumentSyncKind.Incremental completionProvider = new CompletionOptions => [ @@ -128,7 +145,11 @@ import org.eclipse.xtext.validation.Issue documentRangeFormattingProvider = true documentHighlightProvider = true ] - + for (language : allLanguages) { + language.get(ICapabilitiesContributor)?.contribute(capabilities, params) + } + result.capabilities = capabilities + requestManager.lockWrite [ workspaceManager.initialize(baseDir, [this.publishDiagnostics($0, $1)], CancelIndicator.NullImpl) return null @@ -362,9 +383,9 @@ import org.eclipse.xtext.validation.Issue return requestManager.runRead[ cancelIndicator | val uri = params.textDocument.uri.toUri val resourceServiceProvider = uri.resourceServiceProvider - val hoverService = resourceServiceProvider?.get(HoverService) + val hoverService = resourceServiceProvider?.get(IHoverService) if (hoverService === null) - return HoverService.EMPTY_HOVER + return IHoverService.EMPTY_HOVER return workspaceManager.doRead(uri) [ document, resource | hoverService.hover(document, resource, params, cancelIndicator) @@ -409,13 +430,62 @@ import org.eclipse.xtext.validation.Issue override codeAction(CodeActionParams params) { throw new UnsupportedOperationException("TODO: auto-generated method stub") } - + + private def void installURI(List codeLenses, String uri) { + for (lens : codeLenses) { + if (lens.data !== null) { + lens.data = newArrayList(uri, lens.data) + } else { + lens.data = uri + } + } + } + + private def URI uninstallURI(CodeLens lens) { + var URI result = null + if (lens.data instanceof String) { + result = URI.createURI(lens.data.toString) + lens.data = null + } else if (lens.data instanceof List) { + val l = lens.data as List + result = URI.createURI(l.head.toString) + lens.data = l.get(1) + } + return result + } + override codeLens(CodeLensParams params) { - throw new UnsupportedOperationException("TODO: auto-generated method stub") + return requestManager.runRead[ cancelIndicator | + val uri = params.textDocument.uri.toUri + val resourceServiceProvider = uri.resourceServiceProvider + val codeLensService = resourceServiceProvider?.get(ICodeLensService) + if (codeLensService === null) + return emptyList + + return workspaceManager.doRead(uri) [ document, resource | + val result = codeLensService.computeCodeLenses(document, resource, params, cancelIndicator) + installURI(result, uri.toString) + return result + ] + ] } override resolveCodeLens(CodeLens unresolved) { - return CompletableFuture.completedFuture(unresolved) + val uri = uninstallURI(unresolved) + if (uri === null) { + return CompletableFuture.completedFuture(unresolved) + } + return requestManager.runRead[ cancelIndicator | + val resourceServiceProvider = uri.resourceServiceProvider + val resolver = resourceServiceProvider?.get(ICodeLensResolver) + if (resolver === null) + return unresolved + + return workspaceManager.doRead(uri) [ document, resource | + val result = resolver.resolveCodeLens(document, resource, unresolved, cancelIndicator) + return result + ] + ] } override formatting(DocumentFormattingParams params) { diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/WorkspaceManager.xtend b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/WorkspaceManager.xtend index 353b34a5e..e80f3f506 100644 --- a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/WorkspaceManager.xtend +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/WorkspaceManager.xtend @@ -38,96 +38,98 @@ import org.eclipse.xtext.workspace.IWorkspaceConfig * @since 2.11 */ @Log class WorkspaceManager { - - @Inject Provider projectManagerProvider - @Inject IWorkspaceConfigFactory workspaceConfigFactory - @Inject IProjectDescriptionFactory projectDescriptionFactory - BuildManager buildManager - - Map projectName2ProjectManager = newHashMap - URI baseDir - (URI, Iterable)=>void issueAcceptor - IWorkspaceConfig _workspaceConfig - - List buildListeners = newArrayList - - def void addBuildListener(IBuildListener listener) { - this.buildListeners += listener - } - - Map fullIndex = newHashMap() - - Map openDocuments = newHashMap() - - val openedDocumentsContentProvider = new IExternalContentProvider() { + @Inject Provider projectManagerProvider + @Inject IWorkspaceConfigFactory workspaceConfigFactory + @Inject IProjectDescriptionFactory projectDescriptionFactory + BuildManager buildManager - override getActualContentProvider() { - return this - } + Map projectName2ProjectManager = newHashMap - override getContent(URI uri) { - openDocuments.get(uri)?.contents - } + URI baseDir + (URI, Iterable)=>void issueAcceptor + IWorkspaceConfig _workspaceConfig - override hasContent(URI uri) { - openDocuments.containsKey(uri) - } - }; - - @Inject - def void setBuildManager(BuildManager buildManager) { - buildManager.workspaceManager = this - this.buildManager = buildManager - } - - def void initialize(URI baseDir, (URI, Iterable)=>void issueAcceptor, CancelIndicator cancelIndicator) { - this.baseDir = baseDir - this.issueAcceptor = issueAcceptor - refreshWorkspaceConfig(cancelIndicator) - } - - protected def void refreshWorkspaceConfig(CancelIndicator cancelIndicator) { - workspaceConfig = workspaceConfigFactory.getWorkspaceConfig(baseDir) - val newProjects = newArrayList - val Set remainingProjectNames = new HashSet(projectName2ProjectManager.keySet) - workspaceConfig.projects.forEach [ projectConfig | - if(projectName2ProjectManager.containsKey(projectConfig.name)) { - remainingProjectNames.remove(projectConfig.name) - } else { - val projectManager = projectManagerProvider.get - val projectDescription = projectDescriptionFactory.getProjectDescription(projectConfig) - projectManager.initialize(projectDescription, projectConfig, issueAcceptor, openedDocumentsContentProvider, [fullIndex], cancelIndicator) - projectName2ProjectManager.put(projectDescription.name, projectManager) - newProjects.add(projectDescription) - } - ] - for(deletedProject: remainingProjectNames) { - projectName2ProjectManager.remove(deletedProject) - fullIndex.remove(deletedProject) - } - val result = buildManager.doInitialBuild(newProjects, cancelIndicator) - afterBuild(result) - } - - protected def IWorkspaceConfig getWorkspaceConfig() { - if (_workspaceConfig === null) { - val error = new ResponseError(ResponseErrorCode.serverNotInitialized, "Workspace has not been initialized yet.", null) - throw new ResponseErrorException(error) + List buildListeners = newArrayList + + def void addBuildListener(IBuildListener listener) { + this.buildListeners += listener + } + + Map fullIndex = newHashMap() + + Map openDocuments = newHashMap() + + val openedDocumentsContentProvider = new IExternalContentProvider() { + + override getActualContentProvider() { + return this + } + + override getContent(URI uri) { + openDocuments.get(uri)?.contents + } + + override hasContent(URI uri) { + openDocuments.containsKey(uri) + } + }; + + @Inject + def void setBuildManager(BuildManager buildManager) { + buildManager.workspaceManager = this + this.buildManager = buildManager + } + + def void initialize(URI baseDir, (URI, Iterable)=>void issueAcceptor, CancelIndicator cancelIndicator) { + this.baseDir = baseDir + this.issueAcceptor = issueAcceptor + refreshWorkspaceConfig(cancelIndicator) + } + + protected def void refreshWorkspaceConfig(CancelIndicator cancelIndicator) { + workspaceConfig = workspaceConfigFactory.getWorkspaceConfig(baseDir) + val newProjects = newArrayList + val Set remainingProjectNames = new HashSet(projectName2ProjectManager.keySet) + workspaceConfig.projects.forEach [ projectConfig | + if (projectName2ProjectManager.containsKey(projectConfig.name)) { + remainingProjectNames.remove(projectConfig.name) + } else { + val projectManager = projectManagerProvider.get + val projectDescription = projectDescriptionFactory.getProjectDescription(projectConfig) + projectManager.initialize(projectDescription, projectConfig, issueAcceptor, + openedDocumentsContentProvider, [fullIndex], cancelIndicator) + projectName2ProjectManager.put(projectDescription.name, projectManager) + newProjects.add(projectDescription) + } + ] + for (deletedProject : remainingProjectNames) { + projectName2ProjectManager.remove(deletedProject) + fullIndex.remove(deletedProject) + } + val result = buildManager.doInitialBuild(newProjects, cancelIndicator) + afterBuild(result) + } + + protected def IWorkspaceConfig getWorkspaceConfig() { + if (_workspaceConfig === null) { + val error = new ResponseError(ResponseErrorCode.serverNotInitialized, + "Workspace has not been initialized yet.", null) + throw new ResponseErrorException(error) } return _workspaceConfig - } - - protected def void setWorkspaceConfig(IWorkspaceConfig workspaceConfig) { - this._workspaceConfig = workspaceConfig - } - + } + + protected def void setWorkspaceConfig(IWorkspaceConfig workspaceConfig) { + this._workspaceConfig = workspaceConfig + } + protected def void afterBuild(List deltas) { for (listener : buildListeners) { listener.afterBuild(deltas) } } - + def Buildable didChangeFiles(List dirtyFiles, List deletedFiles) { val buildable = buildManager.submit(dirtyFiles, deletedFiles) return [ cancelIndicator | @@ -137,90 +139,90 @@ import org.eclipse.xtext.workspace.IWorkspaceConfig ] } - def List doBuild(List dirtyFiles, List deletedFiles, CancelIndicator cancelIndicator) { - return didChangeFiles(dirtyFiles, deletedFiles).build(cancelIndicator) - } - - def IResourceDescriptions getIndex() { - return new ChunkedResourceDescriptions(fullIndex) - } + def List doBuild(List dirtyFiles, List deletedFiles, + CancelIndicator cancelIndicator) { + return didChangeFiles(dirtyFiles, deletedFiles).build(cancelIndicator) + } - def URI getProjectBaseDir(URI uri) { - val projectConfig = workspaceConfig.findProjectContaining(uri) - return projectConfig?.path - } - - def ProjectManager getProjectManager(URI uri) { - val projectConfig = workspaceConfig.findProjectContaining(uri) - return projectName2ProjectManager.get(projectConfig?.name) - } - - def ProjectManager getProjectManager(String projectName) { - projectName2ProjectManager.get(projectName) - } - - def List getProjectManagers() { - new ArrayList(projectName2ProjectManager.values) - } + def IResourceDescriptions getIndex() { + return new ChunkedResourceDescriptions(fullIndex) + } - def didChange(URI uri, int version, Iterable changes, CancelIndicator cancelIndicator) { - didChange(uri, version, changes).build(cancelIndicator) - } + def URI getProjectBaseDir(URI uri) { + val projectConfig = workspaceConfig.findProjectContaining(uri) + return projectConfig?.path + } - def Buildable didChange(URI uri, int version, Iterable changes) { - if (!openDocuments.containsKey(uri)) { - LOG.error("The document "+uri+" has not been opened.") - return []; - } - val contents = openDocuments.get(uri) - openDocuments.put(uri, contents.applyChanges(changes)) - return didChangeFiles(#[uri], newArrayList) - } - - def didOpen(URI uri, int version, String contents, CancelIndicator cancelIndicator) { - didOpen(uri, version, contents).build(cancelIndicator) - } - - def Buildable didOpen(URI uri, int version, String contents) { - openDocuments.put(uri, new Document(version, contents)) - return didChangeFiles(#[uri], newArrayList) - } - - def didClose(URI uri, CancelIndicator cancelIndicator) { - didClose(uri).build(cancelIndicator) - } - - def Buildable didClose(URI uri) { - openDocuments.remove(uri) - if (exists(uri)) { - return didChangeFiles(#[uri], newArrayList) - } - return didChangeFiles(newArrayList, #[uri]) - } - - protected def boolean exists(URI uri) { - val rs = getProjectManager(uri)?.resourceSet - if (rs === null) - return false - return rs.URIConverter.exists(uri, null) - } - - def T doRead(URI uri, (Document, XtextResource)=>T work) { - val resourceURI = uri.trimFragment - val projectMnr = getProjectManager(resourceURI) - val resource = projectMnr?.getResource(resourceURI) as XtextResource - if (resource === null) { - return work.apply(null, null) - } - var doc = getDocument(resource) - return work.apply(doc, projectMnr.getResource(resourceURI) as XtextResource) - } - - protected def Document getDocument(XtextResource resource) { - return openDocuments.get(resource.URI) - // lets create a transient document, in case a document is not open (e.g. formatting is called just by uri) - ?: new Document(1, resource.parseResult.rootNode.text) - } + def ProjectManager getProjectManager(URI uri) { + val projectConfig = workspaceConfig.findProjectContaining(uri) + return projectName2ProjectManager.get(projectConfig?.name) + } + + def ProjectManager getProjectManager(String projectName) { + projectName2ProjectManager.get(projectName) + } + + def List getProjectManagers() { + new ArrayList(projectName2ProjectManager.values) + } + + def didChange(URI uri, int version, Iterable changes, CancelIndicator cancelIndicator) { + didChange(uri, version, changes).build(cancelIndicator) + } + + def Buildable didChange(URI uri, int version, Iterable changes) { + if (!openDocuments.containsKey(uri)) { + LOG.error("The document " + uri + " has not been opened.") + return []; + } + val contents = openDocuments.get(uri) + openDocuments.put(uri, contents.applyChanges(changes)) + return didChangeFiles(#[uri], newArrayList) + } + + def didOpen(URI uri, int version, String contents, CancelIndicator cancelIndicator) { + didOpen(uri, version, contents).build(cancelIndicator) + } + + def Buildable didOpen(URI uri, int version, String contents) { + openDocuments.put(uri, new Document(version, contents)) + return didChangeFiles(#[uri], newArrayList) + } + + def didClose(URI uri, CancelIndicator cancelIndicator) { + didClose(uri).build(cancelIndicator) + } + + def Buildable didClose(URI uri) { + openDocuments.remove(uri) + if (exists(uri)) { + return didChangeFiles(#[uri], newArrayList) + } + return didChangeFiles(newArrayList, #[uri]) + } + + protected def boolean exists(URI uri) { + val rs = getProjectManager(uri)?.resourceSet + if (rs === null) + return false + return rs.URIConverter.exists(uri, null) + } + + def T doRead(URI uri, (Document, XtextResource)=>T work) { + val resourceURI = uri.trimFragment + val projectMnr = getProjectManager(resourceURI) + val resource = projectMnr?.getResource(resourceURI) as XtextResource + if (resource === null) { + return work.apply(null, null) + } + var doc = getDocument(resource) + return work.apply(doc, projectMnr.getResource(resourceURI) as XtextResource) + } + + protected def Document getDocument(XtextResource resource) { + return openDocuments.get(resource.URI) // lets create a transient document, in case a document is not open (e.g. formatting is called just by uri) + ?: new Document(1, resource.parseResult.rootNode.text) + } public def boolean isDocumentOpen(URI uri) { return openDocuments.containsKey(uri) diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/codelens/ICodeLensResolver.xtend b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/codelens/ICodeLensResolver.xtend new file mode 100644 index 000000000..0398105b4 --- /dev/null +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/codelens/ICodeLensResolver.xtend @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2017 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.codelens + +import org.eclipse.lsp4j.CodeLens +import org.eclipse.xtext.ide.server.Document +import org.eclipse.xtext.resource.XtextResource +import org.eclipse.xtext.util.CancelIndicator + +/** + * @author Sven Efftinge - Initial contribution and API + */ +interface ICodeLensResolver { + + /** + * Resolve the given code lens. + */ + def CodeLens resolveCodeLens(Document document, XtextResource resource, CodeLens codeLens, CancelIndicator indicator); +} \ No newline at end of file diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/codelens/ICodeLensService.xtend b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/codelens/ICodeLensService.xtend new file mode 100644 index 000000000..5ecd02422 --- /dev/null +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/codelens/ICodeLensService.xtend @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2017 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.codelens + +import java.util.List +import org.eclipse.lsp4j.CodeLens +import org.eclipse.lsp4j.CodeLensParams +import org.eclipse.xtext.ide.server.Document +import org.eclipse.xtext.resource.XtextResource +import org.eclipse.xtext.util.CancelIndicator + +/** + * @author Sven Efftinge - Initial contribution and API + */ +interface ICodeLensService { + + /** + * compute code lenses for the given context. + */ + def List computeCodeLenses(Document document, XtextResource resource, CodeLensParams params, CancelIndicator indicator); + +} \ No newline at end of file diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/hover/HoverService.xtend b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/hover/HoverService.xtend index 77bb9263f..1af37a620 100644 --- a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/hover/HoverService.xtend +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/hover/HoverService.xtend @@ -36,9 +36,7 @@ import static extension org.eclipse.xtext.nodemodel.util.NodeModelUtils.* * @since 2.11 */ @Singleton -class HoverService { - - public static val EMPTY_HOVER = new Hover(emptyList, null) +class HoverService implements IHoverService { @Inject extension DocumentExtensions @@ -52,7 +50,7 @@ class HoverService { @Inject extension IEObjectDocumentationProvider - def Hover hover( + override Hover hover( Document document, XtextResource resource, TextDocumentPositionParams params, diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/hover/IHoverService.xtend b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/hover/IHoverService.xtend new file mode 100644 index 000000000..1b134a2b0 --- /dev/null +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/hover/IHoverService.xtend @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2017 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.hover + +import org.eclipse.lsp4j.Hover +import org.eclipse.lsp4j.TextDocumentPositionParams +import org.eclipse.xtext.ide.server.Document +import org.eclipse.xtext.resource.XtextResource +import org.eclipse.xtext.util.CancelIndicator +import com.google.inject.ImplementedBy + +/** + * @author Sven Efftinge - Initial contribution and API + */ +@ImplementedBy(HoverService) +interface IHoverService { + + public static val EMPTY_HOVER = new Hover(emptyList, null) + + /** + * callback for 'textDocument/hover' requests. + */ + def Hover hover(Document document, XtextResource resource, TextDocumentPositionParams params, CancelIndicator cancelIndicator); +} diff --git a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/ICapabilitiesContributor.java b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/ICapabilitiesContributor.java new file mode 100644 index 000000000..6529dad67 --- /dev/null +++ b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/ICapabilitiesContributor.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.xtext.ide.server; + +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.ServerCapabilities; + +/** + * @author Sven Efftinge - Initial contribution and API + */ +@SuppressWarnings("all") +public interface ICapabilitiesContributor { + /** + * Allows an individual language to contribute to and overwrite properties in the server's capabilities. + */ + public abstract ServerCapabilities contribute(final ServerCapabilities capabilities, final InitializeParams params); +} diff --git a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/LanguageServerImpl.java b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/LanguageServerImpl.java index d1701b517..d6e376eb4 100644 --- a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/LanguageServerImpl.java +++ b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/LanguageServerImpl.java @@ -27,6 +27,7 @@ import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.lsp4j.CodeActionParams; import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.CodeLensOptions; import org.eclipse.lsp4j.CodeLensParams; import org.eclipse.lsp4j.ColoringInformation; import org.eclipse.lsp4j.ColoringParams; @@ -83,16 +84,19 @@ import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor; import org.eclipse.xtext.diagnostics.Severity; import org.eclipse.xtext.ide.server.BuildManager; import org.eclipse.xtext.ide.server.Document; +import org.eclipse.xtext.ide.server.ICapabilitiesContributor; import org.eclipse.xtext.ide.server.ILanguageServerAccess; import org.eclipse.xtext.ide.server.ILanguageServerExtension; import org.eclipse.xtext.ide.server.UriExtensions; import org.eclipse.xtext.ide.server.WorkspaceManager; +import org.eclipse.xtext.ide.server.codelens.ICodeLensResolver; +import org.eclipse.xtext.ide.server.codelens.ICodeLensService; import org.eclipse.xtext.ide.server.coloring.IColoringService; 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.formatting.FormattingService; -import org.eclipse.xtext.ide.server.hover.HoverService; +import org.eclipse.xtext.ide.server.hover.IHoverService; import org.eclipse.xtext.ide.server.occurrences.IDocumentHighlightService; import org.eclipse.xtext.ide.server.signatureHelp.ISignatureHelpService; import org.eclipse.xtext.ide.server.symbol.DocumentSymbolService; @@ -169,6 +173,14 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex this.resourceAccess = _workspaceResourceAccess; } + private Iterable getAllLanguages() { + final Function1 _function = (String ext) -> { + final URI synthUri = URI.createURI(("synth:///file." + ext)); + return this.languagesRegistry.getResourceServiceProvider(synthUri); + }; + return ListExtensions.map(IterableExtensions.sort(IterableExtensions.toList(this.languagesRegistry.getExtensionToFactoryMap().keySet())), _function); + } + @Override public CompletableFuture initialize(final InitializeParams params) { if ((this.params != null)) { @@ -188,22 +200,46 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex it.setReferencesProvider(Boolean.valueOf(true)); it.setDocumentSymbolProvider(Boolean.valueOf(true)); it.setWorkspaceSymbolProvider(Boolean.valueOf(true)); + final Function1 _function_1 = (IResourceServiceProvider it_1) -> { + ICodeLensService _get = it_1.get(ICodeLensService.class); + return Boolean.valueOf((_get != null)); + }; + boolean _exists = IterableExtensions.exists(this.getAllLanguages(), _function_1); + if (_exists) { + CodeLensOptions _codeLensOptions = new CodeLensOptions(); + final Procedure1 _function_2 = (CodeLensOptions it_1) -> { + final Function1 _function_3 = (IResourceServiceProvider it_2) -> { + ICodeLensResolver _get = it_2.get(ICodeLensResolver.class); + return Boolean.valueOf((_get != null)); + }; + it_1.setResolveProvider(IterableExtensions.exists(this.getAllLanguages(), _function_3)); + }; + CodeLensOptions _doubleArrow = ObjectExtensions.operator_doubleArrow(_codeLensOptions, _function_2); + it.setCodeLensProvider(_doubleArrow); + } SignatureHelpOptions _signatureHelpOptions = new SignatureHelpOptions(Collections.unmodifiableList(CollectionLiterals.newArrayList("(", ","))); it.setSignatureHelpProvider(_signatureHelpOptions); it.setTextDocumentSync(TextDocumentSyncKind.Incremental); CompletionOptions _completionOptions = new CompletionOptions(); - final Procedure1 _function_1 = (CompletionOptions it_1) -> { + final Procedure1 _function_3 = (CompletionOptions it_1) -> { it_1.setResolveProvider(Boolean.valueOf(false)); it_1.setTriggerCharacters(Collections.unmodifiableList(CollectionLiterals.newArrayList("."))); }; - CompletionOptions _doubleArrow = ObjectExtensions.operator_doubleArrow(_completionOptions, _function_1); - it.setCompletionProvider(_doubleArrow); + CompletionOptions _doubleArrow_1 = ObjectExtensions.operator_doubleArrow(_completionOptions, _function_3); + it.setCompletionProvider(_doubleArrow_1); it.setDocumentFormattingProvider(Boolean.valueOf(true)); it.setDocumentRangeFormattingProvider(Boolean.valueOf(true)); it.setDocumentHighlightProvider(Boolean.valueOf(true)); }; - ServerCapabilities _doubleArrow = ObjectExtensions.operator_doubleArrow(_serverCapabilities, _function); - result.setCapabilities(_doubleArrow); + ServerCapabilities capabilities = ObjectExtensions.operator_doubleArrow(_serverCapabilities, _function); + Iterable _allLanguages = this.getAllLanguages(); + for (final IResourceServiceProvider language : _allLanguages) { + ICapabilitiesContributor _get = language.get(ICapabilitiesContributor.class); + if (_get!=null) { + _get.contribute(capabilities, params); + } + } + result.setCapabilities(capabilities); final Function0 _function_1 = () -> { final Procedure2> _function_2 = (URI $0, Iterable $1) -> { this.publishDiagnostics($0, $1); @@ -537,13 +573,13 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex final Function1 _function = (CancelIndicator cancelIndicator) -> { final URI uri = this._uriExtensions.toUri(params.getTextDocument().getUri()); final IResourceServiceProvider resourceServiceProvider = this.languagesRegistry.getResourceServiceProvider(uri); - HoverService _get = null; + IHoverService _get = null; if (resourceServiceProvider!=null) { - _get=resourceServiceProvider.get(HoverService.class); + _get=resourceServiceProvider.get(IHoverService.class); } - final HoverService hoverService = _get; + final IHoverService hoverService = _get; if ((hoverService == null)) { - return HoverService.EMPTY_HOVER; + return IHoverService.EMPTY_HOVER; } final Function2 _function_1 = (Document document, XtextResource resource) -> { return hoverService.hover(document, resource, params, cancelIndicator); @@ -605,14 +641,82 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex throw new UnsupportedOperationException("TODO: auto-generated method stub"); } + private void installURI(final List codeLenses, final String uri) { + for (final CodeLens lens : codeLenses) { + Object _data = lens.getData(); + boolean _tripleNotEquals = (_data != null); + if (_tripleNotEquals) { + lens.setData(CollectionLiterals.newArrayList(uri, lens.getData())); + } else { + lens.setData(uri); + } + } + } + + private URI uninstallURI(final CodeLens lens) { + URI result = null; + Object _data = lens.getData(); + if ((_data instanceof String)) { + result = URI.createURI(lens.getData().toString()); + lens.setData(null); + } else { + Object _data_1 = lens.getData(); + if ((_data_1 instanceof List)) { + Object _data_2 = lens.getData(); + final List l = ((List) _data_2); + result = URI.createURI(IterableExtensions.head(l).toString()); + lens.setData(l.get(1)); + } + } + return result; + } + @Override public CompletableFuture> codeLens(final CodeLensParams params) { - throw new UnsupportedOperationException("TODO: auto-generated method stub"); + final Function1> _function = (CancelIndicator cancelIndicator) -> { + final URI uri = this._uriExtensions.toUri(params.getTextDocument().getUri()); + final IResourceServiceProvider resourceServiceProvider = this.languagesRegistry.getResourceServiceProvider(uri); + ICodeLensService _get = null; + if (resourceServiceProvider!=null) { + _get=resourceServiceProvider.get(ICodeLensService.class); + } + final ICodeLensService codeLensService = _get; + if ((codeLensService == null)) { + return CollectionLiterals.emptyList(); + } + final Function2> _function_1 = (Document document, XtextResource resource) -> { + final List result = codeLensService.computeCodeLenses(document, resource, params, cancelIndicator); + this.installURI(result, uri.toString()); + return result; + }; + return this.workspaceManager.>doRead(uri, _function_1); + }; + return this.requestManager.>runRead(_function); } @Override public CompletableFuture resolveCodeLens(final CodeLens unresolved) { - return CompletableFuture.completedFuture(unresolved); + final URI uri = this.uninstallURI(unresolved); + if ((uri == null)) { + return CompletableFuture.completedFuture(unresolved); + } + final Function1 _function = (CancelIndicator cancelIndicator) -> { + final IResourceServiceProvider resourceServiceProvider = this.languagesRegistry.getResourceServiceProvider(uri); + ICodeLensResolver _get = null; + if (resourceServiceProvider!=null) { + _get=resourceServiceProvider.get(ICodeLensResolver.class); + } + final ICodeLensResolver resolver = _get; + if ((resolver == null)) { + return unresolved; + } + final Function2 _function_1 = (Document document, XtextResource resource) -> { + final CodeLens result = resolver.resolveCodeLens(document, resource, unresolved, cancelIndicator); + return result; + }; + return this.workspaceManager.doRead(uri, _function_1); + }; + return this.requestManager.runRead(_function); } @Override diff --git a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/WorkspaceManager.java b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/WorkspaceManager.java index 7cf39b4dc..92684647f 100644 --- a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/WorkspaceManager.java +++ b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/WorkspaceManager.java @@ -132,7 +132,8 @@ public class WorkspaceManager { final Provider> _function_1 = () -> { return this.fullIndex; }; - projectManager.initialize(projectDescription, projectConfig, this.issueAcceptor, this.openedDocumentsContentProvider, _function_1, cancelIndicator); + projectManager.initialize(projectDescription, projectConfig, this.issueAcceptor, + this.openedDocumentsContentProvider, _function_1, cancelIndicator); this.projectName2ProjectManager.put(projectDescription.getName(), projectManager); newProjects.add(projectDescription); } @@ -150,7 +151,8 @@ public class WorkspaceManager { protected IWorkspaceConfig getWorkspaceConfig() { if ((this._workspaceConfig == null)) { - final ResponseError error = new ResponseError(ResponseErrorCode.serverNotInitialized, "Workspace has not been initialized yet.", null); + final ResponseError error = new ResponseError(ResponseErrorCode.serverNotInitialized, + "Workspace has not been initialized yet.", null); throw new ResponseErrorException(error); } return this._workspaceConfig; diff --git a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/codelens/ICodeLensResolver.java b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/codelens/ICodeLensResolver.java new file mode 100644 index 000000000..7b9aea39c --- /dev/null +++ b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/codelens/ICodeLensResolver.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2017 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.codelens; + +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.xtext.ide.server.Document; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.CancelIndicator; + +/** + * @author Sven Efftinge - Initial contribution and API + */ +@SuppressWarnings("all") +public interface ICodeLensResolver { + /** + * Resolve the given code lens. + */ + public abstract CodeLens resolveCodeLens(final Document document, final XtextResource resource, final CodeLens codeLens, final CancelIndicator indicator); +} diff --git a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/codelens/ICodeLensService.java b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/codelens/ICodeLensService.java new file mode 100644 index 000000000..7a4411249 --- /dev/null +++ b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/codelens/ICodeLensService.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2017 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.codelens; + +import java.util.List; +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.CodeLensParams; +import org.eclipse.xtext.ide.server.Document; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.CancelIndicator; + +/** + * @author Sven Efftinge - Initial contribution and API + */ +@SuppressWarnings("all") +public interface ICodeLensService { + /** + * compute code lenses for the given context. + */ + public abstract List computeCodeLenses(final Document document, final XtextResource resource, final CodeLensParams params, final CancelIndicator indicator); +} diff --git a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/hover/HoverService.java b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/hover/HoverService.java index 8daeb6172..8fe548c2f 100644 --- a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/hover/HoverService.java +++ b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/hover/HoverService.java @@ -21,6 +21,7 @@ import org.eclipse.xtext.documentation.IEObjectDocumentationProvider; import org.eclipse.xtext.ide.server.Document; import org.eclipse.xtext.ide.server.DocumentExtensions; import org.eclipse.xtext.ide.server.hover.HoverContext; +import org.eclipse.xtext.ide.server.hover.IHoverService; import org.eclipse.xtext.nodemodel.ILeafNode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.parser.IParseResult; @@ -40,9 +41,7 @@ import org.eclipse.xtext.xbase.lib.ListExtensions; */ @Singleton @SuppressWarnings("all") -public class HoverService { - public final static Hover EMPTY_HOVER = new Hover(Collections.>emptyList(), null); - +public class HoverService implements IHoverService { @Inject @Extension private DocumentExtensions _documentExtensions; @@ -59,6 +58,7 @@ public class HoverService { @Extension private IEObjectDocumentationProvider _iEObjectDocumentationProvider; + @Override public Hover hover(final Document document, final XtextResource resource, final TextDocumentPositionParams params, final CancelIndicator cancelIndicator) { final int offset = document.getOffSet(params.getPosition()); final HoverContext context = this.createContext(document, resource, offset); @@ -96,15 +96,15 @@ public class HoverService { protected Hover hover(final HoverContext context) { if ((context == null)) { - return HoverService.EMPTY_HOVER; + return IHoverService.EMPTY_HOVER; } final List> contents = this.getContents(context); if ((contents == null)) { - return HoverService.EMPTY_HOVER; + return IHoverService.EMPTY_HOVER; } final Range range = this.getRange(context); if ((range == null)) { - return HoverService.EMPTY_HOVER; + return IHoverService.EMPTY_HOVER; } return new Hover(contents, range); } diff --git a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/hover/IHoverService.java b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/hover/IHoverService.java new file mode 100644 index 000000000..ef0b99987 --- /dev/null +++ b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/hover/IHoverService.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2017 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.hover; + +import com.google.inject.ImplementedBy; +import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.MarkedString; +import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.xtext.ide.server.Document; +import org.eclipse.xtext.ide.server.hover.HoverService; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.CancelIndicator; +import org.eclipse.xtext.xbase.lib.CollectionLiterals; + +/** + * @author Sven Efftinge - Initial contribution and API + */ +@ImplementedBy(HoverService.class) +@SuppressWarnings("all") +public interface IHoverService { + public final static Hover EMPTY_HOVER = new Hover(CollectionLiterals.>emptyList(), null); + + /** + * callback for 'textDocument/hover' requests. + */ + public abstract Hover hover(final Document document, final XtextResource resource, final TextDocumentPositionParams params, final CancelIndicator cancelIndicator); +} diff --git a/org.eclipse.xtext.testing/src/org/eclipse/xtext/testing/AbstractLanguageServerTest.xtend b/org.eclipse.xtext.testing/src/org/eclipse/xtext/testing/AbstractLanguageServerTest.xtend index c460ff3e5..20f0b1d21 100644 --- a/org.eclipse.xtext.testing/src/org/eclipse/xtext/testing/AbstractLanguageServerTest.xtend +++ b/org.eclipse.xtext.testing/src/org/eclipse/xtext/testing/AbstractLanguageServerTest.xtend @@ -67,6 +67,8 @@ import org.eclipse.xtext.util.Files import org.eclipse.xtext.util.Modules2 import org.junit.Assert import org.junit.Before +import org.eclipse.lsp4j.CodeLens +import org.eclipse.lsp4j.CodeLensParams /** * @author Sven Efftinge - Initial contribution and API @@ -288,6 +290,32 @@ abstract class AbstractLanguageServerTest implements Endpoint { protected dispatch def String toExpectation(ColoringInformation it) { return '''«range.toExpectation» -> [«styles.join(', ')»]'''; } + + protected dispatch def String toExpectation(CodeLens it) { + return command.title + " " +range.toExpectation + } + + @Accessors static class TestCodeLensConfiguration extends TextDocumentPositionConfiguration { + String expectedCodeLensItems = '' + (List)=>void assertCodeLenses = null + } + + protected def void testCodeLens((TestCodeLensConfiguration)=>void configurator) { + val extension configuration = new TestCodeLensConfiguration + configuration.filePath = 'MyModel.' + fileExtension + configurator.apply(configuration) + val filePath = initializeContext(configuration).uri + val codeLenses = languageServer.codeLens(new CodeLensParams=>[ + textDocument = new TextDocumentIdentifier(filePath) + ]) + val result = codeLenses.get.map[languageServer.resolveCodeLens(it).get].toList + + if (configuration.assertCodeLenses !== null) { + configuration.assertCodeLenses.apply(result) + } else { + assertEquals(expectedCodeLensItems, result.toExpectation) + } + } protected def void testCompletion((TestCompletionConfiguration)=>void configurator) { val extension configuration = new TestCompletionConfiguration diff --git a/org.eclipse.xtext.testing/xtend-gen/org/eclipse/xtext/testing/AbstractLanguageServerTest.java b/org.eclipse.xtext.testing/xtend-gen/org/eclipse/xtext/testing/AbstractLanguageServerTest.java index b41332386..ad05bb24a 100644 --- a/org.eclipse.xtext.testing/xtend-gen/org/eclipse/xtext/testing/AbstractLanguageServerTest.java +++ b/org.eclipse.xtext.testing/xtend-gen/org/eclipse/xtext/testing/AbstractLanguageServerTest.java @@ -26,6 +26,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.CodeLensParams; import org.eclipse.lsp4j.ColoringInformation; import org.eclipse.lsp4j.ColoringParams; import org.eclipse.lsp4j.CompletionItem; @@ -83,6 +85,7 @@ import org.eclipse.xtext.testing.ReferenceTestConfiguration; import org.eclipse.xtext.testing.SignatureHelpConfiguration; import org.eclipse.xtext.testing.TestCompletionConfiguration; import org.eclipse.xtext.testing.TextDocumentConfiguration; +import org.eclipse.xtext.testing.TextDocumentPositionConfiguration; import org.eclipse.xtext.testing.WorkspaceSymbolConfiguraiton; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.util.Files; @@ -108,6 +111,31 @@ import org.junit.Before; @FinalFieldsConstructor @SuppressWarnings("all") public abstract class AbstractLanguageServerTest implements Endpoint { + @Accessors + public static class TestCodeLensConfiguration extends TextDocumentPositionConfiguration { + private String expectedCodeLensItems = ""; + + private Procedure1> assertCodeLenses = null; + + @Pure + public String getExpectedCodeLensItems() { + return this.expectedCodeLensItems; + } + + public void setExpectedCodeLensItems(final String expectedCodeLensItems) { + this.expectedCodeLensItems = expectedCodeLensItems; + } + + @Pure + public Procedure1> getAssertCodeLenses() { + return this.assertCodeLenses; + } + + public void setAssertCodeLenses(final Procedure1> assertCodeLenses) { + this.assertCodeLenses = assertCodeLenses; + } + } + @Accessors protected final String fileExtension; @@ -561,6 +589,45 @@ public abstract class AbstractLanguageServerTest implements Endpoint { return _builder.toString(); } + protected String _toExpectation(final CodeLens it) { + String _title = it.getCommand().getTitle(); + String _plus = (_title + " "); + String _expectation = this.toExpectation(it.getRange()); + return (_plus + _expectation); + } + + protected void testCodeLens(final Procedure1 configurator) { + try { + @Extension + final AbstractLanguageServerTest.TestCodeLensConfiguration configuration = new AbstractLanguageServerTest.TestCodeLensConfiguration(); + configuration.setFilePath(("MyModel." + this.fileExtension)); + configurator.apply(configuration); + final String filePath = this.initializeContext(configuration).getUri(); + CodeLensParams _codeLensParams = new CodeLensParams(); + final Procedure1 _function = (CodeLensParams it) -> { + TextDocumentIdentifier _textDocumentIdentifier = new TextDocumentIdentifier(filePath); + it.setTextDocument(_textDocumentIdentifier); + }; + CodeLensParams _doubleArrow = ObjectExtensions.operator_doubleArrow(_codeLensParams, _function); + final CompletableFuture> codeLenses = this.languageServer.codeLens(_doubleArrow); + final Function1 _function_1 = (CodeLens it) -> { + try { + return this.languageServer.resolveCodeLens(it).get(); + } catch (Throwable _e) { + throw Exceptions.sneakyThrow(_e); + } + }; + final List result = IterableExtensions.toList(ListExtensions.map(codeLenses.get(), _function_1)); + if ((configuration.assertCodeLenses != null)) { + configuration.assertCodeLenses.apply(result); + } else { + this.assertEquals(configuration.expectedCodeLensItems, this.toExpectation(result)); + } + } catch (Throwable _e) { + throw Exceptions.sneakyThrow(_e); + } + } + protected void testCompletion(final Procedure1 configurator) { try { @Extension @@ -944,6 +1011,8 @@ public abstract class AbstractLanguageServerTest implements Endpoint { return _toExpectation((Void)null); } else if (it instanceof Map) { return _toExpectation((Map)it); + } else if (it instanceof CodeLens) { + return _toExpectation((CodeLens)it); } else if (it instanceof ColoringInformation) { return _toExpectation((ColoringInformation)it); } else if (it instanceof CompletionItem) {