diff --git a/vscode-extensions/commons/application-properties-metadata/pom.xml b/vscode-extensions/commons/application-properties-metadata/pom.xml index f0ede3136..14ec55193 100644 --- a/vscode-extensions/commons/application-properties-metadata/pom.xml +++ b/vscode-extensions/commons/application-properties-metadata/pom.xml @@ -33,20 +33,22 @@ - - + org.springframework.ide.vscode commons-util ${project.version} - + + org.springframework.ide.vscode + commons-language-server + ${project.version} + \ No newline at end of file diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/CachingValueProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/CachingValueProvider.java new file mode 100644 index 000000000..b3ecc5caa --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/CachingValueProvider.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2016 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.properties.metadata; + +import java.time.Duration; + +import org.springframework.ide.vscode.boot.properties.metadata.ValueProviderRegistry.ValueProviderStrategy; + +/** + * A abstract {@link ValueProviderStrategy} that is mean to help speedup successive invocations of + * content assist with a similar 'query' string. + *

+ * This implementation is meant to be used for providers that use potentially lenghty/expensive searches + * to determine hints. Since content assist hints are requested by Eclipse CA framework directly on + * the UI thread, they can not simply perform a lengthy search and block UI thread until it finished. + *

+ * This implementation therefore does the following: + *

+ * + * TODO: rather than an abstract class this should really be 'Wrapper' class that delegates to another + * {@link ValueProviderStrategy} and adds a cache in front of it. + * + * @author Kris De Volder + */ +public abstract class CachingValueProvider implements ValueProviderStrategy { + + private static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1000); + + /** + * Content assist is called inside UI thread and so doing something lenghty things + * like a JavaSearch will block the UI thread completely freezing the UI. So, we + * only return as many results as can be obtained within this hard TIMEOUT limit. + */ + public static Duration TIMEOUT = DEFAULT_TIMEOUT; + + /** + * The maximum number of results returned for a single request. Used to limit the + * values that are cached per entry. + */ + private int MAX_RESULTS = 500; + +// private Cache, CacheEntry> cache = createCache(); +// +// private class CacheEntry { +// boolean isComplete = false; +// int count = 0; +// Flux values; +// +// public CacheEntry(String query, Flux producer) { +// values = producer +// .take(MAX_RESULTS) +// .cache(MAX_RESULTS); +// values.subscribe(); // create infinite demand so that we actually force cache entries to be fetched upto the max. +// } +// +// @Override +// public String toString() { +// return "CacheEntry [isComplete=" + isComplete + ", count=" + count + "]"; +// } +// +// } +// +// @Override +// public final Flux getValues(IJavaProject javaProject, String query) { +//// debug("CA query: "+query); +// Tuple2 key = key(javaProject, query); +// CacheEntry cached = cache.get(key); +// if (cached==null) { +// cache.put(key, cached = new CacheEntry(query, getValuesIncremental(javaProject, query))); +// } +// return cached.values; +// } +// +// /** +// * Tries to use an already cached, complete result for a query that is a prefix of the current query to speed things up. +// *

+// * Falls back on doing a full-blown search if there's no usable 'prefix-query' in the cache. +// */ +// private Flux getValuesIncremental(IJavaProject javaProject, String query) { +//// debug("trying to solve "+query+" incrementally"); +// String subquery = query; +// while (subquery.length()>=1) { +// subquery = subquery.substring(0, subquery.length()-1); +// CacheEntry cached = cache.get(key(javaProject, subquery)); +// if (cached!=null) { +// System.out.println("cached "+subquery+": "+cached); +// if (cached.isComplete) { +//// debug("filtering "+subquery+" -> "+query); +// return cached.values +//// .doOnNext((hint) -> debug("filter["+query+"]: "+hint.getValue())) +// .filter((hint) -> 0!=FuzzyMatcher.matchScore(query, hint.getValue().toString())); +// } else { +//// debug("subquery "+subquery+" cached but is incomplete"); +// } +// } +// } +//// debug("full search for: "+query); +// return getValuesAsycn(javaProject, query); +// } +// +// protected abstract Flux getValuesAsycn(IJavaProject javaProject, String query); +// +// private Tuple2 key(IJavaProject javaProject, String query) { +// return Tuples.of(javaProject==null?null:javaProject.getElementName(), query); +// } +// +// protected Cache createCache() { +// return new LimitedTimeCache<>(Duration.ofMinutes(1)); +// } + + public static void restoreDefaults() { + TIMEOUT = DEFAULT_TIMEOUT; + } + +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/SpringPropertyIndexProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/SpringPropertyIndexProvider.java new file mode 100644 index 000000000..6229380ca --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/SpringPropertyIndexProvider.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2015 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.properties.metadata; + +import org.springframework.ide.vscode.util.IDocument; +import org.springframework.ide.vscode.boot.properties.util.FuzzyMap; + + +public abstract class SpringPropertyIndexProvider { + + public abstract FuzzyMap getIndex(IDocument doc); + +} diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/Editor.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/Editor.java index 55e030de3..50c5b8e25 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/Editor.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/Editor.java @@ -218,7 +218,7 @@ public class Editor { assertEquals(expect.toString(), actual.toString()); } - private void apply(CompletionItem completion) throws Exception { + public void apply(CompletionItem completion) throws Exception { TextEdit edit = completion.getTextEdit(); String docText = document.getText(); if (edit!=null) { @@ -269,7 +269,7 @@ public class Editor { } } - private List getCompletions() throws Exception { + public List getCompletions() throws Exception { CompletionList cl = harness.getCompletions(this.document, this.getCursor()); ArrayList items = new ArrayList<>(cl.getItems()); Collections.sort(items, new Comparator() { @@ -290,6 +290,10 @@ public class Editor { return items; } + public CompletionItem getFirstCompletion() throws Exception { + return getCompletions().get(0); + } + private Position getCursor() { return document.toPosition(selectionStart); } @@ -302,6 +306,10 @@ public class Editor { throw new UnsupportedOperationException("Not implemented yet!"); } + public void assertNoHover(String string) { + throw new UnsupportedOperationException("Not implemented yet!"); + } + public void setSelection(int start, int end) { Assert.assertTrue(start>=0); Assert.assertTrue(end>=start); @@ -315,4 +323,44 @@ public class Editor { return "Editor(\n"+getText()+"\n)"; } + public void assertLinkTargets(String hoverOver, String... expecteds) { + throw new UnsupportedOperationException("Not implemented yet!"); +// Editor editor = this; +// int pos = editor.middleOf(hoverOver); +// assertTrue("Not found in editor: '"+hoverOver+"'", pos>=0); +// +// List targets = getLinkTargets(editor, pos); +// assertEquals(expecteds.length, targets.size()); +// for (int i = 0; i < expecteds.length; i++) { +// assertEquals(expecteds[i], JavaElementLabels.getElementLabel(targets.get(i), JavaElementLabels.DEFAULT_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES)); +// } + } + + /** + * Get a problem that covers the given text in the editor. Throws exception + * if no matching problem is found. + */ + public Diagnostic assertProblem(String coveredText) throws Exception { + Editor editor = this; + List problems = editor.reconcile(); + for (Diagnostic p : problems) { + String c = editor.getText(p.getRange()); + if (c.equals(coveredText)) { + return p; + } + } + fail("No problem found covering the text '"+coveredText+"' in: \n" + + problemSumary(editor, problems) + ); + return null; //unreachable but compiler doesn't know + } + + public CompletionItem assertFirstQuickfix(Diagnostic problem, String expectLabel) { + throw new UnsupportedOperationException("Not implemented yet!"); + } + + public void assertText(String expected) { + assertEquals(expected, getText()); + } + } diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/IType.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/IType.java new file mode 100644 index 000000000..6ed7f2d34 --- /dev/null +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/IType.java @@ -0,0 +1,9 @@ +package org.springframework.ide.vscode.testharness; + +/** + * Replaces eclipse JDT IType. Maybe we keep this maybe not, if keep, it should move to some + * other place, not stay here in the testharness. + */ +public interface IType { + +} diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/LanguageServerHarness.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/LanguageServerHarness.java index 39f31ea73..a705116fd 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/LanguageServerHarness.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/LanguageServerHarness.java @@ -1,5 +1,7 @@ package org.springframework.ide.vscode.testharness; +import static org.junit.Assert.assertEquals; + import java.io.File; import java.nio.charset.Charset; import java.nio.file.Files; @@ -252,4 +254,30 @@ public class LanguageServerHarness { return File.createTempFile("workingcopy", getFileExtension()).toURI().toString(); } + public void assertCompletion(String textBefore, String expectTextAfter) throws Exception { + Editor editor = newEditor(textBefore); + CompletionItem completion = editor.getFirstCompletion(); + editor.apply(completion); + assertEquals(expectTextAfter, editor.getText()); + } + + public void assertCompletions(String textBefore, String... expectTextAfter) throws Exception { + Editor editor = newEditor(textBefore); + StringBuilder expect = new StringBuilder(); + StringBuilder actual = new StringBuilder(); + for (String after : expectTextAfter) { + expect.append(after); + expect.append("\n-------------------\n"); + } + + List completions = editor.getCompletions(); + for (CompletionItem ci : completions) { + editor = newEditor(textBefore); + editor.apply(ci); + actual.append(editor.getText()); + actual.append("\n-------------------\n"); + } + assertEquals(expect.toString(), actual.toString()); + } + } diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/TestProject.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/TestProject.java new file mode 100644 index 000000000..1c06ceb61 --- /dev/null +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/TestProject.java @@ -0,0 +1,11 @@ +package org.springframework.ide.vscode.testharness; + +import java.nio.file.Path; + +public interface TestProject { + + Path getPath(); + + IType findType(String string); + +} diff --git a/vscode-extensions/commons/pom.xml b/vscode-extensions/commons/pom.xml index fcd31ef6e..e41b048b6 100644 --- a/vscode-extensions/commons/pom.xml +++ b/vscode-extensions/commons/pom.xml @@ -26,6 +26,7 @@ 2.5.0 2.10 0.3.0 + 3.0.2.RELEASE diff --git a/vscode-extensions/vscode-application-yaml/pom.xml b/vscode-extensions/vscode-application-yaml/pom.xml index 4cb086205..65011bbe7 100644 --- a/vscode-extensions/vscode-application-yaml/pom.xml +++ b/vscode-extensions/vscode-application-yaml/pom.xml @@ -44,7 +44,13 @@ org.springframework.ide.vscode commons-language-server - 0.0.1-SNAPSHOT + ${project.version} + + + + org.springframework.ide.vscode + application-properties-metadata + ${project.version} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/AbstractPropsEditorTest.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/AbstractPropsEditorTest.java new file mode 100644 index 000000000..6500c1b29 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/AbstractPropsEditorTest.java @@ -0,0 +1,182 @@ +package org.springframework.ide.vscode.yaml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Before; +import org.springframework.ide.vscode.boot.properties.metadata.SpringPropertyIndexProvider; +import org.springframework.ide.vscode.testharness.Editor; +import org.springframework.ide.vscode.testharness.LanguageServerHarness; +import org.springframework.ide.vscode.testharness.TestProject; +import org.springframework.ide.vscode.yaml.PropertyIndexHarness.ItemConfigurer; + +import io.typefox.lsapi.CompletionItem; + +public class AbstractPropsEditorTest { + + private PropertyIndexHarness md; + private LanguageServerHarness harness; + + public Editor newEditor(String contents) throws Exception { + return harness.newEditor(contents); + } + + @Before + public void setup() throws Exception { + md = new PropertyIndexHarness(); + harness = new LanguageServerHarness(YamlLanguageServer::new); + harness.intialize(null); + } + + public ItemConfigurer data(String id, String type, Object deflt, String description, String... sources) { + return md.data(id, type, deflt, description, sources); + } + + public void defaultTestData() { + md.defaultTestData(); + } + + public TestProject createPredefinedMavenProject(String string) { + notImplemented(); + return null; + } + + public void useProject(TestProject p) { + notImplemented(); //only tests that don't require project context / classpath work for now + } + + /** + * Simulates applying the first completion to a text buffer and checks the result. + */ + public void assertCompletion(String textBefore, String expectTextAfter) throws Exception { + harness.assertCompletion(textBefore, expectTextAfter); + } + + private void notImplemented() { + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Checks that completions contains a completion with a given display string and that that completion + * has a info hover that contains a given snippet of text. + */ + public void assertCompletionWithInfoHover(String editorText, String expectLabel, String expectInfoSnippet) throws Exception { + notImplemented(); + } + + public boolean isEmptyMetadata() { + return md.isEmpty(); + } + + /** + * Checks that applying completions to a given 'textBefore' editor content produces the + * expected results. + */ + public void assertCompletions(String textBefore, String... expectTextAfter) throws Exception { + harness.assertCompletions(textBefore, expectTextAfter); + } + + public void assertNoCompletions(String text) throws Exception { + assertCompletions(text /*NONE*/); + } + + /** + * Checks that completions contains a completion with a given display string (and check that + * it applies as expected). + */ + public void assertCompletionWithLabel(String textBefore, String expectLabel, String expectTextAfter) throws Exception { + Editor editor = newEditor(textBefore); + List completions = editor.getCompletions(); + CompletionItem completion = assertCompletionWithLabel(expectLabel, completions); + editor.apply(completion); + assertEquals(expectTextAfter, editor.getText()); + } + + private CompletionItem assertCompletionWithLabel(String expectLabel, List completions) { + StringBuilder found = new StringBuilder(); + for (CompletionItem c : completions) { + String actualLabel = c.getLabel(); + found.append(actualLabel+"\n"); + if (actualLabel.equals(expectLabel)) { + return c; + } + } + fail("No completion found with label '"+expectLabel+"' in:\n"+found); + return null; //unreachable, but compiler doesn't know that. + } + + public void assertCompletionCount(int expected, String editorText) throws Exception { + Editor editor = newEditor(editorText); + assertEquals(expected, editor.getCompletions().size()); + } + + public void assertCompletionsDisplayString(String editorText, String... completionsLabels) throws Exception { + Editor editor = newEditor(editorText); + List completions = editor.getCompletions(); + String[] actualLabels = new String[completions.size()]; + for (int i = 0; i < actualLabels.length; i++) { + actualLabels[i] = completions.get(i).getLabel(); + } + assertElements(actualLabels, completionsLabels); + } + + public SpringPropertyIndexProvider getIndexProvider() { + return md.getIndexProvider(); + } + + @SafeVarargs + public static void assertElements(T[] actual, T... expect) { + assertElements(Arrays.asList(actual), expect); + } + + @SafeVarargs + public static void assertElements(Collection actual, T... expect) { + Set expectedSet = new HashSet(Arrays.asList(expect)); + + for (T propVal : actual) { + if (!expectedSet.remove(propVal)) { + fail("Unexpected element: "+propVal); + } + } + + if (!expectedSet.isEmpty()) { + StringBuilder missing = new StringBuilder(); + for (T propVal : expectedSet) { + missing.append(propVal+"\n"); + } + fail("Missing elements: \n"+missing); + } + } + + public void assertStyledCompletions(String editorText, StyledStringMatcher... expectStyles) throws Exception { + Editor editor = newEditor(editorText); + List completions = editor.getCompletions(); + assertEquals("Wrong number of elements", expectStyles.length, completions.size()); + for (int i = 0; i < expectStyles.length; i++) { + CompletionItem completion = completions.get(i); + throw new UnsupportedOperationException("Suport for styled labels not implemented"); +// StyledString actualLabel = getStyledDisplayString(completion); +// expectStyles[i].match(actualLabel); + } + } + + + public void deprecate(String key, String replacedBy, String reason) { + md.deprecate(key, replacedBy, reason); + } + + public void keyHints(String id, String... hintValues) { + md.keyHints(id, hintValues); + } + + public void valueHints(String id, String... hintValues) { + md.valueHints(id, hintValues); + } + +} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/ApplicationYamlEditorTest.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/ApplicationYamlEditorTest.java index 11c33e3ca..086bef0a4 100644 --- a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/ApplicationYamlEditorTest.java +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/ApplicationYamlEditorTest.java @@ -1,9 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 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.yaml; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.time.Duration; + +import org.junit.Ignore; import org.junit.Test; +import org.springframework.ide.vscode.boot.properties.metadata.CachingValueProvider; +import org.springframework.ide.vscode.boot.properties.metadata.PropertyInfo; import org.springframework.ide.vscode.testharness.Editor; import org.springframework.ide.vscode.testharness.LanguageServerHarness; -import org.springframework.ide.vscode.yaml.YamlLanguageServer; +import org.springframework.ide.vscode.testharness.TestProject; +import org.springframework.ide.vscode.util.StringUtil; + +import io.typefox.lsapi.CompletionItem; +import io.typefox.lsapi.Diagnostic; /** * This class is a placeholder where we will attempt to copy and port @@ -12,26 +35,15 @@ import org.springframework.ide.vscode.yaml.YamlLanguageServer; * * @author Kris De Volder */ -public class ApplicationYamlEditorTest { +public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { + + //////////////////////////////////////////////////////////////////////////////////////// - @Test public void testReconcileCatchesParseError() throws Exception { - LanguageServerHarness harness = new LanguageServerHarness(YamlLanguageServer::new); - harness.intialize(null); - - Editor editor = harness.newEditor( - "somemap: val\n"+ - "- sequence" - ); - editor.assertProblems( - "-|expected " - ); - } - @Test public void linterRunsOnDocumentOpenAndChange() throws Exception { LanguageServerHarness harness = new LanguageServerHarness(YamlLanguageServer::new); harness.intialize(null); - Editor editor = harness.newEditor( + Editor editor = newEditor( "somemap: val\n"+ "- sequence" ); @@ -49,4 +61,3460 @@ public class ApplicationYamlEditorTest { "z|expected " ); } + + ///////////////////// ported tests from old STS code base //////////////////////////////////////////////// + + @Ignore @Test public void testHovers() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "spring:\n" + + " application:\n" + + " name: foofoo\n" + + " beyond:\n" + + " the-valid: range\n" + + " \n" + + "server:\n" + + " port: 8888" + ); + + editor.assertIsHoverRegion("spring"); + editor.assertIsHoverRegion("application"); + editor.assertIsHoverRegion("name"); + + editor.assertIsHoverRegion("server"); + editor.assertIsHoverRegion("port"); + + editor.assertHoverContains("name", "spring.application.name"); + editor.assertHoverContains("port", "server.port"); + editor.assertHoverContains("8888", "server.port"); // hover over value show info about corresponding key. Is this logical? + + editor.assertNoHover("beyond"); + editor.assertNoHover("the-valid"); + editor.assertNoHover("range"); + + //Note currently, these provide no hovers, but maybe (some of them) should if we index proprty sources and not just the + // properties themselves. + editor.assertNoHover("spring"); + editor.assertNoHover("application"); + editor.assertNoHover("server"); + + + //Test for the case where we can't produce an AST for editor text + editor = newEditor( + "- syntax\n" + + "error:\n" + ); + editor.assertNoHover("syntax"); + editor.assertNoHover("error"); + } + + @Ignore @Test public void testHoverInfoForEnumValueInMapKey() throws Exception { + Editor editor; + TestProject project = createPredefinedMavenProject("boot13"); + useProject(project); + + //This test will fail if source jars haven't been downloaded. + //Probably that is why it fails in CI build? + //Let's check: +// IType type = project.findType("com.fasterxml.jackson.databind.SerializationFeature"); +// System.out.println(">>> source for: "+type.getFullyQualifiedName()); +// System.out.println(downloadSources(type)); +// System.out.println("<<< source for: "+type.getFullyQualifiedName()); + + editor = newEditor( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " indent-output: true" + ); + editor.assertIsHoverRegion("indent-output"); + editor.assertHoverContains("indent-output", "allows enabling (or disabling) indentation"); + + //Also try that it works when spelled in all upper-case + editor = newEditor( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " INDENT_OUTPUT: true" + ); + editor.assertIsHoverRegion("INDENT_OUTPUT"); + editor.assertHoverContains("INDENT_OUTPUT", "allows enabling (or disabling) indentation"); + } + + @Ignore @Test public void testHoverInfoForEnumValueInMapKeyCompletion() throws Exception { + TestProject project = createPredefinedMavenProject("boot13"); + useProject(project); + + //This test will fail if source jars haven't been downloaded. + //Probably that is why it fails in CI build? + //Let's check: +// IType type = project.findType("com.fasterxml.jackson.databind.SerializationFeature"); +// System.out.println(">>> source for: "+type.getFullyQualifiedName()); +// System.out.println(downloadSources(type)); +// System.out.println("<<< source for: "+type.getFullyQualifiedName()); + + assertCompletionWithInfoHover( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " ind<*>" + , // ========== + "indent-output : boolean" + , // ==> + "allows enabling (or disabling) indentation" + ); + } + + @Ignore @Test public void testHoverInfoForValueHintCompletion() throws Exception { + data("my.bonus", "java.lang.String", null, "Bonus type") + .valueHint("small", "A small bonus. For a little extra incentive.") + .valueHint("large", "An large bonus. For the ones who deserve it.") + .valueHint("exorbitant", "Truly outrageous. Who deserves a bonus like that?"); + + assertCompletionWithInfoHover( + "my:\n" + + " bonus: l<*>" + , // ========== + "large" + , // ==> + "For the ones who deserve it" + ); + } + + @Ignore @Test public void testHoverInfoForValueHint() throws Exception { + data("my.bonus", "java.lang.String", null, "Bonus type") + .valueHint("small", "A small bonus. For a little extra incentive.") + .valueHint("large", "An large bonus. For the ones who deserve it.") + .valueHint("exorbitant", "Truly outrageous. Who deserves a bonus like that?"); + + Editor editor = newEditor( + "#comment here\n" + + "my:\n" + + " bonus: large\n" + ); + + editor.assertIsHoverRegion("large"); + editor.assertHoverContains("large", "For the ones who deserve it"); + } + + + @Ignore @Test public void testUserDefinedHoversandLinkTargets() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("foo.link-tester", "demo.LinkTestSubject", null, "for testing different Pojo link cases"); + Editor editor = newEditor( + "#A comment at the start\n" + + "foo:\n" + + " data:\n" + + " wavelen: 666\n" + + " name: foo\n" + + " next: green\n" + + " link-tester:\n" + + " has-it-all: nice\n" + + " strange: weird\n" + + " getter-only: getme\n" + ); + + editor.assertHoverContains("data", "Pojo"); // description from json metadata + editor.assertHoverContains("wavelen", "JavaDoc from field"); // javadoc from field + editor.assertHoverContains("name", "Set the name"); // javadoc from setter + editor.assertHoverContains("next", "Get the next"); // javadoc from getter + + editor.assertLinkTargets("data", "demo.FooProperties.setdata(ColorData)"); + editor.assertLinkTargets("wavelen", "demo.ColorData.setWavelen(double)"); + + } + + @Ignore @Test public void testHyperlinkTargets() throws Exception { + System.out.println(">>> testHyperlinkTargets"); + TestProject p = createPredefinedMavenProject("demo"); + useProject(p); + + Editor editor = newEditor( + "server:\n"+ + " port: 888\n" + + "spring:\n" + + " datasource:\n" + + " login-timeout: 1000\n" + + "flyway:\n" + + " init-sqls: a,b,c\n" + ); + + editor.assertLinkTargets("port", + "org.springframework.boot.autoconfigure.web.ServerProperties.setPort(Integer)" + ); + editor.assertLinkTargets("login-", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.hikariDataSource()", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.tomcatDataSource()", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.dbcpDataSource()" + ); + editor.assertLinkTargets("init-sql", + "org.springframework.boot.autoconfigure.flyway.FlywayProperties.setInitSqls(List)"); + System.out.println("<<< testHyperlinkTargets"); + } + + @Ignore @Test public void testReconcile() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server:\n" + + " port: \n" + + " extracrap: 8080\n" + + "logging:\n"+ + " level:\n" + + " com.acme: INFO\n" + + " snuggem: what?\n" + + "bogus:\n" + + " no: \n" + + " good: true\n" + ); + editor.assertProblems( + "extracrap: 8080|Expecting a 'int' but got a 'Mapping' node", + "snuggem|Unknown property", + "bogus|Unknown property" + ); + } + + @Ignore @Test public void test_STS_4140_StringArrayReconciling() throws Exception { + defaultTestData(); + + Editor editor; + + //Case 0: very focussed test for easy debugging + editor = newEditor( + "flyway:\n" + + " schemas: [MEMBER_VERSION, MEMBER]" + ); + editor.assertProblems(/*NONE*/); + + //Case 1: String[] as a flow sequence + editor = newEditor( + "something-bad: wrong\n"+ + "flyway:\n" + + " url: jdbc:h2:file:~/localdb;IGNORECASE=TRUE;mv_store=false\n" + + " user: admin\n" + + " password: admin\n" + + " schemas: [MEMBER_VERSION, MEMBER]" + ); + editor.assertProblems( + "something-bad|Unknown property" + //No other problems should be reported + ); + + //Case 1: String[] as a block sequence + editor = newEditor( + "something-bad: wrong\n"+ + "flyway:\n" + + " url: jdbc:h2:file:~/localdb;IGNORECASE=TRUE;mv_store=false\n" + + " user: admin\n" + + " password: admin\n" + + " schemas:\n" + + " - MEMBER_VERSION\n" + + " - MEMBER" + ); + editor.assertProblems( + "something-bad|Unknown property" + //No other problems should be reported + ); + } + + @Ignore @Test public void test_STS_4140_StringArrayCompletion() throws Exception { + defaultTestData(); + + assertCompletion( + "#some comment\n" + + "flyway:\n" + + " sch<*>\n" + , //=> + "#some comment\n" + + "flyway:\n" + + " schemas:\n" + + " - <*>\n" + ); + } + + @Ignore @Test public void testReconcileIntegerScalar() throws Exception { + data("server.port", "java.lang.Integer", null, "Port of server"); + data("server.threads", "java.lang.Integer", null, "Number of threads for server threadpool"); + Editor editor = newEditor( + "server:\n" + + " port: \n" + + " 8888\n" + + " threads:\n" + + " not-a-number\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + } + + @Ignore @Test public void testReconcileExpectMapping() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server:\n" + + " - a\n" + + " - b\n" + ); + editor.assertProblems( + "- a\n - b|Expecting a 'Mapping' node but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testReconcileExpectScalar() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server:\n" + + " ? - a\n" + + " - b\n" + + " : c" + ); + editor.assertProblems( + "- a\n - b|Expecting a 'Scalar' node but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testReconcileCamelCaseBasic() throws Exception { + Editor editor; + data("something.with-many-parts", "java.lang.Integer", "For testing tolerance of camelCase", null); + data("something.with-parts.and-more", "java.lang.Integer", "For testing tolerance of camelCase", null); + + //Nothing special... not using camel case + editor = newEditor( + "#Comment for good measure\n" + + "something:\n" + + " with-many-parts: not-a-number" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + + //Now check that reconcile also tolerates camel case and reports no error for it + editor = newEditor( + "#Comment for good measure\n" + + "something:\n" + + " withManyParts: 123" + ); + editor.assertProblems(/*NONE*/); + + //Now check that reconciler traverses camelCase and reports errors assigning to camelCase names + editor = newEditor( + "#Comment for good measure\n" + + "something:\n" + + " withManyParts: not-a-number" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + + //Now check also that camelcase tolerance works if its not in the end of the path + editor = newEditor( + "#Comment for good measure\n" + + "something:\n" + + " withParts:\n" + + " andMore: not-a-number\n" + + " bad: wrong\n" + + " something-bad: also wrong\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'", + "bad|Unknown property", + "something-bad|Unknown property" + ); + } + + @Ignore @Test public void testContentAssistCamelCaseBasic() throws Exception { + data("something.with-many-parts", "java.lang.Integer", "For testing tolerance of camelCase", null); + data("something.with-parts.and-more", "java.lang.Integer", "For testing tolerance of camelCase", null); + + assertCompletion( + "something:\n" + + " withParts:\n" + + " <*>", + // => + "something:\n" + + " withParts:\n" + + " and-more: <*>" + ); + } + + @Ignore @Test public void testReconcileCamelCaseBeanProp() throws Exception { + Editor editor; + + TestProject p = createPredefinedMavenProject("demo"); + useProject(p); + + data("demo.bean", "demo.CamelCaser", "For testing tolerance of camelCase", null); + + //Nothing special... not using camel case + editor = newEditor( + "#Comment for good measure\n" + + "demo:\n" + + " bean:\n"+ + " the-answer-to-everything: not-a-number\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + + //Now check that reconcile also tolerates camel case and reports no error for it + editor = newEditor( + "#Comment for good measure\n" + + "demo:\n" + + " bean:\n"+ + " theAnswerToEverything: 42\n" + ); + editor.assertProblems(/*NONE*/); + + //Now check that reconciler traverses camelCase and reports errors assigning to camelCase names + editor = newEditor( + "#Comment for good measure\n" + + "demo:\n" + + " bean:\n"+ + " theAnswerToEverything: not-a-number\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + + //Now check also that camelcase tolerance works if its not in the end of the path + editor = newEditor( + "#Comment for good measure\n" + + "demo:\n" + + " bean:\n"+ + " theAnswerToEverything: not-a-number\n"+ + " theLeft:\n"+ + " bad: wrong\n"+ + " theAnswerToEverything: not-this\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'", + "bad|Unknown property", + "not-this|Expecting a 'int'" + ); + } + + @Ignore @Test public void testContentAssistCamelCaseBeanProp() throws Exception { + TestProject p = createPredefinedMavenProject("demo"); + useProject(p); + + data("demo.bean", "demo.CamelCaser", "For testing tolerance of camelCase", null); + + assertCompletion( + "demo:\n" + + " bean:\n" + + " theLeft:\n" + + " answer<*>\n", + // => + "demo:\n" + + " bean:\n" + + " theLeft:\n" + + " the-answer-to-everything: <*>\n" + ); + } + + @Ignore @Test public void testContentAssistCamelCaseInsertInExistingContext() throws Exception { + data("demo-bean.first", "java.lang.String", "For testing tolerance of camelCase", null); + data("demo-bean.second", "java.lang.String", "For testing tolerance of camelCase", null); + + //Confirm first it works correctly without camelizing complications +// assertCompletion( +// "demo-bean:\n" + +// " first: numero uno\n" + +// "something-else: separator\n"+ +// "sec<*>", +// // => +// "demo-bean:\n" + +// " first: numero uno\n" + +// " second: <*>\n" + +// "something-else: separator\n" +// ); + + //Now with camels + assertCompletion( + "demoBean:\n" + + " first: numero uno\n" + + "something-else: separator\n"+ + "sec<*>", + // => + "demoBean:\n" + + " first: numero uno\n" + + " second: <*>\n" + + "something-else: separator\n" + ); + } + + + @Ignore @Test public void testReconcileBeanPropName() throws Exception { + TestProject p = createPredefinedMavenProject("demo-list-of-pojo"); + useProject(p); + assertNotNull(p.findType("demo.Foo")); + data("some-foo", "demo.Foo", null, "some Foo pojo property"); + Editor editor = newEditor( + "some-foo:\n" + + " name: Good\n" + + " bogus: Bad\n" + + " ? - a\n"+ + " - b\n"+ + " : Weird\n" + ); + editor.assertProblems( + "bogus|Unknown property 'bogus' for type 'demo.Foo'", + "- a\n - b|Expecting a bean-property name for object of type 'demo.Foo' " + + "but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testIgnoreSpringExpression() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server:\n" + + " port: ${random.int}\n" + //should not be an error + " bad: wrong\n" + ); + editor.assertProblems( + "bad|Unknown property" + ); + } + + @Ignore @Test public void testReconcilePojoArray() throws Exception { + TestProject p = createPredefinedMavenProject("demo-list-of-pojo"); + useProject(p); + assertNotNull(p.findType("demo.Foo")); + + { + Editor editor = newEditor( + "token-bad-guy: problem\n"+ + "volder:\n" + + " foo:\n" + + " list:\n"+ + " - name: Kris\n" + + " description: Kris\n" + + " roles:\n" + + " - Developer\n" + + " - Admin\n" + + " bogus: Bad\n" + ); + + editor.assertProblems( + "token-bad-guy|Unknown property", + //'name' is ok + //'description' is ok + "bogus|Unknown property 'bogus' for type 'demo.Foo'" + ); + } + + { //Pojo array can also be entered as a map with integer keys + + Editor editor = newEditor( + "token-bad-guy: problem\n"+ + "volder:\n" + + " foo:\n" + + " list:\n"+ + " 0:\n"+ + " name: Kris\n" + + " description: Kris\n" + + " roles:\n" + + " 0: Developer\n" + + " one: Admin\n" + + " bogus: Bad\n" + ); + + editor.assertProblems( + "token-bad-guy|Unknown property", + "one|Expecting a 'int' but got 'one'", + "bogus|Unknown property 'bogus' for type 'demo.Foo'" + ); + + } + + } + + @Ignore @Test public void testReconcileSequenceGotAtomicType() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "liquibase:\n" + + " enabled:\n" + + " - element\n" + ); + editor.assertProblems( + "- element|Expecting a 'boolean' but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testReconcileSequenceGotMapType() throws Exception { + data("the-map", "java.util.Map", null, "Nice mappy"); + Editor editor = newEditor( + "the-map:\n" + + " - a\n" + + " - b\n" + ); + editor.assertProblems( + "- a\n - b|Expecting a 'Map' but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testEnumPropertyReconciling() throws Exception { + TestProject p = createPredefinedMavenProject("demo-enum"); + useProject(p); + assertNotNull(p.findType("demo.Color")); + + data("foo.color", "demo.Color", null, "A foonky colour"); + Editor editor = newEditor( + "foo:\n"+ + " color: BLUE\n" + + "---\n" + + "foo:\n" + + " color: RED\n" + + "---\n" + + "foo:\n" + + " color: GREEN\n" + + "---\n" + + "foo:\n" + + " color:\n" + + " bad: BLUE\n" + + "---\n" + + "foo:\n" + + " color: Bogus\n" + ); + + editor.assertProblems( + "bad: BLUE|Expecting a 'demo.Color[RED, GREEN, BLUE]' but got a 'Mapping' node", + "Bogus|Expecting a 'demo.Color[RED, GREEN, BLUE]' but got 'Bogus'" + ); + } + + @Test public void testReconcileSkipIfNoMetadata() throws Exception { + Editor editor = newEditor( + "foo:\n"+ + " color: BLUE\n" + + " color: RED\n" + //technically not allowed to bind same key twice but we don' check this + " color: GREEN\n" + + " color:\n" + + " bad: BLUE\n" + + " color: Bogus\n" + ); + assertTrue(isEmptyMetadata()); + editor.assertProblems(/*NONE*/); + } + + @Test public void testReconcileCatchesParseError() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "somemap: val\n"+ + "- sequence" + ); + editor.assertProblems( + "-|expected " + ); + } + + @Test public void testReconcileCatchesScannerError() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "somemap: \"quotes not closed\n" + ); + editor.assertProblems( + "|unexpected end of stream" + ); + } + + @Ignore @Test public void testContentAssistSimple() throws Exception { + defaultTestData(); + assertCompletion("port<*>", + "server:\n"+ + " port: <*>"); + assertCompletion( + "#A comment\n" + + "port<*>", + "#A comment\n" + + "server:\n"+ + " port: <*>"); + assertCompletions( + "server:\n" + + " nonsense<*>\n" + //=> nothing + ); + } + + @Ignore @Test public void testContentAssistNullContext() throws Exception { + defaultTestData(); + assertCompletions( + "#A comment\n" + + "foo:\n" + + " data:\n" + + " bogus:\n" + + " <*>" + // => nothing + ); + } + + @Ignore @Test public void testContentAssistNested() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + data("server.address", "java.lang.String", "localhost", "Server host address"); + + assertCompletion( + "server:\n"+ + " port: 8888\n" + + " <*>" + , + "server:\n"+ + " port: 8888\n" + + " address: <*>" + ); + + assertCompletion( + "server:\n"+ + " <*>" + , + "server:\n"+ + " address: <*>" + ); + + assertCompletion( + "server:\n"+ + " a<*>" + , + "server:\n"+ + " address: <*>" + ); + + assertCompletion( + "server:\n"+ + " <*>\n" + + " port: 8888" + , + "server:\n"+ + " address: <*>\n" + + " port: 8888" + ); + + assertCompletion( + "server:\n"+ + " a<*>\n" + + " port: 8888" + , + "server:\n"+ + " address: <*>\n" + + " port: 8888" + ); + + } + + @Ignore @Test public void testContentAssistNestedSameLine() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + + assertCompletion( + "server: <*>" + , + "server: \n" + + " port: <*>" + ); + + assertCompletion( + "#something before this stuff\n" + + "server: <*>" + , + "#something before this stuff\n" + + "server: \n" + + " port: <*>" + ); + } + + @Ignore @Test public void testContentAssistInsertCompletionElsewhere() throws Exception { + defaultTestData(); + + assertCompletion( + "server:\n" + + " port: 8888\n" + + " address: \n" + + " servlet-path: \n" + + "spring:\n" + + " activemq:\n" + + "something-else: great\n" + + "aopauto<*>" + , + "server:\n" + + " port: 8888\n" + + " address: \n" + + " servlet-path: \n" + + "spring:\n" + + " activemq:\n" + + " aop:\n" + + " auto: <*>\n" + + "something-else: great\n" + ); + + assertCompletion( + "server:\n"+ + " address: localhost\n"+ + "something: nice\n"+ + "po<*>" + , + "server:\n"+ + " address: localhost\n"+ + " port: <*>\n" + + "something: nice\n" + ); + } + + @Ignore @Test public void testContentAssistInsertCompletionElsewhereInEmptyParent() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + data("server.address", "String", "localhost", "Server host address"); + + assertCompletion( + "#comment\n" + + "server:\n" + + "something:\n" + + " more\n" + + "po<*>" + , + "#comment\n" + + "server:\n" + + " port: <*>\n" + + "something:\n" + + " more\n" + ); + } + + @Ignore @Test public void testContentAssistInetAddress() throws Exception { + //Test that InetAddress is treated as atomic w.r.t indentation + + defaultTestData(); + assertCompletion( + "#set address of server\n" + + "servadd<*>" + , //=> + "#set address of server\n" + + "server:\n"+ + " address: <*>" + ); + + assertCompletion( + "#set address of server\n" + + "server:\n"+ + " port: 888\n" + + "more: stuff\n" + + "servadd<*>" + , //=> + "#set address of server\n" + + "server:\n"+ + " port: 888\n" + + " address: <*>\n" + + "more: stuff\n" + ); + + } + + @Ignore @Test public void testContentAssistInsertCompletionElsewhereThatAlreadyExists() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + data("server.address", "String", "localhost", "Server host address"); + + //inserting something that already exists should just move the cursor to existing node + + assertCompletion( + "server:\n"+ + " port:\n" + + " 8888\n"+ + " address: localhost\n"+ + "something: nice\n"+ + "po<*>" + , + "server:\n"+ + " port:\n"+ + " <*>8888\n" + + " address: localhost\n"+ + "something: nice\n" + ); + + assertCompletion( + "server:\n"+ + " port: 8888\n" + + " address: localhost\n"+ + "something: nice\n"+ + "po<*>" + , + "server:\n"+ + " port: <*>8888\n" + + " address: localhost\n"+ + "something: nice\n" + ); + + assertCompletion( + "server:\n"+ + " port:\n"+ + " address: localhost\n"+ + "something: nice\n"+ + "po<*>" + , + "server:\n"+ + " port:<*>\n" + + " address: localhost\n"+ + "something: nice\n" + ); + } + + + @Ignore @Test public void testContentAssistPropertyWithMapType() throws Exception { + data("foo.mapping", "java.util.Map", null, "Nice little map"); + + //Try in-place completion + assertCompletion( + "map<*>" + , + "foo:\n"+ + " mapping:\n"+ + " <*>" + ); + + //Try 'elswhere' completion + assertCompletion( + "foo:\n" + + " something:\n" + + "more: stuff\n" + + "map<*>" + , + "foo:\n" + + " something:\n" + + " mapping:\n" + + " <*>\n" + + "more: stuff\n" + ); + } + + @Ignore @Test public void testContentAssistPropertyWithArrayType() throws Exception { + data("foo.list", "java.util.List", null, "Nice little list"); + + //Try in-place completion + assertCompletion( + "lis<*>" + , + "foo:\n"+ + " list:\n"+ + " - <*>" + ); + + //Try 'elsewhere' completion + assertCompletion( + "foo:\n" + + " something:\n" + + "more: stuff\n" + + "lis<*>" + , + "foo:\n" + + " something:\n" + + " list:\n" + + " - <*>\n" + + "more: stuff\n" + ); + } + + @Ignore @Test public void testContentAssistPropertyWithPojoType() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + //Try in-place completion + assertCompletion( + "foo.d<*>" + , + "foo:\n" + + " data:\n" + + " <*>" + ); + + //Try 'elsewhere' completion + assertCompletion( + "foo:\n" + + " something:\n" + + "more: stuff\n" + + "foo.d<*>" + , + "foo:\n" + + " something:\n" + + " data:\n" + + " <*>\n" + + "more: stuff\n" + ); + } + + @Ignore @Test public void testContentAssistPropertyWithEnumType() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + //Try in-place completion + assertCompletion( + "foo.co<*>" + , + "foo:\n" + + " color: <*>" + ); + + //Try 'elsewhere' completion + assertCompletion( + "foo:\n" + + " something:\n" + + "more: stuff\n" + + "foo.co<*>" + , + "foo:\n" + + " something:\n" + + " color: <*>\n" + + "more: stuff\n" + ); + } + + @Ignore @Test public void testCompletionForExistingGlobalPropertiesAreDemoted() throws Exception { + data("foo.bar", "java.lang.String", null, null); + data("foo.buttar", "java.lang.String", null, null); + data("foo.baracks", "java.lang.String", null, null); + data("foo.zamfir", "java.lang.String", null, null); + assertCompletions( + "foo:\n" + + " bar: nice\n" + + " baracks: full\n" + + "something:\n" + + " in: between\n"+ + "bar<*>", + //=> + // buttar + "foo:\n" + + " bar: nice\n" + + " baracks: full\n" + + " buttar: <*>\n" + + "something:\n" + + " in: between", + // bar (already existed so becomes navigation) + "foo:\n" + + " bar: <*>nice\n" + + " baracks: full\n" + + "something:\n" + + " in: between", + // baracks (already existed so becomes navigation) + "foo:\n" + + " bar: nice\n" + + " baracks: <*>full\n" + + "something:\n" + + " in: between" + ); + } + + @Ignore @Test public void testCompletionForExistingBeanPropertiesAreDemoted() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + assertCompletions( + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " <*>", + ///// non-existing /// + //color-children + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " color-children:\n"+ + " <*>", + //funky + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " funky: <*>", + //mapped-children + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " mapped-children:\n"+ + " <*>", + //nested + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " nested:\n"+ + " <*>", + //next + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " next: <*>", + //tags + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " tags:\n" + + " - <*>", + //wavelen + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " wavelen: <*>", + ///// existing //// + //children + "foo:\n" + + " data:\n" + + " children:\n" + + " <*>-\n" + + " name: foo", + //name + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: <*>foo" + ); + } + + @Ignore @Test public void testNoCompletionsInsideComments() throws Exception { + defaultTestData(); + + //Ensure this test is not trivially passing because of missing test data + assertCompletion( + "po<*>" + , + "server:\n"+ + " port: <*>" + ); + + assertNoCompletions( + "#po<*>" + ); + } + + @Ignore @Test public void testCompletionsFromDeeplyNestedNode() throws Exception { + String[] names = {"foo", "nested", "bar"}; + int levels = 4; + generateNestedProperties(levels, names, ""); + + assertCompletionCount(81, // 3^4 + "<*>" + ); + + assertCompletionCount(27, // 3^3 + "#comment\n" + + "foo:\n" + + " <*>" + ); + + assertCompletionCount( 9, // 3^2 + "#comment\n" + + "foo:\n" + + " bar: <*>" + ); + + assertCompletionCount( 3, + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + ); + + assertCompletionCount( 9, + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + ); + + assertCompletionCount(27, + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + ); + + assertCompletionCount(81, + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + "<*>" + ); + + + assertCompletion( + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + , + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " bar: <*>" + ); + + assertCompletion( + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + , + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " bar:\n" + + " bar: <*>" + ); + + assertCompletion( + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + , + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " bar:\n" + + " bar: <*>\n" + + " " + ); + + assertCompletion( + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + "<*>" + , + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + "bar:\n" + + " bar:\n" + + " bar:\n" + + " bar: <*>" + ); + } + + @Ignore @Test public void testInsertCompletionIntoDeeplyNestedNode() throws Exception { + String[] names = {"foo", "nested", "bar"}; + int levels = 4; + generateNestedProperties(levels, names, ""); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "bar.foo.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "bar:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "other:\n" + + "foo.foo.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>\n" + + "other:\n" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "foo.foo.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "other:\n" + + "foo.nested.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>\n"+ + "other:\n" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "foo.nested.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>\n" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "other:\n" + + "foo.nested.bar.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " bar: <*>\n" + + "other:\n" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "foo.nested.bar.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " bar: <*>\n" + ); + + } + + @Ignore @Test public void testBooleanValueCompletion() throws Exception { + defaultTestData(); + assertCompletions( + "liquibase:\n" + + " enabled: <*>", + "liquibase:\n" + + " enabled: false<*>", + "liquibase:\n" + + " enabled: true<*>" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " <*>", + "liquibase:\n" + + " enabled:\n" + + " false<*>", + "liquibase:\n" + + " enabled:\n" + + " true<*>" + ); + + assertCompletions( + "liquibase:\n" + + " enabled: f<*>\n", + "liquibase:\n" + + " enabled: false<*>\n" + ); + + assertCompletions( + "liquibase:\n" + + " enabled: t<*>\n", + "liquibase:\n" + + " enabled: true<*>\n" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " f<*>\n", + "liquibase:\n" + + " enabled:\n"+ + " false<*>\n" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " t<*>\n", + "liquibase:\n" + + " enabled:\n"+ + " true<*>\n" + ); + + // booleans can also be completed in upper case? + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " T<*>\n", + "liquibase:\n" + + " enabled:\n" + + " TRUE<*>\n" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " F<*>\n", + "liquibase:\n" + + " enabled:\n" + + " FALSE<*>\n" + ); + + //Dont suggest completion for something that's already complete. Causes odd + // and annoying behavior, like a completion popup after you stopped typing 'true' + assertNoCompletions( + "liquibase:\n" + + " enabled:\n" + + " true<*>" + ); + + //one more... for special char like '-' in the name + + assertCompletions( + "liquibase:\n" + + " check-change-log-location: t<*>", + "liquibase:\n" + + " check-change-log-location: true<*>" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:<*>", + "liquibase:\n" + + " enabled: false<*>", + "liquibase:\n" + + " enabled: true<*>" + ); + + + } + + @Ignore @Test public void testEnumValueCompletion() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("foo.color", "demo.Color", null, "A foonky colour"); + + assertCompletion("foo.c<*>", + "foo:\n" + + " color: <*>" //Should complete on same line because enums are 'simple' values. + ); + + assertCompletion("foo:\n color: R<*>", "foo:\n color: RED<*>"); + assertCompletion("foo:\n color: G<*>", "foo:\n color: GREEN<*>"); + assertCompletion("foo:\n color: B<*>", "foo:\n color: BLUE<*>"); + + assertCompletion("foo:\n color: r<*>", "foo:\n color: red<*>"); + assertCompletion("foo:\n color: g<*>", "foo:\n color: green<*>"); + assertCompletion("foo:\n color: b<*>", "foo:\n color: blue<*>"); + + assertCompletionsDisplayString("foo:\n color: <*>", + "red", "green", "blue" + ); + } + + @Ignore @Test public void testEnumMapValueCompletion() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + assertCompletions( + "foo:\n" + + " nam<*>", + //==> + "foo:\n" + + " name-colors:\n"+ + " <*>", + // or + "foo:\n" + + " color-names:\n"+ + " <*>" + ); + assertCompletionsDisplayString( + "foo:\n"+ + " name-colors:\n" + + " something: <*>", + //=> + "red", "green", "blue" + ); + assertCompletions( + "foo:\n"+ + " name-colors:\n" + + " something: G<*>", + // => + "foo:\n"+ + " name-colors:\n" + + " something: GREEN<*>" + ); + } + + @Ignore @Test public void testEnumMapValueReconciling() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("foo.name-colors", "java.util.Map", null, "Map with colors in its values"); + + Editor editor; + + editor = newEditor( + "foo:\n"+ + " name-colors:\n" + + " jacket: BLUE\n" + + " hat: RED\n" + + " pants: GREEN\n" + + " wrong: NOT_A_COLOR\n" + ); + editor.assertProblems( + "NOT_A_COLOR|Color" + ); + + //lowercase enums should work too + editor = newEditor( + "foo:\n"+ + " name-colors:\n" + + " jacket: blue\n" + + " hat: red\n" + + " pants: green\n" + + " wrong: NOT_A_COLOR\n" + ); + editor.assertProblems( + "NOT_A_COLOR|Color" + ); + } + + @Ignore @Test public void testEnumMapKeyCompletion() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + data("foo.color-names", "java.util.Map", null, "Map with colors in its keys"); + data("foo.color-data", "java.util.Map", null, "Map with colors in its keys, and pojo in values"); + + //Map Enum -> String: + assertCompletions("foo:\n colnam<*>", + "foo:\n" + + " color-names:\n" + + " <*>"); + assertCompletions( + "foo:\n" + + " color-names:\n" + + " <*>", + //=> + "foo:\n" + + " color-names:\n" + + " blue: <*>", + "foo:\n" + + " color-names:\n" + + " green: <*>", + "foo:\n" + + " color-names:\n" + + " red: <*>" + ); + + assertCompletionsDisplayString( + "foo:\n" + + " color-names:\n" + + " <*>", + //=> + "blue : String", "green : String", "red : String" + ); + + assertCompletions( + "foo:\n" + + " color-names:\n" + + " B<*>", + "foo:\n" + + " color-names:\n" + + " BLUE: <*>" + ); + + //Map Enum -> Pojo: + assertCompletions("foo.coldat<*>", + "foo:\n" + + " color-data:\n" + + " <*>"); + assertCompletions( + "foo:\n" + + " color-data:\n" + + " <*>", + // => + "foo:\n" + + " color-data:\n" + + " blue:\n" + + " <*>", + "foo:\n" + + " color-data:\n" + + " green:\n" + + " <*>", + "foo:\n" + + " color-data:\n" + + " red:\n" + + " <*>" + ); + assertCompletions( + "foo:\n" + + " color-data:\n" + + " B<*>", + //=> + "foo:\n" + + " color-data:\n" + + " BLUE:\n" + + " <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " b<*>", + //=> + "foo:\n" + + " color-data:\n" + + " blue:\n" + + " <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data: b<*>", + //=> + "foo:\n" + + " color-data: \n" + + " blue:\n" + + " <*>" + ); + + assertCompletionsDisplayString( + "foo:\n" + + " color-data:\n" + + " <*>", + "red : demo.ColorData", "green : demo.ColorData", "blue : demo.ColorData" + ); + + assertCompletionsDisplayString( + "foo:\n" + + " color-data: <*>\n", + "red : demo.ColorData", "green : demo.ColorData", "blue : demo.ColorData" + ); + + } + + @Ignore @Test public void testPojoReconciling() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + Editor editor = newEditor( + "foo:\n" + + " data:\n" + + " bogus: Something\n" + + " wavelen: 3.0\n" + + " wavelen: not a double\n" + + " wavelen:\n"+ + " more: 3.0\n"+ + " wavelen:\n" + + " - 3.0\n" + ); + editor.assertProblems( + "bogus|Unknown property", + "wavelen|Duplicate", + "wavelen|Duplicate", + "not a double|'double'", + "wavelen|Duplicate", + "more: 3.0|Expecting a 'double' but got a 'Mapping' node", + "wavelen|Duplicate", + "- 3.0|Expecting a 'double' but got a 'Sequence' node" + ); + } + + + @Ignore @Test public void testListOfAtomicCompletions() throws Exception { + data("foo.slist", "java.util.List", null, "list of strings"); + data("foo.ulist", "java.util.List", null, "list of strings"); + data("foo.dlist", "java.util.List", null, "list of doubles"); + assertCompletions("foo:\n u<*>", + "foo:\n" + + " ulist:\n" + + " - <*>"); + assertCompletions("foo:\n d<*>", + "foo:\n" + + " dlist:\n" + + " - <*>"); + assertCompletions("foo:\n sl<*>", + "foo:\n"+ + " slist:\n" + + " - <*>"); + } + + @Ignore @Test public void testEnumsInLowerCaseReconciling() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); + + Editor editor = newEditor( + "simple:\n" + + " pants:\n"+ + " size: NOT_A_SIZE\n"+ + " size: EXTRA_SMALL\n"+ + " size: extra-small\n"+ + " size: small\n"+ + " size: SMALL\n" + ); + editor.assertProblems( + "size|Duplicate", + "NOT_A_SIZE|ClothingSize", + "size|Duplicate", + "size|Duplicate", + "size|Duplicate", + "size|Duplicate" + ); + + editor = newEditor( + "foo:\n" + + " color-names:\n"+ + " red: Rood\n"+ + " green: Groen\n"+ + " blue: Blauw\n" + + " not-a-color: Wrong\n" + + " blue.bad: Blauw\n" + + " blue:\n" + + " bad: Blauw" + ); + editor.assertProblems( + "blue|Duplicate", + "not-a-color|Color", + "blue.bad|Color", + "blue|Duplicate", + "bad: Blauw|Expecting a 'String' but got a 'Mapping'" + ); + + editor = newEditor( + "foo:\n" + + " color-data:\n"+ + " red:\n" + + " next: green\n" + + " next: not a color\n" + + " bogus: green\n" + + " name: Rood\n" + ); + editor.assertProblems( + "next|Duplicate", + "next|Duplicate", + "not a color|Color", + "bogus|Unknown property" + ); + } + + @Ignore @Test public void testEnumsInLowerCaseContentAssist() throws Exception { + TestProject p = createPredefinedMavenProject("demo-enum"); + useProject(p); + assertNotNull(p.findType("demo.ClothingSize")); + + data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); + + assertCompletions( + "simple:\n" + + " pants:\n"+ + " size: S<*>", + "simple:\n" + + " pants:\n"+ + " size: SMALL<*>", + "simple:\n" + + " pants:\n"+ + " size: EXTRA_SMALL<*>" + ); + + assertCompletions( + "simple:\n" + + " pants:\n"+ + " size: s<*>", + "simple:\n" + + " pants:\n"+ + " size: small<*>", + "simple:\n" + + " pants:\n"+ + " size: extra-small<*>" + ); + + assertCompletions( + "simple:\n" + + " pants:\n"+ + " size: ex<*>", + // => + "simple:\n" + + " pants:\n"+ + " size: extra-large<*>", + // or + "simple:\n" + + " pants:\n"+ + " size: extra-small<*>" + ); + + assertCompletions( + "simple:\n" + + " pants:\n"+ + " size: EX<*>", + // => + "simple:\n" + + " pants:\n"+ + " size: EXTRA_LARGE<*>", + // or + "simple:\n" + + " pants:\n"+ + " size: EXTRA_SMALL<*>" + ); + + assertCompletionsDisplayString("foo:\n color: <*>", "red", "green", "blue"); + + assertCompletions("foo:\n color-data: B<*>", "foo:\n color-data: \n BLUE:\n <*>"); + assertCompletions("foo:\n color-data: b<*>", "foo:\n color-data: \n blue:\n <*>"); + assertCompletions("foo:\n color-data: <*>", + "foo:\n color-data: \n blue:\n <*>", + "foo:\n color-data: \n green:\n <*>", + "foo:\n color-data: \n red:\n <*>" + ); + + assertCompletions( + "foo:\n"+ + " color-data:\n"+ + " red: na<*>", + "foo:\n"+ + " color-data:\n"+ + " red: \n"+ + " name: <*>" + ); + } + + @Ignore @Test public void testPojoInListCompletion() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + assertCompletion( + "foo:\n" + + " color-data:\n" + + " red: chi<*>" + ,// => + "foo:\n" + + " color-data:\n" + + " red: \n" + + " children:\n" + + " - <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nex<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nes<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nested:\n"+ + " <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nex<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nes<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nested:\n"+ + " <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: RED\n" + + " wav<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: RED\n" + + " wavelen: <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: RED\n" + + " wav<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: RED\n" + + " wavelen: <*>" + ); + } + + @Ignore @Test public void test_STS4111_NoEmptyLinesGapBeforeInsertedCompletion() throws Exception { + data("spring.application.name", "java.lang.String", null, "The name of the application"); + data("spring.application.index", "java.lang.Integer", true, "App instance index"); + data("spring.considerable.fun", "java.lang.Boolean", true, "Whether the spring fun is considerable"); + + assertCompletions( + "spring:\n" + + " application:\n" + + " index: 12\n" + + "\n" + + "server:\n" + + " port: 8888\n" + + "appname<*>" + , //=> + "spring:\n" + + " application:\n" + + " index: 12\n" + + " name: <*>\n" + + "\n" + + "server:\n" + + " port: 8888" + + ); + + //Also test that: + // - Fully commented lines also count as gaps + // - Gaps of more than one line are also handled correctly + assertCompletions( + "# spring stuff\n" + + "spring:\n" + + " application:\n" + + " index: 12\n" + + "\n" + + "#server stuff\n" + + "server:\n" + + " port: 8888\n" + + "cfun<*>" + , //=> + "# spring stuff\n" + + "spring:\n" + + " application:\n" + + " index: 12\n" + + " considerable:\n" + + " fun: <*>\n" + + "\n" + + "#server stuff\n" + + "server:\n" + + " port: 8888" + + ); + + } + + @Ignore @Test public void testDocumentSeparator() throws Exception { + defaultTestData(); + + assertCompletion( + "flyway:\n" + + " encoding: utf8\n" + + "---\n" + + "flyena<*>", + // => + "flyway:\n" + + " encoding: utf8\n" + + "---\n" + + "flyway:\n" + + " enabled: <*>" + ); + } + + @Ignore @Test public void testMultiProfileYamlReconcile() throws Exception { + Editor editor; + //See https://issuetracker.springsource.com/browse/STS-4144 + defaultTestData(); + + //Narrowly focussed test-case for easier debugging + editor = newEditor( + "spring:\n" + + " profiles: seven\n" + ); + editor.assertProblems(/*NONE*/); + + //More complete test + editor = newEditor( + "spring:\n" + + " profiles: seven\n" + + "server:\n" + + " port: 7777\n" + + "---\n" + + "spring:\n" + + " profiles: eight\n" + + "server:\n" + + " port: 8888\n"+ + "bogus: bad" + ); + editor.assertProblems( + "bogus|Unknown property" + ); + } + + @Ignore @Test public void testReconcileArrayOfPrimitiveType() throws Exception { + data("my.array", "int[]", null, "A primitive array"); + data("my.boxarray", "int[]", null, "An array of boxed primitives"); + data("my.list", "java.util.List", null, "A list of boxed types"); + + Editor editor = newEditor( + "my:\n" + + " array:\n" + + " - 777\n" + + " - bad\n" + + " - 888" + ); + editor.assertProblems( + "bad|Expecting a 'int'" + ); + + editor = newEditor( + "my:\n" + + " boxarray:\n" + + " - 777\n" + + " - bad\n" + + " - 888" + ); + editor.assertProblems( + "bad|Expecting a 'int'" + ); + + editor = newEditor( + "my:\n" + + " list:\n" + + " - 777\n" + + " - bad\n" + + " - 888" + ); + editor.assertProblems( + "bad|Expecting a 'int'" + ); + } + + @Ignore @Test public void test_STS4231() throws Exception { + //Should the 'predefined' project need to be recreated... use the commented code below: +// BootProjectTestHarness projectHarness = new BootProjectTestHarness(ResourcesPlugin.getWorkspace()); +// TestProject project = projectHarness.createBootProject("sts-4231", +// bootVersionAtLeast("1.3.0"), +// withStarters("web", "cloud-config-server") +// ); + + //For more robust test use predefined project which is not so much a moving target: + TestProject project = createPredefinedMavenProject("sts-4231"); + useProject(project); + + assertCompletionsDisplayString( + "info:\n" + + " component: Config Server\n" + + "spring:\n" + + " application:\n" + + " name: configserver\n" + + " jmx:\n" + + " default-domain: cloud.config.server\n" + + " cloud:\n" + + " config:\n" + + " server:\n" + + " git:\n" + + " uri: https://github.com/spring-cloud-samples/config-repo\n" + + " repos:\n" + + " my-repo:\n" + + " <*>\n", + // ==> + "name : String", + "pattern : String[]" + ); + + assertCompletion( + "info:\n" + + " component: Config Server\n" + + "spring:\n" + + " application:\n" + + " name: configserver\n" + + " jmx:\n" + + " default-domain: cloud.config.server\n" + + " cloud:\n" + + " config:\n" + + " server:\n" + + " git:\n" + + " uri: https://github.com/spring-cloud-samples/config-repo\n" + + " repos:\n" + + " my-repo:\n" + + " p<*>\n", + // ==> + "info:\n" + + " component: Config Server\n" + + "spring:\n" + + " application:\n" + + " name: configserver\n" + + " jmx:\n" + + " default-domain: cloud.config.server\n" + + " cloud:\n" + + " config:\n" + + " server:\n" + + " git:\n" + + " uri: https://github.com/spring-cloud-samples/config-repo\n" + + " repos:\n" + + " my-repo:\n" + + " pattern:\n" + + " - <*>\n" + ); + + } + + @Ignore @Test public void test_STS_4254_MapStringObjectReconciling() throws Exception { + Editor editor; + data("info", "java.util.Map", null, "Info for the actuator's info endpoint."); + + editor = newEditor( + "info: not-a-map\n" + ); + editor.assertProblems( + "not-a-map|Expecting a 'Map'" + ); + + editor= newEditor( + "info:\n" + + " build: \n" + + " artifact: foo-bar\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "info:\n" + + " more: \n" + + " deeply:\n" + + " nested: foo-bar\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "booger: Bad\n" + + "info:\n" + + " akey: avalue\n" + ); + editor.assertProblems( + "booger|Unknown property" + ); + } + + @Ignore @Test public void test_STS_4254_MapStringStringReconciling() throws Exception { + Editor editor; + data("info", "java.util.Map", null, "Info for the actuator's info endpoint."); + + editor= newEditor( + "info:\n" + + " build: \n" + + " artifact: foo-bar\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "info:\n" + + " more: \n" + + " deeply:\n" + + " nested: foo-bar\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "booger: Bad\n" + + "info:\n" + + " akey: avalue\n" + ); + editor.assertProblems( + "booger|Unknown property" + ); + + } + + @Ignore @Test public void test_STS_4254_MapStringIntegerReconciling() throws Exception { + Editor editor; + data("info", "java.util.Map", null, "Info for the actuator's info endpoint."); + + editor= newEditor( + "info:\n" + + " build: \n" + + " foo-bar\n" + ); + editor.assertProblems( + "foo-bar|Expecting a 'int'" + ); + + editor= newEditor( + "info:\n" + + " build: 123\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "info:\n" + + " build: \n" + + " number: 123\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "info:\n" + + " build: \n" + + " artifact: abc\n" + ); + editor.assertProblems( + "abc|Expecting a 'int'" + ); + + //A more complex example for good measure + editor= newEditor( + "info:\n" + + " some: \n" + + " nested: foo\n" + + " and: bar\n" + + " or: 444\n" + + " also: bad\n" + ); + editor.assertProblems( + "foo|Expecting a 'int'", + "bar|Expecting a 'int'", + "bad|Expecting a 'int'" + ); + } + + @Ignore @Test public void testDeprecatedReconcileProperty() throws Exception { + data("error.path", "java.lang.String", null, "Path of the error controller."); + Editor editor = newEditor( + "# a comment\n"+ + "error:\n"+ + " path: foo\n" + ); + + deprecate("error.path", "server.error.path", null); + editor.assertProblems( + "path|'error.path' is Deprecated: Use 'server.error.path'" + //no other problems + ); + + deprecate("error.path", "server.error.path", "This is old."); + editor.assertProblems( + "path|'error.path' is Deprecated: Use 'server.error.path' instead. Reason: This is old." + //no other problems + ); + + deprecate("error.path", null, "This is old."); + editor.assertProblems( + "path|'error.path' is Deprecated: This is old." + //no other problems + ); + + deprecate("error.path", null, null); + editor.assertProblems( + "path|'error.path' is Deprecated!" + //no other problems + ); + } + + @Ignore @Test public void testDeprecatedPropertyCompletion() throws Exception { + data("error.path", "java.lang.String", null, "Path of the error controller."); + data("server.error.path", "java.lang.String", null, "Path of the error controller."); + deprecate("error.path", "server.error.path", "This is old."); + assertCompletionsDisplayString( + "errorpa<*>" + , // => + "server.error.path : String", // should be first because it is not deprecated, even though it is not as good a pattern match + "error.path : String" + ); + assertStyledCompletions("error.pa<*>", + StyledStringMatcher.plainFont("server.error.path : String"), + StyledStringMatcher.strikeout("error.path") + ); + } + + @Ignore @Test public void testDeprecatedPropertyHoverInfo() throws Exception { + data("error.path", "java.lang.String", null, "Path of the error controller."); + Editor editor = newEditor( + "# a comment\n"+ + "error:\n" + + " path: foo\n" + ); + + deprecate("error.path", "server.error.path", null); + editor.assertHoverContains("path", "error.path -> server.error.path"); + editor.assertHoverContains("path", "Deprecated!"); + + deprecate("error.path", "server.error.path", "This is old."); + editor.assertHoverContains("path", "error.path -> server.error.path"); + editor.assertHoverContains("path", "Deprecated: This is old"); + + deprecate("error.path", null, "This is old."); + editor.assertHoverContains("path", "Deprecated: This is old"); + + deprecate("error.path", null, null); + editor.assertHoverContains("path", "Deprecated!"); + } + + @Ignore @Test public void testDeprecatedBeanPropertyHoverInfo() throws Exception { + TestProject jp = createPredefinedMavenProject("demo"); + useProject(jp); + data("foo", "demo.Deprecater", null, "A bean with deprecated property."); + Editor editor = newEditor( + "# a comment\n"+ + "foo:\n" + + " name: foo\n" + ); + + editor.assertHoverContains("name", "Deprecated!"); + } + + @Ignore @Test public void testDeprecatedBeanPropertyReconcile() throws Exception { + TestProject jp = createPredefinedMavenProject("demo"); + useProject(jp); + data("foo", "demo.Deprecater", null, "A Bean with deprecated properties"); + + Editor editor = newEditor( + "# comment\n" + + "foo:\n" + + " name: Old faithfull\n" + + " new-name: New and fancy\n" + + " alt-name: alternate\n" + ); + editor.assertProblems( + "name|Property 'name' of type 'demo.Deprecater' is Deprecated!", + "alt-name|Deprecated" + ); + + editor = newEditor( + "# comment\n" + + "foo:\n" + + " alt-name: alternate\n" + ); + //check that message also contains reason and replacement infos. + editor.assertProblems( + "alt-name|Use 'something.else' instead" + ); + editor.assertProblems( + "alt-name|No good anymore" + ); + + + } + + @Ignore @Test public void testDeprecatedBeanPropertyCompletions() throws Exception { + TestProject jp = createPredefinedMavenProject("demo"); + useProject(jp); + data("foo", "demo.Deprecater", null, "A Bean with deprecated properties"); + + assertStyledCompletions( + "foo:\n" + + " nam<*>" + , // => + StyledStringMatcher.plainFont("new-name : String"), + StyledStringMatcher.strikeout("name"), + StyledStringMatcher.strikeout("alt-name") + ); + } + + @Ignore @Test public void testDeprecatedPropertyQuickfixSimple() throws Exception { + //A simple case for starters. The path edits aren't too complicated since there's + //just the one property in the file and only the last part of the 'path' changes. + //So this is a simple 'in-place' edit. + + data("my.old-name", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.old-name", "my.new-name", null); + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n"+ + " new-name: foo\n" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + + "your: stuff" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n"+ + " new-name: foo\n"+ + "your: stuff" + ); + } + + { // Don't move prop if it doesn't need to be moved + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + + " other: bar\n" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n"+ + " new-name: foo\n" + + " other: bar\n" + ); + } + } + + @Ignore @Test public void testDeprecatedPropertyQuickfixMovingValue() throws Exception { + data("my.old-name", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.old-name", "your.new-name", null); + + data("my.stuff", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.stuff", "my.for-sale.stuff", null); + + data("my.long.path.with.many.pieces", "java.lang.String", null, "Old"); + deprecate("my.long.path.with.many.pieces", "my.path.with.many.pieces", null); + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces: foo\n" + ); + + Diagnostic problem = editor.assertProblem("pieces"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.path.with.many.pieces'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n" + + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " cannot: remove this\n"+ + " pieces: foo\n" + ); + + Diagnostic problem = editor.assertProblem("pieces"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.path.with.many.pieces'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " cannot: remove this\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " stuff: foo\n" + ); + + Diagnostic problem = editor.assertProblem("stuff"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.for-sale.stuff'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n" + + " for-sale:\n"+ + " stuff: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "your:\n"+ + " new-name: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + + "your:\n" + + " goodies: nice" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "your:\n"+ + " goodies: nice\n"+ + " new-name: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "your:\n" + + " goodies: nice\n" + + "my:\n" + + " other: stuff\n" + + " old-name: foo\n" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "your:\n"+ + " goodies: nice\n"+ + " new-name: foo\n" + + "my:\n" + + " other: stuff" + ); + } + } + + @Ignore @Test public void testDeprecatedPropertyQuickfixMovingIndentedValue() throws Exception { + data("my.old-name", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.old-name", "your.new-name", null); //same indent level + + data("my.stuff", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.stuff", "my.long.path.with.many.pieces", null); // deeper indent level + + data("my.long.path.with.many.pieces", "java.lang.String", null, "Old"); + deprecate("my.long.path.with.many.pieces", "short.path", null); //shallower level + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces: >\n"+ + " foo spread over\n"+ + " several lines\n" + + " of text\n"+ + "short:\n"+ + " stuff: goes here\n" + ); + Diagnostic problem = editor.assertProblem("pieces"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'short.path'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "short:\n"+ + " stuff: goes here\n" + + " path: >\n" + + " foo spread over\n"+ + " several lines\n" + + " of text\n" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " bits: go here\n"+ + " stuff: >\n" + + " foo spread over\n"+ + " several lines\n" + + " of text\n" + ); + Diagnostic problem = editor.assertProblem("stuff"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.long.path.with.many.pieces'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " bits: go here\n"+ + " path:\n" + + " with:\n" + + " many:\n"+ + " pieces: >\n" + + " foo spread over\n"+ + " several lines\n" + + " of text" + ); + } + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces:\n"+ + "# some\n" + + " - foo spread over\n"+ + " #confusing\n" + + " - several lines\n" + + " #comments\n" + + " - of text\n"+ + "short:\n"+ + " stuff: goes here\n" + ); + Diagnostic problem = editor.assertProblem("pieces"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'short.path'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "short:\n"+ + " stuff: goes here\n" + + " path:\n" + + " # some\n" + + " - foo spread over\n"+ + " #confusing\n" + + " - several lines\n" + + " #comments\n" + + " - of text\n" + ); + } + } + + @Ignore @Test public void testReconcileDuplicateProperties() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "spring:\n" + + " profiles: cloudfoundry\n" + + "spring: \n" + + " application:\n" + + " name: eureka" + ); + editor.assertProblems( + "spring|Duplicate", + "spring|Duplicate" + ); + } + + @Ignore @Test public void testReconcileDuplicatePropertiesNested() throws Exception { + data("foo.person.name", "String", null, "Name of person"); + data("foo.person.family", "String", null, "Family name of person"); + Editor editor = newEditor( + "foo:\n" + + " person:\n" + + " name: Hohohoh\n" + + " person:\n" + + " family:\n" + ); + editor.assertProblems( + "person|Duplicate", + "person|Duplicate" + ); + } + + @Ignore @Test public void testReconcileDuplicatePropertiesInBean() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("some.color", "demo.ColorData", null, "Some info about a color."); + Editor editor = newEditor( + "some:\n" + + " color:\n" + + " name: RED\n" + + " name: GREEN\n" + ); + editor.assertProblems( + "name|Duplicate", + "name|Duplicate" + ); + } + + @Ignore @Test public void testCharSetCompletions() throws Exception { + data("foobar.encoding", "java.nio.charset.Charset", null, "The charset-encoding to use for foobars"); + + assertCompletions( + "foobar:\n" + + " enco<*>" + , // ==> + "foobar:\n" + + " encoding: <*>" + ); + + assertCompletionWithLabel( + "foobar:\n" + + " encoding: UT<*>" + , + "UTF-8" + , + "foobar:\n" + + " encoding: UTF-8<*>" + ); + } + + @Ignore @Test public void testLocaleCompletions() throws Exception { + data("foobar.locale", "java.util.Locale", null, "The locale for foobars"); + + assertCompletions( + "foobar:\n" + + " loca<*>" + , // ==> + "foobar:\n" + + " locale: <*>" + ); + + assertCompletionWithLabel( + "foobar:\n" + + " locale: en<*>" + , + "en_CA" + , + "foobar:\n" + + " locale: en_CA<*>" + ); + } + + @Ignore @Test public void testMimeTypeCompletions() throws Exception { + data("foobar.mime", "org.springframework.util.MimeType", null, "The mimetype for foobars"); + + assertCompletions( + "foobar:\n" + + " mi<*>" + , // ==> + "foobar:\n" + + " mime: <*>" + ); + + assertCompletionWithLabel( + "foobar:\n" + + " mime: json<*>" + , + "application/json; charset=utf-8" + , + "foobar:\n" + + " mime: application/json; charset=utf-8<*>" + ); + } + + @Ignore @Test public void testPropertyValueHintCompletions() throws Exception { + //Test that 'value hints' work when property name is associated with 'value' hints. + // via boot metadata. + useProject(createPredefinedMavenProject("boot13")); + + assertCompletionsDisplayString( + "spring:\n" + + " http:\n" + + " converters:\n" + + " preferred-json-mapper: <*>\n" + , //=> + "gson", + "jackson" + ); + } + + @Ignore @Test public void testPropertyListHintCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + + assertCompletion( + "management:\n" + + " health:\n" + + " status:\n" + + " ord<*>" + , //=> + "management:\n" + + " health:\n" + + " status:\n" + + " order:\n"+ + " - <*>" + ); + + assertCompletionsDisplayString( + "management:\n" + + " health:\n" + + " status:\n" + + " order:\n" + + " - <*>" + , //=> + "DOWN", + "OUT_OF_SERVICE", + "UNKNOWN", + "UP" + ); + } + + @Ignore @Test public void testPropertyMapValueCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + + assertCompletionsDisplayString( + "logging:\n" + + " level:\n" + + " some.package: <*>" + , // => + "trace", + "debug", + "info", + "warn", + "error", + "fatal", + "off" + ); + } + + @Ignore @Test public void testPropertyMapKeyCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + assertCompletionWithLabel( + "logging:\n" + + " level:\n" + + " <*>" + , // => + "root : String" + , + "logging:\n" + + " level:\n" + + " root: <*>" + ); + } + + @Ignore @Test public void testEscapeStringValueStartingWithStar() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + assertCompletions( + "endpoints:\n"+ + " cors:\n"+ + " allowed-headers: \n" + + " - <*>" + , // => + "endpoints:\n"+ + " cors:\n"+ + " allowed-headers: \n" + + " - '*'<*>" + ); + } + + @Ignore @Test public void testEscapeStringValueWithAQuote() throws Exception { + data("foo.quote", "java.lang.String", null, "Character to used to surround quotes"); + valueHints("foo.quote", "\"", "'", "`"); + + assertCompletions( + "foo:\n" + + " quote: <*>" + , // => + "foo:\n" + + " quote: '\"'<*>" + , + "foo:\n" + + " quote: ''''<*>" + , + "foo:\n" + + " quote: '`'<*>" + ); + } + + @Ignore @Test public void testEscapeStringKeyWithAQuote() throws Exception { + data("foo.quote", "java.util.Map", null, "Name of quote characters"); + keyHints("foo.quote", "\"", "'", "`"); + + assertCompletions( + "foo:\n" + + " quote: <*>" + , // => + "foo:\n" + + " quote: \n"+ + " '\"': <*>" + , + "foo:\n" + + " quote: \n"+ + " '''': <*>" + , + "foo:\n" + + " quote: \n"+ + " '`': <*>" + ); + } + + @Ignore @Test public void testLoggerNameCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); // the provider can't be reliably tested if its not allowed to + // fetch all its values (even though in 'production' you + // wouldn't want it to block the UI thread for this long. + useProject(createPredefinedMavenProject("boot13")); + //Finds a package: + assertCompletionWithLabel( + "logging:\n" + + " level:\n" + + " boot.auto<*>" + , //----------------- + "org.springframework.boot.autoconfigure : String" + , // => + "logging:\n" + + " level:\n" + + " org.springframework.boot.autoconfigure: <*>" + ); + + //Finds a type: + assertCompletionWithLabel( + "logging:\n" + + " level:\n" + + " MesgSource<*>" + , //----------------- + "org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration : String" + , // => + "logging:\n" + + " level:\n" + + " org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration: <*>" + ); + } + + @Ignore @Test public void testSimpleResourceCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("boot13")); + + data("my.nice.resource", "org.springframework.core.io.Resource", null, "A very nice resource."); + + assertCompletion( + "my:\n" + + " nice:\n" + + " <*>\n" + ,// => + "my:\n" + + " nice:\n" + + " resource: <*>\n" + ); + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " resource: <*>\n" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " resource:\n" + + " <*>\n" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + + } + + @Ignore @Test public void testClasspathResourceCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("boot13")); + + data("my.nice.resource", "org.springframework.core.io.Resource", null, "A very nice resource."); + data("my.nice.list", "java.util.List", null, "A nice list of resources."); + + //Test 'simple key context' + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " resource: classpath:app<*>\n" + ,// => + "classpath:application.properties", + "classpath:application.yml" + ); + + //Test 'list item' context: + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:app<*>\n" + ,// => + "classpath:application.properties", + "classpath:application.yml" + ); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:app<*>\n" + ,// ========== + "classpath:application.yml" + , // => + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:application.yml<*>\n" + ); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:<*>\n" + ,// ========== + "classpath:application.yml" + , // => + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:application.yml<*>\n" + ); + + //Test 'raw node' context + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:app<*>\n" + ,// => + "classpath:application.properties", + "classpath:application.yml" + ); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:app<*>\n" + ,//=============== + "classpath:application.properties" + ,// => + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:application.properties<*>\n" + ); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:<*>\n" + ,//=============== + "classpath:application.properties" + ,// => + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:application.properties<*>\n" + ); + + // do we find resources in sub-folders too? + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:word<*>\n" + ,//=============== + "classpath:stuff/wordlist.txt" + ,// => + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:stuff/wordlist.txt<*>\n" + ); + } + + @Ignore @Test public void testClassReferenceCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("boot13_with_mongo")); + + assertCompletion( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-na<*>" + , // => + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: <*>" + ); + + assertCompletionsDisplayString( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: <*>" + , // => + "org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy", + "org.springframework.data.mapping.model.CamelCaseSplittingFieldNamingStrategy", + "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", + "org.springframework.data.mapping.model.SnakeCaseFieldNamingStrategy" + ); + + assertCompletionWithLabel( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: <*>" + , //===== + "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy" + , //=> + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy<*>" + ); + + //Test what happens when 'target' type isn't on the classpath: + useProject(createPredefinedMavenProject("boot13")); + assertCompletionsDisplayString( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: <*>" + // => + /*NONE*/ + ); + } + + @Ignore @Test public void testClassReferenceInValueLink() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("boot13_with_mongo")); + + editor = newEditor( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n" + ); + editor.assertLinkTargets("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy"); + + editor = newEditor( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy:\n" + + " org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n" + ); + editor.assertLinkTargets("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy"); + + //Linking should also work for types that aren't valid based on the constraints + editor = newEditor( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: java.lang.String\n" + + "#more stuff" + ); + editor.assertLinkTargets("java.lang.String", "java.lang.String"); + } + + @Ignore @Test public void test_STS_3335_reconcile_list_nested_in_Map_of_String() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("demo-sts-4335")); + + editor = newEditor( + "test-map:\n" + + " test-list-object:\n" + + " color-list:\n" + + " - not-a-color\n"+ + " - RED\n" + + " - GREEN\n" + ); + editor.assertProblems( + "not-a-color|Expecting a 'com.wellsfargo.lendingplatform.web.config.Color" + ); + + editor = newEditor( + "test-map:\n" + + " test-list-object:\n" + + " string-list:\n" + + " - abc\n" + + " - def\n" + ); + editor.assertProblems(/*NONE*/); + + } + + + @Ignore @Test public void test_STS_3335_completions_list_nested_in_Map_of_String() throws Exception { + useProject(createPredefinedMavenProject("demo-sts-4335")); + + assertCompletions( + "test-map:\n" + + " some-string-key:\n" + + " col<*>" + , // => + "test-map:\n" + + " some-string-key:\n" + + " color-list:\n" + + " - <*>" + ); + + assertCompletionsDisplayString( + "test-map:\n" + + " some-string-key:\n" + + " color-list:\n" + + " - <*>" + , // => + "red", "green", "blue" + ); + } + + @Ignore @Test public void testHandleAsResourceContentAssist() throws Exception { + //"name": "my.terms-and-conditions", + // "providers": [ + // { + // "name": "handle-as", + // "parameters": { + // "target": "org.springframework.core.io.Resource" + // } + // } + // ] + data("my.terms-and-conditions", "java.lang.String", null, "Terms and Conditions text file") + .provider("handle-as", "target", "org.springframework.core.io.Resource"); + + assertCompletionsDisplayString( + "my:\n" + + " terms-and-conditions: <*>" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + } + + @Ignore @Test public void testBootBug5905() throws Exception { + useProject(createPredefinedMavenProject("demo-with-resource-prop")); + + //Check the metadata reflects the 'handle-as': + PropertyInfo metadata = getIndexProvider().getIndex(null).get("my.welcome.path"); + assertEquals("org.springframework.core.io.Resource", metadata.getType()); + + //Check the content assist based on it works too: + assertCompletionsDisplayString( + "my:\n"+ + " welcome:\n" + + " path: <*>" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + } + + @Ignore @Test public void testEnumJavaDocShownInValueContentAssist() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("my.background", "demo.Color", null, "Color to use as default background."); + + assertCompletionWithInfoHover( + "my:\n" + + " background: <*>" + , // ========== + "red" + , // ==> + "Hot and delicious" + ); + } + + @Ignore @Test public void testEnumJavaDocShownInValueHover() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("my.background", "demo.Color", null, "Color to use as default background."); + + Editor editor; + + editor = newEditor( + "my:\n" + + " background: red" + ); + editor.assertHoverContains("red", "Hot and delicious"); + + editor = newEditor( + "my:\n" + + " background: RED" + ); + editor.assertHoverContains("RED", "Hot and delicious"); + } + + @Ignore @Test public void testHyperLinkEnumValue() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("demo-enum")); + data("my.background", "demo.Color", null, "Color to use as default background."); + + editor = newEditor( + "my:\n" + + " background: RED" + ); + editor.assertLinkTargets("RED", "demo.Color.RED"); + + editor = newEditor( + "my:\n" + + " background: red" + ); + editor.assertLinkTargets("red", "demo.Color.RED"); + } + + @Ignore @Test public void testHyperLinkEnumValueInMapKey() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("demo-enum")); + data("my.color.map", "java.util.Map", null, "Pretty names for the colors."); + + editor = newEditor( + "my:\n" + + " color:\n" + + " map:\n" + + " RED: Rood\n" + + " green: Groen\n" + ); + editor.assertLinkTargets("RED", "demo.Color.RED"); + editor.assertLinkTargets("green", "demo.Color.GREEN"); + + editor = newEditor( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " INDENT_OUTPUT: true" + ); + editor.assertLinkTargets("INDENT_OUTPUT", + "com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT" + ); + + editor = newEditor( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " indent-output: true" + ); + editor.assertLinkTargets("indent-output", + "com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT" + ); + } + + ///////////////// cruft //////////////////////////////////////////////////////// + + private void generateNestedProperties(int levels, String[] names, String prefix) { + if (levels==0) { + data(prefix, "java.lang.String", null, "Property "+prefix); + } else if (levels > 0) { + for (int i = 0; i < names.length; i++) { + generateNestedProperties(levels-1, names, join(prefix, names[i])); + } + } + } + + private String join(String prefix, String string) { + if (StringUtil.hasText(prefix)) { + return prefix +"." + string; + } + return string; + } + } diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/PropertyIndexHarness.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/PropertyIndexHarness.java new file mode 100644 index 000000000..aee4bc943 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/PropertyIndexHarness.java @@ -0,0 +1,547 @@ +package org.springframework.ide.vscode.yaml; + +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; +import org.springframework.boot.configurationmetadata.Deprecation; +import org.springframework.boot.configurationmetadata.ValueHint; +import org.springframework.boot.configurationmetadata.ValueProvider; +import org.springframework.ide.vscode.boot.properties.metadata.PropertyInfo; +import org.springframework.ide.vscode.boot.properties.metadata.SpringPropertyIndex; +import org.springframework.ide.vscode.boot.properties.metadata.SpringPropertyIndexProvider; +import org.springframework.ide.vscode.boot.properties.metadata.ValueProviderRegistry; +import org.springframework.ide.vscode.boot.properties.util.FuzzyMap; +import org.springframework.ide.vscode.testharness.TestProject; +import org.springframework.ide.vscode.util.IDocument; + +/** + * Provides some convenience apis for test code to create / use test data for a SpringPropertyIndex. + */ +public class PropertyIndexHarness { + + private Map datas = new LinkedHashMap<>(); + private ValueProviderRegistry valueProviders = ValueProviderRegistry.getDefault(); + private SpringPropertyIndex index = null; + private TestProject testProject = null; + + protected SpringPropertyIndexProvider indexProvider = new SpringPropertyIndexProvider() { + public FuzzyMap getIndex(IDocument doc) { + if (index==null) { + Path path = testProject == null ? null : testProject.getPath(); + index = new SpringPropertyIndex(valueProviders, path); + for (ConfigurationMetadataProperty propertyInfo : datas.values()) { + index.add(propertyInfo); + } + } + return index; + } + }; + + public class ItemConfigurer { + + private ConfigurationMetadataProperty item; + + public ItemConfigurer(ConfigurationMetadataProperty item) { + this.item = item; + } + + /** + * Add a provider with a single parameter. + * @return + */ + public ItemConfigurer provider(String name, String paramName, Object paramValue) { + ValueProvider provider = new ValueProvider(); + provider.setName(name); + provider.getParameters().put(paramName, paramValue); + item.getHints().getValueProviders().add(provider); + return this; + } + + /** + * Add a value hint. If description contains a '.' the dot is used + * to break description into a short and long description. + * @return + */ + public ItemConfigurer valueHint(Object value, String description) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + if (description!=null) { + int dotPos = description.indexOf('.'); + if (dotPos>=0) { + hint.setShortDescription( description.substring(0, dotPos)); + } + hint.setDescription(description); + } + item.getHints().getValueHints().add(hint); + return this; + } + } + + + public ItemConfigurer data(String id, String type, Object deflt, String description, + String... source + ) { + ConfigurationMetadataProperty item = new ConfigurationMetadataProperty(); + item.setId(id); + item.setDescription(description); + item.setType(type); + item.setDefaultValue(deflt); + index = null; + datas.put(item.getId(), item); + return new ItemConfigurer(item); + } + + public void keyHints(String id, String... hintValues) { + index = null; + List hints = datas.get(id).getHints().getKeyHints(); + for (String value : hintValues) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + hints.add(hint); + } + } + + public void valueHints(String id, String... hintValues) { + index = null; + List hints = datas.get(id).getHints().getValueHints(); + for (String value : hintValues) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + hints.add(hint); + } + } + + public void deprecate(String key, String replacedBy, String reason) { + index = null; + ConfigurationMetadataProperty info = datas.get(key); + Deprecation d = new Deprecation(); + d.setReplacement(replacedBy); + d.setReason(reason); + info.setDeprecation(d); + } + + /** + * Call this method to add some default test data to the Completion engine's index. + * Note that this data is not added automatically, some test may want to use smaller + * test data sets. + */ + public void defaultTestData() { + data("banner.charset", "java.nio.charset.Charset", "UTF-8", "Banner file encoding."); + data("banner.location", "java.lang.String", "classpath:banner.txt", "Banner file location."); + data("debug", "java.lang.Boolean", "false", "Enable debug logs."); + data("flyway.check-location", "java.lang.Boolean", "false", "Check that migration scripts location exists."); + data("flyway.clean-on-validation-error", "java.lang.Boolean", null, null); + data("flyway.enabled", "java.lang.Boolean", "true", "Enable flyway."); + data("flyway.encoding", "java.lang.String", null, null); + data("flyway.ignore-failed-future-migration", "java.lang.Boolean", null, null); + data("flyway.init-description", "java.lang.String", null, null); + data("flyway.init-on-migrate", "java.lang.Boolean", null, null); + data("flyway.init-sqls", "java.util.List", null, "SQL statements to execute to initialize a connection immediately after obtaining\n it."); + data("flyway.init-version", "org.flywaydb.core.api.MigrationVersion", null, null); + data("flyway.locations", "java.util.List", null, "Locations of migrations scripts."); + data("flyway.out-of-order", "java.lang.Boolean", null, null); + data("flyway.password", "java.lang.String", null, "Login password of the database to migrate."); + data("flyway.placeholder-prefix", "java.lang.String", null, null); + data("flyway.placeholders", "java.util.Map", null, null); + data("flyway.placeholder-suffix", "java.lang.String", null, null); + data("flyway.schemas", "java.lang.String[]", null, null); + data("flyway.sql-migration-prefix", "java.lang.String", null, null); + data("flyway.sql-migration-separator", "java.lang.String", null, null); + data("flyway.sql-migration-suffix", "java.lang.String", null, null); + data("flyway.table", "java.lang.String", null, null); + data("flyway.target", "org.flywaydb.core.api.MigrationVersion", null, null); + data("flyway.url", "java.lang.String", null, "JDBC url of the database to migrate. If not set, the primary configured data source\n is used."); + data("flyway.user", "java.lang.String", null, "Login user of the database to migrate."); + data("flyway.validate-on-migrate", "java.lang.Boolean", null, null); + data("http.mappers.json-pretty-print", "java.lang.Boolean", null, "Enable json pretty print."); + data("http.mappers.json-sort-keys", "java.lang.Boolean", null, "Enable key sorting."); + data("liquibase.change-log", "java.lang.String", "classpath:/db/changelog/db.changelog-master.yaml", "Change log configuration path."); + data("liquibase.check-change-log-location", "java.lang.Boolean", "true", "Check the change log location exists."); + data("liquibase.contexts", "java.lang.String", null, "Comma-separated list of runtime contexts to use."); + data("liquibase.default-schema", "java.lang.String", null, "Default database schema."); + data("liquibase.drop-first", "java.lang.Boolean", "false", "Drop the database schema first."); + data("liquibase.enabled", "java.lang.Boolean", "true", "Enable liquibase support."); + data("liquibase.password", "java.lang.String", null, "Login password of the database to migrate."); + data("liquibase.url", "java.lang.String", null, "JDBC url of the database to migrate. If not set, the primary configured data source\n is used."); + data("liquibase.user", "java.lang.String", null, "Login user of the database to migrate."); + data("logging.config", "java.lang.String", null, "Location of the logging configuration file."); + data("logging.file", "java.lang.String", null, "Log file name."); + data("logging.level", "java.util.Map", null, "Log levels severity mapping. Use 'root' for the root logger."); + data("logging.path", "java.lang.String", null, "Location of the log file."); + data("multipart.file-size-threshold", "java.lang.String", "0", "Threshold after which files will be written to disk. Values can use the suffixed\n \"MB\" or \"KB\" to indicate a Megabyte or Kilobyte size."); + data("multipart.location", "java.lang.String", null, "Intermediate location of uploaded files."); + data("multipart.max-file-size", "java.lang.String", "1Mb", "Max file size. Values can use the suffixed \"MB\" or \"KB\" to indicate a Megabyte or\n Kilobyte size."); + data("multipart.max-request-size", "java.lang.String", "10Mb", "Max request size. Values can use the suffixed \"MB\" or \"KB\" to indicate a Megabyte\n or Kilobyte size."); + data("security.basic.enabled", "java.lang.Boolean", "true", "Enable basic authentication."); + data("security.basic.path", "java.lang.String[]", "[Ljava.lang.Object;@7abd0056", "Comma-separated list of paths to secure."); + data("security.basic.realm", "java.lang.String", "Spring", "HTTP basic realm name."); + data("security.enable-csrf", "java.lang.Boolean", "false", "Enable Cross Site Request Forgery support."); + data("security.filter-order", "java.lang.Integer", "0", "Security filter chain order."); + data("security.headers.cache", "java.lang.Boolean", "false", "Enable cache control HTTP headers."); + data("security.headers.content-type", "java.lang.Boolean", "false", "Enable \"X-Content-Type-Options\" header."); + data("security.headers.frame", "java.lang.Boolean", "false", "Enable \"X-Frame-Options\" header."); + data("security.headers.hsts", "org.springframework.boot.autoconfigure.security.SecurityProperties$Headers$HSTS", null, "HTTP Strict Transport Security (HSTS) mode (none, domain, all)."); + data("security.headers.xss", "java.lang.Boolean", "false", "Enable cross site scripting (XSS) protection."); + data("security.ignored", "java.util.List", null, "Comma-separated list of paths to exclude from the default secured paths."); + data("security.require-ssl", "java.lang.Boolean", "false", "Enable secure channel for all requests."); + data("security.sessions", "org.springframework.security.config.http.SessionCreationPolicy", null, "Session creation policy (always, never, if_required, stateless)."); + data("security.user.name", "java.lang.String", "user", "Default user name."); + data("security.user.password", "java.lang.String", null, "Password for the default user name."); + data("security.user.role", "java.util.List", null, "Granted roles for the default user name."); + data("server.address", "java.net.InetAddress", null, "Network address to which the server should bind to."); + data("server.context-parameters", "java.util.Map", null, "ServletContext parameters."); + data("server.context-path", "java.lang.String", null, "Context path of the application."); + data("server.port", "java.lang.Integer", null, "Server HTTP port."); + data("server.servlet-path", "java.lang.String", "/", "Path of the main dispatcher servlet."); + data("server.session-timeout", "java.lang.Integer", null, "Session timeout in seconds."); + data("server.ssl.ciphers", "java.lang.String[]", null, null); + data("server.ssl.client-auth", "org.springframework.boot.context.embedded.Ssl$ClientAuth", null, null); + data("server.ssl.key-alias", "java.lang.String", null, null); + data("server.ssl.key-password", "java.lang.String", null, null); + data("server.ssl.key-store", "java.lang.String", null, null); + data("server.ssl.key-store-password", "java.lang.String", null, null); + data("server.ssl.key-store-provider", "java.lang.String", null, null); + data("server.ssl.key-store-type", "java.lang.String", null, null); + data("server.ssl.protocol", "java.lang.String", null, null); + data("server.ssl.trust-store", "java.lang.String", null, null); + data("server.ssl.trust-store-password", "java.lang.String", null, null); + data("server.ssl.trust-store-provider", "java.lang.String", null, null); + data("server.ssl.trust-store-type", "java.lang.String", null, null); + data("server.tomcat.access-log-enabled", "java.lang.Boolean", "false", "Enable access log."); + data("server.tomcat.access-log-pattern", "java.lang.String", null, "Format pattern for access logs."); + data("server.tomcat.background-processor-delay", "java.lang.Integer", "30", "Delay in seconds between the invocation of backgroundProcess methods."); + data("server.tomcat.basedir", "java.io.File", null, "Tomcat base directory. If not specified a temporary directory will be used."); + data("server.tomcat.internal-proxies", "java.lang.String", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3}|169\\.254\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "Regular expression that matches proxies that are to be trusted."); + data("server.tomcat.max-http-header-size", "java.lang.Integer", "0", "Maximum size in bytes of the HTTP message header."); + data("server.tomcat.max-threads", "java.lang.Integer", "0", "Maximum amount of worker threads."); + data("server.tomcat.port-header", "java.lang.String", null, "Name of the HTTP header used to override the original port value."); + data("server.tomcat.protocol-header", "java.lang.String", null, "Header that holds the incoming protocol, usually named \"X-Forwarded-Proto\".\n Configured as a RemoteIpValve only if remoteIpHeader is also set."); + data("server.tomcat.remote-ip-header", "java.lang.String", null, "Name of the http header from which the remote ip is extracted. Configured as a\n RemoteIpValve only if remoteIpHeader is also set."); + data("server.tomcat.uri-encoding", "java.lang.String", null, "Character encoding to use to decode the URI."); + data("server.undertow.buffer-size", "java.lang.Integer", null, "Size of each buffer in bytes."); + data("server.undertow.buffers-per-region", "java.lang.Integer", null, "Number of buffer per region."); + data("server.undertow.direct-buffers", "java.lang.Boolean", null, null); + data("server.undertow.io-threads", "java.lang.Integer", null, "Number of I/O threads to create for the worker."); + data("server.undertow.worker-threads", "java.lang.Integer", null, "Number of worker threads."); + data("spring.activemq.broker-url", "java.lang.String", null, "URL of the ActiveMQ broker. Auto-generated by default."); + data("spring.activemq.in-memory", "java.lang.Boolean", "true", "Specify if the default broker URL should be in memory. Ignored if an explicit\n broker has been specified."); + data("spring.activemq.password", "java.lang.String", null, "Login password of the broker."); + data("spring.activemq.pooled", "java.lang.Boolean", "false", "Specify if a PooledConnectionFactory should be created instead of a regular\n ConnectionFactory."); + data("spring.activemq.user", "java.lang.String", null, "Login user of the broker."); + data("spring.aop.auto", "java.lang.Boolean", "true", "Add @EnableAspectJAutoProxy."); + data("spring.aop.proxy-target-class", "java.lang.Boolean", "false", "Whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false)."); + data("spring.application.index", "java.lang.Integer", null, "Application index."); + data("spring.application.name", "java.lang.String", null, "Application name."); + data("spring.batch.initializer.enabled", "java.lang.Boolean", "true", "Create the required batch tables on startup if necessary."); + data("spring.batch.job.enabled", "java.lang.Boolean", "true", "Execute all Spring Batch jobs in the context on startup."); + data("spring.batch.job.names", "java.lang.String", "", "Comma-separated list of job names to execute on startup. By default, all Jobs\n found in the context are executed."); + data("spring.batch.schema", "java.lang.String", "classpath:org/springframework/batch/core/schema-@@platform@@.sql", "Path to the SQL file to use to initialize the database schema."); + data("spring.config.location", "java.lang.String", null, "Config file locations."); + data("spring.config.name", "java.lang.String", "application", "Config file name."); + data("spring.dao.exceptiontranslation.enabled", "java.lang.Boolean", "true", "Enable the PersistenceExceptionTranslationPostProcessor."); + data("spring.data.elasticsearch.cluster-name", "java.lang.String", "elasticsearch", "Elasticsearch cluster name."); + data("spring.data.elasticsearch.cluster-nodes", "java.lang.String", null, "Comma-separated list of cluster node addresses. If not specified, starts a client\n node."); + data("spring.data.elasticsearch.repositories.enabled", "java.lang.Boolean", "true", "Enable Elasticsearch repositories."); + data("spring.data.jpa.repositories.enabled", "java.lang.Boolean", "true", "Enable JPA repositories."); + data("spring.data.mongodb.authentication-database", "java.lang.String", null, "Authentication database name."); + data("spring.data.mongodb.database", "java.lang.String", null, "Database name."); + data("spring.data.mongodb.grid-fs-database", "java.lang.String", null, "GridFS database name."); + data("spring.data.mongodb.host", "java.lang.String", null, "Mongo server host."); + data("spring.data.mongodb.password", "char[]", null, "Login password of the mongo server."); + data("spring.data.mongodb.port", "java.lang.Integer", null, "Mongo server port."); + data("spring.data.mongodb.repositories.enabled", "java.lang.Boolean", "true", "Enable Mongo repositories."); + data("spring.data.mongodb.uri", "java.lang.String", "mongodb://localhost/test", "Mmongo database URI. When set, host and port are ignored."); + data("spring.data.mongodb.username", "java.lang.String", null, "Login user of the mongo server."); + data("spring.data.rest.base-uri", "java.net.URI", null, null); + data("spring.data.rest.default-page-size", "java.lang.Integer", null, null); + data("spring.data.rest.limit-param-name", "java.lang.String", null, null); + data("spring.data.rest.max-page-size", "java.lang.Integer", null, null); + data("spring.data.rest.page-param-name", "java.lang.String", null, null); + data("spring.data.rest.return-body-on-create", "java.lang.Boolean", null, null); + data("spring.data.rest.return-body-on-update", "java.lang.Boolean", null, null); + data("spring.data.rest.sort-param-name", "java.lang.String", null, null); + data("spring.data.solr.host", "java.lang.String", "http://127.0.0.1:8983/solr", "Solr host. Ignored if \"zk-host\" is set."); + data("spring.data.solr.repositories.enabled", "java.lang.Boolean", "true", "Enable Solr repositories."); + data("spring.data.solr.zk-host", "java.lang.String", null, "ZooKeeper host address in the form HOST:PORT."); + data("spring.datasource.abandon-when-percentage-full", "java.lang.Integer", null, null); + data("spring.datasource.access-to-underlying-connection-allowed", "java.lang.Boolean", null, null); + data("spring.datasource.alternate-username-allowed", "java.lang.Boolean", null, null); + data("spring.datasource.auto-commit", "java.lang.Boolean", null, null); + data("spring.datasource.catalog", "java.lang.String", null, null); + data("spring.datasource.commit-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.connection-customizer-class-name", "java.lang.String", null, null); + data("spring.datasource.connection-init-sql", "java.lang.String", null, null); + data("spring.datasource.connection-init-sqls", "java.util.Collection", null, null); + data("spring.datasource.connection-properties", "java.lang.String", null, null); + data("spring.datasource.connection-test-query", "java.lang.String", null, null); + data("spring.datasource.connection-timeout", "java.lang.Long", null, null); + data("spring.datasource.continue-on-error", "java.lang.Boolean", "false", "Do not stop if an error occurs while initializing the database."); + data("spring.datasource.data", "java.lang.String", null, "Data (DML) script resource reference."); + data("spring.datasource.data-source-class-name", "java.lang.String", null, null); + data("spring.datasource.data-source", "java.lang.Object", null, null); + data("spring.datasource.data-source-j-n-d-i", "java.lang.String", null, null); + data("spring.datasource.data-source-properties", "java.util.Properties", null, null); + data("spring.datasource.db-properties", "java.util.Properties", null, null); + data("spring.datasource.default-auto-commit", "java.lang.Boolean", null, null); + data("spring.datasource.default-catalog", "java.lang.String", null, null); + data("spring.datasource.default-read-only", "java.lang.Boolean", null, null); + data("spring.datasource.default-transaction-isolation", "java.lang.Integer", null, null); + data("spring.datasource.driver-class-name", "java.lang.String", null, "Fully qualified name of the JDBC driver. Auto-detected based on the URL by default."); + data("spring.datasource.fair-queue", "java.lang.Boolean", null, null); + data("spring.datasource.idle-timeout", "java.lang.Long", null, null); + data("spring.datasource.ignore-exception-on-pre-load", "java.lang.Boolean", null, null); + data("spring.datasource.initialization-fail-fast", "java.lang.Boolean", null, null); + data("spring.datasource.initialize", "java.lang.Boolean", "true", "Populate the database using 'data.sql'."); + data("spring.datasource.initial-size", "java.lang.Integer", null, null); + data("spring.datasource.init-s-q-l", "java.lang.String", null, null); + data("spring.datasource.isolate-internal-queries", "java.lang.Boolean", null, null); + data("spring.datasource.jdbc4-connection-test", "java.lang.Boolean", null, null); + data("spring.datasource.jdbc-interceptors", "java.lang.String", null, null); + data("spring.datasource.jdbc-url", "java.lang.String", null, null); + data("spring.datasource.jmx-enabled", "java.lang.Boolean", "false", "Enable JMX support (if provided by the underlying pool)."); + data("spring.datasource.jndi-name", "java.lang.String", null, "JNDI location of the datasource. Class, url, username & password are ignored when\n set."); + data("spring.datasource.leak-detection-threshold", "java.lang.Long", null, null); + data("spring.datasource.log-abandoned", "java.lang.Boolean", null, null); + data("spring.datasource.login-timeout", "java.lang.Integer", null, null); + data("spring.datasource.log-validation-errors", "java.lang.Boolean", null, null); + data("spring.datasource.max-active", "java.lang.Integer", null, null); + data("spring.datasource.max-age", "java.lang.Long", null, null); + data("spring.datasource.max-idle", "java.lang.Integer", null, null); + data("spring.datasource.maximum-pool-size", "java.lang.Integer", null, null); + data("spring.datasource.max-lifetime", "java.lang.Long", null, null); + data("spring.datasource.max-open-prepared-statements", "java.lang.Integer", null, null); + data("spring.datasource.max-wait", "java.lang.Integer", null, null); + data("spring.datasource.metric-registry", "java.lang.Object", null, null); + data("spring.datasource.min-evictable-idle-time-millis", "java.lang.Integer", null, null); + data("spring.datasource.min-idle", "java.lang.Integer", null, null); + data("spring.datasource.minimum-idle", "java.lang.Integer", null, null); + data("spring.datasource.name", "java.lang.String", null, null); + data("spring.datasource.num-tests-per-eviction-run", "java.lang.Integer", null, null); + data("spring.datasource.password", "java.lang.String", null, "Login password of the database."); + data("spring.datasource.platform", "java.lang.String", "all", "Platform to use in the schema resource (schema-${platform}.sql)."); + data("spring.datasource.pool-name", "java.lang.String", null, null); + data("spring.datasource.pool-prepared-statements", "java.lang.Boolean", null, null); + data("spring.datasource.propagate-interrupt-state", "java.lang.Boolean", null, null); + data("spring.datasource.read-only", "java.lang.Boolean", null, null); + data("spring.datasource.register-mbeans", "java.lang.Boolean", null, null); + data("spring.datasource.remove-abandoned", "java.lang.Boolean", null, null); + data("spring.datasource.remove-abandoned-timeout", "java.lang.Integer", null, null); + data("spring.datasource.rollback-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.schema", "java.lang.String", null, "Schema (DDL) script resource reference."); + data("spring.datasource.separator", "java.lang.String", ";", "Statement separator in SQL initialization scripts."); + data("spring.datasource.sql-script-encoding", "java.lang.String", null, "SQL scripts encoding."); + data("spring.datasource.suspect-timeout", "java.lang.Integer", null, null); + data("spring.datasource.test-on-borrow", "java.lang.Boolean", null, null); + data("spring.datasource.test-on-connect", "java.lang.Boolean", null, null); + data("spring.datasource.test-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.test-while-idle", "java.lang.Boolean", null, null); + data("spring.datasource.time-between-eviction-runs-millis", "java.lang.Integer", null, null); + data("spring.datasource.transaction-isolation", "java.lang.String", null, null); + data("spring.datasource.url", "java.lang.String", null, "JDBC url of the database."); + data("spring.datasource.use-disposable-connection-facade", "java.lang.Boolean", null, null); + data("spring.datasource.use-equals", "java.lang.Boolean", null, null); + data("spring.datasource.use-lock", "java.lang.Boolean", null, null); + data("spring.datasource.username", "java.lang.String", null, "Login user of the database."); + data("spring.datasource.validation-interval", "java.lang.Long", null, null); + data("spring.datasource.validation-query", "java.lang.String", null, null); + data("spring.datasource.validation-query-timeout", "java.lang.Integer", null, null); + data("spring.datasource.validator-class-name", "java.lang.String", null, null); + data("spring.datasource.xa.data-source-class-name", "java.lang.String", null, "XA datasource fully qualified name."); + data("spring.datasource.xa.properties", "java.util.Map", null, "Properties to pass to the XA data source."); + data("spring.freemarker.allow-request-override", "java.lang.Boolean", null, "Set whether HttpServletRequest attributes are allowed to override (hide) controller\n generated model attributes of the same name."); + data("spring.freemarker.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.freemarker.char-set", "java.lang.String", null, null); + data("spring.freemarker.charset", "java.lang.String", null, "Template encoding."); + data("spring.freemarker.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.freemarker.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.freemarker.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.freemarker.expose-request-attributes", "java.lang.Boolean", null, "Set whether all request attributes should be added to the model prior to merging\n with the template."); + data("spring.freemarker.expose-session-attributes", "java.lang.Boolean", null, "Set whether all HttpSession attributes should be added to the model prior to\n merging with the template."); + data("spring.freemarker.expose-spring-macro-helpers", "java.lang.Boolean", null, "Set whether to expose a RequestContext for use by Spring's macro library, under the\n name \"springMacroRequestContext\"."); + data("spring.freemarker.prefix", "java.lang.String", null, "Prefix that gets prepended to view names when building a URL."); + data("spring.freemarker.request-context-attribute", "java.lang.String", null, "Name of the RequestContext attribute for all views."); + data("spring.freemarker.settings", "java.util.Map", null, "Well-known FreeMarker keys which will be passed to FreeMarker's Configuration."); + data("spring.freemarker.suffix", "java.lang.String", null, "Suffix that gets appended to view names when building a URL."); + data("spring.freemarker.template-loader-path", "java.lang.String[]", new String[] {"snuzzle" ,"buggles"}, "Comma-separated list of template paths."); + data("spring.freemarker.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.groovy.template.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.groovy.template.char-set", "java.lang.String", null, null); + data("spring.groovy.template.charset", "java.lang.String", null, "Template encoding."); + data("spring.groovy.template.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.groovy.template.configuration.auto-escape", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.auto-indent", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.auto-indent-string", "java.lang.String", null, null); + data("spring.groovy.template.configuration.auto-new-line", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.base-template-class", "java.lang.Class", null, null); + data("spring.groovy.template.configuration.cache-templates", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.declaration-encoding", "java.lang.String", null, null); + data("spring.groovy.template.configuration.expand-empty-elements", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration", "java.util.Map", null, "Configuration to pass to TemplateConfiguration."); + data("spring.groovy.template.configuration.locale", "java.util.Locale", null, null); + data("spring.groovy.template.configuration.new-line-string", "java.lang.String", null, null); + data("spring.groovy.template.configuration.resource-loader-path", "java.lang.String", null, null); + data("spring.groovy.template.configuration.use-double-quotes", "java.lang.Boolean", null, null); + data("spring.groovy.template.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.groovy.template.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.groovy.template.prefix", "java.lang.String", "classpath:/templates/", "Prefix that gets prepended to view names when building a URL."); + data("spring.groovy.template.suffix", "java.lang.String", ".tpl", "Suffix that gets appended to view names when building a URL."); + data("spring.groovy.template.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.hornetq.embedded.cluster-password", "java.lang.String", null, "Cluster password. Randomly generated on startup by default"); + data("spring.hornetq.embedded.data-directory", "java.lang.String", null, "Journal file directory. Not necessary if persistence is turned off."); + data("spring.hornetq.embedded.enabled", "java.lang.Boolean", "true", "Enable embedded mode if the HornetQ server APIs are available."); + data("spring.hornetq.embedded.persistent", "java.lang.Boolean", "false", "Enable persistent store."); + data("spring.hornetq.embedded.queues", "java.lang.String[]", "[Ljava.lang.Object;@2f5ce114", "Comma-separate list of queues to create on startup."); + data("spring.hornetq.embedded.server-id", "java.lang.Integer", "0", "Server id. By default, an auto-incremented counter is used."); + data("spring.hornetq.embedded.topics", "java.lang.String[]", "[Ljava.lang.Object;@6272137a", "Comma-separate list of topics to create on startup."); + data("spring.hornetq.host", "java.lang.String", "localhost", "HornetQ broker host."); + data("spring.hornetq.mode", "org.springframework.boot.autoconfigure.jms.hornetq.HornetQMode", null, "HornetQ deployment mode, auto-detected by default. Can be explicitly set to\n \"native\" or \"embedded\"."); + data("spring.hornetq.port", "java.lang.Integer", "5445", "HornetQ broker port."); + data("spring.http.encoding.charset", "java.nio.charset.Charset", null, "Charset of HTTP requests and responses. Added to the \"Content-Type\" header if not\n set explicitly."); + data("spring.http.encoding.enabled", "java.lang.Boolean", "true", "Enable http encoding support."); + data("spring.http.encoding.force", "java.lang.Boolean", "true", "Force the encoding to the configured charset on HTTP requests and responses."); + data("spring.jackson.date-format", "java.lang.String", null, "Date format string (yyyy-MM-dd HH:mm:ss), or a fully-qualified date format class\n name."); + data("spring.jackson.deserialization", "java.util.Map", null, "Jackson on/off features that affect the way Java objects are deserialized."); + data("spring.jackson.generator", "java.util.Map", null, "Jackson on/off features for generators."); + data("spring.jackson.mapper", "java.util.Map", null, "Jackson general purpose on/off features."); + data("spring.jackson.parser", "java.util.Map", null, "Jackson on/off features for parsers."); + data("spring.jackson.property-naming-strategy", "java.lang.String", null, "One of the constants on Jackson's PropertyNamingStrategy\n (CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES). Can also be a fully-qualified class\n name of a PropertyNamingStrategy subclass."); + data("spring.jackson.serialization", "java.util.Map", null, "Jackson on/off features that affect the way Java objects are serialized."); + data("spring.jersey.filter.order", "java.lang.Integer", "0", "Jersey filter chain order."); + data("spring.jersey.init", "java.util.Map", null, "Init parameters to pass to Jersey."); + data("spring.jersey.type", "org.springframework.boot.autoconfigure.jersey.JerseyProperties$Type", null, "Jersey integration type. Can be either \"servlet\" or \"filter\"."); + data("spring.jms.jndi-name", "java.lang.String", null, "Connection factory JNDI name. When set, takes precedence to others connection\n factory auto-configurations."); + data("spring.jms.pub-sub-domain", "java.lang.Boolean", "false", "Specify if the default destination type is topic."); + data("spring.jmx.enabled", "java.lang.Boolean", "true", "Expose management beans to the JMX domain."); + data("spring.jpa.database", "org.springframework.orm.jpa.vendor.Database", null, "Target database to operate on, auto-detected by default. Can be alternatively set\n using the \"databasePlatform\" property."); + data("spring.jpa.database-platform", "java.lang.String", null, "Name of the target database to operate on, auto-detected by default. Can be\n alternatively set using the \"Database\" enum."); + data("spring.jpa.generate-ddl", "java.lang.Boolean", "false", "Initialize the schema on startup."); + data("spring.jpa.hibernate.ddl-auto", "java.lang.String", null, "DDL mode (\"none\", \"validate\", \"update\", \"create\", \"create-drop\"). This is\n actually a shortcut for the \"hibernate.hbm2ddl.auto\" property. Default to\n \"create-drop\" when using an embedded database, \"none\" otherwise."); + data("spring.jpa.hibernate.naming-strategy", "java.lang.Class", null, "Naming strategy fully qualified name."); + data("spring.jpa.open-in-view", "java.lang.Boolean", "true", "Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request."); + data("spring.jpa.properties", "java.util.Map", null, "Additional native properties to set on the JPA provider."); + data("spring.jpa.show-sql", "java.lang.Boolean", "false", "Enable logging of SQL statements."); + data("spring.jta.allow-multiple-lrc", "java.lang.Boolean", null, null); + data("spring.jta.asynchronous2-pc", "java.lang.Boolean", null, null); + data("spring.jta.background-recovery-interval", "java.lang.Integer", null, null); + data("spring.jta.background-recovery-interval-seconds", "java.lang.Integer", null, null); + data("spring.jta.current-node-only-recovery", "java.lang.Boolean", null, null); + data("spring.jta.debug-zero-resource-transaction", "java.lang.Boolean", null, null); + data("spring.jta.default-transaction-timeout", "java.lang.Integer", null, null); + data("spring.jta.disable-jmx", "java.lang.Boolean", null, null); + data("spring.jta.enabled", "java.lang.Boolean", "true", "Enable JTA support."); + data("spring.jta.exception-analyzer", "java.lang.String", null, null); + data("spring.jta.filter-log-status", "java.lang.Boolean", null, null); + data("spring.jta.force-batching-enabled", "java.lang.Boolean", null, null); + data("spring.jta.forced-write-enabled", "java.lang.Boolean", null, null); + data("spring.jta.graceful-shutdown-interval", "java.lang.Integer", null, null); + data("spring.jta.jndi-transaction-synchronization-registry-name", "java.lang.String", null, null); + data("spring.jta.jndi-user-transaction-name", "java.lang.String", null, null); + data("spring.jta.journal", "java.lang.String", null, null); + data("spring.jta.log-dir", "java.lang.String", null, "Transaction logs directory."); + data("spring.jta.log-part1-filename", "java.lang.String", null, null); + data("spring.jta.log-part2-filename", "java.lang.String", null, null); + data("spring.jta.max-log-size-in-mb", "java.lang.Integer", null, null); + data("spring.jta.resource-configuration-filename", "java.lang.String", null, null); + data("spring.jta.server-id", "java.lang.String", null, null); + data("spring.jta.skip-corrupted-logs", "java.lang.Boolean", null, null); + data("spring.jta.transaction-manager-id", "java.lang.String", null, "Transaction manager unique identifier."); + data("spring.jta.warn-about-zero-resource-transaction", "java.lang.Boolean", null, null); + data("spring.mail.default-encoding", "java.lang.String", "UTF-8", "Default MimeMessage encoding."); + data("spring.mail.host", "java.lang.String", null, "SMTP server host."); + data("spring.mail.password", "java.lang.String", null, "Login password of the SMTP server."); + data("spring.mail.port", "java.lang.Integer", null, "SMTP server port."); + data("spring.mail.properties", "java.util.Map", null, "Additional JavaMail session properties."); + data("spring.mail.username", "java.lang.String", null, "Login user of the SMTP server."); + data("spring.main.show-banner", "java.lang.Boolean", "true", "Display the banner when the application runs."); + data("spring.main.sources", "java.util.Set", null, "Sources (class name, package name or XML resource location) used to create the ApplicationContext."); + data("spring.main.web-environment", "java.lang.Boolean", null, "Run the application in a web environment (auto-detected by default)."); + data("spring.mandatory-file-encoding", "java.lang.String", null, "Expected character encoding the application must use."); + data("spring.messages.basename", "java.lang.String", "messages", "Comma-separated list of basenames, each following the ResourceBundle convention.\n Essentially a fully-qualified classpath location. If it doesn't contain a package\n qualifier (such as \"org.mypackage\"), it will be resolved from the classpath root."); + data("spring.messages.cache-seconds", "java.lang.Integer", "-1", "Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles\n are cached forever."); + data("spring.messages.encoding", "java.lang.String", "utf-8", "Message bundles encoding."); + data("spring.mobile.devicedelegatingviewresolver.enabled", "java.lang.Boolean", "false", "Enable device view resolver."); + data("spring.mobile.devicedelegatingviewresolver.mobile-prefix", "java.lang.String", "mobile/", "Prefix that gets prepended to view names for mobile devices."); + data("spring.mobile.devicedelegatingviewresolver.mobile-suffix", "java.lang.String", "", "Suffix that gets appended to view names for mobile devices."); + data("spring.mobile.devicedelegatingviewresolver.normal-prefix", "java.lang.String", "", "Prefix that gets prepended to view names for normal devices."); + data("spring.mobile.devicedelegatingviewresolver.normal-suffix", "java.lang.String", "", "Suffix that gets appended to view names for normal devices."); + data("spring.mobile.devicedelegatingviewresolver.tablet-prefix", "java.lang.String", "tablet/", "Prefix that gets prepended to view names for tablet devices."); + data("spring.mobile.devicedelegatingviewresolver.tablet-suffix", "java.lang.String", "", "Suffix that gets appended to view names for tablet devices."); + data("spring.mobile.sitepreference.enabled", "java.lang.Boolean", "true", "Enable SitePreferenceHandler."); + data("spring.mvc.date-format", "java.lang.String", null, "Date format to use (e.g. dd/MM/yyyy)"); + data("spring.mvc.ignore-default-model-on-redirect", "java.lang.Boolean", "true", "If the the content of the \"default\" model should be ignored during redirect\n scenarios."); + data("spring.mvc.locale", "java.lang.String", null, "Locale to use."); + data("spring.mvc.message-codes-resolver-format", "org.springframework.validation.DefaultMessageCodesResolver$Format", null, "Formatting strategy for message codes (PREFIX_ERROR_CODE, POSTFIX_ERROR_CODE)."); + data("spring.profiles.active", "java.lang.String", null, "Comma-separated list of active profiles. Can be overridden by a command line switch."); + data("spring.profiles.include", "java.lang.String", null, "Unconditionally activate the specified comma separated profiles."); + data("spring.rabbitmq.addresses", "java.lang.String", null, "Comma-separated list of addresses to which the client should connect to."); + data("spring.rabbitmq.dynamic", "java.lang.Boolean", "true", "Create an AmqpAdmin bean."); + data("spring.rabbitmq.host", "java.lang.String", "localhost", "RabbitMQ host."); + data("spring.rabbitmq.password", "java.lang.String", null, "Login to authenticate against the broker."); + data("spring.rabbitmq.port", "java.lang.Integer", "5672", "RabbitMQ port."); + data("spring.rabbitmq.username", "java.lang.String", null, "Login user to authenticate to the broker."); + data("spring.rabbitmq.virtual-host", "java.lang.String", null, "Virtual host to use when connecting to the broker."); + data("spring.redis.database", "java.lang.Integer", "0", "Database index used by the connection factory."); + data("spring.redis.host", "java.lang.String", "localhost", "Redis server host."); + data("spring.redis.password", "java.lang.String", null, "Login password of the redis server."); + data("spring.redis.pool.max-active", "java.lang.Integer", "8", "Max number of connections that can be allocated by the pool at a given time.\n Use a negative value for no limit."); + data("spring.redis.pool.max-idle", "java.lang.Integer", "8", "Max number of \"idle\" connections in the pool. Use a negative value to indicate\n an unlimited number of idle connections."); + data("spring.redis.pool.max-wait", "java.lang.Integer", "-1", "Maximum amount of time (in milliseconds) a connection allocation should block\n before throwing an exception when the pool is exhausted. Use a negative value\n to block indefinitely."); + data("spring.redis.pool.min-idle", "java.lang.Integer", "0", "Target for the minimum number of idle connections to maintain in the pool. This\n setting only has an effect if it is positive."); + data("spring.redis.port", "java.lang.Integer", "6379", "Redis server port."); + data("spring.redis.sentinel.master", "java.lang.String", null, "Name of Redis server."); + data("spring.redis.sentinel.nodes", "java.lang.String", null, "Comma-separated list of host:port pairs."); + data("spring.resources.add-mappings", "java.lang.Boolean", "true", "Enable default resource handling."); + data("spring.resources.cache-period", "java.lang.Integer", null, "Cache period for the resources served by the resource handler, in seconds."); + data("spring.social.auto-connection-views", "java.lang.Boolean", "false", "Enable the connection status view for supported providers."); + data("spring.social.facebook.app-id", "java.lang.String", null, "Application id."); + data("spring.social.facebook.app-secret", "java.lang.String", null, "Application secret."); + data("spring.social.linkedin.app-id", "java.lang.String", null, "Application id."); + data("spring.social.linkedin.app-secret", "java.lang.String", null, "Application secret."); + data("spring.social.twitter.app-id", "java.lang.String", null, "Application id."); + data("spring.social.twitter.app-secret", "java.lang.String", null, "Application secret."); + data("spring.thymeleaf.cache", "java.lang.Boolean", "true", "Enable template caching."); + data("spring.thymeleaf.check-template-location", "java.lang.Boolean", "true", "Check that the templates location exists."); + data("spring.thymeleaf.content-type", "java.lang.String", "text/html", "Content-Type value."); + data("spring.thymeleaf.enabled", "java.lang.Boolean", "true", "Enable MVC Thymeleaf view resolution."); + data("spring.thymeleaf.encoding", "java.lang.String", "UTF-8", "Template encoding."); + data("spring.thymeleaf.excluded-view-names", "java.lang.String[]", null, "Comma-separated list of view names that should be excluded from resolution."); + data("spring.thymeleaf.mode", "java.lang.String", "HTML5", "Template mode to be applied to templates. See also StandardTemplateModeHandlers."); + data("spring.thymeleaf.prefix", "java.lang.String", "classpath:/templates/", "Prefix that gets prepended to view names when building a URL."); + data("spring.thymeleaf.suffix", "java.lang.String", ".html", "Suffix that gets appended to view names when building a URL."); + data("spring.thymeleaf.view-names", "java.lang.String[]", null, "Comma-separated list of view names that can be resolved."); + data("spring.velocity.allow-request-override", "java.lang.Boolean", null, "Set whether HttpServletRequest attributes are allowed to override (hide) controller\n generated model attributes of the same name."); + data("spring.velocity.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.velocity.char-set", "java.lang.String", null, null); + data("spring.velocity.charset", "java.lang.String", null, "Template encoding."); + data("spring.velocity.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.velocity.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.velocity.date-tool-attribute", "java.lang.String", null, "Name of the DateTool helper object to expose in the Velocity context of the view."); + data("spring.velocity.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.velocity.expose-request-attributes", "java.lang.Boolean", null, "Set whether all request attributes should be added to the model prior to merging\n with the template."); + data("spring.velocity.expose-session-attributes", "java.lang.Boolean", null, "Set whether all HttpSession attributes should be added to the model prior to\n merging with the template."); + data("spring.velocity.expose-spring-macro-helpers", "java.lang.Boolean", null, "Set whether to expose a RequestContext for use by Spring's macro library, under the\n name \"springMacroRequestContext\"."); + data("spring.velocity.number-tool-attribute", "java.lang.String", null, "Name of the NumberTool helper object to expose in the Velocity context of the view."); + data("spring.velocity.prefer-file-system-access", "java.lang.Boolean", "true", "Prefer file system access for template loading. File system access enables hot\n detection of template changes."); + data("spring.velocity.prefix", "java.lang.String", null, "Prefix that gets prepended to view names when building a URL."); + data("spring.velocity.properties", "java.util.Map", null, "Additional velocity properties."); + data("spring.velocity.request-context-attribute", "java.lang.String", null, "Name of the RequestContext attribute for all views."); + data("spring.velocity.resource-loader-path", "java.lang.String", "classpath:/templates/", "Template path."); + data("spring.velocity.suffix", "java.lang.String", null, "Suffix that gets appended to view names when building a URL."); + data("spring.velocity.toolbox-config-location", "java.lang.String", null, "Velocity Toolbox config location, for example \"/WEB-INF/toolbox.xml\". Automatically\n loads a Velocity Tools toolbox definition file and expose all defined tools in the\n specified scopes."); + data("spring.velocity.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.view.prefix", "java.lang.String", null, "Spring MVC view prefix."); + data("spring.view.suffix", "java.lang.String", null, "Spring MVC view suffix."); + } + + public boolean isEmpty() { + return datas == null || datas.isEmpty(); + } + + public SpringPropertyIndexProvider getIndexProvider() { + return indexProvider; + } + +} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/StyledStringMatcher.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/StyledStringMatcher.java new file mode 100644 index 000000000..c04fa5e50 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/StyledStringMatcher.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2016 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.yaml; + +//import org.eclipse.jface.viewers.StyledString; +//import org.eclipse.swt.custom.StyleRange; + +public abstract class StyledStringMatcher { + +// public abstract void match(StyledString styledString) throws Exception; + + /** + * Creates a matcher that matches a given {@link StyledString} if a given String + * equals the concatenation of all the text in the {@link StyledString} formatted + * in 'plain' font (currently 'plain' just means that it is not using 'strikeout' + * as that is the only other style we currently care about). + */ + public static StyledStringMatcher plainFont(final String string) { + throw new UnsupportedOperationException("Not yet implemented"); +// return new StyledStringMatcher() { +// @Override +// public String toString() { +// return "plain("+string+")"; +// } +// +// @Override +// public void match(StyledString styledString) throws Exception { +// StringBuilder extractedText = new StringBuilder(); +// String string = styledString.getString(); +// for (StyleRange styleRange : styledString.getStyleRanges()) { +// if (!styleRange.strikeout) { +// extractedText.append(string.substring(styleRange.start, styleRange.start + styleRange.length)); +// } +// } +// assertEquals(string, extractedText.toString()); +// } +// }; + } + + /** + * Creates a matcher that matches a given {@link StyledString} if a given String + * equals the concatenation of all the text in the {@link StyledString} formatted + * in 'strikeout' font. + */ + public static StyledStringMatcher strikeout(final String expectedString) { + throw new UnsupportedOperationException("Not yet implemented"); +// return new StyledStringMatcher() { +// @Override +// public String toString() { +// return "strikeout("+expectedString+")"; +// } +// @Override +// public void match(StyledString styledString) throws Exception { +// StringBuilder extractedText = new StringBuilder(); +// String unstyledString = styledString.getString(); +// for (StyleRange styleRange : styledString.getStyleRanges()) { +// if (styleRange.strikeout) { +// extractedText.append(unstyledString.substring(styleRange.start, styleRange.start + styleRange.length)); +// } +// } +// assertEquals(expectedString, extractedText.toString()); +// } +// }; + } + +} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTest.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTests.java similarity index 56% rename from vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTest.java rename to vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTests.java index bf65645f9..af694eaab 100644 --- a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTest.java +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTests.java @@ -18,10 +18,10 @@ import io.typefox.lsapi.InitializeResult; import io.typefox.lsapi.ServerCapabilities; import io.typefox.lsapi.TextDocumentSyncKind; -public class YamlLanguageServerTest { +public class YamlLanguageServerTests { public static File getTestResource(String name) throws URISyntaxException { - return Paths.get(YamlLanguageServerTest.class.getResource(name).toURI()).toFile(); + return Paths.get(YamlLanguageServerTests.class.getResource(name).toURI()).toFile(); } @Test @@ -39,34 +39,6 @@ public class YamlLanguageServerTest { } - @Test public void completions() throws Exception { - LanguageServerHarness harness = new LanguageServerHarness(YamlLanguageServer::new); - - File workspaceRoot = getTestResource("/workspace/"); - assertExpectedInitResult(harness.intialize(workspaceRoot)); - - TextDocumentInfo doc = harness.openDocument(getTestResource("/workspace/testfile.yml")); - - CompletionList completions = harness.getCompletions(doc, doc.positionOf("foo")); - assertThat(completions.isIncomplete()).isFalse(); - assertThat(completions.getItems()) - .extracting(CompletionItem::getLabel) - .containsExactly("TypeScript", "JavaScript"); - - List resolved = harness.resolveCompletions(completions); - assertThat(resolved) - .extracting(CompletionItem::getLabel) - .containsExactly("TypeScript", "JavaScript"); - - assertThat(resolved) - .extracting(CompletionItem::getDetail) - .containsExactly("TypeScript details", "JavaScript details"); - - assertThat(resolved) - .extracting(CompletionItem::getDocumentation) - .containsExactly("TypeScript docs", "JavaScript docs"); - } - private void assertExpectedInitResult(InitializeResult initResult) { assertThat(initResult.getCapabilities().getCompletionProvider().getResolveProvider()).isTrue(); assertThat(initResult.getCapabilities().getTextDocumentSync()).isEqualTo(TextDocumentSyncKind.Full);