Skip to content

Commit

Permalink
Fix error range DTD declaration missing space
Browse files Browse the repository at this point in the history
If a ELEMENT, ENTITY, or ATTLIST is missing a space after its declaration, i.e.:

```dtd
<!ENTITYnbsp
```

The declaration keyword is used as the error range. i.e.:

```dtd
<!|ENTITY|nbsp
```

Also adds a code action to add a space after these declarations.

Closes eclipse-lemminx#902

Signed-off-by: David Thompson <davthomp@redhat.com>
  • Loading branch information
datho7561 committed Oct 21, 2020
1 parent af09659 commit 4fe7b49
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.ElementDeclUnterminatedCodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.EntityNotDeclaredCodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.FixMissingSpaceCodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.dtd_not_foundCodeAction;
import org.eclipse.lemminx.services.extensions.ICodeActionParticipant;
import org.eclipse.lemminx.services.extensions.diagnostics.IXMLErrorCode;
Expand All @@ -36,7 +37,7 @@

/**
* DTD error code.
*
*
* @see https://wiki.xmldation.com/Support/Validator
*
*/
Expand Down Expand Up @@ -67,6 +68,9 @@ public enum DTDErrorCode implements IXMLErrorCode {
MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL, //
MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN, //
MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED, //
MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL, //
MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL, //
MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL, //
MSG_SPACE_REQUIRED_AFTER_NOTATION_NAME_IN_NOTATIONDECL, //
NotationDeclUnterminated, //
OpenQuoteExpected, //
Expand Down Expand Up @@ -110,7 +114,7 @@ public static DTDErrorCode get(String name) {

/**
* Create the LSP range from the SAX error.
*
*
* @param location
* @param key
* @param arguments
Expand Down Expand Up @@ -195,6 +199,13 @@ public static Range toLSPRange(XMLLocator location, DTDErrorCode code, Object[]
case MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL: {
return XMLPositionUtility.selectDTDDeclTagNameAt(offset, document);
}

case MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL:
case MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL:
case MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL: {
return XMLPositionUtility.selectDTDDeclTagNameAt(offset, document);
}

case dtd_not_found: {
// Check if DTD location comes from a xml-model/@href
String hrefLocation = (String) arguments[1];
Expand All @@ -220,5 +231,8 @@ public static void registerCodeActionParticipants(Map<String, ICodeActionPartici
if (sharedSettings.getWorkspaceSettings().isResourceOperationSupported(ResourceOperationKind.Create)) {
codeActions.put(dtd_not_found.getCode(), new dtd_not_foundCodeAction());
}
codeActions.put(MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL.getCode(), new FixMissingSpaceCodeAction());
codeActions.put(MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL.getCode(), new FixMissingSpaceCodeAction());
codeActions.put(MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL.getCode(), new FixMissingSpaceCodeAction());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2020 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
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.contentmodel.participants.codeactions;

import java.util.List;

import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.commons.CodeActionFactory;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.services.extensions.ICodeActionParticipant;
import org.eclipse.lemminx.services.extensions.IComponentProvider;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;

/**
* Adds a space at the end of the diagnostic range
*
*/
public class FixMissingSpaceCodeAction implements ICodeActionParticipant {

@Override
public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument document, List<CodeAction> codeActions,
SharedSettings sharedSettings, IComponentProvider componentProvider) {
Range diagnosticRange = diagnostic.getRange();
try {
int startOffset = document.offsetAt(diagnosticRange.getStart());
int endOffset = document.offsetAt(diagnosticRange.getEnd());
String text = document.getText();
String value = text.substring(startOffset, endOffset);
codeActions.add(CodeActionFactory.insert("Add space after '" + value + "'", diagnosticRange.getEnd(), " ",
document.getTextDocument(), diagnostic));
} catch (BadLocationException | IndexOutOfBoundsException e) {
codeActions.add(CodeActionFactory.insert("Add space", diagnosticRange.getEnd(), " ",
document.getTextDocument(), diagnostic));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -642,11 +642,82 @@ public void testDTDNotFoundWithPUBLIC() throws Exception {
d(5, 4, 5, 21, XMLSyntaxErrorCode.ETagRequired)); // [4]
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL_DTD() throws Exception {
String xml = "<!ELEMENTasdf (#PCDATA)>";
testDiagnosticsFor(xml, "test.dtd", d(0, 2, 9, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL));
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL_INLINE_DOCTYPE() throws Exception {
String xml = "<!DOCTYPE asdf [\n" + //
" <!ELEMENTasdf (#PCDATA)>\n" + //
"]>";
testDiagnosticsFor(xml, d(1, 4, 11, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL));
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL_DTD_CLOSING_MISSING() throws Exception {
String xml = "<!ELEMENTasdf";
testDiagnosticsFor(xml, "test.dtd", d(0, 2, 9, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL));
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL_INLINE_DOCTYPE_CLOSING_MISSING() throws Exception {
String xml = "<!DOCTYPE asdf [\n" + //
" <!ELEMENTasdf\n" + //
"]>";
testDiagnosticsFor(xml, d(1, 4, 11, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL));
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL() throws Exception {
String xml = "<!ATTLISTasdf";
testDiagnosticsFor(xml, "test.dtd", d(0, 2, 9, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL));
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL() throws Exception {
String xml = "<!ENTITYasdf";
testDiagnosticsFor(xml, "test.dtd", d(0, 2, 8, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL));
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECLCodeAction() throws Exception {
String xml = "<!DOCTYPE asdf [\n" + //
" <!ELEMENTasdf\n" + //
"]>";
Diagnostic diagnostic = d(1, 4, 11, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL);
testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(1, 11, 1, 11, " ")));
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECLCodeAction() throws Exception {
String xml = "<!DOCTYPE asdf [\n" + //
" <!ATTLISTasdf\n" + //
"]>";
Diagnostic diagnostic = d(1, 4, 11, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL);
testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(1, 11, 1, 11, " ")));
}

@Test
public void MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECLCodeAction() throws Exception {
String xml = "<!DOCTYPE asdf [\n" + //
" <!ENTITYasdf\n" + //
"]>";
Diagnostic diagnostic = d(1, 4, 10, DTDErrorCode.MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL);
testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(1, 10, 1, 10, " ")));
}

private static void testDiagnosticsFor(String xml, Diagnostic... expected) {
XMLAssert.testDiagnosticsFor(xml, "src/test/resources/catalogs/catalog.xml", expected);
}

private static void testPublicDiagnosticsFor(String xml, Diagnostic... expected) {
XMLAssert.testDiagnosticsFor(xml, "src/test/resources/catalogs/catalog-public.xml", expected);
}

private static void testDiagnosticsFor(String xml, String fileURI, Diagnostic... expected) {
XMLAssert.testDiagnosticsFor(xml, "src/test/resources/catalogs/catalog.xml", null, fileURI, expected);
}
}

0 comments on commit 4fe7b49

Please sign in to comment.