From e27aece3f80942d11983c5a37f3ebac69ff29af7 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Thu, 19 Oct 2017 16:26:21 +0200 Subject: [PATCH 1/2] early work to provide quick navigation to running apps via live request mappings --- .../boot/java/BootJavaLanguageServer.java | 4 +- .../BootJavaWorkspaceSymbolHandler.java | 14 +++- .../LiveAppURLSymbolProvider.java | 74 +++++++++++++++++++ 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index 46441b849..28cf415ea 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -33,6 +33,7 @@ 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; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; +import org.springframework.ide.vscode.boot.java.requestmapping.LiveAppURLSymbolProvider; 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.scope.ScopeCompletionProcessor; @@ -48,7 +49,6 @@ import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.languageserver.HighlightParams; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; -import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; 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.reconcile.IReconcileEngine; @@ -126,7 +126,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { }); documents.onDocumentSymbol(new BootJavaDocumentSymbolHandler(indexer)); - workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(indexer)); + workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(indexer, new LiveAppURLSymbolProvider(serverParams.runningAppProvider))); BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(this, javaProjectFinder); documents.onCodeLens(codeLensHandler::createCodeLenses); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaWorkspaceSymbolHandler.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaWorkspaceSymbolHandler.java index 5e779b889..90236d9e4 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaWorkspaceSymbolHandler.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaWorkspaceSymbolHandler.java @@ -14,6 +14,7 @@ import java.util.List; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.WorkspaceSymbolParams; +import org.springframework.ide.vscode.boot.java.requestmapping.LiveAppURLSymbolProvider; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; import org.springframework.ide.vscode.commons.languageserver.util.WorkspaceSymbolHandler; @@ -22,15 +23,22 @@ import org.springframework.ide.vscode.commons.languageserver.util.WorkspaceSymbo */ public class BootJavaWorkspaceSymbolHandler implements WorkspaceSymbolHandler { - private SpringIndexer indexer; + private final SpringIndexer indexer; + private final LiveAppURLSymbolProvider liveAppSymbolProvider; - public BootJavaWorkspaceSymbolHandler(SpringIndexer indexer) { + public BootJavaWorkspaceSymbolHandler(SpringIndexer indexer, LiveAppURLSymbolProvider liveAppSymbolProvider) { this.indexer = indexer; + this.liveAppSymbolProvider = liveAppSymbolProvider; } @Override public List handle(WorkspaceSymbolParams params) { - return indexer.getAllSymbols(params.getQuery()); + if (params.getQuery() != null && params.getQuery().startsWith("//")) { + return liveAppSymbolProvider.getSymbols(params.getQuery()); + } + else { + return indexer.getAllSymbols(params.getQuery()); + } } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java new file mode 100644 index 000000000..f04efc773 --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2017 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.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.SymbolKind; +import org.json.JSONObject; +import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; +import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; +import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; +import org.springframework.ide.vscode.commons.util.Log; + +/** + * @author Martin Lippert + */ +public class LiveAppURLSymbolProvider { + + private final RunningAppProvider runningAppProvider; + + public LiveAppURLSymbolProvider(RunningAppProvider runningAppProvider) { + this.runningAppProvider = runningAppProvider; + } + + public List getSymbols(String query) { + System.out.println(query); + + List result = new ArrayList<>(); + + try { + SpringBootApp[] runningApps = runningAppProvider.getAllRunningSpringApps().stream() + .filter((app) -> !app.containsSystemProperty(BootJavaLanguageServer.LANGUAGE_SERVER_PROCESS_PROPERTY)) + .toArray(SpringBootApp[]::new); + + for (SpringBootApp app : runningApps) { + try { + String mappings = app.getRequestMappings(); + JSONObject requestMappings = new JSONObject(mappings); + Iterator keys = requestMappings.keys(); + while (keys.hasNext()) { + String key = keys.next(); + String path = UrlUtil.extractPath(key); + if (path != null) { + String url = UrlUtil.createUrl(app.getHost(), app.getPort(), path); + result.add(new SymbolInformation(url, SymbolKind.File, new Location(url, new Range(new Position(0, 0), new Position(0, 1))))); + } + } + } + catch (Exception e) { + Log.log(e); + } + } + } catch (Exception e) { + Log.log(e); + } + + return result; + } + +} From 3bcc9091b2f2139668f2a381ee795769c6e55365 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Wed, 25 Oct 2017 10:38:54 +0200 Subject: [PATCH 2/2] finished work on simple live app request mappings symbols --- .../LiveAppURLSymbolProvider.java | 29 ++++++---- .../boot/java/requestmapping/UrlUtil.java | 31 ++++++++++ .../vscode/boot/java/utils/SpringIndexer.java | 2 +- .../java/requestmapping/test/UrlUtilTest.java | 58 +++++++++++++++++++ 4 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/UrlUtilTest.java diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java index f04efc773..97b4f7233 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java @@ -48,17 +48,7 @@ public class LiveAppURLSymbolProvider { for (SpringBootApp app : runningApps) { try { - String mappings = app.getRequestMappings(); - JSONObject requestMappings = new JSONObject(mappings); - Iterator keys = requestMappings.keys(); - while (keys.hasNext()) { - String key = keys.next(); - String path = UrlUtil.extractPath(key); - if (path != null) { - String url = UrlUtil.createUrl(app.getHost(), app.getPort(), path); - result.add(new SymbolInformation(url, SymbolKind.File, new Location(url, new Range(new Position(0, 0), new Position(0, 1))))); - } - } + collectLiveAppSymbols(result, app); } catch (Exception e) { Log.log(e); @@ -71,4 +61,21 @@ public class LiveAppURLSymbolProvider { return result; } + private void collectLiveAppSymbols(List result, SpringBootApp app) throws Exception { + String mappings = app.getRequestMappings(); + JSONObject requestMappings = new JSONObject(mappings); + Iterator keys = requestMappings.keys(); + while (keys.hasNext()) { + String key = keys.next(); + String extractedPath = UrlUtil.extractPath(key); + if (extractedPath != null) { + String[] splitPath = UrlUtil.splitPath(extractedPath); + for (String path : splitPath) { + String url = UrlUtil.createUrl(app.getHost(), app.getPort(), path); + result.add(new SymbolInformation(url, SymbolKind.Method, new Location(url, new Range(new Position(0, 0), new Position(0, 1))))); + } + } + } + } + } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java index 27d60fc42..e56501510 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java @@ -10,6 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -64,4 +67,32 @@ public class UrlUtil { return null; } + + public static String[] splitPath(String path) { + if (path.contains("||")) { + List result = new ArrayList<>(); + + String basePath = path.substring(0, path.indexOf("||")).trim(); + result.add(basePath); + + if (basePath.lastIndexOf('/') > 0) { + basePath = basePath.substring(0, basePath.lastIndexOf('/')); + } + + String additionalPaths = path.substring(path.indexOf("||")); + StringTokenizer tokenizer = new StringTokenizer(additionalPaths, "||"); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken().trim(); + if (token.length() > 0) { + result.add(basePath + "/" + token); + } + } + + return result.toArray(new String[result.size()]); + } + else { + return new String[] {path}; + } + } + } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index 64cd34717..e1ed5414e 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -204,7 +204,7 @@ public class SpringIndexer { return null; } - public List getAllSymbols(String query) { + public List getAllSymbols(String query) { if (initializeTask != null) { try { initializeTask.get(); diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/UrlUtilTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/UrlUtilTest.java new file mode 100644 index 000000000..7ddf386f9 --- /dev/null +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/UrlUtilTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2017 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.junit.Test; +import org.springframework.ide.vscode.boot.java.requestmapping.UrlUtil; + +/** + * @author Martin Lippert + */ +public class UrlUtilTest { + + @Test + public void testSplitPathWithoutDuplicate() { + String path = "/superpath"; + String[] splitPath = UrlUtil.splitPath(path); + assertEquals(1, splitPath.length); + assertEquals("/superpath", splitPath[0]); + } + + @Test + public void testSplitPathSimpleCaseWithEmptyOr() { + String path = "/superpath/mypath || "; + String[] splitPath = UrlUtil.splitPath(path); + assertEquals(1, splitPath.length); + assertEquals("/superpath/mypath", splitPath[0]); + } + + @Test + public void testSplitPathSimpleCase() { + String path = "/superpath/mypath || mypath.json"; + String[] splitPath = UrlUtil.splitPath(path); + assertEquals(2, splitPath.length); + assertEquals("/superpath/mypath", splitPath[0]); + assertEquals("/superpath/mypath.json", splitPath[1]); + } + + @Test + public void testSplitPathMultipleCases() { + String path = "/superpath/mypath || mypath.json || somethingelse.what"; + String[] splitPath = UrlUtil.splitPath(path); + assertEquals(3, splitPath.length); + assertEquals("/superpath/mypath", splitPath[0]); + assertEquals("/superpath/mypath.json", splitPath[1]); + assertEquals("/superpath/somethingelse.what", splitPath[2]); + } + +}