Skip to content

Commit

Permalink
Fuzzy Element name codeaction
Browse files Browse the repository at this point in the history
Fixes #589

Refer to #589 for code to test

Signed-off-by: Nikolas Komonen <[email protected]>
  • Loading branch information
NikolasKomonen committed Nov 8, 2019
1 parent b0dbc6b commit b6f4f3a
Show file tree
Hide file tree
Showing 6 changed files with 570 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
*/
package org.eclipse.lsp4xml.commons;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
Expand Down Expand Up @@ -85,4 +88,24 @@ public static CodeAction replace(String title, Range range, String replaceText,
insertContentAction.setEdit(workspaceEdit);
return insertContentAction;
}

public static CodeAction replaceAt(String title, String replaceText, TextDocumentItem document,
Diagnostic diagnostic, Collection<Range> ranges) {
CodeAction insertContentAction = new CodeAction(title);
insertContentAction.setKind(CodeActionKind.QuickFix);
insertContentAction.setDiagnostics(Arrays.asList(diagnostic));

VersionedTextDocumentIdentifier versionedTextDocumentIdentifier = new VersionedTextDocumentIdentifier(
document.getUri(), document.getVersion());
ArrayList<TextEdit> edits = new ArrayList<TextEdit>();
for (Range range : ranges) {
TextEdit edit = new TextEdit(range, replaceText);
edits.add(edit);
}
TextDocumentEdit textDocumentEdit = new TextDocumentEdit(versionedTextDocumentIdentifier, edits);
WorkspaceEdit workspaceEdit = new WorkspaceEdit(Collections.singletonList(Either.forLeft(textDocumentEdit)));

insertContentAction.setEdit(workspaceEdit);
return insertContentAction;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.cvc_attribute_3CodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.cvc_complex_type_2_1CodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.cvc_complex_type_2_3CodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.cvc_complex_type_2_4_aCodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.cvc_complex_type_3_2_2CodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.cvc_complex_type_4CodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.cvc_enumeration_validCodeAction;
Expand Down Expand Up @@ -244,6 +245,7 @@ public static Range toLSPRange(XMLLocator location, XMLSchemaErrorCode code, Obj
}

public static void registerCodeActionParticipants(Map<String, ICodeActionParticipant> codeActions) {
codeActions.put(cvc_complex_type_2_4_a.getCode(), new cvc_complex_type_2_4_aCodeAction());
codeActions.put(cvc_complex_type_2_3.getCode(), new cvc_complex_type_2_3CodeAction());
codeActions.put(cvc_complex_type_4.getCode(), new cvc_complex_type_4CodeAction());
codeActions.put(cvc_type_3_1_1.getCode(), new cvc_type_3_1_1CodeAction());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TreeSet;

import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4xml.commons.CodeActionFactory;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.dom.DOMElement;
import org.eclipse.lsp4xml.dom.DOMNode;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lsp4xml.services.extensions.ICodeActionParticipant;
import org.eclipse.lsp4xml.services.extensions.IComponentProvider;
import org.eclipse.lsp4xml.settings.XMLFormattingOptions;
import org.eclipse.lsp4xml.utils.LevenshteinDistance;
import org.eclipse.lsp4xml.utils.XMLPositionUtility;

/**
* cvc_complex_type_2_4_a
*/
public class cvc_complex_type_2_4_aCodeAction implements ICodeActionParticipant {

private static final float MAX_DISTANCE_DIFF_RATIO = 0.4f;

@Override
public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument document, List<CodeAction> codeActions,
XMLFormattingOptions formattingSettings, IComponentProvider componentProvider) {
try {
int offset = document.offsetAt(diagnostic.getRange().getStart());
DOMNode node = document.findNodeAt(offset);
if (node != null && node.isElement()) {
// Get element from the diagnostic
DOMElement element = (DOMElement) node;
DOMElement parentElement = element.getParentElement();
String tagName = element.getTagName();

// Get the XSD element declaration of the parent element.
ContentModelManager contentModelManager = componentProvider.getComponent(ContentModelManager.class);
CMElementDeclaration cmElement = contentModelManager.findCMElement(parentElement);
if (cmElement != null) {
// Collect all possible elements from the parent element upon the offset start
// of the element
Collection<CMElementDeclaration> possibleElements = cmElement.getPossibleElements(parentElement,
element.getStart());

// When added to these collections, the names will be ordered alphabetically
Collection<String> otherElementNames = new TreeSet<String>(Collator.getInstance());
Collection<String> similarElementNames = new TreeSet<String>(Collator.getInstance());

// Try to collect similar names coming from tag name
for (CMElementDeclaration possibleElement : possibleElements) {
String possibleElementName = possibleElement.getName();
if (isSimilar(possibleElementName, tagName)) {
similarElementNames.add(possibleElementName);
}
else {
otherElementNames.add(possibleElementName);
}
}

// Create ranges for the replace.
List<Range> ranges = new ArrayList<>();
ranges.add(XMLPositionUtility.selectStartTag(element));
Range end = XMLPositionUtility.selectEndTag(element);
if (end != null) {
ranges.add(end);
}

if (!similarElementNames.isEmpty()) {
// // Add code actions for each similar elements
for (String elementName : similarElementNames) {
CodeAction similarCodeAction = CodeActionFactory.replaceAt(
"Did you mean '" + elementName + "'?", elementName, document.getTextDocument(),
diagnostic, ranges);
codeActions.add(similarCodeAction);
}
} else {
// Add code actions for each possible elements
for (String elementName : otherElementNames) {
CodeAction otherCodeAction = CodeActionFactory.replaceAt(
"Replace with '" + elementName + "'", elementName, document.getTextDocument(),
diagnostic, ranges);
codeActions.add(otherCodeAction);
}
}
}
}

} catch (Exception e) {
// Do nothing
}
}

private static boolean isSimilar(String reference, String current) {
int threshold = Math.round(MAX_DISTANCE_DIFF_RATIO * reference.length());
LevenshteinDistance levenshteinDistance = new LevenshteinDistance(threshold);
return levenshteinDistance.apply(reference, current) != -1;
}
}
Loading

0 comments on commit b6f4f3a

Please sign in to comment.