diff --git a/atom-extensions/atom-bosh/package.json b/atom-extensions/atom-bosh/package.json index cb0262a5f..41a2b2446 100644 --- a/atom-extensions/atom-bosh/package.json +++ b/atom-extensions/atom-bosh/package.json @@ -1,6 +1,6 @@ { "name": "bosh-yaml", - "version": "0.1.6", + "version": "0.2.0", "description": "Provides validation and content assist for various Bosh configuration files", "repository": "https://github.com/spring-projects/atom-bosh", "icon": "icon.png", diff --git a/atom-extensions/atom-cf-manifest-yaml/package.json b/atom-extensions/atom-cf-manifest-yaml/package.json index 7a37af272..680f317d1 100644 --- a/atom-extensions/atom-cf-manifest-yaml/package.json +++ b/atom-extensions/atom-cf-manifest-yaml/package.json @@ -1,6 +1,6 @@ { "name": "cf-manifest-yaml", - "version": "0.1.6", + "version": "0.2.0", "description": "Cloud Foundry Deployment Manifest YAML support for Atom", "repository": "https://github.com/spring-projects/atom-cf-manifest-yaml", "icon": "icon.png", diff --git a/atom-extensions/atom-concourse/package.json b/atom-extensions/atom-concourse/package.json index 6403267dc..5295f9203 100644 --- a/atom-extensions/atom-concourse/package.json +++ b/atom-extensions/atom-concourse/package.json @@ -1,6 +1,6 @@ { "name": "concourse-pipeline-yaml", - "version": "0.1.6", + "version": "0.2.0", "description": "Provides validation and content assist for Concourse CI pipeline and task configuration yml files", "repository": "https://github.com/spring-projects/atom-concourse", "icon": "icon.png", diff --git a/atom-extensions/atom-spring-boot/package.json b/atom-extensions/atom-spring-boot/package.json index 55c0ae214..517d6e198 100644 --- a/atom-extensions/atom-spring-boot/package.json +++ b/atom-extensions/atom-spring-boot/package.json @@ -1,6 +1,6 @@ { "name": "spring-boot", - "version": "0.1.6", + "version": "0.2.0", "description": "Spring Boot support for Atom", "repository": "https://github.com/spring-projects/atom-spring-boot", "icon": "icon.png", diff --git a/atom-extensions/atom-spring-boot/properties.json b/atom-extensions/atom-spring-boot/properties.json index 5f72a3b34..fd23a8bb8 100644 --- a/atom-extensions/atom-spring-boot/properties.json +++ b/atom-extensions/atom-spring-boot/properties.json @@ -1,3 +1,3 @@ { - "jarUrl": "https://s3-us-west-1.amazonaws.com/s3-test.spring.io/sts4/fatjars/snapshots/spring-boot-language-server-0.1.5-201803051440.jar" + "jarUrl": "https://s3-us-west-1.amazonaws.com/s3-test.spring.io/sts4/fatjars/snapshots/spring-boot-language-server-0.1.5-201803091709.jar" } diff --git a/headless-services/bosh-language-server/pom.xml b/headless-services/bosh-language-server/pom.xml index 697cc4320..404e35684 100644 --- a/headless-services/bosh-language-server/pom.xml +++ b/headless-services/bosh-language-server/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../commons/pom.xml diff --git a/headless-services/commons/commons-boot-app-cli/pom.xml b/headless-services/commons/commons-boot-app-cli/pom.xml index c78d26c67..5657f8531 100644 --- a/headless-services/commons/commons-boot-app-cli/pom.xml +++ b/headless-services/commons/commons-boot-app-cli/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/commons-cf/pom.xml b/headless-services/commons/commons-cf/pom.xml index 6a194b823..df65c859e 100644 --- a/headless-services/commons/commons-cf/pom.xml +++ b/headless-services/commons/commons-cf/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/commons-gradle/pom.xml b/headless-services/commons/commons-gradle/pom.xml index 30be7b8ea..3e01d4820 100644 --- a/headless-services/commons/commons-gradle/pom.xml +++ b/headless-services/commons/commons-gradle/pom.xml @@ -9,7 +9,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/commons-java/pom.xml b/headless-services/commons/commons-java/pom.xml index ec2677d4f..4a9bce879 100644 --- a/headless-services/commons/commons-java/pom.xml +++ b/headless-services/commons/commons-java/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/commons-language-server/pom.xml b/headless-services/commons/commons-language-server/pom.xml index 0313e223b..e6d143fc6 100644 --- a/headless-services/commons/commons-language-server/pom.xml +++ b/headless-services/commons/commons-language-server/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/DocumentHighlightHandler.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/DocumentHighlightHandler.java new file mode 100644 index 000000000..636495d0e --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/DocumentHighlightHandler.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver.util; + +import java.util.List; + +import org.eclipse.lsp4j.DocumentHighlight; +import org.eclipse.lsp4j.TextDocumentPositionParams; + +@FunctionalInterface +public interface DocumentHighlightHandler { + + List handle(TextDocumentPositionParams position); + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 1463f7e0c..ea4d9f28c 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -355,6 +354,9 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA if (hasDocumentSymbolHandler()) { c.setDocumentSymbolProvider(true); } + if (hasDocumentHighlightHandler()) { + c.setDocumentHighlightProvider(true); + } if (hasCodeLensHandler()) { CodeLensOptions codeLensOptions = new CodeLensOptions(); codeLensOptions.setResolveProvider(hasCodeLensResolveProvider()); @@ -389,6 +391,10 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA return getTextDocumentService().hasDocumentSymbolHandler(); } + private boolean hasDocumentHighlightHandler() { + return getTextDocumentService().hasDocumentHighlightHandler(); + } + private boolean hasCodeLensHandler() { return getTextDocumentService().hasCodeLensHandler(); } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java index e8e3936c3..f7e96b2ce 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java @@ -77,6 +77,7 @@ public class SimpleTextDocumentService implements TextDocumentService { private DefinitionHandler definitionHandler; private ReferencesHandler referencesHandler; private DocumentSymbolHandler documentSymbolHandler; + private DocumentHighlightHandler documentHighlightHandler; private CodeLensHandler codeLensHandler; private CodeLensResolveHandler codeLensResolveHandler; @@ -109,6 +110,11 @@ public class SimpleTextDocumentService implements TextDocumentService { this.documentSymbolHandler = h; } + public synchronized void onDocumentHighlight(DocumentHighlightHandler h) { + Assert.isNull("A DocumentHighlightHandler is already set, multiple handlers not supported yet", documentHighlightHandler); + this.documentHighlightHandler = h; + } + public synchronized void onCompletion(CompletionHandler h) { Assert.isNull("A completion handler is already set, multiple handlers not supported yet", completionHandler); this.completionHandler = h; @@ -260,6 +266,7 @@ public class SimpleTextDocumentService implements TextDocumentService { public final static List NO_REFERENCES = ImmutableList.of(); public final static List NO_SYMBOLS = ImmutableList.of(); public final static List NO_CODELENS = ImmutableList.of(); + public final static List NO_HIGHLIGHTS = ImmutableList.of(); @Override public CompletableFuture, CompletionList>> completion(TextDocumentPositionParams position) { @@ -437,7 +444,13 @@ public class SimpleTextDocumentService implements TextDocumentService { @Override public CompletableFuture> documentHighlight(TextDocumentPositionParams position) { - return CompletableFuture.completedFuture(Collections.emptyList()); + return async.invoke(() -> { + DocumentHighlightHandler handler = this.documentHighlightHandler; + if (handler != null) { + return handler.handle(position); + } + return NO_HIGHLIGHTS; + }); } public boolean hasDefinitionHandler() { @@ -452,6 +465,10 @@ public class SimpleTextDocumentService implements TextDocumentService { return this.documentSymbolHandler!=null; } + public boolean hasDocumentHighlightHandler() { + return this.documentHighlightHandler!=null; + } + public boolean hasCodeLensHandler() { return this.codeLensHandler != null; } diff --git a/headless-services/commons/commons-maven/pom.xml b/headless-services/commons/commons-maven/pom.xml index 6bee0cd22..3673e4122 100644 --- a/headless-services/commons/commons-maven/pom.xml +++ b/headless-services/commons/commons-maven/pom.xml @@ -10,7 +10,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/commons-util/pom.xml b/headless-services/commons/commons-util/pom.xml index 517295f82..2ac8e55cb 100644 --- a/headless-services/commons/commons-util/pom.xml +++ b/headless-services/commons/commons-util/pom.xml @@ -7,7 +7,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/commons-yaml/pom.xml b/headless-services/commons/commons-yaml/pom.xml index 70398f55a..686258d3d 100644 --- a/headless-services/commons/commons-yaml/pom.xml +++ b/headless-services/commons/commons-yaml/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/java-properties/pom.xml b/headless-services/commons/java-properties/pom.xml index 49baea0dd..a9b669f9e 100644 --- a/headless-services/commons/java-properties/pom.xml +++ b/headless-services/commons/java-properties/pom.xml @@ -7,7 +7,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/language-server-test-harness/pom.xml b/headless-services/commons/language-server-test-harness/pom.xml index 79a02669c..2641e0b80 100644 --- a/headless-services/commons/language-server-test-harness/pom.xml +++ b/headless-services/commons/language-server-test-harness/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../pom.xml diff --git a/headless-services/commons/pom.xml b/headless-services/commons/pom.xml index b0c6d8a8d..e24ec1782 100644 --- a/headless-services/commons/pom.xml +++ b/headless-services/commons/pom.xml @@ -6,7 +6,7 @@ org.springframework.ide.vscode commons-parent pom - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT commons-parent diff --git a/headless-services/concourse-language-server/pom.xml b/headless-services/concourse-language-server/pom.xml index 79b8e5f86..ebe4f4c95 100644 --- a/headless-services/concourse-language-server/pom.xml +++ b/headless-services/concourse-language-server/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../commons/pom.xml diff --git a/headless-services/manifest-yaml-language-server/pom.xml b/headless-services/manifest-yaml-language-server/pom.xml index b2f11c6cf..b7513017b 100644 --- a/headless-services/manifest-yaml-language-server/pom.xml +++ b/headless-services/manifest-yaml-language-server/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../commons/pom.xml diff --git a/headless-services/spring-boot-language-server/pom.xml b/headless-services/spring-boot-language-server/pom.xml index a9cc190b7..99993157b 100644 --- a/headless-services/spring-boot-language-server/pom.xml +++ b/headless-services/spring-boot-language-server/pom.xml @@ -8,7 +8,7 @@ org.springframework.ide.vscode commons-parent - 0.1.6-SNAPSHOT + 0.2.0-SNAPSHOT ../commons/pom.xml diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index fc932d065..1d199e5c2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -27,12 +27,14 @@ import org.springframework.ide.vscode.boot.java.conditionals.ConditionalsLiveHov import org.springframework.ide.vscode.boot.java.data.DataRepositorySymbolProvider; import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeLensEngine; import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine; +import org.springframework.ide.vscode.boot.java.handlers.BootJavaDocumentHighlightEngine; import org.springframework.ide.vscode.boot.java.handlers.BootJavaDocumentSymbolHandler; import org.springframework.ide.vscode.boot.java.handlers.BootJavaHoverProvider; import org.springframework.ide.vscode.boot.java.handlers.BootJavaReferencesHandler; import org.springframework.ide.vscode.boot.java.handlers.BootJavaWorkspaceSymbolHandler; import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider; import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider; +import org.springframework.ide.vscode.boot.java.handlers.HighlightProvider; import org.springframework.ide.vscode.boot.java.handlers.HoverProvider; import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; @@ -44,6 +46,7 @@ import org.springframework.ide.vscode.boot.java.requestmapping.LiveAppURLSymbolP import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingHoverProvider; import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider; import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxHandlerCodeLensProvider; +import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouteHighlightProdivder; import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider; import org.springframework.ide.vscode.boot.java.scope.ScopeCompletionProcessor; import org.springframework.ide.vscode.boot.java.snippets.JavaSnippet; @@ -61,6 +64,7 @@ import org.springframework.ide.vscode.commons.languageserver.composable.Language import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.util.CodeLensHandler; +import org.springframework.ide.vscode.commons.languageserver.util.DocumentHighlightHandler; import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler; import org.springframework.ide.vscode.commons.languageserver.util.LSFactory; import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler; @@ -94,6 +98,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent private JavaProjectFinder projectFinder; private BootJavaHoverProvider hoverProvider; private CodeLensHandler codeLensHandler; + private DocumentHighlightHandler highlightsEngine; public BootJavaLanguageServerComponents(SimpleLanguageServer server, LSFactory _params) { this.server = server; @@ -151,7 +156,10 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent codeLensHandler = createCodeLensEngine(); documents.onCodeLens(codeLensHandler); - + + highlightsEngine = createDocumentHighlightEngine(); + documents.onDocumentHighlight(highlightsEngine); + workspaceService.onDidChangeConfiguraton(settings -> { config.handleConfigurationChange(settings); if (config.isBootHintsEnabled()) { @@ -180,6 +188,10 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent return codeLensHandler; } + public DocumentHighlightHandler getDocumentHighlightHandler() { + return highlightsEngine; + } + private void initialize(InitializeParams params) { this.indexer.initialize(server.getWorkspaceRoots()); } @@ -314,6 +326,13 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent return new BootJavaCodeLensEngine(this, codeLensProvider); } + + protected BootJavaDocumentHighlightEngine createDocumentHighlightEngine() { + Collection highlightProvider = new ArrayList<>(); + highlightProvider.add(new WebfluxRouteHighlightProdivder(this)); + + return new BootJavaDocumentHighlightEngine(this, highlightProvider); + } public ProjectObserver getProjectObserver() { return projectObserver; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaDocumentHighlightEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaDocumentHighlightEngine.java new file mode 100644 index 000000000..b44e40ff8 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaDocumentHighlightEngine.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.handlers; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.lsp4j.DocumentHighlight; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; +import org.springframework.ide.vscode.commons.languageserver.util.DocumentHighlightHandler; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public class BootJavaDocumentHighlightEngine implements DocumentHighlightHandler { + + private static Logger log = LoggerFactory.getLogger(BootJavaDocumentHighlightEngine.class); + + private BootJavaLanguageServerComponents server; + private Collection highlightProviders; + + public BootJavaDocumentHighlightEngine(BootJavaLanguageServerComponents server, Collection highlightProviders) { + this.server = server; + this.highlightProviders = highlightProviders; + } + + @Override + public List handle(TextDocumentPositionParams params) { + SimpleTextDocumentService documents = server.getTextDocumentService(); + String docURI = params.getTextDocument().getUri(); + + if (documents.get(docURI) != null) { + TextDocument doc = documents.get(docURI).copy(); + try { + return provideDocumentHighlights(doc, params.getPosition()); + } + catch (Exception e) { + log.error("", e); + } + } + + return SimpleTextDocumentService.NO_HIGHLIGHTS; + } + + private List provideDocumentHighlights(TextDocument document, Position position) { + List result = new ArrayList<>(); + for (HighlightProvider highlightProvider : highlightProviders) { + highlightProvider.provideHighlights(document, position, result); + } + + return result; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/EnhancedSymbolInformation.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/EnhancedSymbolInformation.java index 7a20e0f57..b3fa7c101 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/EnhancedSymbolInformation.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/EnhancedSymbolInformation.java @@ -18,9 +18,9 @@ import org.eclipse.lsp4j.SymbolInformation; public class EnhancedSymbolInformation { private final SymbolInformation symbol; - private final Object additionalInformation; + private final SymbolAddOnInformation[] additionalInformation; - public EnhancedSymbolInformation(SymbolInformation symbol, Object additionalInformation) { + public EnhancedSymbolInformation(SymbolInformation symbol, SymbolAddOnInformation[] additionalInformation) { this.symbol = symbol; this.additionalInformation = additionalInformation; } @@ -29,7 +29,7 @@ public class EnhancedSymbolInformation { return symbol; } - public Object getAdditionalInformation() { + public SymbolAddOnInformation[] getAdditionalInformation() { return additionalInformation; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HighlightProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HighlightProvider.java new file mode 100644 index 000000000..1e97aa7ce --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HighlightProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.handlers; + +import java.util.List; + +import org.eclipse.lsp4j.DocumentHighlight; +import org.eclipse.lsp4j.Position; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public interface HighlightProvider { + + public void provideHighlights(TextDocument document, Position position, List resultAccumulator); + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SymbolAddOnInformation.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SymbolAddOnInformation.java new file mode 100644 index 000000000..99be8fd58 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SymbolAddOnInformation.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.handlers; + +/** + * @author Martin Lippert + */ +public interface SymbolAddOnInformation { +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AtomSourceLinks.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AtomSourceLinks.java new file mode 100644 index 000000000..81d3c973a --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AtomSourceLinks.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.links; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.file.Path; +import java.util.Optional; + +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.text.Region; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +/** + * Source links for Atom client + * + * @author Alex Boyko + * + */ +public class AtomSourceLinks extends AbstractSourceLinks { + + private static Supplier LOG = Suppliers.memoize(() -> LoggerFactory.getLogger(AbstractSourceLinks.class)); + + public AtomSourceLinks(BootJavaLanguageServerComponents server) { + super(server); + } + + @Override + public Optional sourceLinkForResourcePath(Path path) { + try { + return Optional.of("atom://core/open/file?filename=" + URLEncoder.encode(path.toString(), "UTF8")); + } catch (UnsupportedEncodingException e) { + LOG.get().error("Cannot build source URL for " + path, e); + } + return Optional.empty(); + } + + @Override + protected String positionLink(CompilationUnit cu, String fqName) { + if (cu != null) { + Region region = findTypeRegion(cu, fqName); + if (region != null) { + int column = cu.getColumnNumber(region.getOffset()); + int line = cu.getLineNumber(region.getOffset()); + StringBuilder sb = new StringBuilder(); + sb.append("&line="); + sb.append(line); + sb.append("&column="); + sb.append(column + 1); // 1-based columns? + return sb.toString(); + } + } + return null; + } + + @Override + protected Optional jarUrl(IJavaProject project, String fqName, File jarFile) { + // JAR URLs are not supported yet + return Optional.empty(); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/SourceLinkFactory.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/SourceLinkFactory.java index 5f64335bd..8d427bec8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/SourceLinkFactory.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/SourceLinkFactory.java @@ -55,6 +55,8 @@ public final class SourceLinkFactory { return new VSCodeSourceLinks(server); case ECLIPSE: return new EclipseSourceLinks(); + case ATOM: + return new AtomSourceLinks(server); default: return NO_SOURCE_LINKS; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RouteUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RouteUtils.java index 0cc7a4436..6354afbf8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RouteUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RouteUtils.java @@ -14,6 +14,7 @@ import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; +import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; /** * @author Martin Lippert @@ -21,7 +22,7 @@ import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformati public class RouteUtils { public static EnhancedSymbolInformation createRouteSymbol(Location location, String path, - String[] httpMethods, String[] contentTypes, String[] acceptTypes, Object enhancedInformation) { + String[] httpMethods, String[] contentTypes, String[] acceptTypes, SymbolAddOnInformation[] enhancedInformation) { if (path != null && path.length() > 0) { String label = "@" + (path.startsWith("/") ? path : ("/" + path)); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxAcceptTypeFinder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxAcceptTypeFinder.java index c9b5dfa98..85e423ed1 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxAcceptTypeFinder.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxAcceptTypeFinder.java @@ -10,41 +10,53 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert */ public class WebfluxAcceptTypeFinder extends ASTVisitor { - private Set acceptTypes; + private List acceptTypes; + private TextDocument doc; - public WebfluxAcceptTypeFinder() { - this.acceptTypes = new LinkedHashSet<>(); + public WebfluxAcceptTypeFinder(TextDocument doc) { + this.doc = doc; + this.acceptTypes = new ArrayList<>(); } - public Set getAcceptTypes() { + public List getAcceptTypes() { return acceptTypes; } @Override public boolean visit(MethodInvocation node) { IMethodBinding methodBinding = node.resolveMethodBinding(); - - if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { - String name = methodBinding.getName(); - if (name != null && WebfluxUtils.REQUEST_PREDICATE_ACCEPT_TYPE_METHOD.equals(name)) { - String acceptType = WebfluxUtils.extractSimpleNameArgument(node); - if (acceptType != null) { - acceptTypes.add(acceptType); + + try { + if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { + String name = methodBinding.getName(); + if (name != null && WebfluxUtils.REQUEST_PREDICATE_ACCEPT_TYPE_METHOD.equals(name)) { + SimpleName nameArgument = WebfluxUtils.extractSimpleNameArgument(node); + if (nameArgument != null && nameArgument.getFullyQualifiedName() != null) { + Range range = doc.toRange(nameArgument.getStartPosition(), nameArgument.getLength()); + acceptTypes.add(new WebfluxRouteElement(nameArgument.getFullyQualifiedName().toString(), range)); + } } } } + catch (BadLocationException e) { + // ignore + } return !WebfluxUtils.isRouteMethodInvocation(methodBinding); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxContentTypeFinder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxContentTypeFinder.java index 2e8cf56db..b2ab27665 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxContentTypeFinder.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxContentTypeFinder.java @@ -10,53 +10,55 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; -import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert */ public class WebfluxContentTypeFinder extends ASTVisitor { - private Set contentTypes; - private ASTNode root; + private List contentTypes; + private TextDocument doc; - public WebfluxContentTypeFinder(ASTNode root) { - this.root = root; - this.contentTypes = new LinkedHashSet<>(); + public WebfluxContentTypeFinder(TextDocument doc) { + this.doc = doc; + this.contentTypes = new ArrayList<>(); } - public Set getContentTypes() { + public List getContentTypes() { return contentTypes; } @Override public boolean visit(MethodInvocation node) { - boolean visitChildren = true; + IMethodBinding methodBinding = node.resolveMethodBinding(); - if (node != this.root) { - IMethodBinding methodBinding = node.resolveMethodBinding(); - + try { if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { String name = methodBinding.getName(); if (name != null && WebfluxUtils.REQUEST_PREDICATE_CONTENT_TYPE_METHOD.equals(name)) { - String contentType = WebfluxUtils.extractSimpleNameArgument(node); - if (contentType != null) { - contentTypes.add(contentType); + SimpleName nameArgument = WebfluxUtils.extractSimpleNameArgument(node); + if (nameArgument != null && nameArgument.getFullyQualifiedName() != null) { + Range range = doc.toRange(nameArgument.getStartPosition(), nameArgument.getLength()); + contentTypes.add(new WebfluxRouteElement(nameArgument.getFullyQualifiedName().toString(), range)); } } } - - if (WebfluxUtils.isRouteMethodInvocation(methodBinding)) { - visitChildren = false; - } } - return visitChildren; + catch (BadLocationException e) { + // ignore + } + + return !WebfluxUtils.isRouteMethodInvocation(methodBinding); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxElementsInformation.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxElementsInformation.java new file mode 100644 index 000000000..7b4556e2d --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxElementsInformation.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.requestmapping; + +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; + +/** + * @author Martin Lippert + */ +public class WebfluxElementsInformation implements SymbolAddOnInformation { + + private Range[] ranges; + + public WebfluxElementsInformation(Range... ranges) { + this.ranges = ranges; + } + + public Range[] getRanges() { + return ranges; + } + + public boolean contains(Position position) { + for (Range range : ranges) { + if (isEqualOrBefore(range.getStart(), position) && isEqualsOrBehind(range.getEnd(), position)) { + return true; + } + } + + return false; + } + + /** + * returns true if position1 is the same or before position2 in a document + */ + protected boolean isEqualOrBefore(Position position1, Position position2) { + if (position1.getLine() < position2.getLine()) return true; + if (position1.getLine() == position2.getLine() && position1.getCharacter() <= position2.getCharacter()) return true; + return false; + } + + /** + * returns true if position1 is the same or behind position2 in a document + */ + protected boolean isEqualsOrBehind(Position position1, Position position2) { + if (position1.getLine() > position2.getLine()) return true; + if (position1.getLine() == position2.getLine() && position1.getCharacter() >= position2.getCharacter()) return true; + return false; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerCodeLensProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerCodeLensProvider.java index fc14555d9..6fabd7b5b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerCodeLensProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerCodeLensProvider.java @@ -20,6 +20,7 @@ import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Command; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider; +import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -55,7 +56,7 @@ public class WebfluxHandlerCodeLensProvider implements CodeLensProvider { final String handlerClass = methodBinding.getDeclaringClass().getBinaryName().trim(); final String handlerMethod = methodBinding.getMethodDeclaration().toString().trim(); - List handlerInfos = this.springIndexer.getAllAdditionalInformation((addon) -> { + List handlerInfos = this.springIndexer.getAllAdditionalInformation((addon) -> { if (addon instanceof WebfluxHandlerInformation) { WebfluxHandlerInformation handlerInfo = (WebfluxHandlerInformation) addon; return handlerInfo.getHandlerClass() != null && handlerInfo.getHandlerClass().equals(handlerClass) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerInformation.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerInformation.java index 6e8c993df..f13899cb9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerInformation.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerInformation.java @@ -10,10 +10,12 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; +import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; + /** * @author Martin Lippert */ -public class WebfluxHandlerInformation { +public class WebfluxHandlerInformation implements SymbolAddOnInformation { private final String handlerClass; private final String handlerMethod; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxMethodFinder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxMethodFinder.java index 57cbd1a54..57993e48b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxMethodFinder.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxMethodFinder.java @@ -10,28 +10,34 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert */ public class WebfluxMethodFinder extends ASTVisitor { - private Set methods; + private List methods; private ASTNode root; + private TextDocument doc; - public WebfluxMethodFinder(ASTNode root) { + public WebfluxMethodFinder(ASTNode root, TextDocument doc) { this.root = root; - this.methods = new LinkedHashSet<>(); + this.doc = doc; + this.methods = new ArrayList<>(); } - public Set getMethods() { + public List getMethods() { return methods; } @@ -42,15 +48,25 @@ public class WebfluxMethodFinder extends ASTVisitor { if (node != this.root) { IMethodBinding methodBinding = node.resolveMethodBinding(); - if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { - String name = methodBinding.getName(); - if (name != null && WebfluxUtils.REQUEST_PREDICATE_HTTPMETHOD_METHODS.contains(name)) { - methods.add(name); - } - else if (name != null && WebfluxUtils.REQUEST_PREDICATE_METHOD_METHOD.equals(name)) { - methods.add(WebfluxUtils.extractQualifiedNameArgument(node)); + try { + if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { + String name = methodBinding.getName(); + if (name != null && WebfluxUtils.REQUEST_PREDICATE_HTTPMETHOD_METHODS.contains(name)) { + Range range = doc.toRange(node.getStartPosition(), node.getLength()); + methods.add(new WebfluxRouteElement(name, range)); + } + else if (name != null && WebfluxUtils.REQUEST_PREDICATE_METHOD_METHOD.equals(name)) { + QualifiedName qualifiedName = WebfluxUtils.extractQualifiedNameArgument(node); + if (qualifiedName.getName() != null) { + Range range = doc.toRange(qualifiedName.getStartPosition(), qualifiedName.getLength()); + methods.add(new WebfluxRouteElement(qualifiedName.getName().toString(), range)); + } + } } } + catch (BadLocationException e) { + // ignore + } if (WebfluxUtils.isRouteMethodInvocation(methodBinding)) { visitChildren = false; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxPathFinder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxPathFinder.java index 60a323295..d9c6bc3d1 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxPathFinder.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxPathFinder.java @@ -10,24 +10,34 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.StringLiteral; +import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert */ public class WebfluxPathFinder extends ASTVisitor { - private String path; + private List path; private ASTNode root; + private TextDocument doc; - public WebfluxPathFinder(ASTNode root) { + public WebfluxPathFinder(ASTNode root, TextDocument doc) { this.root = root; + this.doc = doc; + this.path = new ArrayList<>(); } - public String getPath() { + public List getPath() { return path; } @@ -38,12 +48,21 @@ public class WebfluxPathFinder extends ASTVisitor { if (node != this.root) { IMethodBinding methodBinding = node.resolveMethodBinding(); - if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { - String name = methodBinding.getName(); - if (name != null && WebfluxUtils.REQUEST_PREDICATE_ALL_PATH_METHODS.contains(name)) { - path = WebfluxUtils.extractStringLiteralArgument(node); + try { + if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { + String name = methodBinding.getName(); + if (name != null && WebfluxUtils.REQUEST_PREDICATE_ALL_PATH_METHODS.contains(name)) { + StringLiteral stringLiteral = WebfluxUtils.extractStringLiteralArgument(node); + if (stringLiteral != null) { + Range range = doc.toRange(stringLiteral.getStartPosition(), stringLiteral.getLength()); + path.add(new WebfluxRouteElement(stringLiteral.getLiteralValue(), range)); + } + } } } + catch (BadLocationException e) { + // ignore + } if (WebfluxUtils.isRouteMethodInvocation(methodBinding)) { visitChildren = false; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteElement.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteElement.java new file mode 100644 index 000000000..122aab7e6 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteElement.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.requestmapping; + +import org.eclipse.lsp4j.Range; + +/** + * @author Martin Lippert + */ +public class WebfluxRouteElement { + + private String element; + private Range elementRange; + + public WebfluxRouteElement(String element, Range elementRange) { + super(); + this.element = element; + this.elementRange = elementRange; + } + + public String getElement() { + return element; + } + + public Range getElementRange() { + return elementRange; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteHighlightProdivder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteHighlightProdivder.java new file mode 100644 index 000000000..57c67b9aa --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteHighlightProdivder.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.requestmapping; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.lsp4j.DocumentHighlight; +import org.eclipse.lsp4j.Position; +import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; +import org.springframework.ide.vscode.boot.java.handlers.HighlightProvider; +import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public class WebfluxRouteHighlightProdivder implements HighlightProvider { + + private final SpringIndexer springIndexer; + + public WebfluxRouteHighlightProdivder(BootJavaLanguageServerComponents bootJavaLanguageServerComponents) { + this.springIndexer = bootJavaLanguageServerComponents.getSpringIndexer(); + } + + @Override + public void provideHighlights(TextDocument document, Position position, List resultAccumulator) { + System.out.println(" PROVIDE HIGHLIGHTS: " + position.getLine() + "/" + position.getCharacter()); + + this.springIndexer.getAdditonalInformation(document.getUri()) + .stream() + .filter(addon -> { + if (addon instanceof WebfluxElementsInformation) { + WebfluxElementsInformation handlerInfo = (WebfluxElementsInformation) addon; + + if (handlerInfo.contains(position)) { + return true; + } + } + return false; + }) + .flatMap(addon -> Arrays.asList(((WebfluxElementsInformation) addon).getRanges()).stream()) + .forEach(range -> resultAccumulator.add(new DocumentHighlight(range))); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java index 9f0ea3d65..5f2b58d8f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java @@ -13,7 +13,6 @@ package org.springframework.ide.vscode.boot.java.requestmapping; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Set; import java.util.function.Function; import org.eclipse.jdt.core.dom.ASTNode; @@ -25,10 +24,15 @@ import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Range; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; +import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -87,22 +91,30 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } protected void extractMappingSymbol(MethodInvocation node, TextDocument doc, List result) { - String path = extractPath(node); - - String[] httpMethods = extractMethods(node); - String[] contentTypes = extractContentTypes(node); - String[] acceptTypes = extractAcceptTypes(node); + WebfluxRouteElement[] pathElements = extractPath(node, doc); + WebfluxRouteElement[] httpMethods = extractMethods(node, doc); + WebfluxRouteElement[] contentTypes = extractContentTypes(node, doc); + WebfluxRouteElement[] acceptTypes = extractAcceptTypes(node, doc); int methodNameStart = node.getName().getStartPosition(); int invocationStart = node.getStartPosition(); - if (path != null && path.length() > 0) { + StringBuilder pathBuilder = new StringBuilder(); + for (WebfluxRouteElement pathElement : pathElements) { + pathBuilder.insert(0, pathElement.getElement()); + } + + String path = pathBuilder.toString(); + + if (path.length() > 0) { try { Location location = new Location(doc.getUri(), doc.toRange(methodNameStart, node.getLength() - (methodNameStart - invocationStart))); WebfluxHandlerInformation handler = extractHandlerInformation(node, path, httpMethods, contentTypes, acceptTypes); + WebfluxElementsInformation elements = extractElementsInformation(pathElements, httpMethods, contentTypes, acceptTypes); - result.add(RouteUtils.createRouteSymbol(location, path, httpMethods, contentTypes, acceptTypes, handler)); + result.add(RouteUtils.createRouteSymbol(location, path, getElementStrings(httpMethods), + getElementStrings(contentTypes), getElementStrings(acceptTypes), new SymbolAddOnInformation[] {handler, elements})); } catch (BadLocationException e) { e.printStackTrace(); @@ -110,38 +122,55 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } } - private String extractPath(MethodInvocation routerInvocation) { - WebfluxPathFinder pathFinder = new WebfluxPathFinder(routerInvocation); - routerInvocation.accept(pathFinder); + private WebfluxElementsInformation extractElementsInformation(WebfluxRouteElement[] path, WebfluxRouteElement[] methods, + WebfluxRouteElement[] contentTypes, WebfluxRouteElement[] acceptTypes) { + List allRanges = new ArrayList<>(); - List path = new ArrayList<>(); - String firstPath = pathFinder.getPath(); - if (firstPath != null) { - path.add(firstPath); + WebfluxRouteElement[][] allElements = new WebfluxRouteElement[][] {path, methods, contentTypes, acceptTypes}; + for (int i = 0; i < allElements.length; i++) { + for (int j = 0; j < allElements[i].length; j++) { + allRanges.add(allElements[i][j].getElementRange()); + } } + return new WebfluxElementsInformation((Range[]) allRanges.toArray(new Range[allRanges.size()])); + } + + private WebfluxRouteElement[] extractPath(MethodInvocation routerInvocation, TextDocument doc) { + WebfluxPathFinder pathFinder = new WebfluxPathFinder(routerInvocation, doc); + List arguments = routerInvocation.arguments(); + for (Object argument : arguments) { + if (argument != null && argument instanceof ASTNode) { + ((ASTNode)argument).accept(pathFinder); + } + } + + List path = pathFinder.getPath(); + extractNestedValue(routerInvocation, path, (methodInvocation) -> { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); String methodName = methodBinding.getName(); - if (WebfluxUtils.REQUEST_PREDICATE_PATH_METHOD.equals(methodName)) { - String additionalPath = WebfluxUtils.extractStringLiteralArgument(methodInvocation); - if (additionalPath != null && additionalPath.length() > 0) { - return additionalPath; + try { + if (WebfluxUtils.REQUEST_PREDICATE_PATH_METHOD.equals(methodName)) { + StringLiteral stringLiteral = WebfluxUtils.extractStringLiteralArgument(methodInvocation); + if (stringLiteral != null) { + Range range = doc.toRange(stringLiteral.getStartPosition(), stringLiteral.getLength()); + return new WebfluxRouteElement(stringLiteral.getLiteralValue(), range); + } } } + catch (BadLocationException e) { + // ignore + } return null; }); - StringBuilder result = new StringBuilder(); - path.stream().forEach(part -> result.insert(0, part)); - - return result.toString(); - + return (WebfluxRouteElement[]) path.toArray(new WebfluxRouteElement[path.size()]); } - private String[] extractMethods(MethodInvocation routerInvocation) { - WebfluxMethodFinder methodFinder = new WebfluxMethodFinder(routerInvocation); + private WebfluxRouteElement[] extractMethods(MethodInvocation routerInvocation, TextDocument doc) { + WebfluxMethodFinder methodFinder = new WebfluxMethodFinder(routerInvocation, doc); List arguments = routerInvocation.arguments(); for (Object argument : arguments) { if (argument != null && argument instanceof ASTNode) { @@ -149,23 +178,33 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } } - final Set methods = methodFinder.getMethods(); + final List methods = methodFinder.getMethods(); extractNestedValue(routerInvocation, methods, (methodInvocation) -> { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); String methodName = methodBinding.getName(); - if (WebfluxUtils.REQUEST_PREDICATE_METHOD_METHOD.equals(methodName)) { - return WebfluxUtils.extractQualifiedNameArgument(methodInvocation); + try { + if (WebfluxUtils.REQUEST_PREDICATE_METHOD_METHOD.equals(methodName)) { + QualifiedName qualifiedName = WebfluxUtils.extractQualifiedNameArgument(methodInvocation); + if (qualifiedName.getName() != null) { + Range range = doc.toRange(qualifiedName.getStartPosition(), qualifiedName.getLength()); + return new WebfluxRouteElement(qualifiedName.getName().toString(), range); + } + } } + catch (BadLocationException e) { + // ignore + } + return null; }); - return (String[]) methods.toArray(new String[methods.size()]); + return (WebfluxRouteElement[]) methods.toArray(new WebfluxRouteElement[methods.size()]); } - private String[] extractAcceptTypes(MethodInvocation routerInvocation) { - WebfluxAcceptTypeFinder typeFinder = new WebfluxAcceptTypeFinder(); + private WebfluxRouteElement[] extractAcceptTypes(MethodInvocation routerInvocation, TextDocument doc) { + WebfluxAcceptTypeFinder typeFinder = new WebfluxAcceptTypeFinder(doc); List arguments = routerInvocation.arguments(); for (Object argument : arguments) { if (argument != null && argument instanceof ASTNode) { @@ -173,23 +212,33 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } } - Set acceptTypes = typeFinder.getAcceptTypes(); + final List acceptTypes = typeFinder.getAcceptTypes(); extractNestedValue(routerInvocation, acceptTypes, (methodInvocation) -> { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); String methodName = methodBinding.getName(); - if (WebfluxUtils.REQUEST_PREDICATE_ACCEPT_TYPE_METHOD.equals(methodName)) { - return WebfluxUtils.extractSimpleNameArgument(methodInvocation); + try { + if (WebfluxUtils.REQUEST_PREDICATE_ACCEPT_TYPE_METHOD.equals(methodName)) { + SimpleName nameArgument = WebfluxUtils.extractSimpleNameArgument(methodInvocation); + if (nameArgument != null && nameArgument.getFullyQualifiedName() != null) { + Range range = doc.toRange(nameArgument.getStartPosition(), nameArgument.getLength()); + return new WebfluxRouteElement(nameArgument.getFullyQualifiedName().toString(), range); + } + } } + catch (BadLocationException e) { + // ignore + } + return null; }); - return (String[]) acceptTypes.toArray(new String[acceptTypes.size()]); + return (WebfluxRouteElement[]) acceptTypes.toArray(new WebfluxRouteElement[acceptTypes.size()]); } - private String[] extractContentTypes(MethodInvocation routerInvocation) { - WebfluxContentTypeFinder contentTypeFinder = new WebfluxContentTypeFinder(routerInvocation); + private WebfluxRouteElement[] extractContentTypes(MethodInvocation routerInvocation, TextDocument doc) { + WebfluxContentTypeFinder contentTypeFinder = new WebfluxContentTypeFinder(doc); List arguments = routerInvocation.arguments(); for (Object argument : arguments) { if (argument != null && argument instanceof ASTNode) { @@ -197,22 +246,32 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } } - Set contentTypes = contentTypeFinder.getContentTypes(); + final List contentTypes = contentTypeFinder.getContentTypes(); extractNestedValue(routerInvocation, contentTypes, (methodInvocation) -> { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); String methodName = methodBinding.getName(); - if (WebfluxUtils.REQUEST_PREDICATE_CONTENT_TYPE_METHOD.equals(methodName)) { - return WebfluxUtils.extractSimpleNameArgument(methodInvocation); + try { + if (WebfluxUtils.REQUEST_PREDICATE_CONTENT_TYPE_METHOD.equals(methodName)) { + SimpleName nameArgument = WebfluxUtils.extractSimpleNameArgument(methodInvocation); + if (nameArgument != null && nameArgument.getFullyQualifiedName() != null) { + Range range = doc.toRange(nameArgument.getStartPosition(), nameArgument.getLength()); + return new WebfluxRouteElement(nameArgument.getFullyQualifiedName().toString(), range); + } + } } + catch (BadLocationException e) { + // ignore + } + return null; }); - return (String[]) contentTypes.toArray(new String[contentTypes.size()]); + return (WebfluxRouteElement[]) contentTypes.toArray(new WebfluxRouteElement[contentTypes.size()]); } - private void extractNestedValue(ASTNode node, Collection values, Function extractor) { + private void extractNestedValue(ASTNode node, Collection values, Function extractor) { if (node == null || node instanceof TypeDeclaration) { return; } @@ -228,7 +287,7 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { for (Object argument : arguments) { if (argument instanceof MethodInvocation) { MethodInvocation nestedMethod = (MethodInvocation) argument; - String value = extractor.apply(nestedMethod); + WebfluxRouteElement value = extractor.apply(nestedMethod); if (value != null) { values.add(value); } @@ -241,7 +300,9 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { extractNestedValue(node.getParent(), values, extractor); } - private WebfluxHandlerInformation extractHandlerInformation(MethodInvocation node, String path, String[] httpMethods, String[] contentTypes, String[] acceptTypes) { + private WebfluxHandlerInformation extractHandlerInformation(MethodInvocation node, String path, WebfluxRouteElement[] httpMethods, + WebfluxRouteElement[] contentTypes, WebfluxRouteElement[] acceptTypes) { + List arguments = node.arguments(); if (arguments != null) { @@ -257,7 +318,7 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { String handlerMethod = methodBinding.getMethodDeclaration().toString(); if (handlerMethod != null) handlerMethod = handlerMethod.trim(); - return new WebfluxHandlerInformation(handlerClass, handlerMethod, path, httpMethods, contentTypes, acceptTypes); + return new WebfluxHandlerInformation(handlerClass, handlerMethod, path, getElementStrings(httpMethods), getElementStrings(contentTypes), getElementStrings(acceptTypes)); } } } @@ -265,5 +326,15 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { return null; } + + private String[] getElementStrings(WebfluxRouteElement[] routeElements) { + List result = new ArrayList<>(); + + for (int i = 0; i < routeElements.length; i++) { + result.add(routeElements[i].getElement()); + } + + return (String[]) result.toArray(new String[result.size()]); + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java index b42bd79b4..a0a18d681 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java @@ -41,41 +41,34 @@ public class WebfluxUtils { public static final Set REQUEST_PREDICATE_ALL_PATH_METHODS = new HashSet<>(Arrays.asList(REQUEST_PREDICATE_PATH_METHOD, "GET", "POST", "DELETE", "PUT", "PATCH", "HEAD", "OPTIONS")); - public static String extractStringLiteralArgument(MethodInvocation node) { + public static StringLiteral extractStringLiteralArgument(MethodInvocation node) { List arguments = node.arguments(); if (arguments != null && arguments.size() > 0) { Object object = arguments.get(0); if (object instanceof StringLiteral) { - String path = ((StringLiteral) object).getLiteralValue(); - return path; + return (StringLiteral) object; } } return null; } - public static String extractQualifiedNameArgument(MethodInvocation node) { + public static QualifiedName extractQualifiedNameArgument(MethodInvocation node) { List arguments = node.arguments(); if (arguments != null && arguments.size() > 0) { Object object = arguments.get(0); if (object instanceof QualifiedName) { - QualifiedName qualifiedName = (QualifiedName) object; - if (qualifiedName.getName() != null) { - return qualifiedName.getName().toString(); - } + return (QualifiedName) object; } } return null; } - public static String extractSimpleNameArgument(MethodInvocation node) { + public static SimpleName extractSimpleNameArgument(MethodInvocation node) { List arguments = node.arguments(); if (arguments != null && arguments.size() > 0) { Object object = arguments.get(0); if (object instanceof SimpleName) { - SimpleName name = (SimpleName) object; - if (name.getFullyQualifiedName() != null) { - return name.getFullyQualifiedName().toString(); - } + return (SimpleName) object; } } return null; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index 971384da2..c9450129d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -58,6 +58,7 @@ import org.springframework.ide.vscode.boot.BootLanguageServerParams; import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies; import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; +import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -82,9 +83,10 @@ public class SpringIndexer { private final AnnotationHierarchyAwareLookup symbolProviders; private final List symbols; - private final List addonInformation; + private final List addonInformation; + private final ConcurrentMap> symbolsByDoc; - private final ConcurrentMap> addonInformationByDoc; + private final ConcurrentMap> addonInformationByDoc; private final Thread updateWorker; private final BlockingQueue updateQueue; @@ -314,7 +316,7 @@ public class SpringIndexer { return this.symbolsByDoc.get(docURI); } - public List getAllAdditionalInformation(Predicate filter) { + public List getAllAdditionalInformation(Predicate filter) { waitForInitializeTask(); if (filter != null) { @@ -325,7 +327,7 @@ public class SpringIndexer { } } - public List getAdditonalInformation(String docURI) { + public List getAdditonalInformation(String docURI) { waitForInitializeTask(); return this.addonInformationByDoc.get(docURI); } @@ -390,9 +392,9 @@ public class SpringIndexer { symbols.removeAll(oldSymbols); } - List oldAddInInformation = addonInformationByDoc.remove(docURI); - if (oldAddInInformation != null) { - addonInformation.removeAll(oldAddInInformation); + List oldAddOnInformation = addonInformationByDoc.remove(docURI); + if (oldAddOnInformation != null) { + addonInformation.removeAll(oldAddOnInformation); } AtomicReference docRef = new AtomicReference<>(); @@ -501,8 +503,8 @@ public class SpringIndexer { symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList()).add(enhancedSymbol.getSymbol()); if (enhancedSymbol.getAdditionalInformation() != null) { - addonInformation.add(enhancedSymbol.getAdditionalInformation()); - addonInformationByDoc.computeIfAbsent(docURI, s -> new ArrayList()).add(enhancedSymbol.getAdditionalInformation()); + addonInformation.addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); + addonInformationByDoc.computeIfAbsent(docURI, s -> new ArrayList()).addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); } }); } @@ -522,8 +524,8 @@ public class SpringIndexer { symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList()).add(enhancedSymbol.getSymbol()); if (enhancedSymbol.getAdditionalInformation() != null) { - addonInformation.add(enhancedSymbol.getAdditionalInformation()); - addonInformationByDoc.computeIfAbsent(docURI, s -> new ArrayList()).add(enhancedSymbol.getAdditionalInformation()); + addonInformation.addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); + addonInformationByDoc.computeIfAbsent(docURI, s -> new ArrayList()).addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); } }); } @@ -547,8 +549,8 @@ public class SpringIndexer { symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList()).add(enhancedSymbol.getSymbol()); if (enhancedSymbol.getAdditionalInformation() != null) { - addonInformation.add(enhancedSymbol.getAdditionalInformation()); - addonInformationByDoc.computeIfAbsent(docURI, s -> new ArrayList()).add(enhancedSymbol.getAdditionalInformation()); + addonInformation.addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); + addonInformationByDoc.computeIfAbsent(docURI, s -> new ArrayList()).addAll(Arrays.asList(enhancedSymbol.getAdditionalInformation())); } }); } @@ -711,7 +713,7 @@ public class SpringIndexer { symbols.removeAll(oldSymbols); } - List oldAddInInformation = addonInformationByDoc.remove(docURI); + List oldAddInInformation = addonInformationByDoc.remove(docURI); if (oldAddInInformation != null) { addonInformation.removeAll(oldAddInInformation); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java index 0d8586774..574740ffc 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java @@ -23,6 +23,7 @@ import java.util.stream.Collectors; import org.eclipse.lsp4j.SymbolInformation; import org.junit.Before; import org.junit.Test; +import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxHandlerInformation; import org.springframework.ide.vscode.project.harness.BootJavaLanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; @@ -50,7 +51,7 @@ public class WebFluxMappingSymbolProviderTest { assertTrue(containsSymbol(symbols, "@/users - Content-Type: application/json", docUri, 13, 1, 13, 74)); assertTrue(containsSymbol(symbols, "@/users/{username} - Content-Type: application/json", docUri, 18, 1, 18, 85)); - List addons = getAdditionalInformation(docUri); + List addons = getAdditionalInformation(docUri); assertNull(addons); } @@ -67,8 +68,8 @@ public class WebFluxMappingSymbolProviderTest { assertTrue(containsSymbol(symbols, "@/quotes -- GET - Accept: application/json", docUri, 24, 5, 24, 86)); assertTrue(containsSymbol(symbols, "@/quotes -- GET - Accept: application/stream+json", docUri, 25, 5, 25, 94)); - List addons = getAdditionalInformation(docUri); - assertEquals(4, addons.size()); + List addons = getAdditionalInformation(docUri); + assertEquals(8, addons.size()); WebfluxHandlerInformation handlerInfo1 = getWebfluxHandler(addons, "/hello", "GET").get(0); assertEquals("/hello", handlerInfo1.getPath()); @@ -115,8 +116,8 @@ public class WebFluxMappingSymbolProviderTest { assertTrue(containsSymbol(symbols, "@/person/ -- POST - Content-Type: application/json", docUri, 29, 6, 29, 83)); assertTrue(containsSymbol(symbols, "@/person -- GET - Accept: application/json", docUri, 28, 7, 28, 60)); - List addons = getAdditionalInformation(docUri); - assertEquals(3, addons.size()); + List addons = getAdditionalInformation(docUri); + assertEquals(6, addons.size()); WebfluxHandlerInformation handlerInfo1 = getWebfluxHandler(addons, "/person/{id}", "GET").get(0); assertEquals("/person/{id}", handlerInfo1.getPath()); @@ -155,8 +156,8 @@ public class WebFluxMappingSymbolProviderTest { assertTrue(containsSymbol(symbols, "@/ -- POST - Accept: application/json - Content-Type: application/json,application/pdf", docUri, 31, 6, 31, 117)); assertTrue(containsSymbol(symbols, "@/person -- GET,HEAD - Accept: text/plain,application/json", docUri, 30, 7, 30, 113)); - List addons = getAdditionalInformation(docUri); - assertEquals(3, addons.size()); + List addons = getAdditionalInformation(docUri); + assertEquals(6, addons.size()); WebfluxHandlerInformation handlerInfo1 = getWebfluxHandler(addons, "/person/{id}", "GET").get(0); assertEquals("/person/{id}", handlerInfo1.getPath()); @@ -199,8 +200,8 @@ public class WebFluxMappingSymbolProviderTest { assertTrue(containsSymbol(symbols, "@/person/ -- POST - Content-Type: application/json", docUri, 34, 5, 34, 82)); assertTrue(containsSymbol(symbols, "@/nestedDelete -- DELETE", docUri, 35, 42, 35, 93)); - List addons = getAdditionalInformation(docUri); - assertEquals(6, addons.size()); + List addons = getAdditionalInformation(docUri); + assertEquals(12, addons.size()); WebfluxHandlerInformation handlerInfo1 = getWebfluxHandler(addons, "/person/sub1/sub2/{id}", "GET").get(0); assertEquals("/person/sub1/sub2/{id}", handlerInfo1.getPath()); @@ -272,11 +273,11 @@ public class WebFluxMappingSymbolProviderTest { return harness.getServerWrapper().getComponents().getSpringIndexer().getSymbols(docUri); } - private List getAdditionalInformation(String docUri) { + private List getAdditionalInformation(String docUri) { return harness.getServerWrapper().getComponents().getSpringIndexer().getAdditonalInformation(docUri); } - private List getWebfluxHandler(List addons, String path, String httpMethod) { + private List getWebfluxHandler(List addons, String path, String httpMethod) { return addons.stream() .filter((obj) -> obj instanceof WebfluxHandlerInformation) .map((obj -> (WebfluxHandlerInformation) obj)) diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebfluxElementsInformationTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebfluxElementsInformationTest.java new file mode 100644 index 000000000..ee9a5d3fb --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebfluxElementsInformationTest.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.requestmapping.test; + +import static org.junit.Assert.*; + +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.junit.Test; +import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxElementsInformation; + +/** + * @author Martin Lippert + */ +public class WebfluxElementsInformationTest { + + @Test + public void testContainsSingleCharacterRange() { + Range range = new Range(new Position(3, 10), new Position(3, 10)); + WebfluxElementsInformation information = new WebfluxElementsInformation(new Range[] {range}); + + assertFalse(information.contains(new Position(3, 9))); + assertTrue(information.contains(new Position(3, 10))); + assertFalse(information.contains(new Position(3, 11))); + } + + @Test + public void testContainsSingleLineRange() { + Range range = new Range(new Position(3, 10), new Position(3, 20)); + WebfluxElementsInformation information = new WebfluxElementsInformation(new Range[] {range}); + + assertFalse(information.contains(new Position(3, 5))); + assertTrue(information.contains(new Position(3, 11))); + assertFalse(information.contains(new Position(3, 25))); + + assertFalse(information.contains(new Position(1, 12))); + assertFalse(information.contains(new Position(2, 1))); + assertFalse(information.contains(new Position(4, 21))); + } + + @Test + public void testContainsMultipleLineRange() { + Range range = new Range(new Position(2, 10), new Position(4, 5)); + WebfluxElementsInformation information = new WebfluxElementsInformation(new Range[] {range}); + + assertFalse(information.contains(new Position(1, 1))); + assertFalse(information.contains(new Position(1, 11))); + assertFalse(information.contains(new Position(2, 1))); + + assertFalse(information.contains(new Position(2, 9))); + assertTrue(information.contains(new Position(2, 10))); + assertTrue(information.contains(new Position(2, 11))); + assertTrue(information.contains(new Position(2, 40))); + assertTrue(information.contains(new Position(3, 1))); + assertTrue(information.contains(new Position(3, 12))); + assertTrue(information.contains(new Position(3, 50))); + assertTrue(information.contains(new Position(4, 1))); + assertTrue(information.contains(new Position(4, 5))); + assertFalse(information.contains(new Position(4, 6))); + assertFalse(information.contains(new Position(4, 10))); + + assertFalse(information.contains(new Position(5, 1))); + assertFalse(information.contains(new Position(5, 20))); + } + + @Test + public void testContainsMultipleRanges() { + Range range1 = new Range(new Position(2, 10), new Position(3, 20)); + Range range2 = new Range(new Position(5, 2), new Position(5, 3)); + Range range3 = new Range(new Position(10, 10), new Position(20, 20)); + Range range4 = new Range(new Position(4, 40), new Position(6, 3)); + + WebfluxElementsInformation information = new WebfluxElementsInformation(new Range[] {range1, range2, range3, range4}); + + assertFalse(information.contains(new Position(2, 9))); + assertTrue(information.contains(new Position(2, 10))); + assertTrue(information.contains(new Position(3, 19))); + assertTrue(information.contains(new Position(3, 20))); + assertFalse(information.contains(new Position(3, 21))); + + assertTrue(information.contains(new Position(5, 1))); + assertTrue(information.contains(new Position(5, 2))); + assertTrue(information.contains(new Position(5, 3))); + assertTrue(information.contains(new Position(5, 4))); + + assertFalse(information.contains(new Position(4, 39))); + assertTrue(information.contains(new Position(4, 40))); + assertTrue(information.contains(new Position(4, 41))); + + assertTrue(information.contains(new Position(6, 2))); + assertTrue(information.contains(new Position(6, 3))); + assertFalse(information.contains(new Position(6, 4))); + + assertFalse(information.contains(new Position(9, 10))); + assertFalse(information.contains(new Position(10, 9))); + assertTrue(information.contains(new Position(10, 10))); + assertTrue(information.contains(new Position(10, 21))); + assertTrue(information.contains(new Position(15, 3))); + assertTrue(information.contains(new Position(20, 20))); + assertFalse(information.contains(new Position(20, 21))); + assertFalse(information.contains(new Position(23, 1))); + } + +} diff --git a/vscode-extensions/vscode-bosh/package.json b/vscode-extensions/vscode-bosh/package.json index c6b11ed5e..e250262d5 100644 --- a/vscode-extensions/vscode-bosh/package.json +++ b/vscode-extensions/vscode-bosh/package.json @@ -3,7 +3,7 @@ "displayName": "Bosh Editor", "description": "Provides validation and content assist for various Bosh configuration files", "icon": "icon.png", - "version": "0.1.6", + "version": "0.2.0", "publisher": "Pivotal", "repository": { "type": "git", diff --git a/vscode-extensions/vscode-concourse/package.json b/vscode-extensions/vscode-concourse/package.json index 7079769de..4444d212b 100644 --- a/vscode-extensions/vscode-concourse/package.json +++ b/vscode-extensions/vscode-concourse/package.json @@ -3,7 +3,7 @@ "displayName": "Concourse CI Pipeline Editor", "description": "Provides validation and content assist for Concourse CI pipeline and task configuration yml files", "icon": "icon.png", - "version": "0.1.6", + "version": "0.2.0", "publisher": "Pivotal", "repository": { "type": "git", diff --git a/vscode-extensions/vscode-manifest-yaml/package.json b/vscode-extensions/vscode-manifest-yaml/package.json index 0159b1be9..2c45af68d 100644 --- a/vscode-extensions/vscode-manifest-yaml/package.json +++ b/vscode-extensions/vscode-manifest-yaml/package.json @@ -3,7 +3,7 @@ "displayName": "Cloudfoundry Manifest YML Support", "description": "Adds linting, content assist and hoverinfo's for Cloudfoundry Deployment Manifests (a.k.a. `manifest.yml`) files.", "icon": "icon.png", - "version": "0.1.6", + "version": "0.2.0", "publisher": "Pivotal", "repository": { "type": "git", diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 9f086fb02..bdd9dcf72 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -3,7 +3,7 @@ "displayName": "Spring Boot Tools", "description": "Provides validation and content assist for Spring Boot `application.properties`, `application.yml` properties files. As well as Boot-specific support for `.java` files.", "icon": "spring-boot-logo.png", - "version": "0.1.6", + "version": "0.2.0", "publisher": "Pivotal", "repository": { "type": "git",