//TODO: implement real implicit closing logic from HTML5 spec // see http://www.w3.org/html/wg/drafts/html/master/syntax.html#syntax-tag-omission public static bool IsImplicitlyClosedBy(this XElement parent, INamedXObject current) { //inline and paragraph tags are implicitly closed by block tags and paragraph tags if (ElementTypes.IsInline(parent.Name.Name) || ElementTypes.IsParagraph(parent.Name.Name)) { return(ElementTypes.IsBlock(current.Name.Name) || ElementTypes.IsParagraph(current.Name.Name)); } return(false); }
public override State PushChar(char c, MonoDevelop.Xml.StateEngine.IParseContext context, ref string rollback) { //NOTE: This is (mostly) duplicated in HtmlTagState //handle "paragraph" tags implicitly closed by block-level elements if (context.CurrentStateLength == 1 && context.PreviousState is XmlNameState) { XClosingTag ct = (XClosingTag)context.Nodes.Peek(); //Note: the node stack will always be at least 1 deep due to the XDocument XElement parent = context.Nodes.Peek(1) as XElement; while (parent != null && parent.Name.IsValid && !parent.Name.HasPrefix && !ct.Name.HasPrefix && ct.Name.IsValid && string.Compare(ct.Name.Name, parent.Name.Name, StringComparison.OrdinalIgnoreCase) != 0 && !ElementTypes.IsInline(ct.Name.Name) && (ElementTypes.IsInline(parent.Name.Name) || ElementTypes.IsParagraph(parent.Name.Name)) ) { context.Nodes.Pop(); context.Nodes.Pop(); if (warnAutoClose) { context.LogWarning(string.Format("Tag '{0}' implicitly closed by closing tag '{1}'.", parent.Name.Name, ct.Name.Name), parent.Region); } //parent.Region.End = element.Region.Start; //parent.Region.End.Column = Math.Max (parent.Region.End.Column - 1, 1); parent.Close(parent); context.Nodes.Push(ct); parent = context.Nodes.Peek(1) as XElement; } } return(base.PushChar(c, context, ref rollback)); }
public override State PushChar(char c, IParseContext context, ref string rollback) { if (context.CurrentStateLength == 0 && context.PreviousState is HtmlScriptBodyState) { return(Parent); } //NOTE: This is (mostly) duplicated in HtmlClosingTagState //handle "paragraph" tags implicitly closed by block-level elements if (context.CurrentStateLength == 1 && context.PreviousState is XmlNameState) { XElement element = (XElement)context.Nodes.Peek(); //Note: the node stack will always be at least 1 deep due to the XDocument XElement parent = context.Nodes.Peek(1) as XElement; while (parent != null && parent.Name.IsValid && !parent.Name.HasPrefix && !element.Name.HasPrefix && element.Name.IsValid && !ElementTypes.IsInline(element.Name.Name) && (ElementTypes.IsInline(parent.Name.Name) || ElementTypes.IsParagraph(parent.Name.Name)) ) { context.Nodes.Pop(); context.Nodes.Pop(); if (warnAutoClose) { context.LogWarning(string.Format("Tag '{0}' implicitly closed by tag '{1}'.", parent.Name.Name, element.Name.Name), parent.Region); } //parent.Region.End = element.Region.Start; //parent.Region.End.Column = Math.Max (parent.Region.End.Column - 1, 1); parent.Close(parent); context.Nodes.Push(element); parent = context.Nodes.Peek(1) as XElement; } } State ret = base.PushChar(c, context, ref rollback); if (ret == Parent && c == '>') { var element = context.Nodes.Peek() as XElement; if (element != null && !element.Name.HasPrefix && element.Name.IsValid) { if (element.Name.Name.Equals("script", StringComparison.OrdinalIgnoreCase)) { return(ScriptState); } else if (ElementTypes.IsEmpty(element.Name.Name)) { element.Close(element); context.Nodes.Pop(); if (warnAutoClose) { context.LogWarning(string.Format("Implicitly closed empty tag '{0}'", element.Name.Name), element.Region); } } } } return(ret); }