提交 07c8cac3 编写于 作者: S Sam Harwell

Support lists in Quick Info

上级 93bd62b3
......@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.VisualStudio.Text.Adornments;
using Roslyn.Utilities;
......@@ -10,18 +12,70 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense
internal static class Helpers
{
internal static IEnumerable<object> BuildClassifiedTextElements(ImmutableArray<TaggedText> taggedTexts)
{
var index = 0;
return BuildClassifiedTextElements(taggedTexts, ref index);
}
private static IReadOnlyCollection<object> BuildClassifiedTextElements(ImmutableArray<TaggedText> taggedTexts, ref int index)
{
// This method produces a sequence of zero or more paragraphs
var paragraphs = new List<object>();
// Each paragraph is constructed from one or more lines
var currentParagraph = new List<ClassifiedTextElement>();
var currentParagraph = new List<object>();
// Each line is constructed from one or more inline elements
var currentRuns = new List<ClassifiedTextRun>();
foreach (var part in taggedTexts)
for (; index < taggedTexts.Length; index++)
{
var part = taggedTexts[index];
if (part.Tag == TextTags.ContainerStart)
{
if (currentRuns.Count > 0)
{
// This line break means the end of a line within a paragraph.
currentParagraph.Add(new ClassifiedTextElement(currentRuns));
currentRuns.Clear();
}
index++;
var nestedElements = BuildClassifiedTextElements(taggedTexts, ref index);
if (nestedElements.Count <= 1)
{
currentParagraph.Add(new ContainerElement(
ContainerElementStyle.Wrapped,
new ClassifiedTextElement(new ClassifiedTextRun(ClassificationTypeNames.Text, part.Text)),
new ContainerElement(ContainerElementStyle.Stacked, nestedElements)));
}
else
{
currentParagraph.Add(new ContainerElement(
ContainerElementStyle.Wrapped,
new ClassifiedTextElement(new ClassifiedTextRun(ClassificationTypeNames.Text, part.Text)),
new ContainerElement(
ContainerElementStyle.Stacked,
nestedElements.First(),
new ContainerElement(
ContainerElementStyle.Stacked | ContainerElementStyle.VerticalPadding,
nestedElements.Skip(1)))));
}
continue;
}
else if (part.Tag == TextTags.ContainerEnd)
{
// Return the current result and let the caller continue
break;
}
if (part.Tag == TextTags.ContainerStart
|| part.Tag == TextTags.ContainerEnd)
{
continue;
}
if (part.Tag == TextTags.LineBreak)
{
if (currentRuns.Count > 0)
......@@ -72,7 +126,7 @@ internal static IEnumerable<object> BuildClassifiedTextElements(ImmutableArray<T
return paragraphs;
}
internal static object CreateParagraphFromLines(IReadOnlyList<ClassifiedTextElement> lines)
internal static object CreateParagraphFromLines(IReadOnlyList<object> lines)
{
Contract.ThrowIfFalse(lines.Count > 0);
......
......@@ -66,7 +66,7 @@ public void ListTag()
</item>
</list>";
var expected = @"Here is an example of a bulleted list: Item 1. Item 2.";
var expected = "Here is an example of a bulleted list:\r\n\r\n• Item 1.\r\n• Item 2.";
TestFormat(comment, expected);
}
......
......@@ -39,5 +39,7 @@ public static class TextTags
public const string EnumMember = nameof(EnumMember);
public const string ExtensionMethod = nameof(ExtensionMethod);
public const string Constant = nameof(Constant);
internal const string ContainerStart = nameof(ContainerStart);
internal const string ContainerEnd = nameof(ContainerEnd);
}
}
......@@ -11,6 +11,14 @@ namespace Microsoft.CodeAnalysis.DocumentationComments
{
internal abstract class AbstractDocumentationCommentFormattingService : IDocumentationCommentFormattingService
{
private enum DocumentationCommentListType
{
None,
Bullet,
Number,
Table,
}
private class FormatterState
{
private bool _anyNonWhitespaceSinceLastPara;
......@@ -22,6 +30,7 @@ private class FormatterState
private static TaggedText s_newlinePart = new TaggedText(TextTags.LineBreak, "\r\n");
internal readonly List<TaggedText> Builder = new List<TaggedText>();
private readonly List<(DocumentationCommentListType type, int index, bool renderedItem)> _listStack = new List<(DocumentationCommentListType type, int index, bool renderedItem)>();
internal SemanticModel SemanticModel { get; set; }
internal int Position { get; set; }
......@@ -59,6 +68,45 @@ public void AppendParts(IEnumerable<TaggedText> parts)
_anyNonWhitespaceSinceLastPara = true;
}
public void PushList(DocumentationCommentListType listType)
{
_listStack.Add((listType, 0, false));
MarkBeginOrEndPara();
}
public void NextListItem()
{
if (_listStack.Count == 0)
{
return;
}
var (type, index, renderedItem) = _listStack[_listStack.Count - 1];
if (renderedItem)
{
Builder.Add(new TaggedText(TextTags.ContainerEnd, string.Empty));
}
_listStack[_listStack.Count - 1] = (type, index + 1, false);
MarkLineBreak();
}
public void PopList()
{
if (_listStack.Count == 0)
{
return;
}
if (_listStack[_listStack.Count - 1].renderedItem)
{
Builder.Add(new TaggedText(TextTags.ContainerEnd, string.Empty));
}
_listStack.RemoveAt(_listStack.Count - 1);
MarkBeginOrEndPara();
}
public void MarkBeginOrEndPara()
{
// If this is a <para> with nothing before it, then skip it.
......@@ -120,6 +168,33 @@ private void EmitPendingChars()
_pendingParagraphBreak = false;
_pendingLineBreak = false;
_pendingSingleSpace = false;
for (var i = 0; i < _listStack.Count; i++)
{
if (_listStack[i].renderedItem)
{
continue;
}
switch (_listStack[i].type)
{
case DocumentationCommentListType.Bullet:
Builder.Add(new TaggedText(TextTags.ContainerStart, "• "));
break;
case DocumentationCommentListType.Number:
Builder.Add(new TaggedText(TextTags.ContainerStart, $"{_listStack[i].index}. "));
break;
case DocumentationCommentListType.Table:
case DocumentationCommentListType.None:
default:
Builder.Add(new TaggedText(TextTags.ContainerStart, string.Empty));
break;
}
_listStack[i] = (_listStack[i].type, _listStack[i].index, renderedItem: true);
}
}
}
......@@ -204,6 +279,36 @@ private static void AppendTextFromNode(FormatterState state, XNode node, Compila
return;
}
if (name == DocumentationCommentXmlNames.ListElementName)
{
var rawListType = element.Attribute(DocumentationCommentXmlNames.TypeAttributeName)?.Value;
DocumentationCommentListType listType;
switch (rawListType)
{
case "table":
listType = DocumentationCommentListType.Table;
break;
case "number":
listType = DocumentationCommentListType.Number;
break;
case "bullet":
listType = DocumentationCommentListType.Bullet;
break;
default:
listType = DocumentationCommentListType.None;
break;
}
state.PushList(listType);
}
else if (name == DocumentationCommentXmlNames.ItemElementName)
{
state.NextListItem();
}
if (name == DocumentationCommentXmlNames.ParaElementName
|| name == DocumentationCommentXmlNames.CodeElementName)
{
......@@ -224,6 +329,17 @@ private static void AppendTextFromNode(FormatterState state, XNode node, Compila
{
state.MarkBeginOrEndPara();
}
if (name == DocumentationCommentXmlNames.ListElementName)
{
state.PopList();
}
if (name == DocumentationCommentXmlNames.TermElementName)
{
state.AppendSingleSpace();
state.AppendString("–");
}
}
private static void AppendTextFromAttribute(FormatterState state, XElement element, XAttribute attribute, string attributeNameToParse, SymbolDisplayPartKind kind)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册