From 5606fa7a29caa627a526278a86d5d2c023eedf33 Mon Sep 17 00:00:00 2001 From: Nikolas Komonen Date: Fri, 15 Nov 2019 14:44:27 -0500 Subject: [PATCH] CodeAction for missing end tag Creates a code action for both MarkupEntityMismatch and ETagRequired errors Fixes #588 Signed-off-by: Nikolas Komonen --- .../org/eclipse/lsp4xml/dom/DOMDocument.java | 4 +- .../participants/XMLSyntaxErrorCode.java | 4 + .../codeactions/ETagRequiredCodeAction.java | 35 ++++++++ .../MarkupEntityMismatchCodeAction.java | 84 +++++++++++++++++++ .../XMLSyntaxDiagnosticsTest.java | 26 ++++-- 5 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/codeactions/ETagRequiredCodeAction.java create mode 100644 org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/codeactions/MarkupEntityMismatchCodeAction.java diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMDocument.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMDocument.java index a433ff401..37daaab90 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMDocument.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMDocument.java @@ -76,9 +76,9 @@ public List 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 { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/XMLSyntaxErrorCode.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/XMLSyntaxErrorCode.java index 434aabfdd..5e26fd2b3 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/XMLSyntaxErrorCode.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/XMLSyntaxErrorCode.java @@ -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; @@ -211,5 +213,7 @@ public static void registerCodeActionParticipants(Map codeActions, + XMLFormattingOptions formattingSettings, IComponentProvider componentProvider) { + MarkupEntityMismatchCodeAction.createEndTagInsertCodeAction(diagnostic, range, document, codeActions, formattingSettings, componentProvider); + } + + +} \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/codeactions/MarkupEntityMismatchCodeAction.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/codeactions/MarkupEntityMismatchCodeAction.java new file mode 100644 index 000000000..e4a315230 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/codeactions/MarkupEntityMismatchCodeAction.java @@ -0,0 +1,84 @@ +/******************************************************************************* +* 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.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 codeActions, + XMLFormattingOptions formattingSettings, IComponentProvider componentProvider) { + createEndTagInsertCodeAction(diagnostic, range, document, codeActions, formattingSettings, componentProvider); + } + + public static void createEndTagInsertCodeAction(Diagnostic diagnostic, Range range, DOMDocument document, List 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; + + XMLSyntaxErrorCode code = XMLSyntaxErrorCode.get(diagnostic.getCode()); + switch (code) { + case MarkupEntityMismatch: + endPosition = document.positionAt(document.getEnd()); + if (endPosition.getLine() > startPosition.getLine()) { + endPosition.setCharacter(startPosition.getCharacter()); + } + break; + case ETagRequired: + endPosition = document.positionAt(element.getStartTagCloseOffset() + 1); + break; + default: + return; + } + + String elementName = element.getTagName(); + CodeAction action = CodeActionFactory.insert("Close with ''", endPosition, "", document.getTextDocument(), diagnostic); + codeActions.add(action); + } catch (BadLocationException e) { + LOGGER.log(Level.WARNING, "Exception while resolving the code action for " + diagnostic.getCode() + ":", e); + } + } + + +} \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java index 89a63534a..7d00bf5dd 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java @@ -182,7 +182,9 @@ public void testETagRequired() throws Exception { " Name\r\n" + // " \r\n" + // " "; - 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, ""))); } @Test @@ -200,7 +202,9 @@ public void testETagRequired3() throws Exception { " \r\n" + " \r\n" + ""; - 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, ""))); } /** @@ -255,9 +259,21 @@ public void testLessThanAttValue() throws Exception { public void testMarkupEntityMismatch() throws Exception { String xml = "\r\n" + "\r\n" - + "\r\n" + // - ""; - testDiagnosticsFor(xml, d(1, 1, 1, 9, XMLSyntaxErrorCode.MarkupEntityMismatch)); + + "\r\n" + + ""; + + Diagnostic d = d(1, 1, 1, 9, XMLSyntaxErrorCode.MarkupEntityMismatch); + testDiagnosticsFor(xml, d); + testCodeActionsFor(xml, d, ca(d, te(3, 0, 3, 0, ""))); + } + + @Test + public void testMarkupEntityMismatch2() throws Exception { + String xml = ""; + + Diagnostic d = d(0, 1, 0, 4, XMLSyntaxErrorCode.MarkupEntityMismatch); + testDiagnosticsFor(xml, d); + testCodeActionsFor(xml, d, ca(d, te(0, 5, 0, 5, ""))); } @Test