Skip to content

Commit

Permalink
CodeAction for missing end tag
Browse files Browse the repository at this point in the history
Creates a code action for both MarkupEntityMismatch and ETagRequired errors

Fixes #588

Signed-off-by: Nikolas Komonen <nikolaskomonen@gmail.com>
  • Loading branch information
NikolasKomonen committed Nov 22, 2019
1 parent bc9f68b commit e7aa564
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ public List<DOMNode> getRoots() {
return super.getChildren();
}

public Position positionAt(int position) throws BadLocationException {
public Position positionAt(int offset) throws BadLocationException {
checkCanceled();
return textDocument.positionAt(position);
return textDocument.positionAt(offset);
}

public int offsetAt(Position position) throws BadLocationException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.dom.DOMDocumentType;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.ETagRequiredCodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.ElementUnterminatedCodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.EqRequiredInAttributeCodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.MarkupEntityMismatchCodeAction;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions.OpenQuoteExpectedCodeAction;
import org.eclipse.lsp4xml.services.extensions.ICodeActionParticipant;
import org.eclipse.lsp4xml.services.extensions.diagnostics.IXMLErrorCode;
Expand Down Expand Up @@ -211,5 +213,7 @@ public static void registerCodeActionParticipants(Map<String, ICodeActionPartici
codeActions.put(ElementUnterminated.getCode(), new ElementUnterminatedCodeAction());
codeActions.put(EqRequiredInAttribute.getCode(), new EqRequiredInAttributeCodeAction());
codeActions.put(OpenQuoteExpected.getCode(), new OpenQuoteExpectedCodeAction());
codeActions.put(MarkupEntityMismatch.getCode(), new MarkupEntityMismatchCodeAction());
codeActions.put(ETagRequired.getCode(), new ETagRequiredCodeAction());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions;

import java.util.List;

import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.services.extensions.ICodeActionParticipant;
import org.eclipse.lsp4xml.services.extensions.IComponentProvider;
import org.eclipse.lsp4xml.settings.XMLFormattingOptions;

/**
* ETagRequiredCodeAction
*/
public class ETagRequiredCodeAction implements ICodeActionParticipant {

@Override
public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument document, List<CodeAction> codeActions,
XMLFormattingOptions formattingSettings, IComponentProvider componentProvider) {
MarkupEntityMismatchCodeAction.createEndTagInsertCodeAction(diagnostic, range, document, codeActions, formattingSettings, componentProvider);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (c) 2019 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/el-v20.html
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/

package org.eclipse.lsp4xml.extensions.contentmodel.participants.codeactions;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4xml.commons.BadLocationException;
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.participants.XMLSchemaErrorCode;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.XMLSyntaxErrorCode;
import org.eclipse.lsp4xml.services.extensions.ICodeActionParticipant;
import org.eclipse.lsp4xml.services.extensions.IComponentProvider;
import org.eclipse.lsp4xml.settings.XMLFormattingOptions;

/**
* MarkupEntityMismatchCodeAction is a code action that triggers when the end tag of the
* root element is missing. This will provide a codeaction that inserts that missing
* end tag.
*/
public class MarkupEntityMismatchCodeAction implements ICodeActionParticipant {
private static final Logger LOGGER = Logger.getLogger(MarkupEntityMismatchCodeAction.class.getName());


@Override
public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument document, List<CodeAction> codeActions,
XMLFormattingOptions formattingSettings, IComponentProvider componentProvider) {
createEndTagInsertCodeAction(diagnostic, range, document, codeActions, formattingSettings, componentProvider);
}

public static void createEndTagInsertCodeAction(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.isElement()) {
return;
}

DOMElement element = (DOMElement) node;
int startOffset = element.getStartTagOpenOffset();
Position startPosition = document.positionAt(startOffset);
Position endPosition;
if(XMLSyntaxErrorCode.MarkupEntityMismatch.toString().equals(diagnostic.getCode())) {
endPosition = document.positionAt(document.getEnd());
if(endPosition.getLine() > startPosition.getLine()) {
endPosition.setCharacter(startPosition.getCharacter());
}
}
else if(XMLSyntaxErrorCode.ETagRequired.toString().equals(diagnostic.getCode())) {
endPosition = document.positionAt(element.getStartTagCloseOffset() + 1);
}
else {
return;
}

String elementName = element.getTagName();
CodeAction action = CodeActionFactory.insert("Close with '</" + elementName + ">'", endPosition, "</" + elementName + ">", document.getTextDocument(), diagnostic);
codeActions.add(action);
} catch (BadLocationException e) {
LOGGER.log(Level.WARNING, "Exception while resolving the code action for " + diagnostic.getCode() + ":", e);
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ public void testETagRequired() throws Exception {
" <Nm>Name\r\n" + //
" </UltmtDbtr> \r\n" + //
" </Nm> ";
testDiagnosticsFor(xml, d(1, 5, 1, 7, XMLSyntaxErrorCode.ETagRequired));
Diagnostic d = d(1, 5, 1, 7, XMLSyntaxErrorCode.ETagRequired);
testDiagnosticsFor(xml, d);
testCodeActionsFor(xml, d, ca(d, te(1, 8, 1, 8, "</Nm>")));
}

@Test
Expand All @@ -200,7 +202,9 @@ public void testETagRequired3() throws Exception {
" <Ad>\r\n" +
" <Ph>\r\n" +
"</UltmtDbtr>";
testDiagnosticsFor(xml, d(3, 5, 3, 7, XMLSyntaxErrorCode.ETagRequired));
Diagnostic d = d(3, 5, 3, 7, XMLSyntaxErrorCode.ETagRequired);
testDiagnosticsFor(xml, d);
testCodeActionsFor(xml, d, ca(d, te(3, 8, 3, 8, "</Ph>")));
}

/**
Expand Down Expand Up @@ -255,9 +259,21 @@ public void testLessThanAttValue() throws Exception {
public void testMarkupEntityMismatch() throws Exception {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
+ "<Document xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03\">\r\n"
+ "<CstmrCdtTrfInitn>\r\n" + //
"</CstmrCdtTrfInitn>";
testDiagnosticsFor(xml, d(1, 1, 1, 9, XMLSyntaxErrorCode.MarkupEntityMismatch));
+ "<CstmrCdtTrfInitn>\r\n"
+ "</CstmrCdtTrfInitn>";

Diagnostic d = d(1, 1, 1, 9, XMLSyntaxErrorCode.MarkupEntityMismatch);
testDiagnosticsFor(xml, d);
testCodeActionsFor(xml, d, ca(d, te(3, 0, 3, 0, "</Document>")));
}

@Test
public void testMarkupEntityMismatch2() throws Exception {
String xml = "<ABC>";

Diagnostic d = d(0, 1, 0, 4, XMLSyntaxErrorCode.MarkupEntityMismatch);
testDiagnosticsFor(xml, d);
testCodeActionsFor(xml, d, ca(d, te(0, 5, 0, 5, "</ABC>")));
}

@Test
Expand Down

0 comments on commit e7aa564

Please sign in to comment.