private void ConvertHtmlTextNode(HtmlStringNode htmlStringNode) { var origString = htmlStringNode.String; var text = origString.TrimEnd(); _lastBlockIsMarkdown = true; bool isFirstCharWhitespace = origString.Length > 1 && char.IsWhiteSpace(origString[0]); if (_options.LinesMaxLength != 0) { string[] words = text.Split(SpacesAndNewLines, StringSplitOptions.RemoveEmptyEntries); for (var i = 0; i < words.Length; i++) { AppendWithBreak(words[i], i == 0 && !isFirstCharWhitespace); } } else { AppendWithBreak(text, true); } if (origString.Length > 1 && char.IsWhiteSpace(origString[^ 1]) && !_result.IsLastCharWhitespaceOrLeadingPunctuation())
private HtmlElementNode ParseElementNode(HtmlParser.ElementContext elementContext) { var content = new List <Node>(elementContext.content().Length); foreach (var contentContext in elementContext.content()) { content.Add(ParseContent(contentContext)); } bool voidElement = elementContext.voidElementTag() != null; var tagName = new HtmlStringNode(voidElement ? (ITerminalNode)elementContext.voidElementTag().GetChild(0) : elementContext.TAG_NAME()); var attributes = new Dictionary <string, HtmlAttributeNode>(); foreach (HtmlParser.AttributeContext attributeContext in elementContext.attribute()) { var nameNode = new HtmlStringNode(attributeContext.TAG_NAME()); var valueTerminal = attributeContext.ATTR_VALUE(); var valueSymbol = valueTerminal.Symbol; string value = valueSymbol.Text.Trim('\'', '"'); var valueNode = new HtmlStringNode(valueTerminal, value, valueSymbol.StartIndex, valueSymbol.StopIndex - valueSymbol.StartIndex + 1); attributes.Add(nameNode.String.ToLowerInvariant(), new HtmlAttributeNode(attributeContext, nameNode, valueNode)); } HtmlStringNode?address = null; bool isImage = false; string tagNameString = tagName.String.ToLowerInvariant(); string? addressAttrName = null; if (tagNameString == "a") { addressAttrName = "href"; } else if (tagNameString == "img") { addressAttrName = "src"; isImage = true; } else if (tagNameString == LinkmapTagName) { Link?srcLink = null, dstLink = null; if (!attributes.TryGetValue("src", out HtmlAttributeNode? srcNode)) { _logger.Error($"{LinkmapTagName} element should contain src attribute at {tagName.LineColumnSpan}"); } else { srcLink = Link.Create(srcNode.Value, srcNode.Value.String); Link?existingLink; if ((existingLink = _linksMap.Keys.FirstOrDefault(key => key.Address.Equals(srcLink.Address))) != null) { _logger.Warn($"{LinkmapTagName} \"{srcLink.Node.Substring}\" at {srcLink.Node.LineColumnSpan} replaces linkmap at {existingLink.Node.LineColumnSpan}"); } } if (!attributes.TryGetValue("dst", out HtmlAttributeNode? dstNode)) { _logger.Error($"{LinkmapTagName} element should contain dst attribute at {tagName.LineColumnSpan}"); } else { dstLink = Link.Create(dstNode.Value, dstNode.Value.String); } if (srcLink != null && dstLink != null) { if (srcLink.Address == HeaderImageLink) { _headerImageLink = dstLink; } else { _linksMap[srcLink] = dstLink; } } } else if (tagNameString == IncludeTagName) { if (!attributes.TryGetValue("src", out HtmlAttributeNode? srcNode)) { _logger.Error($"{IncludeTagName} element should contain src attribute at {tagName.LineColumnSpan}"); } else { var srcLink = Link.Create(srcNode.Value, srcNode.Value.String); if (srcLink is LocalLink localLink) { var rootDirectory = Path.GetDirectoryName(_file.Name) ?? ""; var includeFilePath = Path.Combine(rootDirectory, localLink.Address); if (!File.Exists(includeFilePath)) { _logger.Error($"File {includeFilePath} does not exist at {localLink.Node.LineColumnSpan}"); } else { var includeFile = new TextFile(File.ReadAllText(includeFilePath), includeFilePath); var parser = new Parser(_options, _logger, includeFile); var includeParseResult = parser.Parse(); foreach (KeyValuePair <Node, Link> pair in includeParseResult.Links) { _links.Add(pair.Key, pair.Value); } foreach ((Link includeKey, Link includeValue) in includeParseResult.LinksMap) { Link?existingLinkMap; if ((existingLinkMap = _linksMap.Keys.FirstOrDefault(key => key.Address.Equals(includeKey.Address))) != null) { _logger.Warn($"{LinkmapTagName} \"{existingLinkMap.Node.Substring}\" at {existingLinkMap.Node.LineColumnSpan} replaces " + $"linkmap at {includeKey.Node.LineColumnSpan} at {includeKey.Node.File.Name}"); } else { _linksMap.Add(includeKey, includeValue); } } foreach ((string anchorKey, Anchor anchorValue) in includeParseResult.Anchors) { if (_anchors.TryGetValue(anchorKey, out Anchor? existingAnchor)) { _logger.Warn($"Anchor {existingAnchor.Address} at {existingAnchor.Node.LineColumnSpan} replaces " + $"anchor at {anchorValue.Node.LineColumnSpan} at {anchorValue.Node.File.Name}"); } else { _anchors.Add(anchorKey, anchorValue); } } content.Add(includeParseResult.Node); } } else { _logger.Error($"Only local files can be included via <include/> element at {srcLink.Node.LineColumnSpan}"); } } } if (addressAttrName != null) { if (attributes.TryGetValue(addressAttrName, out HtmlAttributeNode? htmlAttributeNode)) { address = htmlAttributeNode.Value; } else { _logger.Error($"Element <{tagNameString}> does not contain required '{addressAttrName}' attribute at {tagName.LineColumnSpan}"); } } var closingTag = elementContext.GetChild(elementContext.ChildCount - 1); var result = new HtmlElementNode(elementContext, tagName, attributes, content, closingTag is ParserRuleContext parserRuleContext ? new HtmlStringNode(parserRuleContext) : new HtmlStringNode((ITerminalNode)closingTag)); if (address != null) { _links.Add(result, Link.Create(result, address.String, isImage, address.Start, address.Length)); } return(result); }