Spring properties postfix computed in completion item resolve

This commit is contained in:
aboyko
2023-09-28 10:22:12 -04:00
parent 862fd9b992
commit 1dfba38500
32 changed files with 471 additions and 113 deletions

View File

@@ -134,7 +134,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
CompositeCompletionEngine compositeCompletionEngine = new CompositeCompletionEngine();
completionEngines.forEach(compositeCompletionEngine::add);
completionEngineAdapter = server.createCompletionEngineAdapter(compositeCompletionEngine);
completionEngineAdapter.setMaxCompletions(100);
completionEngineAdapter.setMaxCompletions(-1);
documents.onCompletion(completionEngineAdapter::getCompletions);
documents.onCompletionResolve(completionEngineAdapter::resolveCompletion);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2019 Pivotal, Inc.
* Copyright (c) 2015, 2023 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
@@ -17,7 +17,7 @@ import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
public abstract class AbstractPropertyProposal extends ScoreableProposal {
@Override
public String getDetail() {
return niceTypeName(getType());
@@ -101,7 +101,7 @@ public abstract class AbstractPropertyProposal extends ScoreableProposal {
public final DocumentEdits getTextEdit() {
return this.proposalApplier;
}
// @Override
// public void apply(IDocument document) {
// try {

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2014, 2018 Pivotal, Inc.
* Copyright (c) 2014, 2023 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
@@ -62,11 +62,11 @@ public class PropertyCompletionFactory {
};
}
public ScoreableProposal property(IDocument doc, DocumentEdits applier, Match<PropertyInfo> prop, TypeUtil typeUtil) {
public AbstractPropertyProposal property(IDocument doc, DocumentEdits applier, Match<PropertyInfo> prop, TypeUtil typeUtil) {
return new PropertyProposal(doc, applier, prop, typeUtil);
}
public ScoreableProposal beanProperty(IDocument doc, final String contextProperty, final Type contextType, final String pattern, final TypedProperty property, final double score, DocumentEdits applier, final TypeUtil typeUtil) {
public AbstractPropertyProposal beanProperty(IDocument doc, final String contextProperty, final Type contextType, final String pattern, final TypedProperty property, final double score, DocumentEdits applier, final TypeUtil typeUtil) {
AbstractPropertyProposal proposal = new AbstractPropertyProposal(doc, applier) {
@Override

View File

@@ -14,7 +14,6 @@ import java.util.Collection;
import java.util.List;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.springframework.ide.vscode.boot.java.data.providers.DataRepositoryCompletionProvider;
@@ -25,8 +24,8 @@ import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.IRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
* @author Martin Lippert
@@ -44,12 +43,7 @@ public class DataRepositoryCompletionProcessor implements CompletionProvider {
}
@Override
public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type,
int offset, IDocument doc, Collection<ICompletionProposal> completions) {
}
@Override
public void provideCompletions(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
public void provideCompletions(ASTNode node, int offset, TextDocument doc, Collection<ICompletionProposal> completions) {
TypeDeclaration type = ASTUtils.findDeclaringType(node);
DataRepositoryDefinition repo = getDataRepositoryDefinition(type);
if(repo != null && repo.getDomainType() != null){

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.java.data;
import java.util.Optional;
import java.util.function.Supplier;
import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
@@ -24,12 +25,12 @@ public class FindByCompletionProposal implements ICompletionProposal {
private DocumentEdits edits;
private String details;
private Renderable doc;
private Optional<DocumentEdits> additionalEdits;
private Supplier<DocumentEdits> additionalEdits;
private String filter;
private boolean triggerNextCompletion;
public FindByCompletionProposal(String label, CompletionItemKind kind, DocumentEdits edits, String details,
Renderable doc, Optional<DocumentEdits> additionalEdits, String filter, boolean triggerNextCompletion) {
Renderable doc, Supplier<DocumentEdits> additionalEdits, String filter, boolean triggerNextCompletion) {
super();
this.label = label;
this.kind = kind;
@@ -41,7 +42,7 @@ public class FindByCompletionProposal implements ICompletionProposal {
this.triggerNextCompletion = triggerNextCompletion;
}
public static ICompletionProposal createProposal(int offset, CompletionItemKind completionItemKind, String prefix, String label, String completion, boolean triggerNextCompletion, Optional<DocumentEdits> additionalEdits) {
public static ICompletionProposal createProposal(int offset, CompletionItemKind completionItemKind, String prefix, String label, String completion, boolean triggerNextCompletion, Supplier<DocumentEdits> additionalEdits) {
DocumentEdits edits = new DocumentEdits(null, false);
String filter = label;
if (prefix != null && label.startsWith(prefix)) {
@@ -84,8 +85,8 @@ public class FindByCompletionProposal implements ICompletionProposal {
}
@Override
public Optional<DocumentEdits> getAdditionalEdit() {
return additionalEdits;
public Optional<Supplier<DocumentEdits>> getAdditionalEdit() {
return Optional.ofNullable(additionalEdits);
}
@Override

View File

@@ -11,7 +11,6 @@
package org.springframework.ide.vscode.boot.java.data.providers;
import java.util.Collection;
import java.util.Optional;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.lsp4j.CompletionItemKind;
@@ -34,7 +33,7 @@ public class DataRepositoryQueryStartCompletionProvider implements DataRepositor
for(QueryMethodSubject queryMethodSubject : QueryMethodSubject.QUERY_METHOD_SUBJECTS){
String toInsert = queryMethodSubject.key() + "By";
if(prefix == null || (toInsert.length() > localPrefix.length() && toInsert.startsWith(localPrefix)) || isOffsetAfterWhitespace(doc, offset)) {
completions.add(FindByCompletionProposal.createProposal(offset, CompletionItemKind.Text, prefix, toInsert, toInsert, true, Optional.empty()));
completions.add(FindByCompletionProposal.createProposal(offset, CompletionItemKind.Text, prefix, toInsert, toInsert, true, null));
}
}
}

View File

@@ -76,7 +76,7 @@ public class DataRepositoryStandardCompletionProvider implements DataRepositoryC
completion.append(StringUtils.uncapitalize(domainProperty.getName()));
completion.append(");");
return FindByCompletionProposal.createProposal(offset, CompletionItemKind.Method, prefix, label.toString(), completion.toString(), false, ASTUtils.getImportsEdit((CompilationUnit)node.getRoot(), imprts, doc));
return FindByCompletionProposal.createProposal(offset, CompletionItemKind.Method, prefix, label.toString(), completion.toString(), false, () -> ASTUtils.getImportsEdit((CompilationUnit)node.getRoot(), imprts, doc).orElse(null));
}
}

View File

@@ -14,8 +14,8 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
@@ -114,8 +114,7 @@ public class DataRepositoryPrefixSensitiveCompletionProvider implements DataRepo
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, true);
ICompletionProposal proposal = new FindByCompletionProposal(toReplace, CompletionItemKind.Text, edits, "property " + toReplace, null, null, lastWord, true);
completions.add(proposal);
}
}
@@ -141,7 +140,7 @@ public class DataRepositoryPrefixSensitiveCompletionProvider implements DataRepo
newText.append(";");
int replaceStart = calculateReplaceOffset(offset, localPrefix, fullPrefix, returnType);
edits.replace(replaceStart, offset, newText.toString());
Optional<DocumentEdits> additionalEdits = ASTUtils.getImportsEdit((CompilationUnit) node.getRoot(), imports, doc);
Supplier<DocumentEdits> additionalEdits = () -> ASTUtils.getImportsEdit((CompilationUnit) node.getRoot(), imports, doc).orElse(null);
ICompletionProposal proposal = new FindByCompletionProposal(signature, CompletionItemKind.Method, edits, null, null, additionalEdits, signature, false);
completions.add(proposal);
}

View File

@@ -64,7 +64,7 @@ public class BootJavaCompletionEngine implements ICompletionEngine, LanguageSpec
});
}
private void collectCompletionsForAnnotations(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
private void collectCompletionsForAnnotations(ASTNode node, int offset, TextDocument doc, Collection<ICompletionProposal> completions) {
Annotation annotation = null;
ASTNode exactNode = node;

View File

@@ -16,14 +16,14 @@ import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
* @author Martin Lippert
*/
public interface CompletionProvider {
void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type, int offset, IDocument doc, Collection<ICompletionProposal> completions);
void provideCompletions(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions);
default void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, Collection<ICompletionProposal> completions) {};
default void provideCompletions(ASTNode node, int offset, TextDocument doc, Collection<ICompletionProposal> completions) {};
}

View File

@@ -20,7 +20,7 @@ import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
* @author Martin Lippert
@@ -29,7 +29,7 @@ public class ScopeCompletionProcessor implements CompletionProvider {
@Override
public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type,
int offset, IDocument doc, Collection<ICompletionProposal> completions) {
int offset, TextDocument doc, Collection<ICompletionProposal> completions) {
try {
if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair) {
@@ -82,8 +82,4 @@ public class ScopeCompletionProcessor implements CompletionProvider {
}
}
@Override
public void provideCompletions(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
}
}

View File

@@ -12,6 +12,7 @@ package org.springframework.ide.vscode.boot.java.snippets;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
@@ -20,8 +21,6 @@ import org.springframework.ide.vscode.commons.languageserver.completion.IComplet
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import com.google.common.base.Supplier;
public class JavaSnippet {
private JavaSnippetContext context;

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.snippets;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -20,8 +21,6 @@ import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import com.google.common.base.Supplier;
/**
* Respobsible for converting eclipse-like template string into lsp snippet text.
* @author Kris De Volder

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.java.snippets;
import java.util.Optional;
import java.util.function.Supplier;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.lsp4j.CompletionItemKind;
@@ -22,8 +23,6 @@ import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import com.google.common.base.Supplier;
public class JavaSnippetCompletion implements ICompletionProposal{
private DocumentRegion query;
@@ -64,7 +63,7 @@ public class JavaSnippetCompletion implements ICompletionProposal{
}
@Override
public Optional<DocumentEdits> getAdditionalEdit() {
return javaSnippet.getImports().flatMap(imports -> ASTUtils.getImportsEdit(cu, imports, query.getDocument()));
public Optional<java.util.function.Supplier<DocumentEdits>> getAdditionalEdit() {
return javaSnippet.getImports().map(imports -> () -> ASTUtils.getImportsEdit(cu, imports, query.getDocument()).orElse(null));
}
}

View File

@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java.snippets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
@@ -23,8 +24,6 @@ import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import com.google.common.base.Supplier;
public class JavaSnippetManager {
private List<JavaSnippet> snippets = new ArrayList<>();

View File

@@ -37,6 +37,7 @@ import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.FuzzyMap;
import org.springframework.ide.vscode.commons.util.FuzzyMap.Match;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
* @author Martin Lippert
@@ -55,7 +56,7 @@ public class ValueCompletionProcessor implements CompletionProvider {
@Override
public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type,
int offset, IDocument doc, Collection<ICompletionProposal> completions) {
int offset, TextDocument doc, Collection<ICompletionProposal> completions) {
try {
// case: @Value(<*>)
@@ -103,10 +104,6 @@ public class ValueCompletionProcessor implements CompletionProvider {
}
}
@Override
public void provideCompletions(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
}
private void computeProposalsForSimpleName(ASTNode node, Collection<ICompletionProposal> completions, int offset,
IDocument doc) {
String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition());

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2019 Pivotal, Inc.
* Copyright (c) 2016, 2023 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
@@ -235,11 +235,9 @@ public class PropertiesCompletionProposalsCalculator {
for (TypedProperty prop : objectProperties) {
double score = FuzzyMatcher.matchScore(prefix, prop.getName());
if (score!=0) {
Type valueType = prop.getType();
String postFix = propertyCompletionPostfix(typeUtil, valueType);
DocumentEdits edits = new DocumentEdits(doc, false);
edits.delete(navOffset+1, offset);
edits.insert(offset, prop.getName()+postFix);
edits.lazyInsert(offset, prop.getName(), () -> prop.getName() + propertyCompletionPostfix(typeUtil, prop.getType()));
proposals.add(
completionFactory.beanProperty(doc, null, type, prefix, prop, score, edits, typeUtil)
);
@@ -344,10 +342,10 @@ public class PropertiesCompletionProposalsCalculator {
try {
docEdits = LazyProposalApplier.from(() -> {
try {
Type type = TypeParser.parse(match.data.getType());
DocumentEdits edits = new DocumentEdits(doc, false);
edits.delete(offset-prefix.length(), offset);
edits.insert(offset, match.data.getId() + propertyCompletionPostfix(typeUtil, type));
String key = match.data.getId();
edits.lazyInsert(offset, key, () -> key + propertyCompletionPostfix(typeUtil, TypeParser.parse(match.data.getType())));
return edits;
} catch (Throwable t) {
log.error("{}", t);

View File

@@ -26,13 +26,11 @@ import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.common.InformationTemplates;
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
import org.springframework.ide.vscode.boot.common.RelaxedNameConfig;
import org.springframework.ide.vscode.boot.configurationmetadata.ConfigurationMetadataGroup;
import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation;
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.metadata.IndexNavigator;
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
import org.springframework.ide.vscode.boot.metadata.PropertyInfo.PropertySource;
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex;
import org.springframework.ide.vscode.boot.metadata.hints.HintProvider;
import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint;
@@ -46,7 +44,6 @@ import org.springframework.ide.vscode.boot.metadata.types.TypedProperty;
import org.springframework.ide.vscode.boot.metadata.util.PropertyDocUtils;
import org.springframework.ide.vscode.boot.properties.hover.PropertiesDefinitionCalculator;
import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlASTReconciler;
import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlReconcileEngine;
import org.springframework.ide.vscode.commons.java.IField;
import org.springframework.ide.vscode.commons.java.IJavaElement;
import org.springframework.ide.vscode.commons.java.IJavaProject;
@@ -208,7 +205,7 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
//property not yet defined
Type type = p.getType();
edits.delete(queryOffset, query);
edits.createPathInPlace(contextNode, relativePath, queryOffset, appendTextFor(type));
edits.createPathInPlace(contextNode, relativePath, queryOffset, () -> appendTextFor(type));
proposals.add(completionFactory.beanProperty(doc.getDocument(),
contextPath.toPropString(), getType(),
query, p, score, edits, typeUtil)
@@ -511,15 +508,14 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
// context. If it doesn't we can create it as any child of the context
// so that includes, right at place the user is typing now.
SNode existingNode = contextNode.traverse(nextSegment);
String appendText = appendTextFor(TypeParser.parse(match.data.getType()));
if (existingNode==null) {
edits.createPathInPlace(contextNode, relativePath, queryOffset, appendText);
edits.createPathInPlace(contextNode, relativePath, queryOffset, () -> appendTextFor(TypeParser.parse(match.data.getType())));
} else {
String wholeLine = file.getLineTextAtOffset(queryOffset);
if (wholeLine.trim().equals(query.trim())) {
edits.deleteLineBackwardAtOffset(queryOffset);
}
edits.createPath(getContextRoot(file), YamlPath.fromProperty(match.data.getId()), appendText);
edits.createPath(getContextRoot(file), YamlPath.fromProperty(match.data.getId()), () -> appendTextFor(TypeParser.parse(match.data.getType())));
}
return edits;
});

View File

@@ -204,6 +204,7 @@ public abstract class AbstractPropsEditorTest {
for (int i = 0; i < actualLabels.length; i++) {
actualLabels[i] = completions.get(i).getLabel();
if (includeDetail) {
completions.set(i, harness.resolveCompletionItem(completions.get(i)));
String detail = completions.get(i).getDetail();
if (detail != null && !detail.isEmpty()) {
actualLabels[i] += " : " + detail;
@@ -221,7 +222,10 @@ public abstract class AbstractPropsEditorTest {
completionDetails[i] = expectCompletions[i][1];
}
Editor editor = newEditor(editorText);
List<CompletionItem> completions = editor.getCompletions();
List<CompletionItem> completions = editor.getCompletions()
.stream()
.map(ci -> harness.resolveCompletionItem(ci))
.collect(Collectors.toList());
String[] actualLabels = new String[completions.size()];
String[] actualDetails = new String[completions.size()];
for (int i = 0; i < completions.size(); i++) {

View File

@@ -73,15 +73,15 @@ public class XmlBeansHyperlinkTest {
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
private MavenJavaProject project;
private Level originalLevel;
// private Level originalLevel;
@BeforeEach
public void setup() throws Exception {
final Logger logger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
originalLevel = logger.getLevel();
logger.setLevel(Level.DEBUG);
log.debug("-------------------------------------------------");
// final Logger logger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// originalLevel = logger.getLevel();
// logger.setLevel(Level.DEBUG);
//
// log.debug("-------------------------------------------------");
harness.intialize(null);
Map<String, Object> supportXML = new HashMap<>();
@@ -105,12 +105,12 @@ public class XmlBeansHyperlinkTest {
initProject.get(1500, TimeUnit.SECONDS);
}
@AfterEach
public void tearDown() {
log.debug("-------------------------------------------------");
final Logger logger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
logger.setLevel(originalLevel);
}
// @AfterEach
// public void tearDown() {
// log.debug("-------------------------------------------------");
// final Logger logger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// logger.setLevel(originalLevel);
// }
@Test
void testBeanClassHyperlink() throws Exception {