/* Function: SkipBlock * If the iterator is on an opening tag it will move past it, all contained content, and its closing tag and return true. * Otherwise returns false. */ protected void SkipBlock(ref XMLIterator iterator) { #if DEBUG if (iterator.IsOnTag(TagForm.Opening) == false) { throw new Exception("Can only call SkipBlock() when the iterator is on an opening tag."); } #endif TagStack tagStack = new TagStack(); tagStack.OpenTag(iterator.TagType); iterator.Next(); while (iterator.IsInBounds && !tagStack.IsEmpty) { if (iterator.Type == XMLElementType.Tag) { if (iterator.TagForm == TagForm.Opening) { tagStack.OpenTag(iterator.TagType); } else if (iterator.TagForm == TagForm.Closing) { tagStack.CloseTag(iterator.TagType); } // Ignore standalone tags } iterator.Next(); } }
/* Function: Parse * * Attempts to parse the passed comment into <Topics>. Returns whether it was successful, and if so, adds them * to the list. These fields will be set: * * - CommentLineNumber * - Body, if present * - Summary, if available */ public bool Parse(PossibleDocumentationComment sourceComment, List <Topic> topics) { XMLIterator iterator = new XMLIterator(sourceComment.Start.FirstToken(Tokenization.LineBoundsMode.Everything), sourceComment.End.FirstToken(Tokenization.LineBoundsMode.Everything)); while (iterator.Type == XMLElementType.Indent || iterator.Type == XMLElementType.LineBreak) { iterator.Next(); } if (iterator.Type != XMLElementType.Tag) { return(false); } XMLComment xmlComment = new XMLComment(); while (iterator.IsInBounds) { if (TryToGetTopLevelTextBlock(ref iterator, xmlComment) || TryToGetTopLevelListItem(ref iterator, xmlComment)) { } else if (iterator.IsOnTag(TagForm.Opening)) { SkipBlock(ref iterator); } else { iterator.Next(); } } Topic topic = GenerateTopic(xmlComment); if (topic != null) { topic.CommentLineNumber = sourceComment.Start.LineNumber; topics.Add(topic); } return(true); }
/* Function: TryToGetTopLevelTextBlock * If the iterator is on a summary, remark, returns, or top-level example tag it will convert it to NDMarkup, add it to the comment * in a text block, move the iterator past it, and return true. Otherwise it returns false and nothing is changed. */ protected bool TryToGetTopLevelTextBlock(ref XMLIterator iterator, XMLComment comment) { if (iterator.IsOnTag("summary", TagForm.Opening) == false && iterator.IsOnTag("remark", TagForm.Opening) == false && iterator.IsOnTag("remarks", TagForm.Opening) == false && iterator.IsOnTag("example", TagForm.Opening) == false && iterator.IsOnTag("returns", TagForm.Opening) == false && iterator.IsOnTag("value", TagForm.Opening) == false) { return(false); } string keyword = iterator.TagType; string blockType = keyword; if (keyword == "remarks") { blockType = "remark"; } XMLComment.TextBlock block = comment.GetTextBlock(blockType); TagStack tagStack = new TagStack(); tagStack.OpenTag(keyword); iterator.Next(); GetText(ref iterator, block.Text, tagStack); tagStack.CloseAllTags(block.Text); if (iterator.IsOnTag(keyword, TagForm.Closing)) { iterator.Next(); } return(true); }
private void ParseSpriteTags(string strText) { if (string.IsNullOrEmpty(strText) || -1 == strText.IndexOf("quad")) { _ResetSpriteTags(0); return; } ImageCache imageCache = null; if (m_SupportCache) { imageCache = CacheManager.Instance.GetImgCache(strText.GetHashCode()); if (imageCache != null) { List <ImageCache.CacheImageTag> imageCacheParms = imageCache.GetParams(); for (int i = 0; i < imageCacheParms.Count; ++i) { SpriteTag tag = _GetSpriteTag(1); if (imageCacheParms[i].paramDic != null) { var iter = imageCacheParms[i].paramDic.GetEnumerator(); while (iter.MoveNext()) { tag.CheckSetValue(imageCacheParms[i].position, iter.Current.Key, iter.Current.Value); } iter.Dispose(); } } _ResetSpriteTags(imageCacheParms.Count); } } int imgCount = 0; XMLIterator.Begin(strText, false); string textBlock = string.Empty; mTagPos.Clear(); int tagLength = 0; int mImgTagCount = 0; int line = 0; int richXMLTagCount = 0; while (XMLIterator.NextTag()) { mParses.Clear(); if (XMLIterator.tagName == mImgTag) { { line = Regex.Matches(strText.Substring(0, XMLIterator.parsePos), "\n").Count; mTagPos[mImgTag] = XMLIterator.parsePos + XMLIterator.tagLength; XMLIterator.GetAttributes(mParses); SpriteTag tag = _GetSpriteTag(imgCount); tagLength += XMLIterator.tagLength; foreach (var val in mParses) { tag.CheckSetValue(XMLIterator.parsePos + mImgTagCount - tagLength - line - richXMLTagCount, val.Key, val.Value); } mImgTagCount++; } if (m_SupportCache) { SpriteTag tag = _GetSpriteTag(imgCount); imageCache = CacheManager.Instance.AddImgCache(strText.GetHashCode()); ImageCache.CacheImageTag cacheTag = new ImageCache.CacheImageTag(); if (cacheTag.paramDic == null) { cacheTag.paramDic = new Dictionary <string, string>(); } cacheTag.position = tag.GetVertexStartIndex(); cacheTag.paramDic["name"] = tag.GetName(); cacheTag.paramDic["src"] = tag.GetSrc(); cacheTag.paramDic["width"] = tag.GetWidth(); cacheTag.paramDic["height"] = tag.GetHeight(); if (m_SupportDynamicChart) { cacheTag.paramDic["start"] = tag.GetStartFrame().ToString(); cacheTag.paramDic["end"] = tag.GetEndFrame().ToString(); } imageCache.AddParams(cacheTag); } ++imgCount; } else { if (XMLIterator.tagName.StartsWith("color") || XMLIterator.tagName.StartsWith("b") || XMLIterator.tagName.StartsWith("i") || XMLIterator.tagName.StartsWith("size")) { richXMLTagCount += XMLIterator.tagLength; } } } _ResetSpriteTags(imgCount); }
/* Function: GetList * Converts the contents of a list tag to NDMarkup and adds it to the output. The iterator should be on an opening list tag * and when it ends it will be past the closing tag. */ protected void GetList(ref XMLIterator iterator, StringBuilder output, TagStack tagStack) { #if DEBUG if (iterator.IsOnTag("list", TagForm.Opening) == false) { throw new Exception("GetList() can only be called when the iterator is on an opening list tag."); } #endif tagStack.OpenTag("list"); int surroundingListTagIndex = tagStack.Count - 1; iterator.Next(); List <ListItem> items = new List <ListItem>(); ListItem currentItem = new ListItem(); StringBuilder stringBuilder = new StringBuilder(); // To reuse while (iterator.IsInBounds) { if (iterator.IsOnTag("list", TagForm.Closing)) { iterator.Next(); break; } else if (iterator.IsOnTag("item") || iterator.IsOnTag("listheader")) { if (iterator.TagForm == TagForm.Opening) { currentItem = new ListItem(); currentItem.IsHeading = (iterator.TagType == "listheader"); } else if (iterator.TagForm == TagForm.Closing) { if (currentItem.Term != null) { currentItem.Term = Normalize(currentItem.Term.Trim()); if (currentItem.Term == "") { currentItem.Term = null; } else if (currentItem.IsHeading) { currentItem.Term = "<b>" + currentItem.Term + "</b>"; } } if (currentItem.Description != null) { currentItem.Description = Normalize(currentItem.Description.Trim()); if (currentItem.Description == "") { currentItem.Description = null; } else if (currentItem.IsHeading) { currentItem.Description = currentItem.Description.Replace("<p>", "<p><b>"); currentItem.Description = currentItem.Description.Replace("</p>", "</b></p>"); } } if (currentItem.Term != null || currentItem.Description != null) { items.Add(currentItem); } currentItem = new ListItem(); } iterator.Next(); } else if (iterator.IsOnTag("term", TagForm.Opening)) { tagStack.OpenTag("term"); iterator.Next(); stringBuilder.Remove(0, stringBuilder.Length); GetSimpleText(ref iterator, stringBuilder, tagStack); currentItem.Term = stringBuilder.ToString(); if (iterator.TagType == "term" && iterator.TagForm == TagForm.Closing) { iterator.Next(); } tagStack.CloseTag("term"); } else if (iterator.IsOnTag("description", TagForm.Opening)) { tagStack.OpenTag("description"); iterator.Next(); stringBuilder.Remove(0, stringBuilder.Length); GetText(ref iterator, stringBuilder, tagStack); currentItem.Description = stringBuilder.ToString(); if (iterator.TagType == "description" && iterator.TagForm == TagForm.Closing) { iterator.Next(); } tagStack.CloseTag("description"); } else if (iterator.IsOnTag(TagForm.Opening)) { SkipBlock(ref iterator); } else { iterator.Next(); } } tagStack.CloseTag(surroundingListTagIndex); if (items.Count > 0) { bool hasTerms = false; bool hasDescriptions = false; for (int i = 0; i < items.Count && (hasTerms == false || hasDescriptions == false); i++) { if (items[i].Term != null) { hasTerms = true; } if (items[i].Description != null) { hasDescriptions = true; } } if (hasTerms && hasDescriptions) { output.Append("<dl>"); foreach (var item in items) { output.Append("<de>"); if (item.Term != null) { output.Append(item.Term); } output.Append("</de><dd>"); if (item.Description != null) { output.Append(item.Description); } output.Append("</dd>"); } output.Append("</dl>"); } else // doesn't have both { output.Append("<ul>"); foreach (var item in items) { output.Append("<li>"); // The format only allows for descriptions without terms, but we'll support terms without descriptions as well. if (item.Term != null) { output.Append(item.Term); } if (item.Description != null) { output.Append(item.Description); } output.Append("</li>"); } output.Append("</ul>"); } } }
/* Function: GetCode * Converts the contents of a code tag to NDMarkup and adds it to the output. The iterator should be on an opening code tag * and when it ends it will be past the closing tag. */ protected void GetCode(ref XMLIterator iterator, StringBuilder output, TagStack tagStack) { #if DEBUG if (iterator.IsOnTag("code", TagForm.Opening) == false) { throw new Exception("GetCode() can only be called when the iterator is on an opening code tag."); } #endif output.Append("<pre type=\"code\">"); tagStack.OpenTag("code", "</pre>"); int surroundingCodeTagIndex = tagStack.Count - 1; iterator.Next(); List <CodeLine> lines = new List <CodeLine>(); CodeLine currentLine = new CodeLine(); currentLine.Indent = -1; // Don't use text immediately following the code tag to figure out the shared indent. currentLine.Text = null; for (;;) { if (iterator.IsInBounds == false) { lines.Add(currentLine); break; } else if (iterator.IsOnTag(TagForm.Closing)) { int openingTagIndex = tagStack.FindTag(iterator.TagType); if (openingTagIndex != -1 && openingTagIndex <= surroundingCodeTagIndex) { lines.Add(currentLine); break; } // Otherwise let it fall through to be treated as text. } if (iterator.IsOn(XMLElementType.LineBreak)) { lines.Add(currentLine); currentLine = new CodeLine(); currentLine.Indent = 0; currentLine.Text = null; } else if (iterator.IsOn(XMLElementType.Indent)) { currentLine.Indent = iterator.Indent; } else // entity, unhandled tag, text { if (currentLine.Text == null) { currentLine.Text = iterator.String; } else { currentLine.Text += iterator.String; } } iterator.Next(); } Normalize(lines); // Build the output. for (int i = 0; i < lines.Count; i++) { if (lines[i].Indent >= 1) { output.Append(' ', lines[i].Indent); } if (lines[i].Text != null) { output.EntityEncodeAndAppend(lines[i].Text); } if (i < lines.Count - 1) { output.Append("<br>"); } } tagStack.CloseTag(surroundingCodeTagIndex, output); }
/* Function: GetSimpleText * Converts a block of plain unformatted text to NDMarkup and adds it to the output. Unlike <GetText()> this will not surround the * output in paragraph tags. It ends when it reaches the closing tag for anything already on the tag stack. */ protected void GetSimpleText(ref XMLIterator iterator, StringBuilder output, TagStack tagStack) { int surroundingTagCount = tagStack.Count; while (iterator.IsInBounds) { if (iterator.IsOn(XMLElementType.Text)) { output.EntityEncodeAndAppend(iterator.String); iterator.Next(); } else if (iterator.IsOn(XMLElementType.EntityChar)) { output.EntityEncodeAndAppend(iterator.EntityValue); iterator.Next(); } else if (iterator.IsOn(XMLElementType.LineBreak)) { // Add a literal line break. We'll replace these with spaces or double spaces later. Right now we can't decide // which it should be because you can't run a regex directly on a StringBuilder and it would be inefficient to convert // it to a string on every line break. output.Append('\n'); iterator.Next(); } else if (iterator.IsOnTag("paramref") || iterator.IsOnTag("typeparamref")) { // Can't assume all the properties are set string name = iterator.TagProperty("name"); if (name != null) { output.Append(name); } iterator.Next(); } else if (iterator.IsOnTag(TagForm.Opening)) { tagStack.OpenTag(iterator.TagType); iterator.Next(); } else if (iterator.IsOnTag(TagForm.Closing)) { int openingTagIndex = tagStack.FindTag(iterator.TagType); if (openingTagIndex == -1) { } else if (openingTagIndex <= surroundingTagCount - 1) { break; } else { tagStack.CloseTag(openingTagIndex, output); } iterator.Next(); } else { // Ignore indent. Spaces between words will be handled by line breaks. // Ignore unrecognized standalone tags. iterator.Next(); } } if (tagStack.Count > surroundingTagCount) { tagStack.CloseTag(surroundingTagCount, output); } }
/* Function: GetText * Converts a block of formatted text to NDMarkup and adds it to the output. It ends when it reaches the closing tag for anything * already on the tag stack. */ protected void GetText(ref XMLIterator iterator, StringBuilder output, TagStack tagStack) { output.Append("<p>"); tagStack.OpenTag(null, "</p>"); int surroundingPTagIndex = tagStack.Count - 1; while (iterator.IsInBounds) { if (iterator.IsOn(XMLElementType.Text)) { output.EntityEncodeAndAppend(iterator.String); iterator.Next(); } else if (iterator.IsOn(XMLElementType.EntityChar)) { output.EntityEncodeAndAppend(iterator.EntityValue); iterator.Next(); } else if (iterator.IsOn(XMLElementType.LineBreak)) { // Add a literal line break. We'll replace these with spaces or double spaces later. Right now we can't decide // which it should be because you can't run a regex directly on a StringBuilder and it would be inefficient to convert // it to a string on every line break. output.Append('\n'); iterator.Next(); } else if (iterator.IsOnTag("para")) { // Text can appear both inside and outside of <para> tags, and whitespace can appear between <para> tags that // can be mistaken for content, so rather than put in a lot of logic we handle it in a very dirty but simple way. Every // <para> tag--opening, closing, standalone (technically invalid)--causes a paragraph break. Normalize() will clean it // up for us afterwards. tagStack.CloseTag(surroundingPTagIndex + 1, output); // Reuse our surrounding tag output.Append("</p><p>"); iterator.Next(); } else if (iterator.IsOnTag("code", TagForm.Opening)) { output.Append("</p>"); GetCode(ref iterator, output, tagStack); output.Append("<p>"); } else if (iterator.IsOnTag("example", TagForm.Opening)) { // <example> can be nested in addition to a top-level tag. output.Append("</p><h>"); output.EntityEncodeAndAppend( Engine.Locale.Get("NaturalDocs.Engine", "XML.Heading.example") ); output.Append("</h><p>"); tagStack.OpenTag("example", "</p><p>"); iterator.Next(); } else if (iterator.IsOnTag("list", TagForm.Opening)) { output.Append("</p>"); GetList(ref iterator, output, tagStack); output.Append("<p>"); } else if (iterator.IsOnTag("paramref") || iterator.IsOnTag("typeparamref")) { // Can't assume all the properties are set string name = iterator.TagProperty("name"); if (name != null) { output.EntityEncodeAndAppend(name); } iterator.Next(); } else if (iterator.IsOnTag("see", TagForm.Standalone)) { // Can't assume all the properties are set string cref = iterator.TagProperty("cref"); if (cref != null) { output.Append("<link type=\"naturaldocs\" originaltext=\""); output.EntityEncodeAndAppend(cref); output.Append("\">"); } else { string langword = iterator.TagProperty("langword"); if (langword != null) { output.EntityEncodeAndAppend(langword); } } iterator.Next(); } else if (iterator.IsOnTag(TagForm.Opening)) { tagStack.OpenTag(iterator.TagType); iterator.Next(); } else if (iterator.IsOnTag(TagForm.Closing)) { int openingTagIndex = tagStack.FindTag(iterator.TagType); if (openingTagIndex == -1) { } else if (openingTagIndex < surroundingPTagIndex) { break; } else { tagStack.CloseTag(openingTagIndex, output); } iterator.Next(); } else { // Ignore indent. Spaces between words will be handled by line breaks. // Ignore unrecognized standalone tags. iterator.Next(); } } tagStack.CloseTag(surroundingPTagIndex, output); }
/* Function: TryToGetTopLevelListItem * If the iterator is on a param, typeparam, exception, or permission tag it will convert it to NDMarkup, add it to the * comment in a list block, move the iterator past it, and return true. Otherwise it returns false and nothing is changed. */ protected bool TryToGetTopLevelListItem(ref XMLIterator iterator, XMLComment comment) { if (iterator.IsOnTag("param", TagForm.Opening) == false && iterator.IsOnTag("exception", TagForm.Opening) == false && iterator.IsOnTag("permission", TagForm.Opening) == false && iterator.IsOnTag("typeparam", TagForm.Opening) == false && iterator.IsOnTag("see", TagForm.Opening) == false && iterator.IsOnTag("see", TagForm.Standalone) == false && iterator.IsOnTag("seealso", TagForm.Opening) == false && iterator.IsOnTag("seealso", TagForm.Standalone) == false) { return(false); } string keyword = iterator.TagType; if (keyword == "see") { keyword = "seealso"; } XMLComment.ListBlock block = comment.GetListBlock(keyword); string name = null; string description = null; if (keyword == "param" || keyword == "typeparam") { name = iterator.TagProperty("name"); } else { name = iterator.TagProperty("cref") ?? iterator.TagProperty("langword"); } if (iterator.TagForm == TagForm.Opening) { TagStack tagStack = new TagStack(); tagStack.OpenTag(keyword); iterator.Next(); StringBuilder descriptionBuilder = new StringBuilder(); GetText(ref iterator, descriptionBuilder, tagStack); tagStack.CloseAllTags(descriptionBuilder); description = Normalize(descriptionBuilder.ToString()); if (iterator.IsOnTag(keyword, TagForm.Closing)) { iterator.Next(); } } else { iterator.Next(); } if (name != null) { block.Add(name, description); } return(true); }
private void ParseLinkTags(ref string strText) { if (string.IsNullOrEmpty(strText)) { _ResetLinkTags(0); return; } mUnderlineTagInfos.Clear(); LinkCache linkCache = null; if (m_SupportCache) { linkCache = CacheManager.Instance.GetLinkCache(strText.GetHashCode()); if (linkCache != null) { for (int i = 0; i < linkCache.GetParams().Count; ++i) { LinkCache.CacheLinkTag tag = linkCache.GetParams()[i]; LinkTag selftag = _GetLinkTag(i); selftag.SetStartIndex(tag.StartIndex); selftag.SetEndIndex(tag.EndIndex); mUnderlineTagInfos.Add(selftag); if (tag.paramDic != null) { var iter = tag.paramDic.GetEnumerator(); while (iter.MoveNext()) { selftag.CheckSetValue(iter.Current.Key, iter.Current.Value); } iter.Dispose(); } } strText = linkCache.GetResultString(); _ResetSpriteTags(linkCache.GetParams().Count); return; } } int linkCount = 0; mTextBuilder.Length = 0; XMLIterator.Begin(strText, false); string textBlock = string.Empty; mTagPos.Clear(); while (XMLIterator.NextTag()) { mParses.Clear(); if (XMLIterator.tagName == mLinkTag) { textBlock = XMLIterator.GetText(false); if (!mTagPos.ContainsKey(mLinkTag)) { XMLIterator.tagOffset += XMLIterator.tagOffset; mTagPos[mLinkTag] = (XMLIterator.parsePos - XMLIterator.tagOffset) * 4; LinkTag tag = _GetLinkTag(lineCount); mUnderlineTagInfos.Add(tag); tag.SetStartIndex(mTagPos[mLinkTag]); XMLIterator.GetAttributes(mParses); foreach (var val in mParses) { tag.CheckSetValue(val.Key, val.Value); } } else { LinkTag tag = _GetLinkTag(lineCount); XMLIterator.tagOffset += XMLIterator.tagLength; tag.SetEndIndex((XMLIterator.parsePos - XMLIterator.tagOffset) * 4); mTagPos.Remove(mLinkTag); if (m_SupportCache) { linkCache = CacheManager.Instance.AddLinkCache(strText.GetHashCode()); LinkCache.CacheLinkTag cacheTag = new LinkCache.CacheLinkTag(); cacheTag.StartIndex = tag.GetStartIndex(); cacheTag.EndIndex = tag.GetEndIndex(); if (cacheTag.paramDic == null) { cacheTag.paramDic = new Dictionary <string, string>(); } var iter = tag.GetParams().GetEnumerator(); while (iter.MoveNext()) { cacheTag.paramDic.Add(iter.Current.Key, iter.Current.Value); } linkCache.AddParams(cacheTag); } ++lineCount; } } else { textBlock = XMLIterator.GetText(false); mTextBuilder.Append(textBlock); mTextBuilder.Append(XMLIterator.GetTagSource()); } } textBlock = XMLIterator.GetText(false); mTextBuilder.Append(textBlock); string result = mTextBuilder.ToString(); if (m_SupportCache) { linkCache = CacheManager.Instance.GetLinkCache(strText.GetHashCode()); if (linkCache != null) { linkCache.SetResultString(result); } } strText = result; _ResetLinkTags(linkCount); }
void Parse(string aSource) { XML lastOpenNode = null; sNodeStack.Clear(); XMLIterator.Begin(aSource); while (XMLIterator.NextTag()) { if (XMLIterator.tagType == XMLTagType.Start || XMLIterator.tagType == XMLTagType.Void) { XML childNode; if (lastOpenNode != null) { childNode = new XML(); } else { if (this.name != null) { Cleanup(); throw new Exception("Invalid xml format - no root node."); } childNode = this; } childNode.name = XMLIterator.tagName; childNode._attributes = XMLIterator.GetAttributes(childNode._attributes); if (lastOpenNode != null) { if (XMLIterator.tagType != XMLTagType.Void) { sNodeStack.Push(lastOpenNode); } if (lastOpenNode._children == null) { lastOpenNode._children = new XMLList(); } lastOpenNode._children.Add(childNode); } if (XMLIterator.tagType != XMLTagType.Void) { lastOpenNode = childNode; } } else if (XMLIterator.tagType == XMLTagType.End) { if (lastOpenNode == null || lastOpenNode.name != XMLIterator.tagName) { Cleanup(); throw new Exception("Invalid xml format - <" + XMLIterator.tagName + "> dismatched."); } if (lastOpenNode._children == null || lastOpenNode._children.Count == 0) { lastOpenNode.text = XMLIterator.GetText(); } if (sNodeStack.Count > 0) { lastOpenNode = sNodeStack.Pop(); } else { lastOpenNode = null; } } } }