-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve CodeAction to insert expected elements to support choice
Signed-off-by: Jessica He <jhe@redhat.com>
- Loading branch information
1 parent
1d51ef6
commit 5759686
Showing
17 changed files
with
1,001 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
...nx/src/main/java/com/thaiopensource/relaxng/pattern/PossibleRequiredElementsFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Red Hat Inc. and others. | ||
* All rights reserved. This program and the accompanying materials | ||
* which accompanies this distribution, and is available at | ||
* http://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat Inc. - initial API and implementation | ||
*******************************************************************************/ | ||
package com.thaiopensource.relaxng.pattern; | ||
|
||
import java.util.Set; | ||
|
||
public class PossibleRequiredElementsFunction extends AbstractPatternFunction<Void> { | ||
|
||
private final Set<String> requiredElementNames; | ||
|
||
public PossibleRequiredElementsFunction(Set<String> requiredElementNames) { | ||
this.requiredElementNames = requiredElementNames; | ||
} | ||
|
||
@Override | ||
public Void caseOther(Pattern p) { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Void caseElement(ElementPattern p) { | ||
return caseNamed(p.getNameClass()); | ||
} | ||
|
||
@Override | ||
public Void caseGroup(GroupPattern p) { | ||
return union(p); | ||
} | ||
|
||
@Override | ||
public Void caseChoice(ChoicePattern p) { | ||
return union(p); | ||
} | ||
|
||
@Override | ||
public Void caseInterleave(InterleavePattern p) { | ||
return union(p); | ||
} | ||
|
||
@Override | ||
public Void caseAfter(AfterPattern p) { | ||
return p.getOperand1().apply(this); | ||
} | ||
|
||
@Override | ||
public Void caseOneOrMore(OneOrMorePattern p) { | ||
return p.getOperand().apply(this); | ||
} | ||
|
||
private Void caseNamed(NameClass nc) { | ||
if (!(nc instanceof SimpleNameClass)) | ||
return null; | ||
requiredElementNames.add(((SimpleNameClass) nc).getName().getLocalName()); | ||
return null; | ||
} | ||
|
||
private Void union(BinaryPattern p) { | ||
Pattern p1 = p.getOperand1(); | ||
if (!p1.isNullable()) { | ||
p1.apply(this); | ||
} | ||
Pattern p2 = p.getOperand2(); | ||
if (!p2.isNullable()) { | ||
p2.apply(this); | ||
} | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
...ons/contentmodel/participants/codeactions/missingelement/MissingElementDataConstants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Red Hat Inc. and others. | ||
* All rights reserved. This program and the accompanying materials | ||
* which accompanies this distribution, and is available at | ||
* http://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat Inc. - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.missingelement; | ||
|
||
/** | ||
* Data entry field used in the codeAction/resolve for inserting missing | ||
* elements. | ||
* | ||
*/ | ||
public class MissingElementDataConstants { | ||
|
||
/** | ||
* The element name used to identify which choice element is to be generated. | ||
*/ | ||
public static final String DATA_ELEMENT_FIELD = "element"; | ||
|
||
/** | ||
* Indicates whether only required elements is to be generated or all. | ||
*/ | ||
public static final String DATA_REQUIRED_FIELD = "onlyGenerateRequired"; | ||
|
||
} |
145 changes: 145 additions & 0 deletions
145
...tentmodel/participants/codeactions/missingelement/required_element_missingCodeAction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/** | ||
* Copyright (c) 2023 Red Hat Inc. and others. | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v2.0 | ||
* which accompanies this distribution, and is available at | ||
* http://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat Inc. - initial API and implementation | ||
*/ | ||
package org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.missingelement; | ||
|
||
import static org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.missingelement.MissingElementDataConstants.DATA_REQUIRED_FIELD; | ||
|
||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.eclipse.lemminx.commons.BadLocationException; | ||
import org.eclipse.lemminx.commons.CodeActionFactory; | ||
import org.eclipse.lemminx.dom.DOMDocument; | ||
import org.eclipse.lemminx.dom.DOMElement; | ||
import org.eclipse.lemminx.dom.DOMNode; | ||
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager; | ||
import org.eclipse.lemminx.extensions.contentmodel.utils.XMLGenerator; | ||
import org.eclipse.lemminx.services.data.DataEntryField; | ||
import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionParticipant; | ||
import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionRequest; | ||
import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionResolvesParticipant; | ||
import org.eclipse.lsp4j.CodeAction; | ||
import org.eclipse.lsp4j.CodeActionKind; | ||
import org.eclipse.lsp4j.Diagnostic; | ||
import org.eclipse.lsp4j.Position; | ||
import org.eclipse.lsp4j.Range; | ||
import org.eclipse.lsp4j.jsonrpc.CancelChecker; | ||
|
||
import com.google.gson.JsonObject; | ||
|
||
/** | ||
* CodeAction to insert expected child elements within an element | ||
* | ||
* Given this XML where the expected child elements are not present as defined | ||
* in the relaxNG schema: | ||
* | ||
* <servlet></servlet> Error: Child elements are missing from element: - servlet | ||
* | ||
* | ||
* To fix the error, the code action will suggest inserting the expected | ||
* elements inside the parent tag | ||
* | ||
*/ | ||
|
||
public class required_element_missingCodeAction implements ICodeActionParticipant { | ||
|
||
private final Map<String, ICodeActionResolvesParticipant> resolveCodeActionParticipants; | ||
|
||
public required_element_missingCodeAction() { | ||
// Register available resolvers. | ||
resolveCodeActionParticipants = new HashMap<>(); | ||
resolveCodeActionParticipants.put(required_element_missingCodeActionResolver.PARTICIPANT_ID, | ||
new required_element_missingCodeActionResolver()); | ||
} | ||
|
||
@Override | ||
public void doCodeAction(ICodeActionRequest request, List<CodeAction> codeActions, CancelChecker cancelChecker) { | ||
DOMDocument document = request.getDocument(); | ||
Diagnostic diagnostic = request.getDiagnostic(); | ||
try { | ||
Range diagnosticRange = diagnostic.getRange(); | ||
int startOffset = document.offsetAt(diagnosticRange.getStart()) + 1; | ||
DOMNode node = document.findNodeAt(startOffset); | ||
|
||
if (node == null || !node.isElement()) { | ||
return; | ||
} | ||
|
||
DOMElement element = (DOMElement) node; | ||
XMLGenerator generator = request.getXMLGenerator(); | ||
|
||
String insertStrRequired = null; | ||
String insertStrAll = null; | ||
|
||
if (!request.canSupportResolve()) { | ||
insertStrRequired = generator.generateMissingElements(request.getComponent(ContentModelManager.class), | ||
element, true); | ||
insertStrAll = generator.generateMissingElements(request.getComponent(ContentModelManager.class), | ||
element, false); | ||
} | ||
CodeAction insertRequriedExpectedElementCodeAction = createInsertExpectedElementsCodeAction(true, | ||
element, insertStrRequired, request, cancelChecker); | ||
|
||
CodeAction insertAllExpectedElementCodeAction = createInsertExpectedElementsCodeAction(false, | ||
element, insertStrAll, request, cancelChecker); | ||
|
||
codeActions.add(insertRequriedExpectedElementCodeAction); | ||
codeActions.add(insertAllExpectedElementCodeAction); | ||
|
||
} catch (BadLocationException e) { | ||
// do nothing | ||
} | ||
} | ||
|
||
private static CodeAction createInsertExpectedElementsCodeAction(boolean isGenerateRequired, DOMElement domElement, | ||
String insertStr, ICodeActionRequest request, CancelChecker cancelChecker) | ||
throws BadLocationException { | ||
Diagnostic diagnostic = request.getDiagnostic(); | ||
DOMDocument document = request.getDocument(); | ||
String title = isGenerateRequired ? "Insert only required elements" : "Insert all expected elements"; | ||
if (request.canSupportResolve()) { | ||
JsonObject data = DataEntryField.createData(document.getDocumentURI(), | ||
required_element_missingCodeActionResolver.PARTICIPANT_ID); | ||
return insertExpectedElementsUnresolvedCodeAction(title, diagnostic, data, isGenerateRequired); | ||
} else { | ||
return insertExpectedElementCodeAction(document, title, domElement, insertStr, diagnostic); | ||
} | ||
} | ||
|
||
private static CodeAction insertExpectedElementsUnresolvedCodeAction(String title, | ||
Diagnostic diagnostic, JsonObject data, boolean isGenerateRequired) { | ||
CodeAction codeAction = new CodeAction(title); | ||
codeAction.setDiagnostics(Collections.singletonList(diagnostic)); | ||
codeAction.setKind(CodeActionKind.QuickFix); | ||
String required_field = isGenerateRequired ? "true" : "false"; | ||
data.addProperty(DATA_REQUIRED_FIELD, required_field); | ||
codeAction.setData(data); | ||
|
||
return codeAction; | ||
} | ||
|
||
public ICodeActionResolvesParticipant getResolveCodeActionParticipant(String participantId) { | ||
return resolveCodeActionParticipants.get(participantId); | ||
} | ||
|
||
private static CodeAction insertExpectedElementCodeAction(DOMDocument document, String title, | ||
DOMElement element, String insertStr, Diagnostic diagnostic) throws BadLocationException { | ||
Position childElementPositionStartTag = document.positionAt(element.getStartTagCloseOffset() + 1); | ||
Position childElementPositionEndTag = document.positionAt(element.getEndTagOpenOffset()); | ||
|
||
Range targetRange = new Range(childElementPositionStartTag, childElementPositionEndTag); | ||
return CodeActionFactory.replace(title, targetRange, insertStr, document.getTextDocument(), diagnostic); | ||
} | ||
} |
Oops, something went wrong.