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

code actions for quickfix for invalid property #339

Merged
Merged
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
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2024 IBM Corporation and others.
* Copyright (c) 2020, 2025 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -13,9 +13,12 @@
package io.openliberty.tools.langserver;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;

import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionOptions;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
Expand Down Expand Up @@ -69,6 +72,7 @@ private ServerCapabilities createServerCapabilities() {
capabilities.setTextDocumentSync(TextDocumentSyncKind.Full);
capabilities.setHoverProvider(Boolean.TRUE);
capabilities.setCompletionProvider(new CompletionOptions(Boolean.TRUE, Arrays.asList("=")));
capabilities.setCodeActionProvider(new CodeActionOptions(List.of(CodeActionKind.QuickFix)));
return capabilities;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2023 IBM Corporation and others.
* Copyright (c) 2020, 2025 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -12,6 +12,10 @@
*******************************************************************************/
package io.openliberty.tools.langserver;

import io.openliberty.tools.langserver.codeactions.CodeActionParticipant;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
Expand Down Expand Up @@ -139,6 +143,12 @@ public CompletableFuture<CompletionItem> resolveCompletionItem(CompletionItem un
return CompletableFuture.completedFuture(unresolved);
}

@Override
public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(CodeActionParams params) {
LOGGER.info("codeAction: "+ params.getTextDocument());
return new CodeActionParticipant(this,libertyLanguageServer).getCodeActions(params);
}

private void validate(List<String> uris) {
if (uris.isEmpty()) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (c) 2025 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package io.openliberty.tools.langserver.codeactions;

import io.openliberty.tools.langserver.LibertyLanguageServer;
import io.openliberty.tools.langserver.LibertyTextDocumentService;
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.jsonrpc.messages.Either;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public class CodeActionParticipant {

private LibertyTextDocumentService libertyTextDocumentService;
private LibertyLanguageServer libertyLanguageServer;

public CodeActionParticipant(LibertyTextDocumentService libertyTextDocumentService, LibertyLanguageServer libertyLanguageServer) {
this.libertyTextDocumentService = libertyTextDocumentService;
this.libertyLanguageServer = libertyLanguageServer;
}

/**
*
* @param params
* @return
*/
public CompletableFuture<List<Either<Command, CodeAction>>> getCodeActions(CodeActionParams params) {
CodeActionContext context = params.getContext();
if (context != null) {
List<Either<Command, CodeAction>> codeActions = new ArrayList<>();
List<String> codeActionsType = context.getOnly();
// code action type is coming null from vscode
if (codeActionsType == null || codeActionsType.contains(CodeActionKind.QuickFix)) {
codeActions.addAll(computeQuickfixes(params));
}
return CompletableFuture.supplyAsync(() -> codeActions);
} else {
return CompletableFuture.completedFuture(Collections.emptyList());
}
}

private List<Either<Command, CodeAction>> computeQuickfixes(CodeActionParams params) {
// compute InvalidPropertyValueQuickfix
// need to change this logic when we have more types of quick fixes
return new ArrayList<>(new InvalidPropertyValueQuickfix(libertyTextDocumentService, libertyLanguageServer)
.apply(params));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*******************************************************************************
* Copyright (c) 2025 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package io.openliberty.tools.langserver.codeactions;

import io.openliberty.tools.langserver.LibertyLanguageServer;
import io.openliberty.tools.langserver.LibertyTextDocumentService;
import io.openliberty.tools.langserver.ls.LibertyTextDocument;
import io.openliberty.tools.langserver.utils.ParserFileHelperUtil;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentItem;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class CodeActionQuickfixFactory {

protected LibertyTextDocumentService libertyTextDocumentService;
protected LibertyLanguageServer libertyLanguageServer;

protected CodeActionQuickfixFactory(LibertyTextDocumentService libertyTextDocumentService, LibertyLanguageServer libertyLanguageServer) {
this.libertyTextDocumentService = libertyTextDocumentService;
this.libertyLanguageServer = libertyLanguageServer;
}

/**
* returns list of code actions or commands.
* called from CodeActionParticipant
* @param params code action params
* @return codeaction
*/
public List<Either<Command, CodeAction>> apply(CodeActionParams params) {
TextDocumentItem openedDocument = libertyTextDocumentService.getOpenedDocument(params.getTextDocument().getUri());
List<Diagnostic> diagnostics = params.getContext().getDiagnostics();
List<Either<Command, CodeAction>> res = new ArrayList<>();
for (Diagnostic diagnostic : diagnostics) {
if (diagnostic.getCode() != null && getErrorCode().equals(diagnostic.getCode().getLeft())) {
String line = new ParserFileHelperUtil().getLine(new LibertyTextDocument(openedDocument), diagnostic.getRange().getStart().getLine());
if (line != null) {
// fetch all completion values and shows them as quick fix
// completion returns empty list if no completion item is present
List<String> possibleProperties = retrieveCompletionValues(openedDocument, diagnostic.getRange().getStart());
for (String mostProbableProperty : possibleProperties) {
// expected format for a code action is <Command,CodeAction>
res.add(Either.forRight(createCodeAction(params, diagnostic, mostProbableProperty)));
}
}
}
}
return res;
}

/**
* used to create code action object for quickfix
* @param params codeaction params
* @param diagnostic diagnostic for which code action to be shown
* @param possibleProperty completion value
* @return codeaction
*/
protected CodeAction createCodeAction(CodeActionParams params, Diagnostic diagnostic, String possibleProperty) {
CodeAction codeAction = new CodeAction("Replace value with " + possibleProperty);
codeAction.setDiagnostics(Collections.singletonList(diagnostic));
codeAction.setKind(CodeActionKind.QuickFix);
Map<String, List<TextEdit>> changes = new HashMap<>();
TextEdit textEdit = new TextEdit(diagnostic.getRange(), possibleProperty);
changes.put(params.getTextDocument().getUri(), List.of(textEdit));
codeAction.setEdit(new WorkspaceEdit(changes));
return codeAction;
}

protected abstract List<String> retrieveCompletionValues(TextDocumentItem textDocumentItem, Position position);

protected abstract String getErrorCode();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright (c) 2025 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package io.openliberty.tools.langserver.codeactions;

import io.openliberty.tools.langserver.LibertyLanguageServer;
import io.openliberty.tools.langserver.LibertyTextDocumentService;
import io.openliberty.tools.langserver.ls.LibertyTextDocument;
import io.openliberty.tools.langserver.model.propertiesfile.PropertiesEntryInstance;
import io.openliberty.tools.langserver.utils.ParserFileHelperUtil;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentItem;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static io.openliberty.tools.langserver.diagnostic.LibertyPropertiesDiagnosticService.ERROR_CODE_INVALID_PROPERTY_VALUE;

public class InvalidPropertyValueQuickfix extends CodeActionQuickfixFactory {

private static final Logger LOGGER = Logger.getLogger(InvalidPropertyValueQuickfix.class.getName());

public InvalidPropertyValueQuickfix(LibertyTextDocumentService libertyTextDocumentService, LibertyLanguageServer libertyLanguageServer) {
super(libertyTextDocumentService, libertyLanguageServer);
}

protected String getErrorCode() {
return ERROR_CODE_INVALID_PROPERTY_VALUE;
}

/**
* retrieve list of completion items for a property key
* @param textDocumentItem text document
* @param position current position, used to compute key
* @return list of string of completion item names
*/
@Override
protected List<String> retrieveCompletionValues(TextDocumentItem textDocumentItem,
Position position) {
List<String> completionValues = new ArrayList<>();
try {
LibertyTextDocument openedDocument = libertyLanguageServer.getTextDocumentService().getOpenedDocument(textDocumentItem.getUri());
String line = new ParserFileHelperUtil().getLine(openedDocument, position);
PropertiesEntryInstance propertiesEntryInstance = new PropertiesEntryInstance(line, openedDocument);
// get all completions for current property key
CompletableFuture<List<CompletionItem>> completions = propertiesEntryInstance.getPropertyValueInstance().getCompletions("", position);
// map text values from completion items
completionValues = completions.thenApply(completionItems -> completionItems.stream()
.map(it -> it.getTextEdit().getLeft().getNewText())
.collect(Collectors.toList()))
.get();
} catch (InterruptedException e) {
LOGGER.severe("Interruption while computing possible properties for quickfix. Error message is " + e.getMessage());
Thread.currentThread().interrupt();
} catch (Exception e) {
LOGGER.severe("Exception while computing possible properties for quickfix. Error message is " + e.getMessage());
}
return completionValues;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022, 2024 IBM Corporation and others.
* Copyright (c) 2022, 2025 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -39,7 +39,7 @@ public class LibertyPropertiesDiagnosticService {

private static final Logger LOGGER = Logger.getLogger(LibertyPropertiesDiagnosticService.class.getName());
private static final ResourceBundle DiagnosticMessages = ResourceBundle.getBundle("DiagnosticMessages", Locale.getDefault());

public static final String ERROR_CODE_INVALID_PROPERTY_VALUE = "unknown_property_value";

public Map<String, PropertiesValidationResult> compute(String text, LibertyTextDocument openedDocument) {
Map<String, PropertiesValidationResult> errors = new HashMap<>();
Expand Down Expand Up @@ -86,7 +86,9 @@ private List<Diagnostic> computeInvalidValuesDiagnostic(PropertiesValidationResu

// Currently the last arg (getIntegerRange) is only used for the Integer messages which use {2}. Otherwise null is passed and is ignored by the other messages.
String message = MessageFormat.format(messageTemplate, validationResult.getValue(), property, ServerPropertyValues.getIntegerRange(property));
lspDiagnostics.add(new Diagnostic(computeRange(validationResult, lineContentInError), message, DiagnosticSeverity.Error, "Liberty Config Language Server"));
Diagnostic diagnostic = new Diagnostic(computeRange(validationResult, lineContentInError), message, DiagnosticSeverity.Error, "Liberty Config Language Server");
diagnostic.setCode(ERROR_CODE_INVALID_PROPERTY_VALUE);
lspDiagnostics.add(diagnostic);
}
return lspDiagnostics;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022, 2024 IBM Corporation and others.
* Copyright (c) 2022, 2025 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -108,4 +108,8 @@ public CompletableFuture<List<CompletionItem>> getCompletions(Position position)
}
return CompletableFuture.completedFuture(Collections.emptyList());
}

public PropertiesValueInstance getPropertyValueInstance() {
return propertyValueInstance;
}
}
Loading