From 607f072238efdd72b1ba0e6b5dfdf85a64cce81d Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Wed, 14 May 2025 11:54:16 +0200 Subject: [PATCH] GH-1560: avoid duplicate property keys in completion proposala Fixes GH-1560 --- ...ditionalOnPropertyCompletionProcessor.java | 13 +++++++--- .../ConditionalOnPropertyCompletionTest.java | 25 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalOnPropertyCompletionProcessor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalOnPropertyCompletionProcessor.java index 0c55609d6..f012a62c0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalOnPropertyCompletionProcessor.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalOnPropertyCompletionProcessor.java @@ -12,6 +12,7 @@ package org.springframework.ide.vscode.boot.java.conditionals; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -66,10 +67,11 @@ public class ConditionalOnPropertyCompletionProcessor implements AnnotationAttri private List findProperties(IJavaProject project, String prefix) { List result = new ArrayList<>(); + Set propertyKeys = new HashSet<>(); // First the 'real' properties, Then also add 'ad-hoc' properties - addPropertyProposals(indexProvider.getIndex(project).getProperties(), prefix, result); - addPropertyProposals(adHocIndexProvider.getIndex(project), prefix, result); + addPropertyProposals(indexProvider.getIndex(project).getProperties(), prefix, result, propertyKeys); + addPropertyProposals(adHocIndexProvider.getIndex(project), prefix, result, propertyKeys); result.sort((p1, p2) -> p1.getLabel().compareTo(p2.getLabel())); @@ -86,9 +88,14 @@ public class ConditionalOnPropertyCompletionProcessor implements AnnotationAttri return new ArrayList<>(prefixes); } - private void addPropertyProposals(FuzzyMap properties, String prefix, List result) { + private void addPropertyProposals(FuzzyMap properties, String prefix, List result, Set propertyKeys) { properties.forEach(propertyInfo -> { String propID = propertyInfo.getId(); + + // avoid duplicate property keys + if (!propertyKeys.add(propID)) { + return; + } if (prefix != null) { if (prefix.length() > 0 diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalOnPropertyCompletionTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalOnPropertyCompletionTest.java index fd9a4c478..ffa108f6f 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalOnPropertyCompletionTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalOnPropertyCompletionTest.java @@ -147,6 +147,31 @@ public class ConditionalOnPropertyCompletionTest { assertEquals("spring.boot.prop1", completions.get(2).getLabel()); } + @Test + public void testConditionalOnPropertyCompletionWithAdhocProperties() throws Exception { + adHocProperties.add("adhoc.prop"); + + List completions = getCompletions("@ConditionalOnProperty(<*>)"); + assertEquals(4, completions.size()); + + assertEquals("adhoc.prop", completions.get(0).getLabel()); + assertEquals("data.prop2", completions.get(1).getLabel()); + assertEquals("else.prop3", completions.get(2).getLabel()); + assertEquals("spring.boot.prop1", completions.get(3).getLabel()); + } + + @Test + public void testConditionalOnPropertyCompletionWithAdhocPropertiesDuplicatedKey() throws Exception { + adHocProperties.add("data.prop2"); + + List completions = getCompletions("@ConditionalOnProperty(<*>)"); + assertEquals(3, completions.size()); + + assertEquals("data.prop2", completions.get(0).getLabel()); + assertEquals("else.prop3", completions.get(1).getLabel()); + assertEquals("spring.boot.prop1", completions.get(2).getLabel()); + } + @Test public void testConditionalOnPropertyCompletionWithoutPrefixAttributeWithNameAttribute() throws Exception { List completions = getCompletions("@ConditionalOnProperty(name=<*>)");