completions for predicate keywords in Spring Data repositories
This commit is contained in:
@@ -33,9 +33,9 @@ record DataRepositoryMethodNameParseResult(
|
||||
*/
|
||||
boolean performFullCompletion,
|
||||
/**
|
||||
* the last entered word, which completion options should be used for completing the expression.
|
||||
* the last entered word (if it is not a keyword), which completion options should be used for completing the expression.
|
||||
*
|
||||
* e.g. {@code First} in {@code findByFirst} which could be completed to {@code findByFirstName}
|
||||
* e.g. {@code First} in {@code findByFirst} which could be completed to {@code findByFirstName} or {@code null} if it is a keyword or non-existent
|
||||
*/
|
||||
String lastWord,
|
||||
/**
|
||||
|
||||
@@ -30,9 +30,7 @@ class DataRepositoryMethodParser {
|
||||
|
||||
private static final Map<String, List<QueryPredicateKeywordInfo>> PREDICATE_KEYWORDS_GROUPED_BY_FIRST_WORD = QueryPredicateKeywordInfo.PREDICATE_KEYWORDS
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(info->{
|
||||
return findFirstWord(info.keyword());
|
||||
}));
|
||||
.collect(Collectors.groupingBy(info -> findFirstWord(info.keyword())));
|
||||
|
||||
private final String prefix;
|
||||
private final Map<String, List<DomainProperty>> propertiesGroupedByFirstWord;
|
||||
|
||||
@@ -47,22 +47,55 @@ public class DataRepositoryPrefixSensitiveCompletionProvider implements DataRepo
|
||||
if (parseResult.lastWord() == null || !propertiesByName.containsKey(parseResult.lastWord())) {
|
||||
addPropertyProposals(completions, offset, repoDef, parseResult);
|
||||
}
|
||||
addPredicateKeywordProposals(completions, offset, prefix, parseResult, propertiesByName);
|
||||
}
|
||||
}
|
||||
|
||||
private void addPredicateKeywordProposals(Collection<ICompletionProposal> completions, int offset, String prefix, DataRepositoryMethodNameParseResult parseResult, Map<String, DomainProperty> propertiesByName) {
|
||||
String lastWord = findLastWordWithoutPrefixingProperty(parseResult, propertiesByName);
|
||||
for (QueryPredicateKeywordInfo predicate : QueryPredicateKeywordInfo.PREDICATE_KEYWORDS){
|
||||
if (parseResult.allowedKeywordTypes().contains(predicate.type())) {
|
||||
createLastWordReplacementCompletion(completions, offset, parseResult, lastWord, predicate.keyword());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String findLastWordWithoutPrefixingProperty(DataRepositoryMethodNameParseResult parseResult, Map<String, DomainProperty> propertiesByName) {
|
||||
String lastWord = parseResult.lastWord();
|
||||
if (lastWord == null) {
|
||||
return "";
|
||||
}
|
||||
if (propertiesByName.containsKey(lastWord)) {
|
||||
return "";
|
||||
}
|
||||
for (int i = lastWord.length() - 1; i >= 0; i--) {
|
||||
if (Character.isUpperCase(lastWord.charAt(i))) {
|
||||
String substring = lastWord.substring(0,i);
|
||||
if (propertiesByName.containsKey(substring)) {
|
||||
return lastWord.substring(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return lastWord;
|
||||
}
|
||||
|
||||
private void addPropertyProposals(Collection<ICompletionProposal> completions, int offset, DataRepositoryDefinition repoDef, DataRepositoryMethodNameParseResult parseResult) {
|
||||
for(DomainProperty property : repoDef.getDomainType().getProperties()){
|
||||
String lastWord = parseResult.lastWord();
|
||||
if (lastWord == null) {
|
||||
lastWord = "";
|
||||
}
|
||||
if (property.getName().startsWith(lastWord)) {
|
||||
DocumentEdits edits = new DocumentEdits(null, false);
|
||||
edits.replace(offset - lastWord.length(), offset, property.getName());
|
||||
DocumentEdits additionalEdits = new DocumentEdits(null, false);
|
||||
ICompletionProposal proposal = new FindByCompletionProposal(property.getName(), CompletionItemKind.Text, edits, "property " + property.getName(), null, Optional.of(additionalEdits), lastWord);
|
||||
completions.add(proposal);
|
||||
}
|
||||
String toReplace = property.getName();
|
||||
createLastWordReplacementCompletion(completions, offset, parseResult, parseResult.lastWord(), toReplace);
|
||||
}
|
||||
}
|
||||
|
||||
private void createLastWordReplacementCompletion(Collection<ICompletionProposal> completions, int offset, DataRepositoryMethodNameParseResult parseResult, String lastWord, String toReplace) {
|
||||
if (lastWord == null) {
|
||||
lastWord = "";
|
||||
}
|
||||
if (toReplace.startsWith(lastWord)) {
|
||||
DocumentEdits edits = new DocumentEdits(null, false);
|
||||
edits.replace(offset - lastWord.length(), offset, toReplace);
|
||||
DocumentEdits additionalEdits = new DocumentEdits(null, false);
|
||||
ICompletionProposal proposal = new FindByCompletionProposal(toReplace, CompletionItemKind.Text, edits, "property " + toReplace, null, Optional.of(additionalEdits), lastWord);
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,11 +138,35 @@ public class DataRepositoryCompletionProcessorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByComplexExpression() throws Exception {
|
||||
void testFindByComplexExpression() throws Exception {
|
||||
checkCompletions("findByResponsibleEmployee", "List<Customer> findByResponsibleEmployee(Employee responsibleEmployee);");
|
||||
checkCompletions("findByResponsibleEmployee_SocialSecurityNumber", "List<Customer> findByResponsibleEmployee_SocialSecurityNumber(Long responsibleEmployee_SocialSecurityNumber);");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendKeywords() throws Exception {
|
||||
checkCompletions("findByFirstName",
|
||||
"findByFirstNameAnd",
|
||||
"findByFirstNameExists",
|
||||
"findByFirstNameIgnoreCase",
|
||||
"findByFirstNameIsLessThanEqual",
|
||||
"findByFirstNameOr",
|
||||
"findByFirstNameOrderBy");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendKeywordsWithPreviousKeyword() throws Exception {
|
||||
checkCompletions("findByFirstNameAnd",
|
||||
"findByFirstNameAndNot");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendKeywordsStartAlreadyPresent() throws Exception {
|
||||
checkCompletions("findByFirstNameA",
|
||||
"findByFirstNameAfter",
|
||||
"findByFirstNameAnd");
|
||||
}
|
||||
|
||||
private void checkCompletions(String alredyPresent, String... expectedCompletions) throws Exception {
|
||||
prepareCase("{\n}", "{\n\t" + alredyPresent + "<*>");
|
||||
assertContainsAnnotationCompletions(Arrays.stream(expectedCompletions).map(expected -> "\t" + expected + "<*>").toArray(String[]::new));
|
||||
|
||||
Reference in New Issue
Block a user