Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ public List<? extends Problem> getProblems(final Element element) {
return this.getProblemSupport().getProblems(element);
}

public void validateLater(final Procedure0 arg0) {
this.getProblemSupport().validateLater(arg0);
public void validateLater(final Procedure0 validationCallback) {
this.getProblemSupport().validateLater(validationCallback);
}

public MutableAnnotationTypeDeclaration findAnnotationType(final String qualifiedName) {
Expand Down Expand Up @@ -226,32 +226,32 @@ public AnnotationReference newAnnotationReference(final AnnotationReference anno
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationReference);
}

public AnnotationReference newAnnotationReference(final AnnotationReference arg0, final Procedure1<AnnotationReferenceBuildContext> arg1) {
return this.getAnnotationReferenceProvider().newAnnotationReference(arg0, arg1);
public AnnotationReference newAnnotationReference(final AnnotationReference annotationReference, final Procedure1<AnnotationReferenceBuildContext> initializer) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationReference, initializer);
}

public AnnotationReference newAnnotationReference(final Class<?> annotationClass) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationClass);
}

public AnnotationReference newAnnotationReference(final Class<?> arg0, final Procedure1<AnnotationReferenceBuildContext> arg1) {
return this.getAnnotationReferenceProvider().newAnnotationReference(arg0, arg1);
public AnnotationReference newAnnotationReference(final Class<?> annotationClass, final Procedure1<AnnotationReferenceBuildContext> initializer) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationClass, initializer);
}

public AnnotationReference newAnnotationReference(final String annotationTypeName) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationTypeName);
}

public AnnotationReference newAnnotationReference(final String arg0, final Procedure1<AnnotationReferenceBuildContext> arg1) {
return this.getAnnotationReferenceProvider().newAnnotationReference(arg0, arg1);
public AnnotationReference newAnnotationReference(final String annotationTypeName, final Procedure1<AnnotationReferenceBuildContext> initializer) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationTypeName, initializer);
}

public AnnotationReference newAnnotationReference(final Type annotationTypeDelcaration) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationTypeDelcaration);
}

public AnnotationReference newAnnotationReference(final Type arg0, final Procedure1<AnnotationReferenceBuildContext> arg1) {
return this.getAnnotationReferenceProvider().newAnnotationReference(arg0, arg1);
public AnnotationReference newAnnotationReference(final Type annotationTypeDelcaration, final Procedure1<AnnotationReferenceBuildContext> initializer) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationTypeDelcaration, initializer);
}

public boolean exists(final Path path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ public List<? extends Problem> getProblems(final Element element) {
return this.getProblemSupport().getProblems(element);
}

public void validateLater(final Procedure0 arg0) {
this.getProblemSupport().validateLater(arg0);
public void validateLater(final Procedure0 validationCallback) {
this.getProblemSupport().validateLater(validationCallback);
}

public MutableAnnotationTypeDeclaration findAnnotationType(final String qualifiedName) {
Expand Down Expand Up @@ -219,32 +219,32 @@ public AnnotationReference newAnnotationReference(final AnnotationReference anno
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationReference);
}

public AnnotationReference newAnnotationReference(final AnnotationReference arg0, final Procedure1<AnnotationReferenceBuildContext> arg1) {
return this.getAnnotationReferenceProvider().newAnnotationReference(arg0, arg1);
public AnnotationReference newAnnotationReference(final AnnotationReference annotationReference, final Procedure1<AnnotationReferenceBuildContext> initializer) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationReference, initializer);
}

public AnnotationReference newAnnotationReference(final Class<?> annotationClass) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationClass);
}

public AnnotationReference newAnnotationReference(final Class<?> arg0, final Procedure1<AnnotationReferenceBuildContext> arg1) {
return this.getAnnotationReferenceProvider().newAnnotationReference(arg0, arg1);
public AnnotationReference newAnnotationReference(final Class<?> annotationClass, final Procedure1<AnnotationReferenceBuildContext> initializer) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationClass, initializer);
}

public AnnotationReference newAnnotationReference(final String annotationTypeName) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationTypeName);
}

public AnnotationReference newAnnotationReference(final String arg0, final Procedure1<AnnotationReferenceBuildContext> arg1) {
return this.getAnnotationReferenceProvider().newAnnotationReference(arg0, arg1);
public AnnotationReference newAnnotationReference(final String annotationTypeName, final Procedure1<AnnotationReferenceBuildContext> initializer) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationTypeName, initializer);
}

public AnnotationReference newAnnotationReference(final Type annotationTypeDelcaration) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationTypeDelcaration);
}

public AnnotationReference newAnnotationReference(final Type arg0, final Procedure1<AnnotationReferenceBuildContext> arg1) {
return this.getAnnotationReferenceProvider().newAnnotationReference(arg0, arg1);
public AnnotationReference newAnnotationReference(final Type annotationTypeDelcaration, final Procedure1<AnnotationReferenceBuildContext> initializer) {
return this.getAnnotationReferenceProvider().newAnnotationReference(annotationTypeDelcaration, initializer);
}

public boolean exists(final Path path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
Expand All @@ -37,6 +36,7 @@
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.xtext.ide.editor.quickfix.DiagnosticResolution;
import org.eclipse.xtext.ide.editor.quickfix.DiagnosticResolutionInfo;
import org.eclipse.xtext.ide.editor.quickfix.IQuickFixProvider;
import org.eclipse.xtext.ide.server.Document;
import org.eclipse.xtext.ide.server.ILanguageServerAccess;
Expand Down Expand Up @@ -94,31 +94,33 @@ public String getExpectedResult() {
private FileExtensionProvider fileExtensionProvider;

/**
* Test that the expected quickfixes are offered on a given validation issue in a given DSL text.
* Test that the expected quickfixes are offered on a given validation issue
* in a given DSL text.
*
* @param fileContents
* The initial DSL text.
* @param issueCode
* The code of the validation issue the offered quickfixes to test.
* @param quickfixes
* The quickfixes that are expected to be offered on the given <code>issueCode</code>. Each expected quickfix should be described by the
* following triple:
* <ol>
* <li>the quickfix label</li>
* <li>the quickfix description</li>
* <li>the DSL text after the quickfix application</li>
* </ol>
* @param fileContents The initial DSL text.
* @param issueCode The code of the validation issue the offered quickfixes
* to test.
* @param quickfixes The quickfixes that are expected to be offered on the
* given <code>issueCode</code>. Each expected quickfix should be described
* by the following triple:
* <ol>
* <li>the quickfix label</li>
* <li>the quickfix description</li>
* <li>the DSL text after the quickfix application</li>
* </ol>
*/
protected void assertQuickfixesOn(String fileContents, String issueCode, EClass type, QuickfixExpectation... quickfixes) {
protected void assertQuickfixesOn(String fileContents, String issueCode, EClass type,
QuickfixExpectation... quickfixes) {
String normalizedContents = toUnixLineSeparator(fileContents);
quickfixesAreOffered(createInMemoryFile(normalizedContents, type), issueCode, normalizedContents, quickfixes);
}

protected void assertQuickFixOn(String fileContents, String expected, String quickFixLabel, String issueCode, EClass elementType) {

protected void assertQuickFixOn(String fileContents, String expected, String quickFixLabel, String issueCode,
EClass elementType) {
QuickfixExpectation quickfix = new QuickfixExpectation(quickFixLabel, quickFixLabel, expected);
assertQuickfixesOn(fileContents.toString(), issueCode, elementType, quickfix);
}

@SuppressWarnings("unchecked")
private <T> T createInMemoryFile(CharSequence content, EClass type) {
InMemoryURIHandler fs = new InMemoryURIHandler();
Expand All @@ -131,7 +133,8 @@ private <T> T createInMemoryFile(CharSequence content, EClass type) {
return (T) quickFixTestHelper.findFirstOfTypeInFile(rs, fileName, type.getInstanceClass());
}

private void quickfixesAreOffered(EObject target, String issueCode, String originalText, QuickfixExpectation... expected) {
private void quickfixesAreOffered(EObject target, String issueCode, String originalText,
QuickfixExpectation... expected) {
List<QuickfixExpectation> expectedSorted = IterableExtensions.sortBy(Arrays.asList(expected), it -> it.label);
ICompositeNode elementNode = NodeModelUtils.getNode(target);
LineAndColumn elementStartPosition = NodeModelUtils.getLineAndColumn(elementNode, elementNode.getOffset());
Expand All @@ -156,12 +159,14 @@ public void addBuildListener(ILanguageServerAccess.IBuildListener listener) {
}

@Override
public <T extends Object> CompletableFuture<T> doRead(String uri, Function<ILanguageServerAccess.Context, T> function) {
public <T extends Object> CompletableFuture<T> doRead(String uri,
Function<ILanguageServerAccess.Context, T> function) {
return CompletableFuture.completedFuture(doSyncRead(uri, function));
}

@Override
public <T extends Object> CompletableFuture<T> doReadIndex(Function<? super ILanguageServerAccess.IndexContext, ? extends T> function) {
public <T extends Object> CompletableFuture<T> doReadIndex(
Function<? super ILanguageServerAccess.IndexContext, ? extends T> function) {
return null;
}

Expand All @@ -182,16 +187,16 @@ public LanguageClient getLanguageClient() {

@Override
public ResourceSet newLiveScopeResourceSet(URI uri) {
//re-using the existing ResourceSet because it contains the URI protocol mapping for "inmemory" resources.
ResourceSet resourceSet = options.getResource().getResourceSet();
return resourceSet;
// re-using the existing ResourceSet because it contains the URI
// protocol mapping for "inmemory" resources.
return options.getResource().getResourceSet();
}

@Override
public <T> T doSyncRead(String uri, Function<Context, T> function) {
ILanguageServerAccess.Context ctx = new ILanguageServerAccess.Context(options.getResource(), options.getDocument(),
true, CancelIndicator.NullImpl);
return function.apply(ctx);
ILanguageServerAccess.Context ctx = new ILanguageServerAccess.Context(options.getResource(),
options.getDocument(), true, CancelIndicator.NullImpl);
return function.apply(ctx);
}
});
CodeActionParams codeActionParams = new CodeActionParams();
Expand All @@ -204,27 +209,28 @@ public <T> T doSyncRead(String uri, Function<Context, T> function) {

options.setCodeActionParams(codeActionParams);

for (QuickfixExpectation expectedIssueResolution: expectedSorted) {
List<DiagnosticResolution> actualIssueResolutions =
quickFixProvider.getResolutions(options, issue).stream()
.filter(r -> r.getLabel().equals(expectedIssueResolution.getLabel()))
.collect(Collectors.toList());
for (QuickfixExpectation expectedIssueResolution : expectedSorted) {
List<DiagnosticResolution> actualIssueResolutions = quickFixProvider.getResolutions(options, issue).stream()
.filter(r -> r.getLabel().equals(expectedIssueResolution.getLabel())).toList();
assertEquals("More than one quickfix available!", 1, actualIssueResolutions.size());

DiagnosticResolution actualIssueResolution = actualIssueResolutions.get(0);

assertEquals(expectedIssueResolution.label, actualIssueResolution.getLabel());
assertEquals(expectedIssueResolution.description, actualIssueResolution.getLabel());

assertIssueResolutionResult(toUnixLineSeparator(expectedIssueResolution.getExpectedResult()), actualIssueResolution, originalText,
options.getDocument());
assertIssueResolutionResult(toUnixLineSeparator(expectedIssueResolution.getExpectedResult()),
actualIssueResolution, options.getDocument());
}
}

private void assertIssueResolutionResult(String expectedResult, DiagnosticResolution actualIssueResolution, String originalText,
private void assertIssueResolutionResult(String expectedResult, DiagnosticResolution actualIssueResolution,
Document doc) {
WorkspaceEdit edit = actualIssueResolution.apply();
List<TextEdit> edits = edit.getChanges().values().stream().flatMap(List::stream).collect(Collectors.toList());
String id = quickFixProvider.cacheResolution(actualIssueResolution);
DiagnosticResolutionInfo info = new DiagnosticResolutionInfo();
info.setResolutionId(id);
WorkspaceEdit edit = quickFixProvider.resolveResolution(info);
List<TextEdit> edits = edit.getChanges().values().stream().flatMap(List::stream).toList();
Document changedDocument = doc.applyChanges(edits);

assertEquals(expectedResult, changedDocument.getContents());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import org.eclipse.xtext.ide.tests.testlanguage.validation.TestLanguageValidator;
import org.eclipse.xtext.xbase.lib.StringExtensions;

import jakarta.inject.Singleton;

@Singleton
public class TestLanguageQuickFixProvider extends AbstractDeclarativeIdeQuickfixProvider {

public static String EMF_QF_LABEL = "Change element name to first upper using object modification";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,42 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.apache.log4j.Logger;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.xtext.ide.server.codeActions.ICodeActionService2.Options;

import com.google.common.annotations.Beta;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.Provider;

import jakarta.inject.Singleton;

/**
* @author Heinrich Weichert
*
* @since 2.24
*/
@Beta
@Singleton
public class AbstractDeclarativeIdeQuickfixProvider implements IQuickFixProvider {

private static final Logger LOG = Logger.getLogger(AbstractDeclarativeIdeQuickfixProvider.class);

private Map<String, List<Method>> methods = new ConcurrentHashMap<>();

private final Map<String, List<Method>> methods = new ConcurrentHashMap<>();
private final Map<String, DiagnosticResolution> resolutionIds = new ConcurrentHashMap<>();

@Inject
private Provider<DiagnosticResolutionAcceptor> issueResolutionAcceptorProvider;

private boolean getFixMethodPredicate(Method input, String issueCode) {
for (QuickFix annotation : input.getAnnotationsByType(QuickFix.class)) {
boolean result = annotation != null && Objects.equals(issueCode,annotation.value())
boolean result = annotation != null && Objects.equals(issueCode, annotation.value())
&& input.getParameterTypes().length == 1 && Void.TYPE == input.getReturnType()
&& input.getParameterTypes()[0].isAssignableFrom(DiagnosticResolutionAcceptor.class);
if (result) {
Expand All @@ -70,25 +75,25 @@ public List<DiagnosticResolution> getResolutions(Options options, Diagnostic dia

private List<Method> collectMethods(Class<? extends AbstractDeclarativeIdeQuickfixProvider> clazz,
String issueCode) {
return Arrays.stream(clazz.getMethods()).filter(method -> getFixMethodPredicate(method, issueCode))
.collect(Collectors.toList());
return Arrays.stream(clazz.getMethods()).filter(method -> getFixMethodPredicate(method, issueCode)).toList();
}

@Override
public boolean handlesDiagnostic(Diagnostic diagnostic) {
return !getFixMethods(diagnostic).isEmpty();
return !getFixMethods(diagnostic).isEmpty();
}

public List<Method> getFixMethods(Diagnostic diagnostic) {
if (diagnostic == null || diagnostic.getCode() == null || diagnostic.getMessage() == null || diagnostic.getSeverity() == null) {
if (diagnostic == null || diagnostic.getCode() == null || diagnostic.getMessage() == null
|| diagnostic.getSeverity() == null) {
return Collections.emptyList();
}
String issueCode = diagnostic.getCode().getLeft();
if (Strings.isNullOrEmpty(issueCode)) {
return Collections.emptyList();
}
return methods.computeIfAbsent(issueCode, c -> this.collectMethods(getClass(), c));

return methods.computeIfAbsent(issueCode, c -> this.collectMethods(getClass(), c));
}

/**
Expand All @@ -105,4 +110,17 @@ public List<Method> getFixMethods(Diagnostic diagnostic) {
protected List<TextEdit> createTextEdit(Diagnostic diagnostic, String text) {
return Collections.singletonList(new TextEdit(diagnostic.getRange(), text));
}

@Override
public WorkspaceEdit resolveResolution(DiagnosticResolutionInfo info) {
DiagnosticResolution edit = resolutionIds.remove(info.getResolutionId());
return edit == null ? new WorkspaceEdit() : edit.apply();
}

@Override
public String cacheResolution(DiagnosticResolution resolution) {
String id = UUID.randomUUID().toString();
resolutionIds.put(id, resolution);
return id;
}
}
Loading
Loading