Skip to content

Commit

Permalink
Move CodeAction provides next to MagikChecks/MagikTypedChecks
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenLooman committed Sep 1, 2023
1 parent 79ce8b4 commit ec49533
Show file tree
Hide file tree
Showing 18 changed files with 473 additions and 188 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nl.ramsolutions.sw.magik.checks;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import nl.ramsolutions.sw.magik.checks.checks.CommentRegularExpressionCheck;
import nl.ramsolutions.sw.magik.checks.checks.CommentedCodeCheck;
Expand Down Expand Up @@ -100,6 +101,14 @@ public static List<Class<?>> getChecks() {
XPathCheck.class);
}

/**
* Get the {@link MagikCheck}s which have a {@link MagikCheckFixer}.
* @return
*/
public static Map<Class<?>, List<Class<?>>> getFixers() {
return Map.of();
}

/**
* Get {@link MagikCheck}s which are disabled by default.
* @return List of {@link MagikCheck}s.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package nl.ramsolutions.sw.magik.checks;

import java.util.List;
import nl.ramsolutions.sw.magik.CodeAction;
import nl.ramsolutions.sw.magik.MagikFile;
import nl.ramsolutions.sw.magik.Range;

/**
* Base class to provide automatic fixes for Magik checks.
*/
public abstract class MagikCheckFixer {

/**
* Provide automatic fixes for violations detected by the sibling check.
* @return
*/
public abstract List<CodeAction> provideCodeActions(MagikFile magikFile, Range range);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import nl.ramsolutions.sw.magik.Location;
import nl.ramsolutions.sw.magik.Position;
import nl.ramsolutions.sw.magik.Range;
import nl.ramsolutions.sw.magik.TextEdit;

/**
* Utility class for converting LSP4J <-> MagikLanguageServer.
Expand Down Expand Up @@ -64,4 +65,16 @@ public static org.eclipse.lsp4j.Location locationToLsp4j(final Location location
return new org.eclipse.lsp4j.Location(uri, rangeLsp4j);
}

/**
* Convert a TextEdit to LSP4J.
* @param textEdit TextEdit to convert.
* @return TextEdit in LSP4J.
*/
public static org.eclipse.lsp4j.TextEdit textEditToLsp4j(final TextEdit textEdit) {
final Range range = textEdit.getRange();
final String newText = textEdit.getNewText();
final org.eclipse.lsp4j.Range rangeLsp4j = Lsp4jConversion.rangeToLsp4j(range);
return new org.eclipse.lsp4j.TextEdit(rangeLsp4j, newText);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import java.io.Serializable;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import nl.ramsolutions.sw.magik.MagikTypedFile;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
Expand Down Expand Up @@ -41,52 +41,29 @@ public int compare(final Position position0, final Position position1) {

}

/**
* Test if {@code range1} overlaps with or is included by {@code range2}.
* @param range1 Range 1.
* @param range2 Range 2.
* @return True if {@code wantedRange} overlaps with or is included by {@code testedRange}.
*/
public static boolean rangeOverlaps(final Range range1, final Range range2) {
// 5 Cases:
// 1. wantedRange ends before testedRange start --> false
// 2. wantedRange before testedRange and ends in testedRange --> true
// 3. wantedRange in testedRange --> true
// 4. wantedRange in testedRange and after testedRange --> true
// 5. wantedRange starts after testedRange end --> false

// Testing case 1/5 is enough.
final PositionComparator comparator = new PositionComparator();
return !(
comparator.compare(range1.getEnd(), range2.getStart()) < 0
|| comparator.compare(range1.getStart(), range2.getEnd()) > 0);
}

/**
* Create a {@link CodeAction}.
* @param magikFile The {@link MagikTypedFile}.
* @param range The {@link nl.ramsolutions.sw.magik.Range}.
* @param newText The new text.
* @param description The description.
* @param textEdits The {@link TextEdit}s.
* @return The {@link CodeAction}.
*/
public static CodeAction createCodeAction(
final MagikTypedFile magikFile,
final nl.ramsolutions.sw.magik.Range range,
final String newText,
final String description) {
final Range rangeLsp4j = Lsp4jConversion.rangeToLsp4j(range);
final TextEdit textEdit = new TextEdit(rangeLsp4j, newText);
final String description,
final List<nl.ramsolutions.sw.magik.TextEdit> textEdits) {
final List<org.eclipse.lsp4j.TextEdit> lsp4jTextEdits =
textEdits.stream()
.map(Lsp4jConversion::textEditToLsp4j)
.collect(Collectors.toList());
final VersionedTextDocumentIdentifier versionedTextDocumentIdentifier =
new VersionedTextDocumentIdentifier(magikFile.getUri().toString(), TEXT_DOCUMENT_VERSION);
final TextDocumentEdit textDocumentEdit = new TextDocumentEdit(
versionedTextDocumentIdentifier,
List.of(textEdit));
final WorkspaceEdit edit = new WorkspaceEdit(List.of(Either.forLeft(textDocumentEdit)));
final TextDocumentEdit textDocumentEdit = new TextDocumentEdit(versionedTextDocumentIdentifier, lsp4jTextEdits);
final WorkspaceEdit workspaceEdit = new WorkspaceEdit(List.of(Either.forLeft(textDocumentEdit)));

final CodeAction codeAction = new CodeAction(description);
codeAction.setKind(CodeActionKind.QuickFix);
codeAction.setEdit(edit);
codeAction.setEdit(workspaceEdit);
return codeAction;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import nl.ramsolutions.sw.magik.MagikTypedFile;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.ReadOnlyTypeKeeperAdapter;
Expand Down Expand Up @@ -469,9 +470,19 @@ public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(final Cod
range.getEnd().getLine(), range.getEnd().getCharacter());

final MagikTypedFile magikFile = this.openFiles.get(textDocument);
final nl.ramsolutions.sw.magik.Range magikRange = Lsp4jConversion.rangeFromLsp4j(range);
final CodeActionContext context = params.getContext();
return CompletableFuture.supplyAsync(() ->
this.codeActionProvider.provideCodeActions(magikFile, range, context));
return CompletableFuture.supplyAsync(() -> {
final List<nl.ramsolutions.sw.magik.CodeAction> codeActions =
this.codeActionProvider.provideCodeActions(magikFile, magikRange, context);
return codeActions.stream()
.map(codeAction -> Lsp4jUtils.createCodeAction(
magikFile,
codeAction.getTitle(),
codeAction.getEdits()))
.map(codeAction -> Either.<Command, CodeAction>forRight(codeAction))
.collect(Collectors.toList());
});
}

}
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
package nl.ramsolutions.sw.magik.languageserver.codeactions;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import nl.ramsolutions.sw.magik.CodeAction;
import nl.ramsolutions.sw.magik.MagikTypedFile;
import org.eclipse.lsp4j.CodeAction;
import nl.ramsolutions.sw.magik.Range;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Code action provider.
*/
public class CodeActionProvider {

private final MethodReturnTypeUpdateProvider methodReturnTypeUpdateProvider = new MethodReturnTypeUpdateProvider();
private final ParameterTypeProvider parameterTypeProvider = new ParameterTypeProvider();
static final Logger LOGGER = LoggerFactory.getLogger(CodeActionProvider.class);

private final MagikChecksCodeActionProvider checksCodeActionProvider = new MagikChecksCodeActionProvider();
private final MagikTypedChecksCodeActionProvider typedChecksCodeActionProvider =
new MagikTypedChecksCodeActionProvider();

/**
* Set server capabilities.
Expand All @@ -34,15 +38,22 @@ public void setCapabilities(final ServerCapabilities capabilities) {
* @param context Code action context.
* @return List of code actions.
*/
public List<Either<Command, CodeAction>> provideCodeActions(
public List<CodeAction> provideCodeActions(
final MagikTypedFile magikFile,
final Range range,
final CodeActionContext context) {
return Stream.concat(
this.methodReturnTypeUpdateProvider.provideCodeActions(magikFile, range, context).stream(),
this.parameterTypeProvider.provideCodeActions(magikFile, range, context).stream()
)
.collect(Collectors.toList());
try {
return Stream.concat(
this.checksCodeActionProvider.provideCodeActions(magikFile, range, context).stream(),
this.typedChecksCodeActionProvider.provideCodeActions(magikFile, range, context).stream()
)
.collect(Collectors.toList());
} catch (final ReflectiveOperationException exception) {
LOGGER.error(exception.getMessage(), exception);
}

// Safety.
return Collections.emptyList();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package nl.ramsolutions.sw.magik.languageserver.codeactions;

import java.util.ArrayList;
import java.util.List;
import nl.ramsolutions.sw.magik.CodeAction;
import nl.ramsolutions.sw.magik.MagikTypedFile;
import nl.ramsolutions.sw.magik.Range;
import nl.ramsolutions.sw.magik.checks.CheckList;
import nl.ramsolutions.sw.magik.checks.MagikCheckFixer;
import org.eclipse.lsp4j.CodeActionContext;

/**
* Provide {@link CodeAction}s for {@link MagikCheck}s.
*/
public class MagikChecksCodeActionProvider {

/**
* Provide {@link CodeAction} for {@link MagikCheck} checks.
* @param magikFile {@link MagikTypedFile{} to check on.
* @param range {@link Range} to get {@link CodeAction}s for.
* @param context Context, not used currently.
* @return List of {@link CodeAction}s.
* @throws ReflectiveOperationException
*/
public List<CodeAction> provideCodeActions(
final MagikTypedFile magikFile,
final Range range,
final CodeActionContext context) throws ReflectiveOperationException {
// TODO: Only for enabled checks.

final List<CodeAction> codeActions = new ArrayList<>();
for (final List<Class<?>> fixerClassses : CheckList.getFixers().values()) {
for (final Class<?> fixerClass : fixerClassses) {
final MagikCheckFixer fixer =
(MagikCheckFixer) fixerClass.getDeclaredConstructor().newInstance();
List<CodeAction> fixerCodeActions = fixer.provideCodeActions(magikFile, range);
codeActions.addAll(fixerCodeActions);
}
}
return codeActions;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package nl.ramsolutions.sw.magik.languageserver.codeactions;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import nl.ramsolutions.sw.magik.CodeAction;
import nl.ramsolutions.sw.magik.MagikTypedFile;
import nl.ramsolutions.sw.magik.Range;
import nl.ramsolutions.sw.magik.typedchecks.CheckList;
import nl.ramsolutions.sw.magik.typedchecks.MagikTypedCheck;
import nl.ramsolutions.sw.magik.typedchecks.MagikTypedCheckFixer;
import org.eclipse.lsp4j.CodeActionContext;

/**
* Provide {@link CodeAction}s for {@link MagikTypedCheck}s.
*/
public class MagikTypedChecksCodeActionProvider {

/**
* Provide {@link CodeAction} for {@link MagikTypedCheck} checks.
* @param magikFile {@link MagikTypedFile{} to check on.
* @param range {@link Range} to get {@link CodeAction}s for.
* @param context Context, not used currently.
* @return List of {@link CodeAction}s.
* @throws ReflectiveOperationException
*/
public List<CodeAction> provideCodeActions(
final MagikTypedFile magikFile,
final Range range,
final CodeActionContext context) throws ReflectiveOperationException {
// TODO: Only for enabled checks.

final List<CodeAction> codeActions = new ArrayList<>();
for (final Entry<Class<?>, List<Class<?>>> entry : CheckList.getFixers().entrySet()) {
final Class<?> checkClass = entry.getKey();
final List<Class<?>> fixerClassses = entry.getValue();
for (final Class<?> fixerClass : fixerClassses) {


final MagikTypedCheckFixer fixer =
(MagikTypedCheckFixer) fixerClass.getDeclaredConstructor().newInstance();
List<CodeAction> fixerCodeActions = fixer.provideCodeActions(magikFile, range);
codeActions.addAll(fixerCodeActions);
}
}
return codeActions;
}

private boolean isCheckEnabled(final Class<?> checkClass) {
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import nl.ramsolutions.sw.magik.analysis.typing.types.UndefinedType;
import nl.ramsolutions.sw.magik.api.MagikGrammar;
import nl.ramsolutions.sw.magik.languageserver.Lsp4jConversion;
import nl.ramsolutions.sw.magik.languageserver.Lsp4jUtils;
import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
Expand All @@ -43,17 +42,15 @@ public void setCapabilities(final ServerCapabilities capabilities) {
/**
* Provide inlay hints for the given file.
* @param magikFile Magik file.
* @param range Range in file.
* @param lsp4jrange Range in file.
* @return List of inlay hints.
*/
public List<InlayHint> provideInlayHints(final MagikTypedFile magikFile, final org.eclipse.lsp4j.Range range) {
public List<InlayHint> provideInlayHints(final MagikTypedFile magikFile, final org.eclipse.lsp4j.Range lsp4jrange) {
// Get argument hints from method invocations.
final AstNode topNode = magikFile.getTopNode();
final Range range = Lsp4jConversion.rangeFromLsp4j(lsp4jrange);
return topNode.getDescendants(MagikGrammar.METHOD_INVOCATION).stream()
.filter(node -> Lsp4jUtils.rangeOverlaps(
range,
Lsp4jConversion.rangeToLsp4j(
Range.fromTree(node))))
.filter(node -> Range.fromTree(node).overlapsWith(range))
.map(node -> this.getMethodInvocationInlayHints(magikFile, node))
.flatMap(List::stream)
.collect(Collectors.toList());
Expand Down
Loading

0 comments on commit ec49533

Please sign in to comment.