Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for import organization #883

Merged
merged 19 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from 9 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 @@ -35,6 +35,7 @@ import com.regnosys.rosetta.ide.hover.RosettaHoverService
import org.eclipse.xtext.ide.editor.contentassist.IdeContentProposalProvider
import com.regnosys.rosetta.ide.contentassist.RosettaContentProposalProvider


maria77102 marked this conversation as resolved.
Show resolved Hide resolved
/**
* Use this class to register ide components.
*/
Expand Down
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,25 @@
package com.regnosys.rosetta.ide.quickfix;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import javax.inject.Inject;

import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.xtext.ide.editor.quickfix.DiagnosticResolution;
import org.eclipse.xtext.ide.editor.quickfix.IQuickFixProvider;
import org.eclipse.xtext.ide.server.codeActions.ICodeActionService2;

import com.regnosys.rosetta.ide.util.CodeActionUtils;
import com.regnosys.rosetta.rosetta.RosettaModel;
import com.regnosys.rosetta.utils.ImportManagementService;

/*
* TODO: contribute to Xtext.
* This is a patch of org.eclipse.xtext.ide.server.codeActions.QuickFixCodeActionService.
Expand All @@ -43,56 +44,42 @@ public class RosettaQuickFixCodeActionService implements ICodeActionService2 {

@Inject
private IQuickFixProvider quickfixes;
@Inject
private ImportManagementService importManagementService;
@Inject
private CodeActionUtils codeActionUtils;

@Override
public List<Either<Command, CodeAction>> getCodeActions(Options options) {
boolean handleQuickfixes = options.getCodeActionParams().getContext().getOnly() == null
|| options.getCodeActionParams().getContext().getOnly().isEmpty()
|| options.getCodeActionParams().getContext().getOnly().contains(CodeActionKind.QuickFix);

if (!handleQuickfixes) {
return Collections.emptyList();
}

List<Either<Command, CodeAction>> result = new ArrayList<>();
for (Diagnostic diagnostic : options.getCodeActionParams().getContext().getDiagnostics()) {
Options diagnosticOptions = createOptionsForSingleDiagnostic(options, diagnostic);
List<DiagnosticResolution> resolutions = quickfixes.getResolutions(diagnosticOptions, diagnostic).stream()
.sorted(Comparator.nullsLast(Comparator.comparing(DiagnosticResolution::getLabel)))
.collect(Collectors.toList());
for (DiagnosticResolution resolution : resolutions) {
result.add(Either.forRight(createFix(resolution, diagnostic)));
if (handleQuickfixes) {
List<Diagnostic> diagnostics = options.getCodeActionParams().getContext().getDiagnostics();

// Handle Sorting Imports
RosettaModel model = (RosettaModel) options.getResource().getContents().get(0);
if (!importManagementService.isSorted(model.getImports())) {
Diagnostic sortingDiagnostic = codeActionUtils.createSortImportsDiagnostic(
(RosettaModel) options.getResource().getContents().get(0));
diagnostics.add(sortingDiagnostic);
}
}
return result;
}

private CodeAction createFix(DiagnosticResolution resolution, Diagnostic diagnostic) {
CodeAction codeAction = new CodeAction();
codeAction.setDiagnostics(Collections.singletonList(diagnostic));
codeAction.setTitle(resolution.getLabel());
codeAction.setEdit(resolution.apply());
codeAction.setKind(CodeActionKind.QuickFix);
for (Diagnostic diagnostic : diagnostics) {
Options diagnosticOptions = codeActionUtils.createOptionsForSingleDiagnostic(options, diagnostic);
List<DiagnosticResolution> resolutions = quickfixes.getResolutions(diagnosticOptions, diagnostic)
.stream().sorted(Comparator.nullsLast(Comparator.comparing(DiagnosticResolution::getLabel)))
.collect(Collectors.toList());
for (DiagnosticResolution resolution : resolutions) {
result.add(Either
.forRight(codeActionUtils.createUnresolvedFix(resolution, options.getCodeActionParams(), diagnostic)));
}
}
}

return codeAction;
}

private Options createOptionsForSingleDiagnostic(Options base, Diagnostic diagnostic) {
Options options = new Options();
options.setCancelIndicator(base.getCancelIndicator());
options.setDocument(base.getDocument());
options.setLanguageServerAccess(base.getLanguageServerAccess());
options.setResource(base.getResource());

CodeActionParams baseParams = base.getCodeActionParams();
CodeActionContext baseContext = baseParams.getContext();
CodeActionContext context = new CodeActionContext(List.of(diagnostic), baseContext.getOnly());
context.setTriggerKind(baseContext.getTriggerKind());
CodeActionParams params = new CodeActionParams(baseParams.getTextDocument(), diagnostic.getRange(), context);

options.setCodeActionParams(params);

return options;
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

package com.regnosys.rosetta.ide.quickfix;

import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.ROSETTA_OPERATION__OPERATOR;

import java.util.List;

import javax.inject.Inject;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;
Expand All @@ -29,15 +32,22 @@
import org.eclipse.xtext.ide.editor.quickfix.QuickFix;
import org.eclipse.xtext.ide.server.Document;

import com.regnosys.rosetta.ide.util.CodeActionUtils;
import com.regnosys.rosetta.ide.util.RangeUtils;
import com.regnosys.rosetta.validation.RosettaIssueCodes;
import com.regnosys.rosetta.rosetta.Import;
import com.regnosys.rosetta.rosetta.RosettaModel;
import com.regnosys.rosetta.rosetta.expression.RosettaUnaryOperation;
import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.*;
import com.regnosys.rosetta.utils.ImportManagementService;
import com.regnosys.rosetta.validation.RosettaIssueCodes;

public class RosettaQuickFixProvider extends AbstractDeclarativeIdeQuickfixProvider {
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
@Inject
private RangeUtils rangeUtils;

@Inject
private ImportManagementService importManagementService;
@Inject
private CodeActionUtils codeActionUtils;

@QuickFix(RosettaIssueCodes.REDUNDANT_SQUARE_BRACKETS)
public void fixRedundantSquareBrackets(DiagnosticResolutionAcceptor acceptor) {
acceptor.accept("Remove square brackets.", (Diagnostic diagnostic, EObject object, Document document) -> {
Expand All @@ -47,7 +57,7 @@ public void fixRedundantSquareBrackets(DiagnosticResolutionAcceptor acceptor) {
return createTextEdit(diagnostic, edited);
});
}

@QuickFix(RosettaIssueCodes.MANDATORY_SQUARE_BRACKETS)
public void fixMandatorySquareBrackets(DiagnosticResolutionAcceptor acceptor) {
acceptor.accept("Add square brackets.", (Diagnostic diagnostic, EObject object, Document document) -> {
Expand All @@ -57,16 +67,65 @@ public void fixMandatorySquareBrackets(DiagnosticResolutionAcceptor acceptor) {
return createTextEdit(diagnostic, edited);
});
}

@QuickFix(RosettaIssueCodes.MANDATORY_THEN)
public void fixMandatoryThen(DiagnosticResolutionAcceptor acceptor) {
acceptor.accept("Add `then`.", (Diagnostic diagnostic, EObject object, Document document) -> {
RosettaUnaryOperation op = (RosettaUnaryOperation)object;
RosettaUnaryOperation op = (RosettaUnaryOperation) object;
Range range = rangeUtils.getRange(op, ROSETTA_OPERATION__OPERATOR);
String original = document.getSubstring(range);
String edited = "then " + original;
TextEdit edit = new TextEdit(range, edited);
return List.of(edit);
});
}

@QuickFix(RosettaIssueCodes.UNUSED_IMPORT)
@QuickFix(RosettaIssueCodes.DUPLICATE_IMPORT)
public void fixUnoptimizedImports(DiagnosticResolutionAcceptor acceptor) {
acceptor.accept("Optimize imports.", (Diagnostic diagnostic, EObject object, Document document) -> {
SimonCockx marked this conversation as resolved.
Show resolved Hide resolved
Import importObj = (Import) object;
EObject container = importObj.eContainer();

if (container instanceof RosettaModel) {
RosettaModel model = (RosettaModel) container;
List<Import> imports = model.getImports();

Range importsRange = codeActionUtils.getImportsRange(imports);

importManagementService.cleanupImports(model);
String sortedImportsText = importManagementService.toString(imports);

return List.of(new TextEdit(importsRange, sortedImportsText));
}

// if not model, return empty list of edits
return List.of();

});
}

@QuickFix(RosettaIssueCodes.UNSORTED_IMPORTS)
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
public void sortImports(DiagnosticResolutionAcceptor acceptor) {
acceptor.accept("Sort imports.", (Diagnostic diagnostic, EObject object, Document document) -> {
Import importObj = (Import) object;
EObject container = importObj.eContainer();

if (container instanceof RosettaModel) {
RosettaModel model = (RosettaModel) container;
EList<Import> imports = model.getImports();

Range importsRange = codeActionUtils.getImportsRange(imports);

importManagementService.sortImports(imports);
String sortedImportsText = importManagementService.toString(imports);

return List.of(new TextEdit(importsRange, sortedImportsText));
}

// if not model, return empty list of edits
return List.of();
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,56 @@

package com.regnosys.rosetta.ide.server;

import org.eclipse.xtext.ide.server.LanguageServerImpl;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import javax.inject.Inject;

import org.eclipse.emf.common.util.URI;
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionOptions;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.FormattingOptions;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.InlayHintParams;
import org.eclipse.lsp4j.InlayHintRegistrationOptions;
import org.eclipse.lsp4j.SemanticTokens;
import org.eclipse.lsp4j.SemanticTokensDelta;
import org.eclipse.lsp4j.SemanticTokensDeltaParams;
import org.eclipse.lsp4j.SemanticTokensParams;
import org.eclipse.lsp4j.SemanticTokensRangeParams;
import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.xtext.ide.editor.quickfix.DiagnosticResolution;
import org.eclipse.xtext.ide.editor.quickfix.IQuickFixProvider;
import org.eclipse.xtext.ide.server.LanguageServerImpl;
import org.eclipse.xtext.ide.server.codeActions.ICodeActionService2;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.util.CancelIndicator;

import com.google.common.collect.Iterables;
import com.regnosys.rosetta.formatting2.FormattingOptionsAdaptor;
import com.regnosys.rosetta.ide.inlayhints.IInlayHintsResolver;
import com.regnosys.rosetta.ide.inlayhints.IInlayHintsService;
import com.regnosys.rosetta.ide.semantictokens.ISemanticTokensService;
import com.regnosys.rosetta.ide.semantictokens.SemanticToken;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;

import javax.inject.Inject;
import com.regnosys.rosetta.ide.util.CodeActionUtils;

/**
* TODO: contribute to Xtext.
*
*/
public class RosettaLanguageServerImpl extends LanguageServerImpl implements RosettaLanguageServer{
@Inject FormattingOptionsAdaptor formattingOptionsAdapter;
@Inject CodeActionUtils codeActionUtils;

@Override
protected ServerCapabilities createServerCapabilities(InitializeParams params) {
Expand All @@ -54,6 +78,14 @@ protected ServerCapabilities createServerCapabilities(InitializeParams params) {
serverCapabilities.setInlayHintProvider(inlayHintRegistrationOptions);
}

if (resourceServiceProvider.get(ICodeActionService2.class) != null) {
CodeActionOptions codeActionProvider = new CodeActionOptions();
codeActionProvider.setResolveProvider(true);
codeActionProvider.setCodeActionKinds(List.of(CodeActionKind.QuickFix));
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
codeActionProvider.setWorkDoneProgress(true);
serverCapabilities.setCodeActionProvider(codeActionProvider);
}

ISemanticTokensService semanticTokensService = resourceServiceProvider.get(ISemanticTokensService.class);
if (semanticTokensService != null) {
SemanticTokensWithRegistrationOptions semanticTokensOptions = new SemanticTokensWithRegistrationOptions();
Expand Down Expand Up @@ -185,4 +217,44 @@ public CompletableFuture<FormattingOptions> getDefaultFormattingOptions() {
return CompletableFuture.failedFuture(e);
}
}

@Override
public CompletableFuture<CodeAction> resolveCodeAction(CodeAction unresolved) {
return getRequestManager().runRead((cancelIndicator) -> resolveCodeAction(unresolved, cancelIndicator));
}

protected CodeAction resolveCodeAction(CodeAction codeAction, CancelIndicator cancelIndicator) {
CodeActionParams codeActionParams = codeActionUtils.getCodeActionParams(codeAction);

if (codeActionParams.getTextDocument() == null) {
return null;
}

URI uri = getURI(codeActionParams.getTextDocument());

IQuickFixProvider quickfixes = getService(uri, IQuickFixProvider.class);

return getWorkspaceManager().doRead(uri, (doc, resource) -> {
ICodeActionService2.Options baseOptions = new ICodeActionService2.Options();
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
baseOptions.setDocument(doc);
baseOptions.setResource(resource);
baseOptions.setLanguageServerAccess(getLanguageServerAccess());
baseOptions.setCodeActionParams(codeActionParams);
baseOptions.setCancelIndicator(cancelIndicator);

Diagnostic diagnostic = Iterables.getOnlyElement(codeAction.getDiagnostics());

ICodeActionService2.Options options = codeActionUtils.createOptionsForSingleDiagnostic(baseOptions,
diagnostic);

List<DiagnosticResolution> resolutions = quickfixes.getResolutions(options, diagnostic).stream()
.sorted(Comparator.nullsLast(Comparator.comparing(DiagnosticResolution::getLabel)))
.filter(r -> r.getLabel().equals(codeAction.getTitle())).collect(Collectors.toList());

// since a CodeAction has only one diagnostic, only one resolution should be found
codeAction.setEdit(resolutions.get(0).apply());

return codeAction;
});
}
}
Loading
Loading