diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java index a9af79cc88..8972fd0c09 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java @@ -257,4 +257,8 @@ public static boolean containsCharacters(char[] valChars, char[] queryChars) { return queryindex == queryChars.length; } + + public static String snakeCaseToHyphens(String snakeString) { + return snakeString.replace('_', '-').toLowerCase(); + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java index 978928e8a7..780cfc584e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java @@ -11,11 +11,13 @@ package org.springframework.ide.vscode.boot.yaml.reconcile; -import static org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType.*; +import static org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType.YAML_DEPRECATED_ERROR; +import static org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType.YAML_DEPRECATED_WARNING; import static org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType.YAML_DUPLICATE_KEY; import static org.springframework.ide.vscode.commons.yaml.ast.NodeUtil.asScalar; import static org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST.getChildren; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -60,6 +62,8 @@ import org.yaml.snakeyaml.nodes.ScalarNode; import org.yaml.snakeyaml.nodes.SequenceNode; +import com.google.common.collect.ImmutableSet; + /** * @author Kris De Volder */ @@ -139,24 +143,21 @@ protected boolean isIgnoreScalarAssignmentTo(String propName) { private void reconcile(YamlFileAST root, NodeTuple entry, IndexNavigator nav) { Node keyNode = entry.getKeyNode(); - String key = asScalar(keyNode); - if (key==null) { + String _key = asScalar(keyNode); + if (_key==null) { expectScalar(keyNode); } else { - IndexNavigator subNav = nav.selectSubProperty(key); - PropertyInfo match = subNav.getExactMatch(); - PropertyInfo extension = subNav.getExtensionCandidate(); - if (match==null && extension==null) { - //nothing found for this key. Maybe user is using camelCase variation of the key? - String keyAlias = StringUtil.camelCaseToHyphens(key); - IndexNavigator subNavAlias = nav.selectSubProperty(keyAlias); + IndexNavigator subNav = null; + PropertyInfo match = null; + PropertyInfo extension = null; + //Try different 'relaxed' variants for this key. Maybe user is using camelCase or snake case? + for (String key : keyAliases(_key)) { + IndexNavigator subNavAlias = nav.selectSubProperty(key); match = subNavAlias.getExactMatch(); extension = subNavAlias.getExtensionCandidate(); if (match!=null || extension!=null) { - //Got something for the alias, so use that instead. - //Note: do not swap for alias unless we actually found something. - // This gives more logical errors (in terms of user's key, not its canonical alias) subNav = subNavAlias; + break; //stop at first alias that gives a result. } } if (match!=null && extension!=null) { @@ -183,6 +184,14 @@ private void reconcile(YamlFileAST root, NodeTuple entry, IndexNavigator nav) { } } + private Collection keyAliases(String originalKey) { + ImmutableSet.Builder builder = ImmutableSet.builder(); + builder.add(originalKey); + builder.add(StringUtil.camelCaseToHyphens(originalKey)); + builder.add(StringUtil.snakeCaseToHyphens(originalKey)); + return builder.build(); + } + /** * Reconcile a node given the type that we expect the node to be. */ diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index c3975ddc6e..4a2b6550a1 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -71,37 +71,37 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { //////////////////////////////////////////////////////////////////////////////////////// - @Ignore //Ignored because this bug is not yet fixed so the test fails. @Test public void bug_GH_327() throws Exception { //See https://github.com/spring-projects/sts4/issues/327 - data("spring.resources.static-locations", "java.lang.String[]", null, "Blah"); - data("spring.devtools.restart.additional-paths", "java.util.List", null, "Blah blah"); + data("spring.resources.static-locations", "java.lang.Boolean", null, "Blah"); + data("spring.devtools.restart.additional-paths", "java.lang.Boolean", null, "Blah blah"); Editor editor; - + + //Also check whether reconciler understands the structure when inside of a 'relaxed' name key editor = harness.newEditor( "spring:\n" + " resources:\n" + - " static_locations: []\n" + + " static_locations: bad\n" + " devtools:\n" + " restart:\n" + - " additional_paths: []\n" + " additional_paths: wrong\n" ); - editor.assertProblems(/*NONE*/); - - //Also check whether reconciler understands the structure when inside of a 'relaxed' name key + editor.assertProblems( + "bad|boolean", //TODO: fill in proper expected message instead of XXXX + "wrong|boolean" //TODO: fill in proper expected message instead of XXXX + ); + + // basic check editor = harness.newEditor( "spring:\n" + " resources:\n" + - " static_locations: not-a-list-1\n" + + " static_locations: true\n" + " devtools:\n" + " restart:\n" + - " additional_paths: not-a-list-2\n" - ); - editor.assertProblems( - "not-a-list-1|XXXX", //TODO: fill in proper expected message instead of XXXX - "not-a-list-2|XXXX" //TODO: fill in proper expected message instead of XXXX + " additional_paths: false\n" ); + editor.assertProblems(/*NONE*/); } @Test public void bug_165724475() throws Exception {