[283155] HTML Smart Insert closes tags that should have no end tag
diff --git a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/autoedit/StructuredAutoEditStrategyHTML.java b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/autoedit/StructuredAutoEditStrategyHTML.java index 5950b8a..d824b6c 100644 --- a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/autoedit/StructuredAutoEditStrategyHTML.java +++ b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/autoedit/StructuredAutoEditStrategyHTML.java
@@ -22,16 +22,26 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.texteditor.ITextEditorExtension3; +import org.eclipse.wst.html.core.internal.contentmodel.HTMLElementDeclaration; +import org.eclipse.wst.html.core.internal.document.HTMLDocumentTypeEntry; +import org.eclipse.wst.html.core.internal.document.HTMLDocumentTypeRegistry; +import org.eclipse.wst.html.core.internal.provisional.HTMLCMProperties; import org.eclipse.wst.html.ui.internal.HTMLUIPlugin; import org.eclipse.wst.html.ui.internal.preferences.HTMLUIPreferenceNames; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; +import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; +import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; import org.eclipse.wst.xml.ui.internal.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; /** @@ -138,9 +148,13 @@ IStructuredDocumentRegion region = node.getEndStructuredDocumentRegion(); if (region != null && region.getRegions().size() > 0 && region.getRegions().get(0).getType() == DOMRegionContext.XML_END_TAG_OPEN && !isClosedByParent) return; - command.text += "</" + getElementName(node, command.offset) + ">"; //$NON-NLS-1$ //$NON-NLS-2$ - command.shiftsCaret = false; - command.caretOffset = command.offset + 1; + CMElementDeclaration decl = getCMElementDeclaration(node); + // If it's XHTML, always generate the end tag + if (isXHTML(node) || shouldGenerateEndTag(decl)) { + command.text += "</" + getElementName(node, command.offset) + ">"; //$NON-NLS-1$ //$NON-NLS-2$ + command.shiftsCaret = false; + command.caretOffset = command.offset + 1; + } } } @@ -223,6 +237,60 @@ } return false; } + + /** + * Based on the content model, determine if an end tag should be generated + * @param elementDecl the content model element declaration + * @return true if the end tag should be generated; false otherwise. + */ + private boolean shouldGenerateEndTag(CMElementDeclaration elementDecl) { + if (elementDecl == null) + return false; + if (elementDecl instanceof HTMLElementDeclaration) { + if (((Boolean) elementDecl.getProperty(HTMLCMProperties.IS_JSP)).booleanValue()) { + if (elementDecl.getContentType() == CMElementDeclaration.EMPTY) + return false; + } + else { + String ommission = (String) elementDecl.getProperty(HTMLCMProperties.OMIT_TYPE); + if (ommission.equals(HTMLCMProperties.Values.OMIT_END) || ommission.equals(HTMLCMProperties.Values.OMIT_END_DEFAULT) || ommission.equals(HTMLCMProperties.Values.OMIT_END_MUST)) { + return false; + } + } + } + + if (elementDecl.getContentType() == CMElementDeclaration.EMPTY) + return false; + return true; + } + + private CMElementDeclaration getCMElementDeclaration(Node node) { + CMElementDeclaration result = null; + if (node.getNodeType() == Node.ELEMENT_NODE) { + ModelQuery modelQuery = ModelQueryUtil.getModelQuery(node.getOwnerDocument()); + if (modelQuery != null) { + result = modelQuery.getCMElementDeclaration((Element) node); + } + } + return result; + } + + /** + * Is the node part of an XHTML document + * @param node + * @return + */ + private boolean isXHTML(Node node) { + Document doc = node.getOwnerDocument(); + if (!(doc instanceof IDOMDocument)) + return false; + String typeid = ((IDOMDocument) doc).getDocumentTypeId(); + if (typeid != null) { + HTMLDocumentTypeEntry entry = HTMLDocumentTypeRegistry.getInstance().getEntry(typeid); + return (entry != null && entry.isXMLType()); + } + return false; + } /** * Return the active text editor if possible, otherwise the active editor