From 61b7e0b4112685059f92cd8e88efc2aac6288f17 Mon Sep 17 00:00:00 2001 From: WMJ Date: Tue, 1 Jan 2019 09:27:40 +0800 Subject: [PATCH] ! Redesigned Super Quick Info + Made text in Super Quick Info selectable (right click to copy) - Fixed potential crash in Quick Info introduced in previous version --- Codist/Config.cs | 3 +- Codist/Controls/ThemedText.cs | 101 ++++++++- Codist/Helpers/SymbolFormatter.cs | 56 ++--- Codist/Helpers/ThemeHelper.cs | 5 +- Codist/Helpers/ToolTipFactory.cs | 2 +- Codist/Helpers/WpfHelper.VS.cs | 43 ++-- Codist/Helpers/WpfHelper.cs | 52 ++++- Codist/Helpers/XmlDocRenderer.cs | 115 +++++++++- .../CSharpSuperQuickInfoPage.Designer.cs | 43 ++-- Codist/Options/CSharpSuperQuickInfoPage.cs | 45 ++-- Codist/QuickInfo/CSharpQuickInfo.cs | 201 +++++++++++------- Codist/QuickInfo/QuickInfoOverrider.cs | 40 +++- Codist/SmartBars/CSharpSmartBar.cs | 6 +- Codist/SmartBars/SmartBar.CommonEdit.cs | 17 +- TestProject/Comments.cs | 1 + TestProject/TestPage.cs | 1 + 16 files changed, 540 insertions(+), 191 deletions(-) diff --git a/Codist/Config.cs b/Codist/Config.cs index fc04def0..160977fa 100644 --- a/Codist/Config.cs +++ b/Codist/Config.cs @@ -460,7 +460,8 @@ public enum QuickInfoOptions OverrideDefaultDocumentation = 1 << 17, DocumentationFromBaseType = 1 << 18, DocumentationFromInheritDoc = 1 << 19, - TextOnlyDoc = 1 << 22, + TextOnlyDoc = 1 << 21, + ExceptionDoc = 1 << 22, ReturnsDoc = 1 << 23, RemarksDoc = 1 << 24, AlternativeStyle = 1 << 25, diff --git a/Codist/Controls/ThemedText.cs b/Codist/Controls/ThemedText.cs index d3278e53..54c59cfc 100644 --- a/Codist/Controls/ThemedText.cs +++ b/Codist/Controls/ThemedText.cs @@ -1,4 +1,6 @@ -using System.Windows; +using System; +using System.Reflection; +using System.Windows; using System.Windows.Controls; using Microsoft.VisualStudio.PlatformUI; @@ -6,9 +8,18 @@ namespace Codist.Controls { sealed class ThemedTipText : TextBlock { + static ThemedTipText() { + FocusableProperty.OverrideMetadata(typeof(ThemedTipText), new FrameworkPropertyMetadata(true)); + TextEditorWrapper.RegisterCommandHandlers(typeof(ThemedTipText), true, true, true); + + // remove the focus rectangle around the control + FocusVisualStyleProperty.OverrideMetadata(typeof(ThemedTipText), new FrameworkPropertyMetadata((object)null)); + } + readonly TextEditorWrapper _editor; public ThemedTipText() { TextWrapping = TextWrapping.Wrap; Foreground = ThemeHelper.ToolTipTextBrush; + _editor = TextEditorWrapper.CreateFor(this); } public ThemedTipText(string text) : this() { Inlines.Add(text); @@ -27,6 +38,58 @@ public ThemedTipText UnderlineLastRun() { return this; } } + sealed class ThemedTipDocument : StackPanel + { + public ThemedTipDocument Append(ThemedTipParagraph block) { + Children.Add(block); + return this; + } + public ThemedTipDocument AppendTitle(int imageId, string text) { + Children.Add(new ThemedTipParagraph(imageId, new ThemedTipText(text, true))); + return this; + } + public void ApplySizeLimit() { + var w = Config.Instance.QuickInfoMaxWidth - (WpfHelper.IconRightMargin + ThemeHelper.DefaultIconSize + WpfHelper.SmallMarginSize + WpfHelper.SmallMarginSize + 22/*scrollbar width*/); + if (w <= 0) { + return; + } + foreach (var item in Children) { + var r = item as ThemedTipParagraph; + if (r != null) { + r.Content.MaxWidth = w; + } + } + } + public UIElement Host() { + return this; + } + } + sealed class ThemedTipParagraph : StackPanel + { + const int PlaceHolderSize = WpfHelper.IconRightMargin + ThemeHelper.DefaultIconSize; + + ThemedTipParagraph() { + Margin = WpfHelper.TinyMargin; + Orientation = Orientation.Horizontal; + } + public ThemedTipParagraph(int iconId, TextBlock content) : this() { + if (iconId == 0) { + Children.Add(new Border { Height = WpfHelper.IconRightMargin, Width = PlaceHolderSize }); + } + else { + var icon = ThemeHelper.GetImage(iconId).WrapMargin(WpfHelper.GlyphMargin); + icon.VerticalAlignment = VerticalAlignment.Top; + Children.Add(icon); + } + Children.Add(Content = content ?? new ThemedTipText()); + } + public ThemedTipParagraph(int iconId) : this(iconId, null) { + } + public ThemedTipParagraph(TextBlock content) : this(0, content) { + } + public Image Icon => Children[0] as Image; + public TextBlock Content { get; private set; } + } sealed class ThemedToolBarText : TextBlock { public ThemedToolBarText() { @@ -48,4 +111,40 @@ public ThemedMenuText(string text, bool bold) : this() { this.Append(text, bold); } } + + // https://stackoverflow.com/questions/136435/any-way-to-make-a-wpf-textblock-selectable + sealed class TextEditorWrapper + { + private static readonly Type TextEditorType = Type.GetType("System.Windows.Documents.TextEditor, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); + private static readonly PropertyInfo IsReadOnlyProp = TextEditorType.GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly PropertyInfo TextViewProp = TextEditorType.GetProperty("TextView", BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly MethodInfo RegisterMethod = TextEditorType.GetMethod("RegisterCommandHandlers", + BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(Type), typeof(bool), typeof(bool), typeof(bool) }, null); + + private static readonly Type TextContainerType = Type.GetType("System.Windows.Documents.ITextContainer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); + private static readonly PropertyInfo TextContainerTextViewProp = TextContainerType.GetProperty("TextView"); + + private static readonly PropertyInfo TextContainerProp = typeof(TextBlock).GetProperty("TextContainer", BindingFlags.Instance | BindingFlags.NonPublic); + + public static void RegisterCommandHandlers(Type controlType, bool acceptsRichContent, bool readOnly, bool registerEventListeners) { + RegisterMethod.Invoke(null, new object[] { controlType, acceptsRichContent, readOnly, registerEventListeners }); + } + + public static TextEditorWrapper CreateFor(TextBlock tb) { + var textContainer = TextContainerProp.GetValue(tb); + + var editor = new TextEditorWrapper(textContainer, tb, false); + IsReadOnlyProp.SetValue(editor._editor, true); + TextViewProp.SetValue(editor._editor, TextContainerTextViewProp.GetValue(textContainer)); + + return editor; + } + + private readonly object _editor; + + public TextEditorWrapper(object textContainer, FrameworkElement uiScope, bool isUndoEnabled) { + _editor = Activator.CreateInstance(TextEditorType, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance, + null, new[] { textContainer, uiScope, isUndoEnabled }, null); + } + } } diff --git a/Codist/Helpers/SymbolFormatter.cs b/Codist/Helpers/SymbolFormatter.cs index de47cabd..0ff3dfa6 100644 --- a/Codist/Helpers/SymbolFormatter.cs +++ b/Codist/Helpers/SymbolFormatter.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using System.Linq; using System.Windows.Controls; +using System.Windows.Documents; using System.Windows.Media; using AppHelpers; using Microsoft.CodeAnalysis; @@ -257,17 +258,17 @@ internal TextBlock ToUIText(TextBlock block, ImmutableArray p return block; } - internal void ToUIText(TextBlock block, TypedConstant constant) { + internal void ToUIText(InlineCollection block, TypedConstant constant) { switch (constant.Kind) { case TypedConstantKind.Primitive: if (constant.Value is bool) { - block.Append((bool)constant.Value ? "true" : "false", Keyword); + block.Add(WpfHelper.Render((bool)constant.Value ? "true" : "false", Keyword)); } else if (constant.Value is string) { - block.Append(constant.ToCSharpString(), Text); + block.Add(WpfHelper.Render(constant.ToCSharpString(), Text)); } else { - block.Append(constant.ToCSharpString(), Number); + block.Add(WpfHelper.Render(constant.ToCSharpString(), Number)); } break; case TypedConstantKind.Enum: @@ -283,75 +284,76 @@ internal void ToUIText(TextBlock block, TypedConstant constant) { var flags = items.ToArray(); for (int i = 0; i < flags.Length; i++) { if (i > 0) { - block.Append(" | "); + block.Add(" | "); } - block.Append(constant.Type.Name + "." + flags[i].Name, Enum); + block.Add(WpfHelper.Render(constant.Type.Name + "." + flags[i].Name, Enum)); } } else { - block.Append(constant.Type.Name + en.Substring(en.LastIndexOf('.')), Enum); + block.Add(WpfHelper.Render(constant.Type.Name + en.Substring(en.LastIndexOf('.')), Enum)); } break; case TypedConstantKind.Type: - block.Append("typeof", Keyword).Append("(") - .AddSymbol(constant.Value as ITypeSymbol, null, this) - .Append(")"); + block.Add(WpfHelper.Render("typeof", Keyword)); + block.Add("("); + ToUIText(block, constant.Value as ITypeSymbol, null); + block.Add(")"); break; case TypedConstantKind.Array: - block.Append("{"); + block.Add("{"); bool c = false; foreach (var item in constant.Values) { if (c == false) { c = true; } else { - block.Append(", "); + block.Add(", "); } ToUIText(block, item); } - block.Append("}"); + block.Add("}"); break; default: - block.Append(constant.ToCSharpString()); + block.Add(constant.ToCSharpString()); break; } } - internal TextBlock ToUIText(TextBlock block, AttributeData item) { + internal void ToUIText(InlineCollection block, AttributeData item) { var a = item.AttributeClass.Name; - block.Append("[") - .AddSymbol(item.AttributeConstructor ?? (ISymbol)item.AttributeClass, a.EndsWith("Attribute", StringComparison.Ordinal) ? a.Substring(0, a.Length - 9) : a, Class); + block.Add("["); + block.Add(WpfHelper.Render(item.AttributeConstructor ?? (ISymbol)item.AttributeClass, a.EndsWith("Attribute", StringComparison.Ordinal) ? a.Substring(0, a.Length - 9) : a, Class)); if (item.ConstructorArguments.Length == 0 && item.NamedArguments.Length == 0) { var node = item.ApplicationSyntaxReference?.GetSyntax() as Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax; if (node != null && node.ArgumentList?.Arguments.Count > 0) { - block.Append(node.ArgumentList.ToString(), ThemeHelper.SystemGrayTextBrush); + block.Add(WpfHelper.Render(node.ArgumentList.ToString(), ThemeHelper.SystemGrayTextBrush)); } - block.Append("]"); - return block; + block.Add("]"); + return; } - block.Append("("); + block.Add("("); int i = 0; foreach (var arg in item.ConstructorArguments) { if (++i > 1) { - block.Append(", "); + block.Add(", "); } ToUIText(block, arg); } foreach (var arg in item.NamedArguments) { if (++i > 1) { - block.Append(", "); + block.Add(", "); } var attrMember = item.AttributeClass.GetMembers(arg.Key).FirstOrDefault(m => m.Kind == SymbolKind.Field || m.Kind == SymbolKind.Property); if (attrMember != null) { - block.Append(arg.Key, attrMember.Kind == SymbolKind.Property ? Property : Field); + block.Add(WpfHelper.Render(arg.Key, attrMember.Kind == SymbolKind.Property ? Property : Field)); } else { - block.Append(arg.Key, false, true, null); + block.Add(WpfHelper.Render(arg.Key, false, true, null)); } - block.Append("="); + block.Add("="); ToUIText(block, arg.Value); } - return block.Append(")]"); + block.Add(")]"); } internal void UpdateSyntaxHighlights(IEditorFormatMap formatMap) { diff --git a/Codist/Helpers/ThemeHelper.cs b/Codist/Helpers/ThemeHelper.cs index 531984e1..eb6d1d8a 100644 --- a/Codist/Helpers/ThemeHelper.cs +++ b/Codist/Helpers/ThemeHelper.cs @@ -16,6 +16,8 @@ namespace Codist { static class ThemeHelper { + internal const int DefaultIconSize = 16; + static readonly Microsoft.VisualStudio.Text.Classification.IClassificationFormatMap __ToolTipFormatMap = ServicesHelper.Instance.ClassificationFormatMap.GetClassificationFormatMap("tooltip"); static ThemeHelper() { RefreshThemeCache(); @@ -88,13 +90,12 @@ public static void GetFontSettings(string categoryGuid, out string fontName, out /// /// The image id. public static CrispImage GetImage(int imageId, int size = 0) { - const int DEFAULT_SIZE = 16; var moniker = new ImageMoniker { Guid = KnownImageIds.ImageCatalogGuid, Id = imageId }; if (size < 1) { - size = DEFAULT_SIZE; + size = DefaultIconSize; } var image = new CrispImage { Moniker = moniker, diff --git a/Codist/Helpers/ToolTipFactory.cs b/Codist/Helpers/ToolTipFactory.cs index a978589a..7e6546c8 100644 --- a/Codist/Helpers/ToolTipFactory.cs +++ b/Codist/Helpers/ToolTipFactory.cs @@ -40,7 +40,7 @@ public static Controls.ThemedToolTip CreateToolTip(ISymbol symbol, Compilation c content.Append("\nconst: " + f.ConstantValue?.ToString()); // sometimes the const value could be null } foreach (var attr in symbol.GetAttributes()) { - SymbolFormatter.Empty.ToUIText(content.AppendLine(), attr); + SymbolFormatter.Empty.ToUIText(content.AppendLine().Inlines, attr); } var doc = new XmlDoc(symbol, compilation); var summary = doc.Summary ?? (Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.DocumentationFromInheritDoc) ? doc.ExplicitInheritDoc?.Summary : null); diff --git a/Codist/Helpers/WpfHelper.VS.cs b/Codist/Helpers/WpfHelper.VS.cs index b2e382a4..3713fb02 100644 --- a/Codist/Helpers/WpfHelper.VS.cs +++ b/Codist/Helpers/WpfHelper.VS.cs @@ -5,6 +5,7 @@ using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; +using System.Xml.Linq; using AppHelpers; using Codist.Controls; using Microsoft.CodeAnalysis; @@ -47,6 +48,12 @@ public static TextBlock AddSymbol(this TextBlock block, ISymbol symbol, string a } return block; } + public static Paragraph AddSymbol(this Paragraph paragraph, ISymbol symbol, string alias, SymbolFormatter formatter) { + if (symbol != null) { + formatter.ToUIText(paragraph.Inlines, symbol, alias); + } + return paragraph; + } public static TextBlock AddSymbol(this TextBlock block, ISymbol symbol, string alias, WpfBrush brush) { if (symbol != null) { block.Inlines.Add(symbol.Render(alias, false, brush)); @@ -65,6 +72,10 @@ public static TextBlock AddSymbolDisplayParts(this TextBlock block, ImmutableArr public static TextBlock AddSymbolDisplayParts(this TextBlock block, ImmutableArray parts, SymbolFormatter formatter, int argIndex) { return formatter.ToUIText(block, parts, argIndex); } + public static TextBlock AddXmlDoc(this TextBlock paragraph, XElement content, XmlDocRenderer docRenderer) { + docRenderer.Render(content, paragraph.Inlines); + return paragraph; + } public static ContextMenu CreateContextMenuForSourceLocations(string symbolName, ImmutableArray refs) { var menu = new ContextMenu { Resources = SharedDictionaryManager.ContextMenu @@ -101,10 +112,10 @@ public static WpfColor GetColor(this IEditorFormatMap map, string formatName, st ? color : EmptyColor; } - public static Run Render(this ISymbol symbol, string alias, WpfBrush brush) { + public static Inline Render(this ISymbol symbol, string alias, WpfBrush brush) { return symbol.Render(alias, brush == null, brush); } - public static Run Render(this ISymbol symbol, string alias, bool bold, WpfBrush brush) { + public static Inline Render(this ISymbol symbol, string alias, bool bold, WpfBrush brush) { var run = new SymbolLink(symbol, alias, Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.ClickAndGo)); if (bold || brush == null) { run.FontWeight = FontWeights.Bold; @@ -136,11 +147,13 @@ public static void SetUITextRenderOptions(DependencyObject element, bool optimiz sealed class SymbolLink : Run { readonly ISymbol _Symbol; - ImmutableArray _References; + public SymbolLink(ISymbol symbol, string alias, bool clickAndGo) { Text = alias ?? symbol.Name; _Symbol = symbol; - if (clickAndGo) { + TextDecorations = null; + + if (clickAndGo && symbol.ContainingAssembly.GetSourceType() != AssemblySource.Metadata) { MouseEnter += InitInteraction; } ToolTipOpening += ShowSymbolToolTip; @@ -150,30 +163,28 @@ public SymbolLink(ISymbol symbol, string alias, bool clickAndGo) { void InitInteraction(object sender, MouseEventArgs e) { MouseEnter -= InitInteraction; - _References = _Symbol.GetSourceLocations(); - if (_References.Length > 0) { - Cursor = Cursors.Hand; - Highlight(sender, e); - MouseEnter += Highlight; - MouseLeave += Leave; - MouseLeftButtonUp += GotoSymbol; - } + Highlight(sender, e); + MouseEnter += Highlight; + MouseLeave += Leave; + MouseLeftButtonDown += GotoSymbol; } void Highlight(object sender, MouseEventArgs e) { Background = SystemColors.HighlightBrush.Alpha(0.3); + Cursor = Cursors.Hand; } void Leave(object sender, MouseEventArgs e) { Background = WpfBrushes.Transparent; } - void GotoSymbol(object sender, MouseButtonEventArgs e) { - if (_References.Length == 1) { - _References[0].GoToSource(); + void GotoSymbol(object sender, RoutedEventArgs e) { + var r = _Symbol.GetSourceLocations(); + if (r.Length == 1) { + r[0].GoToSource(); } else { if (ContextMenu == null) { - ContextMenu = CreateContextMenuForSourceLocations(_Symbol.MetadataName, _References); + ContextMenu = CreateContextMenuForSourceLocations(_Symbol.MetadataName, r); } ContextMenu.IsOpen = true; } diff --git a/Codist/Helpers/WpfHelper.cs b/Codist/Helpers/WpfHelper.cs index c7555ebe..b5a9ad47 100644 --- a/Codist/Helpers/WpfHelper.cs +++ b/Codist/Helpers/WpfHelper.cs @@ -14,15 +14,17 @@ namespace Codist { static partial class WpfHelper { + internal const int IconRightMargin = 5; + internal const int SmallMarginSize = 3; internal static readonly WpfColor EmptyColor = new WpfColor(); internal static readonly Thickness NoMargin = new Thickness(0); internal static readonly Thickness TinyMargin = new Thickness(1); - internal static readonly Thickness SmallMargin = new Thickness(3); + internal static readonly Thickness SmallMargin = new Thickness(SmallMarginSize); internal static readonly Thickness MiddleMargin = new Thickness(6); - internal static readonly Thickness GlyphMargin = new Thickness(0, 0, 5, 0); + internal static readonly Thickness GlyphMargin = new Thickness(0, 0, IconRightMargin, 0); internal static readonly Thickness ScrollerMargin = new Thickness(0, 0, 3, 0); internal static readonly Thickness TopItemMargin = new Thickness(0, 3, 0, 0); - internal static readonly Thickness SmallHorizontalMargin = new Thickness(3, 0, 3, 0); + internal static readonly Thickness SmallHorizontalMargin = new Thickness(SmallMarginSize, 0, SmallMarginSize, 0); internal static readonly Thickness MenuItemMargin = new Thickness(6, 0, 6, 0); #region TextBlock and Run @@ -64,6 +66,26 @@ public static TTextBlock Append(this TTextBlock block, UIElement ele block.Inlines.Add(new InlineUIContainer(element) { BaselineAlignment = BaselineAlignment.TextTop }); return block; } + public static Paragraph Append(this Paragraph block, string text) { + block.Inlines.Add(text); + return block; + } + public static Paragraph Append(this Paragraph block, string text, bool bold) { + block.Inlines.Add(Render(text, bold, false, null)); + return block; + } + public static Paragraph Append(this Paragraph block, string text, bool bold, WpfBrush brush) { + block.Inlines.Add(Render(text, bold, false, brush)); + return block; + } + public static Paragraph Append(this Paragraph block, Inline inline) { + block.Inlines.Add(inline); + return block; + } + public static Paragraph Append(this Paragraph block, UIElement element) { + block.Inlines.Add(new InlineUIContainer(element) { BaselineAlignment = BaselineAlignment.TextTop }); + return block; + } public static TTextBlock Append(this TTextBlock block, string text) where TTextBlock : TextBlock { block.Inlines.Add(new Run(text)); @@ -223,11 +245,25 @@ public static TChild GetFirstVisualChild(this DependencyObject obj, Pred if (r != null && (predicate == null || predicate(r))) { return r; } - else { - r = GetFirstVisualChild(c, predicate); - if (r != null) { - return r; - } + r = GetFirstVisualChild(c, predicate); + if (r != null) { + return r; + } + } + return null; + } + public static TChild GetLastVisualChild(this DependencyObject obj, Predicate predicate = null) + where TChild : DependencyObject { + var count = VisualTreeHelper.GetChildrenCount(obj); + for (int i = count - 1; i >= 0; i--) { + var c = VisualTreeHelper.GetChild(obj, i); + var r = c as TChild; + if (r != null && (predicate == null || predicate(r))) { + return r; + } + r = GetLastVisualChild(c, predicate); + if (r != null) { + return r; } } return null; diff --git a/Codist/Helpers/XmlDocRenderer.cs b/Codist/Helpers/XmlDocRenderer.cs index 7240bae3..aea5a4fa 100644 --- a/Codist/Helpers/XmlDocRenderer.cs +++ b/Codist/Helpers/XmlDocRenderer.cs @@ -29,7 +29,17 @@ public void Render(XElement content, TextBlock text) { } Render(content, text.Inlines); } - + public void Render(XElement content, Controls.ThemedTipDocument doc) { + if (content == null || content.HasElements == false && content.IsEmpty) { + return; + } + var paragraph = new Controls.ThemedTipParagraph(Microsoft.VisualStudio.Imaging.KnownImageIds.Comment); + doc.Append(paragraph); + Render(content, doc, paragraph); + if (paragraph.Content.Inlines.FirstInline == null) { + doc.Children.Remove(paragraph); + } + } public void Render(XContainer content, InlineCollection text) { const int LIST_UNDEFINED = -1, LIST_BULLET = -2, LIST_NOT_NUMERIC = -3; var listNum = LIST_UNDEFINED; @@ -133,6 +143,109 @@ public void Render(XContainer content, InlineCollection text) { } } } + public void Render(XContainer content, Controls.ThemedTipDocument doc, Controls.ThemedTipParagraph paragraph) { + const int LIST_UNDEFINED = -1, LIST_BULLET = -2, LIST_NOT_NUMERIC = -3; + var listNum = LIST_UNDEFINED; + foreach (var item in content.Nodes()) { + switch (item.NodeType) { + case XmlNodeType.Element: + var e = item as XElement; + var name = e.Name.LocalName; + switch (name) { + case "para": + case "listheader": + case "item": + case "code": + if (paragraph.Content.Inlines.FirstInline != null) { + doc.Append(paragraph = new Controls.ThemedTipParagraph(new Controls.ThemedTipText())); + } + if (name == "item") { + if (listNum == LIST_UNDEFINED) { + switch ((e.Parent as XElement).Attribute("type")?.Value) { + case "number": listNum = 0; break; + case "bullet": listNum = LIST_BULLET; break; + default: listNum = LIST_NOT_NUMERIC; break; + } + } + if (listNum >= 0) { + paragraph.Content.Append(new Run((++listNum).ToString() + ". ") { Foreground = ThemeHelper.SystemGrayTextBrush, FontWeight = System.Windows.FontWeights.Bold }); + } + else if (listNum == LIST_BULLET) { + paragraph.Content.Append(new Run(" \u00B7 ") { Foreground = ThemeHelper.SystemGrayTextBrush, FontWeight = System.Windows.FontWeights.Bold }); + } + } + Render(e, doc, paragraph); + if (paragraph.Content.Inlines.FirstInline == null) { + paragraph.Content.Inlines.Add(new LineBreak()); + } + break; + case "see": + var see = e.Attribute("cref"); + if (see != null) { + RenderXmlDocSymbol(see.Value, paragraph.Content.Inlines, SymbolKind.Alias); + } + else if ((see = e.Attribute("langword")) != null) { + RenderXmlDocSymbol(see.Value, paragraph.Content.Inlines, SymbolKind.DynamicType); + } + break; + case "paramref": + var paramName = e.Attribute("name"); + if (paramName != null) { + RenderXmlDocSymbol(paramName.Value, paragraph.Content.Inlines, SymbolKind.Parameter); + } + break; + case "typeparamref": + var typeParamName = e.Attribute("name"); + if (typeParamName != null) { + RenderXmlDocSymbol(typeParamName.Value, paragraph.Content.Inlines, SymbolKind.TypeParameter); + } + break; + case "c": + StyleInner(e, paragraph.Content.Inlines, new Bold() { Background = ThemeHelper.ToolWindowBackgroundBrush, Foreground = ThemeHelper.ToolWindowTextBrush }); + break; + case "b": + StyleInner(e, paragraph.Content.Inlines, new Bold()); + break; + case "i": + StyleInner(e, paragraph.Content.Inlines, new Italic()); + break; + case "u": + StyleInner(e, paragraph.Content.Inlines, new Underline()); + break; + //case "list": + //case "description": + default: + Render(e, doc, paragraph); + break; + } + break; + case XmlNodeType.Text: + string t = (item as XText).Value; + var parentName = item.Parent.Name.LocalName; + if (parentName != "code") { + var previous = (item.PreviousNode as XElement)?.Name?.LocalName; + if (previous == null || IsInlineElementName(previous) == false) { + t = item.NextNode == null ? t.Trim() : t.TrimStart(); + } + else if (item.NextNode == null) { + t = t.TrimEnd(); + } + t = _FixWhitespaces.Replace(t.Replace('\n', ' '), " "); + } + if (t.Length > 0) { + paragraph.Content.Append(new Run(t)); + } + break; + case XmlNodeType.CDATA: + paragraph.Content.Append(new Run((item as XText).Value)); + break; + case XmlNodeType.EntityReference: + case XmlNodeType.Entity: + paragraph.Content.Append(new Run(item.ToString())); + break; + } + } + } internal void RenderXmlDocSymbol(string symbol, InlineCollection inlines, SymbolKind symbolKind) { switch (symbolKind) { diff --git a/Codist/Options/CSharpSuperQuickInfoPage.Designer.cs b/Codist/Options/CSharpSuperQuickInfoPage.Designer.cs index ec565f32..c6632f85 100644 --- a/Codist/Options/CSharpSuperQuickInfoPage.Designer.cs +++ b/Codist/Options/CSharpSuperQuickInfoPage.Designer.cs @@ -39,10 +39,12 @@ private void InitializeComponent() { this._CSharpAttributesQuickInfoBox = new System.Windows.Forms.CheckBox(); this._OptionTabs = new System.Windows.Forms.TabControl(); this._DefaultPage = new System.Windows.Forms.TabPage(); + this._CSharpExceptionDocBox = new System.Windows.Forms.CheckBox(); this._AlternativeStyleBox = new System.Windows.Forms.CheckBox(); this._CSharpRemarksDocBox = new System.Windows.Forms.CheckBox(); this._CSharpReturnsDocBox = new System.Windows.Forms.CheckBox(); this._CSharpTextOnlyDocBox = new System.Windows.Forms.CheckBox(); + this._CSharpInheritDocCrefBox = new System.Windows.Forms.CheckBox(); this._CSharpDocumentationBaseTypeBox = new System.Windows.Forms.CheckBox(); this.groupBox4 = new System.Windows.Forms.GroupBox(); this._QuickInfoMaxHeightBox = new System.Windows.Forms.NumericUpDown(); @@ -52,7 +54,6 @@ private void InitializeComponent() { this._CSharpOverrideDefaultXmlDocBox = new System.Windows.Forms.CheckBox(); this._ClickAndGoBox = new System.Windows.Forms.CheckBox(); this._AdditionalItemsPage = new System.Windows.Forms.TabPage(); - this._CSharpInheritDocCrefBox = new System.Windows.Forms.CheckBox(); this._OptionTabs.SuspendLayout(); this._DefaultPage.SuspendLayout(); this.groupBox4.SuspendLayout(); @@ -196,6 +197,7 @@ private void InitializeComponent() { // // _DefaultPage // + this._DefaultPage.Controls.Add(this._CSharpExceptionDocBox); this._DefaultPage.Controls.Add(this._AlternativeStyleBox); this._DefaultPage.Controls.Add(this._CSharpRemarksDocBox); this._DefaultPage.Controls.Add(this._CSharpReturnsDocBox); @@ -213,20 +215,30 @@ private void InitializeComponent() { this._DefaultPage.Text = "Default"; this._DefaultPage.UseVisualStyleBackColor = true; // + // _CSharpExceptionDocBox + // + this._CSharpExceptionDocBox.AutoSize = true; + this._CSharpExceptionDocBox.Location = new System.Drawing.Point(32, 156); + this._CSharpExceptionDocBox.Name = "_CSharpExceptionDocBox"; + this._CSharpExceptionDocBox.Size = new System.Drawing.Size(253, 19); + this._CSharpExceptionDocBox.TabIndex = 7; + this._CSharpExceptionDocBox.Text = "Override XML Doc"; + this._CSharpExceptionDocBox.UseVisualStyleBackColor = true; + // // _AlternativeStyleBox // this._AlternativeStyleBox.AutoSize = true; this._AlternativeStyleBox.Location = new System.Drawing.Point(15, 181); this._AlternativeStyleBox.Name = "_AlternativeStyleBox"; this._AlternativeStyleBox.Size = new System.Drawing.Size(197, 19); - this._AlternativeStyleBox.TabIndex = 7; + this._AlternativeStyleBox.TabIndex = 8; this._AlternativeStyleBox.Text = "Use alternative style"; this._AlternativeStyleBox.UseVisualStyleBackColor = true; // // _CSharpRemarksDocBox // this._CSharpRemarksDocBox.AutoSize = true; - this._CSharpRemarksDocBox.Location = new System.Drawing.Point(32, 156); + this._CSharpRemarksDocBox.Location = new System.Drawing.Point(268, 131); this._CSharpRemarksDocBox.Name = "_CSharpRemarksDocBox"; this._CSharpRemarksDocBox.Size = new System.Drawing.Size(205, 19); this._CSharpRemarksDocBox.TabIndex = 6; @@ -253,6 +265,16 @@ private void InitializeComponent() { this._CSharpTextOnlyDocBox.Text = "Allow text only (no ) XML Doc"; this._CSharpTextOnlyDocBox.UseVisualStyleBackColor = true; // + // _CSharpInheritDocCrefBox + // + this._CSharpInheritDocCrefBox.AutoSize = true; + this._CSharpInheritDocCrefBox.Location = new System.Drawing.Point(32, 81); + this._CSharpInheritDocCrefBox.Name = "_CSharpInheritDocCrefBox"; + this._CSharpInheritDocCrefBox.Size = new System.Drawing.Size(357, 19); + this._CSharpInheritDocCrefBox.TabIndex = 3; + this._CSharpInheritDocCrefBox.Text = "Inherit from target"; + this._CSharpInheritDocCrefBox.UseVisualStyleBackColor = true; + // // _CSharpDocumentationBaseTypeBox // this._CSharpDocumentationBaseTypeBox.AutoSize = true; @@ -274,7 +296,7 @@ private void InitializeComponent() { this.groupBox4.Location = new System.Drawing.Point(6, 211); this.groupBox4.Name = "groupBox4"; this.groupBox4.Size = new System.Drawing.Size(506, 59); - this.groupBox4.TabIndex = 8; + this.groupBox4.TabIndex = 9; this.groupBox4.TabStop = false; this.groupBox4.Text = "Quick Info Item Size (0: Unlimited)"; // @@ -316,7 +338,7 @@ private void InitializeComponent() { // _QuickInfoMaxWidthBox // this._QuickInfoMaxWidthBox.Increment = new decimal(new int[] { - 200, + 100, 0, 0, 0}); @@ -372,16 +394,6 @@ private void InitializeComponent() { this._AdditionalItemsPage.Text = "Additional Items"; this._AdditionalItemsPage.UseVisualStyleBackColor = true; // - // _CSharpInheritDocCrefBox - // - this._CSharpInheritDocCrefBox.AutoSize = true; - this._CSharpInheritDocCrefBox.Location = new System.Drawing.Point(32, 81); - this._CSharpInheritDocCrefBox.Name = "_CSharpInheritDocCrefBox"; - this._CSharpInheritDocCrefBox.Size = new System.Drawing.Size(357, 19); - this._CSharpInheritDocCrefBox.TabIndex = 3; - this._CSharpInheritDocCrefBox.Text = "Inherit from target"; - this._CSharpInheritDocCrefBox.UseVisualStyleBackColor = true; - // // CSharpSuperQuickInfoPage // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); @@ -432,5 +444,6 @@ private void InitializeComponent() { private System.Windows.Forms.CheckBox _CSharpRemarksDocBox; private System.Windows.Forms.CheckBox _AlternativeStyleBox; private System.Windows.Forms.CheckBox _CSharpInheritDocCrefBox; + private System.Windows.Forms.CheckBox _CSharpExceptionDocBox; } } diff --git a/Codist/Options/CSharpSuperQuickInfoPage.cs b/Codist/Options/CSharpSuperQuickInfoPage.cs index 1f3ff030..9dfe6502 100644 --- a/Codist/Options/CSharpSuperQuickInfoPage.cs +++ b/Codist/Options/CSharpSuperQuickInfoPage.cs @@ -36,6 +36,7 @@ void CSharpSuperQuickInfoPage_Load(object sender, EventArgs e) { = _CSharpTextOnlyDocBox.Enabled = _CSharpReturnsDocBox.Enabled = _CSharpRemarksDocBox.Enabled + = _CSharpExceptionDocBox.Enabled = _CSharpOverrideDefaultXmlDocBox.Checked)); _CSharpDeclarationQuickInfoBox.CheckedChanged += _UI.HandleEvent(() => Config.Instance.Set(QuickInfoOptions.Declaration, _CSharpDeclarationQuickInfoBox.Checked)); _CSharpSymbolLocationQuickInfoBox.CheckedChanged += _UI.HandleEvent(() => Config.Instance.Set(QuickInfoOptions.SymbolLocation, _CSharpSymbolLocationQuickInfoBox.Checked)); @@ -50,6 +51,7 @@ void CSharpSuperQuickInfoPage_Load(object sender, EventArgs e) { _CSharpInheritDocCrefBox.CheckedChanged += _UI.HandleEvent(() => Config.Instance.Set(QuickInfoOptions.DocumentationFromInheritDoc, _CSharpInheritDocCrefBox.Checked)); _CSharpReturnsDocBox.CheckedChanged += _UI.HandleEvent(() => Config.Instance.Set(QuickInfoOptions.ReturnsDoc, _CSharpReturnsDocBox.Checked)); _CSharpRemarksDocBox.CheckedChanged += _UI.HandleEvent(() => Config.Instance.Set(QuickInfoOptions.RemarksDoc, _CSharpRemarksDocBox.Checked)); + _CSharpExceptionDocBox.CheckedChanged += _UI.HandleEvent(() => Config.Instance.Set(QuickInfoOptions.ExceptionDoc, _CSharpExceptionDocBox.Checked)); _CSharpTextOnlyDocBox.CheckedChanged += _UI.HandleEvent(() => Config.Instance.Set(QuickInfoOptions.TextOnlyDoc, _CSharpTextOnlyDocBox.Checked)); _QuickInfoMaxWidthBox.ValueChanged += _UI.HandleEvent(() => Config.Instance.QuickInfoMaxWidth = (double)_QuickInfoMaxWidthBox.Value); _QuickInfoMaxHeightBox.ValueChanged += _UI.HandleEvent(() => Config.Instance.QuickInfoMaxHeight = (double)_QuickInfoMaxHeightBox.Value); @@ -60,36 +62,39 @@ void CSharpSuperQuickInfoPage_Load(object sender, EventArgs e) { void LoadConfig(Config config) { _UI.DoWithLock(() => { - _AlternativeStyleBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.AlternativeStyle); - _ClickAndGoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.ClickAndGo); - _CSharpAttributesQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.Attributes); + var o = config.QuickInfoOptions; + _AlternativeStyleBox.Checked = o.MatchFlags(QuickInfoOptions.AlternativeStyle); + _ClickAndGoBox.Checked = o.MatchFlags(QuickInfoOptions.ClickAndGo); + _CSharpAttributesQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.Attributes); _CSharpBaseTypeQuickInfoBox.Checked = _CSharpBaseTypeInheritenceQuickInfoBox.Enabled - = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.BaseType); - _CSharpBaseTypeInheritenceQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.BaseTypeInheritence); - _CSharpDeclarationQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.Declaration); + = o.MatchFlags(QuickInfoOptions.BaseType); + _CSharpBaseTypeInheritenceQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.BaseTypeInheritence); + _CSharpDeclarationQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.Declaration); _CSharpInterfacesQuickInfoBox.Checked = _CSharpInterfaceInheritenceQuickInfoBox.Enabled - = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.Interfaces); - _CSharpInterfaceInheritenceQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.InterfacesInheritence); - _CSharpInterfaceImplementationsQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.InterfaceImplementations); - _CSharpSymbolLocationQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.SymbolLocation); - _CSharpReturnsDocBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.ReturnsDoc); - _CSharpRemarksDocBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.RemarksDoc); + = o.MatchFlags(QuickInfoOptions.Interfaces); + _CSharpInterfaceInheritenceQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.InterfacesInheritence); + _CSharpInterfaceImplementationsQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.InterfaceImplementations); + _CSharpSymbolLocationQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.SymbolLocation); + _CSharpReturnsDocBox.Checked = o.MatchFlags(QuickInfoOptions.ReturnsDoc); + _CSharpRemarksDocBox.Checked = o.MatchFlags(QuickInfoOptions.RemarksDoc); + _CSharpExceptionDocBox.Checked = o.MatchFlags(QuickInfoOptions.ExceptionDoc); _CSharpOverrideDefaultXmlDocBox.Checked = _CSharpDocumentationBaseTypeBox.Enabled = _CSharpInheritDocCrefBox.Enabled = _CSharpTextOnlyDocBox.Enabled = _CSharpReturnsDocBox.Enabled = _CSharpRemarksDocBox.Enabled - = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.OverrideDefaultDocumentation); - _CSharpNumberQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.NumericValues); - _CSharpStringQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.String); - _CSharpParameterQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.Parameter); - _CSharpTypeParameterQuickInfoBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.TypeParameters); - _CSharpDocumentationBaseTypeBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.DocumentationFromBaseType); - _CSharpInheritDocCrefBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.DocumentationFromInheritDoc); - _CSharpTextOnlyDocBox.Checked = config.QuickInfoOptions.MatchFlags(QuickInfoOptions.TextOnlyDoc); + = _CSharpExceptionDocBox.Enabled + = o.MatchFlags(QuickInfoOptions.OverrideDefaultDocumentation); + _CSharpNumberQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.NumericValues); + _CSharpStringQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.String); + _CSharpParameterQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.Parameter); + _CSharpTypeParameterQuickInfoBox.Checked = o.MatchFlags(QuickInfoOptions.TypeParameters); + _CSharpDocumentationBaseTypeBox.Checked = o.MatchFlags(QuickInfoOptions.DocumentationFromBaseType); + _CSharpInheritDocCrefBox.Checked = o.MatchFlags(QuickInfoOptions.DocumentationFromInheritDoc); + _CSharpTextOnlyDocBox.Checked = o.MatchFlags(QuickInfoOptions.TextOnlyDoc); _QuickInfoMaxWidthBox.Value = (decimal)(config.QuickInfoMaxWidth >= 0 && config.QuickInfoMaxWidth < (double)_QuickInfoMaxWidthBox.Maximum ? config.QuickInfoMaxWidth : 0); _QuickInfoMaxHeightBox.Value = (decimal)(config.QuickInfoMaxHeight >= 0 && config.QuickInfoMaxHeight < (double)_QuickInfoMaxHeightBox.Maximum ? config.QuickInfoMaxHeight : 0); }); diff --git a/Codist/QuickInfo/CSharpQuickInfo.cs b/Codist/QuickInfo/CSharpQuickInfo.cs index c6232e1c..4766a93b 100644 --- a/Codist/QuickInfo/CSharpQuickInfo.cs +++ b/Codist/QuickInfo/CSharpQuickInfo.cs @@ -18,6 +18,8 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.Shell; +using System.Windows.Documents; +using Microsoft.VisualStudio.Imaging; namespace Codist.QuickInfo { @@ -59,6 +61,14 @@ public void AugmentQuickInfoSession(IQuickInfoSession session, IList qiC var querySpan = new SnapshotSpan(subjectTriggerPoint, 0); var semanticModel = _SemanticModel; if (semanticModel == null) { + //var container = currentSnapshot.AsText().Container; + //if (Workspace.TryGetWorkspace(container, out var workspace)) { + // var docs = workspace.GetRelatedDocumentIds(container); + // foreach (var item in docs) { + // var d = workspace.CurrentSolution.GetDocument(item); + // var p = workspace.CurrentSolution.GetProject(item.ProjectId); + // } + //} var doc = currentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (doc == null) { goto EXIT; @@ -190,7 +200,7 @@ void OverrideDocumentation(SyntaxNode node, IQuickInfoOverrider qiWrapper, ISymb } symbol = symbol.GetAliasTarget(); var doc = new XmlDoc(symbol, _SemanticModel.Compilation); - var info = new ThemedTipText(); + var tip = new ThemedTipDocument(); var docRenderer = new XmlDocRenderer(_SemanticModel.Compilation, SymbolFormatter.Instance, symbol); var summary = doc.GetDescription(symbol); if (summary == null) { @@ -205,29 +215,40 @@ void OverrideDocumentation(SyntaxNode node, IQuickInfoOverrider qiWrapper, ISymb } } if (inheritDoc != null && summary != null) { - info.Append("Documentation from ") - .AddSymbol(inheritDoc.Symbol.ContainingType, null, _SymbolFormatter) - .Append(".") - .AddSymbol(inheritDoc.Symbol, null, _SymbolFormatter) - .Append(":\n"); + tip.Append(new ThemedTipParagraph(new ThemedTipText() + .Append("Documentation from ") + .AddSymbol(inheritDoc.Symbol.ContainingType, null, _SymbolFormatter) + .Append(".") + .AddSymbol(inheritDoc.Symbol, null, _SymbolFormatter) + .Append(":")) + ); } } if (summary != null) { if (summary.Name.LocalName == XmlDocRenderer.XmlDocNodeName && Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.TextOnlyDoc) == false) { return; } - docRenderer.Render(summary, info); + docRenderer.Render(summary, tip); } if (Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.TypeParameters) && (symbol.Kind == SymbolKind.Method || symbol.Kind == SymbolKind.NamedType)) { var typeParams = symbol.GetTypeParameters(); if (typeParams.IsDefaultOrEmpty == false) { + var para = new ThemedTipParagraph(KnownImageIds.TypeDefinition); foreach (var param in typeParams) { var p = doc.GetTypeParameter(param.Name); if (p == null) { continue; } - info.AppendLine().Append(param.Name, _SymbolFormatter.TypeParameter).Append(": "); - docRenderer.Render(p, info.Inlines); + if (para.Content.Inlines.FirstInline != null) { + para.Content.AppendLine(); + } + para.Content + .Append(param.Name, _SymbolFormatter.TypeParameter) + .Append(": ") + .AddXmlDoc(p, docRenderer); + } + if (para.Content.Inlines.FirstInline != null) { + tip.Append(para); } } } @@ -236,8 +257,11 @@ void OverrideDocumentation(SyntaxNode node, IQuickInfoOverrider qiWrapper, ISymb || symbol.Kind == SymbolKind.NamedType && (symbol as INamedTypeSymbol).TypeKind == TypeKind.Delegate)) { var returns = doc.Returns ?? doc.ExplicitInheritDoc?.Returns; if (returns != null && returns.FirstNode != null) { - info.AppendLine().AppendLine().Append("Returns", true).Append(": "); - docRenderer.Render(returns, info.Inlines); + tip.Append(new ThemedTipParagraph(KnownImageIds.Return, new ThemedTipText() + .Append("Returns", true) + .Append(": ") + .AddXmlDoc(returns, docRenderer)) + ); } } if (Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.RemarksDoc) @@ -245,13 +269,32 @@ void OverrideDocumentation(SyntaxNode node, IQuickInfoOverrider qiWrapper, ISymb && symbol.Kind != SymbolKind.TypeParameter) { var remarks = doc.Remarks ?? doc.ExplicitInheritDoc?.Remarks; if (remarks != null && remarks.FirstNode != null) { - info.AppendLine().AppendLine().Append("Remarks", true).Append(": ").AppendLine(); - docRenderer.Render(remarks, info.Inlines); + tip.Append(new ThemedTipParagraph(KnownImageIds.CommentGroup, new ThemedTipText() + .Append("Remarks", true) + .Append(": ") + .AddXmlDoc(remarks, docRenderer)) + ); } } + if (tip.Children.Count > 0) { + qiWrapper.OverrideDocumentation(tip.Host()); + } - if (info.Inlines.FirstInline != null) { - qiWrapper.OverrideDocumentation(info); + if (Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.ExceptionDoc) + && (symbol.Kind == SymbolKind.Method || symbol.Kind == SymbolKind.Property || symbol.Kind == SymbolKind.Event) + && doc.Exceptions != null) { + var exceptions = doc.Exceptions; + var p = new ThemedTipParagraph(KnownImageIds.StatusInvalidOutline, new ThemedTipText("Exception:", true)); + foreach (var ex in doc.Exceptions) { + var et = ex.Attribute("cref"); + if (et != null) { + docRenderer.RenderXmlDocSymbol(et.Value, p.Content.AppendLine().Inlines, SymbolKind.NamedType); + docRenderer.Render(ex, p.Content.Append(": ").Inlines); + } + } + if (p.Content.Inlines.Count > 1) { + qiWrapper.OverrideException(new ThemedTipDocument().Append(p)); + } } } @@ -264,11 +307,11 @@ void IDisposable.Dispose() { } static void ShowCandidateInfo(IList qiContent, SymbolInfo symbolInfo, SyntaxNode node) { - var info = new StackPanel().Add(new ThemedTipText("Maybe...", true)); + var info = new ThemedTipDocument().AppendTitle(KnownImageIds.CodeInformation, "Maybe..."); foreach (var item in symbolInfo.CandidateSymbols) { - info.Add(ToUIText(item)); + info.Append(new ThemedTipParagraph(item.GetImageId(), ToUIText(item))); } - qiContent.Add(info.Scrollable()); + qiContent.Add(info); } void TextBuffer_Changing(object sender, TextContentChangingEventArgs e) { @@ -503,29 +546,27 @@ void ShowOverloadsInfo(IList qiContent, SyntaxNode node, IMethodSymbol m if (overloads.Length < 2) { return; } - var overloadInfo = new StackPanel().Add(new ThemedTipText("Method overload:", true)); + var overloadInfo = new ThemedTipDocument().AppendTitle(KnownImageIds.MethodSet, "Method overload:"); foreach (var item in overloads) { if (item.Equals(method) || item.Kind != SymbolKind.Method) { continue; } - overloadInfo.Add(new ThemedTipText() - .SetGlyph(ThemeHelper.GetImage(item.GetImageId())) - .AddSymbolDisplayParts(item.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter, -1) + overloadInfo.Append(new ThemedTipParagraph(item.GetImageId(), new ThemedTipText() + .AddSymbolDisplayParts(item.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter, -1)) ); } if (overloadInfo.Children.Count > 1) { - qiContent.Add(overloadInfo.Scrollable()); + qiContent.Add(overloadInfo); } } static void ShowMethodTypeArguments(IList qiContent, SyntaxNode node, IMethodSymbol method) { - var info = new StackPanel(); + var info = new ThemedTipDocument(); var l = method.TypeArguments.Length; - info.Add(new ThemedTipText("Type argument:", true)); + var content = new ThemedTipText("Type argument:", true); + info.Append(new ThemedTipParagraph(KnownImageIds.Template, content)); for (int i = 0; i < l; i++) { - var argInfo = new ThemedTipText(); - ShowTypeParameterInfo(method.TypeParameters[i], method.TypeArguments[i], argInfo); - info.Add(argInfo); + ShowTypeParameterInfo(method.TypeParameters[i], method.TypeArguments[i], content.AppendLine()); } qiContent.Add(info); } @@ -536,15 +577,12 @@ static void ShowNamespaceInfo(IList qiContent, SyntaxNode node, INamespa } var namespaces = nsSymbol.GetNamespaceMembers().ToImmutableArray().Sort(Comparer.Create((x, y) => String.Compare(x.Name, y.Name, StringComparison.Ordinal))); if (namespaces.Length > 0) { - var info = new StackPanel(); - info.Add(new ThemedTipText("Namespace:", true)); + var info = new ThemedTipDocument(); + info.AppendTitle(KnownImageIds.Namespace, "Namespace:"); foreach (var ns in namespaces) { - info.Add(new ThemedTipText() - .SetGlyph(ThemeHelper.GetImage(Microsoft.VisualStudio.Imaging.KnownImageIds.Namespace)) - .Append(ns.Name, _SymbolFormatter.Namespace) - ); + info.Append(new ThemedTipParagraph(KnownImageIds.Namespace, new ThemedTipText().Append(ns.Name, _SymbolFormatter.Namespace))); } - qiContent.Add(info.Scrollable()); + qiContent.Add(info); } var members = nsSymbol.GetTypeMembers().Sort(Comparer.Create((x, y) => String.Compare(x.Name, y.Name))); @@ -599,8 +637,8 @@ static void ShowConstInfo(IList qiContent, SyntaxNode node, ISymbol symb else if (Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.NumericValues)) { var s = ShowNumericForms(value, NumericForm.None); if (s != null) { - qiContent.Add(s); ShowEnumInfo(qiContent, node, symbol.ContainingType, false); + qiContent.Add(s); } } } @@ -615,7 +653,7 @@ static void ShowInterfaceImplementation(IList qiContent, Syntax return; } var explicitIntfs = new List(3); - StackPanel info = null; + ThemedTipDocument info = null; var returnType = symbol.GetReturnType(); var parameters = symbol.GetParameters(); foreach (var intf in interfaces) { @@ -629,9 +667,9 @@ static void ShowInterfaceImplementation(IList qiContent, Syntax } } if (explicitIntfs.Count > 0) { - info = new StackPanel().Add(new ThemedTipText("Implements:", true)); + info = new ThemedTipDocument().AppendTitle(KnownImageIds.ImplementInterface, "Implements:"); foreach (var item in explicitIntfs) { - info.Add(ToUIText(item)); + info.Append(new ThemedTipParagraph(item.GetImageId(), ToUIText(item))); } } if (explicitImplementations != null) { @@ -639,13 +677,12 @@ static void ShowInterfaceImplementation(IList qiContent, Syntax explicitIntfs.AddRange(explicitImplementations.Select(i => i.ContainingType)); if (explicitIntfs.Count > 0) { if (info == null) { - info = new StackPanel(); + info = new ThemedTipDocument(); } - var p = new StackPanel().Add(new ThemedTipText("Explicit implements:", true)); + info.AppendTitle(KnownImageIds.ImplementInterface, "Explicit implements:"); foreach (var item in explicitIntfs) { - p.Add(ToUIText(item)); + info.Append(new ThemedTipParagraph(item.GetImageId(), ToUIText(item))); } - info.Add(p); } } if (info != null) { @@ -767,15 +804,16 @@ static StackPanel ShowStringInfo(string sv) { } static void ShowAttributes(IList qiContent, ImmutableArray attrs, int position) { - var info = new StackPanel().Add(new ThemedTipText("Attribute:", true)); + var p = new ThemedTipParagraph(KnownImageIds.FormPostBodyParameterNode, new ThemedTipText() + .Append("Attribute:", true)); foreach (var item in attrs) { if (item.AttributeClass.IsAccessible(true) == false) { continue; } - info.Children.Add(_SymbolFormatter.ToUIText(new ThemedTipText(), item)); + _SymbolFormatter.ToUIText(p.Content.AppendLine().Inlines, item); } - if (info.Children.Count > 1) { - qiContent.Add(info.Scrollable()); + if (p.Content.Inlines.Count > 1) { + qiContent.Add(new ThemedTipDocument().Append(p).Host()); } } @@ -784,14 +822,15 @@ static void ShowBaseType(IList qiContent, ITypeSymbol typeSymbol, int po if (baseType == null || baseType.IsCommonClass() != false) { return; } - var info = new ThemedTipText("Base type: ", true) + var classList = new ThemedTipText("Base type: ", true) .AddSymbol(baseType, null, _SymbolFormatter.Class); + var info = new ThemedTipDocument().Append(new ThemedTipParagraph(KnownImageIds.ParentChild, classList)); while (Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.BaseTypeInheritence) && (baseType = baseType.BaseType) != null) { if (baseType.IsAccessible(false) && baseType.IsCommonClass() == false) { - info.Append(" - ").AddSymbol(baseType, null, _SymbolFormatter.Class); + classList.Append(" - ").AddSymbol(baseType, null, _SymbolFormatter.Class); } } - qiContent.Add(info.LimitSize()); + qiContent.Add(info); } static void ShowEnumInfo(IList qiContent, SyntaxNode node, INamedTypeSymbol type, bool fromEnum) { @@ -803,8 +842,9 @@ static void ShowEnumInfo(IList qiContent, SyntaxNode node, INamedTypeSym if (t == null) { return; } - var s = new StackPanel() - .Add(new ThemedTipText("Enum underlying type: ", true).AddSymbolDisplayParts(t.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter)); + var content = new ThemedTipText("Enum underlying type: ", true).AddSymbolDisplayParts(t.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter); + var s = new ThemedTipDocument() + .Append(new ThemedTipParagraph(KnownImageIds.Enumeration, content)); if (fromEnum == false) { qiContent.Add(s); return; @@ -841,22 +881,22 @@ static void ShowEnumInfo(IList qiContent, SyntaxNode node, INamedTypeSym if (min == null) { return; } - s.Add(new ThemedTipText("Field count: ", true).Append(c.ToString())) - .Add(new ThemedTipText("Min: ", true) + content.AppendLine().Append("Field count: ", true).Append(c.ToString()) + .AppendLine().Append("Min: ", true) .Append(min.ToString() + "(") .Append(minName.Name, _SymbolFormatter.Enum) - .Append(")")) - .Add(new ThemedTipText("Max: ", true) + .Append(")") + .AppendLine().Append("Max: ", true) .Append(max.ToString() + "(") .Append(maxName.Name, _SymbolFormatter.Enum) - .Append(")")); + .Append(")"); if (type.GetAttributes().FirstOrDefault(a => a.AttributeClass.ToDisplayString() == "System.FlagsAttribute") != null) { var d = Convert.ToString(Convert.ToInt64(bits), 2); - s.Add(new ThemedTipText("All flags: ", true) + content.AppendLine().Append("All flags: ", true) .Append(d) .Append(" (") .Append(d.Length.ToString()) - .Append(d.Length > 1 ? " bits)" : " bit)")); + .Append(d.Length > 1 ? " bits)" : " bit)"); } qiContent.Add(s); } @@ -896,28 +936,28 @@ static void ShowInterfaces(IList qiContent, ITypeSymbol type, int positi if (declaredInterfaces.Count == 0 && inheritedInterfaces.Count == 0 && disposable == null) { return; } - var stack = new StackPanel().Add(new ThemedTipText("Interface:", true)); + var info = new ThemedTipDocument().AppendTitle(KnownImageIds.Interface, "Interface:"); if (disposable != null) { var t = ToUIText(disposable); if (interfaces.Contains(disposable) == false) { t.Append(" (inherited)"); } - stack.Add(t); + info.Append(new ThemedTipParagraph(KnownImageIds.PartWarning, t)); } foreach (var item in declaredInterfaces) { if (item == disposable) { continue; } - stack.Add(ToUIText(item)); + info.Append(new ThemedTipParagraph(item.GetImageId(), ToUIText(item))); } foreach (var item in inheritedInterfaces) { - stack.Add(ToUIText(item).Append(" (inherited)")); + info.Append(new ThemedTipParagraph(item.GetImageId(), ToUIText(item).Append(" (inherited)"))); } - qiContent.Add(stack.Scrollable()); + qiContent.Add(info); } static void ShowDeclarationModifier(IList qiContent, ISymbol symbol) { - qiContent.Add(_SymbolFormatter.ShowSymbolDeclaration(symbol, new ThemedTipText(), true, false)); + qiContent.Add(new ThemedTipParagraph(KnownImageIds.ControlAltDel, _SymbolFormatter.ShowSymbolDeclaration(symbol, new ThemedTipText(), true, false))); } void ShowParameterInfo(IList qiContent, SyntaxNode node) { @@ -986,35 +1026,36 @@ void ShowParameterInfo(IList qiContent, SyntaxNode node, SyntaxNode argu } } var doc = argName != null ? new XmlDoc(m.MethodKind == MethodKind.DelegateInvoke ? m.ContainingSymbol : m, _SemanticModel.Compilation).GetParameter(argName) : null; - var info = new ThemedTipText("Argument", true) + var content = new ThemedTipText("Argument", true) .Append(" of ") .AddSymbolDisplayParts(m.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter, argIndex); + var info = new ThemedTipDocument().Append(new ThemedTipParagraph(KnownImageIds.Parameter, content)); m = symbol.Symbol as IMethodSymbol; + if (doc != null) { + content.Append("\n" + argName, true, true, _SymbolFormatter.Parameter).Append(": "); + new XmlDocRenderer(_SemanticModel.Compilation, _SymbolFormatter, m).Render(doc, content.Inlines); + } if (m.IsGenericMethod) { for (int i = 0; i < m.TypeArguments.Length; i++) { - info.Append("\n"); - ShowTypeParameterInfo(m.TypeParameters[i], m.TypeArguments[i], info); + content.Append("\n"); + ShowTypeParameterInfo(m.TypeParameters[i], m.TypeArguments[i], content); } } - if (doc != null) { - info.Append("\n" + argName, true, true, _SymbolFormatter.Parameter).Append(": "); - new XmlDocRenderer(_SemanticModel.Compilation, SymbolFormatter.Empty, m).Render(doc, info.Inlines); - } - foreach (var item in info.Inlines) { + foreach (var item in content.Inlines) { if (item.Foreground == null) { item.Foreground = ThemeHelper.ToolTipTextBrush; } } - qiContent.Add(info.Scrollable()); + qiContent.Add(info); } else if (symbol.CandidateSymbols.Length > 0) { - var info = new StackPanel(); - info.Add(new ThemedTipText("Maybe", true).Append(" argument of")); + var info = new ThemedTipDocument(); + info.Append(new ThemedTipParagraph(KnownImageIds.Parameter, new ThemedTipText("Maybe", true).Append(" argument of"))); foreach (var candidate in symbol.CandidateSymbols) { - info.Add(new ThemedTipText().AddSymbolDisplayParts(candidate.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter, argName == null ? argIndex : Int32.MinValue)); + info.Append(new ThemedTipParagraph(candidate.GetImageId(), new ThemedTipText().AddSymbolDisplayParts(candidate.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter, argName == null ? argIndex : Int32.MinValue))); } - qiContent.Add(info.Scrollable()); + qiContent.Add(info); } else if (argList.Parent.IsKind(SyntaxKind.InvocationExpression)) { var methodName = (argList.Parent as InvocationExpressionSyntax).Expression.ToString(); @@ -1067,9 +1108,7 @@ static StackPanel ShowNumericForms(string dec, byte[] bytes) { } static TextBlock ToUIText(ISymbol symbol) { - return new ThemedTipText() - .SetGlyph(ThemeHelper.GetImage(symbol.GetImageId())) - .AddSymbolDisplayParts(symbol.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter, -1); + return new ThemedTipText().AddSymbolDisplayParts(symbol.ToDisplayParts(WpfHelper.QuickInfoSymbolDisplayFormat), _SymbolFormatter, -1); } diff --git a/Codist/QuickInfo/QuickInfoOverrider.cs b/Codist/QuickInfo/QuickInfoOverrider.cs index 88206e27..7c3055e0 100644 --- a/Codist/QuickInfo/QuickInfoOverrider.cs +++ b/Codist/QuickInfo/QuickInfoOverrider.cs @@ -8,8 +8,8 @@ using AppHelpers; using Codist.Controls; using Microsoft.CodeAnalysis; -using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Imaging; +using Microsoft.VisualStudio.Language.Intellisense; namespace Codist.QuickInfo { @@ -19,6 +19,7 @@ interface IQuickInfoOverrider void ApplyClickAndGo(ISymbol symbol); void LimitQuickInfoItemSize(IList qiContent); void OverrideDocumentation(UIElement docElement); + void OverrideException(UIElement exceptionDoc); } static class QuickInfoOverrider @@ -119,6 +120,9 @@ public void LimitQuickInfoItemSize(IList qiContent) { public void OverrideDocumentation(UIElement docElement) { _Overrider.DocElement = docElement; } + public void OverrideException(UIElement exceptionDoc) { + _Overrider.ExceptionDoc = exceptionDoc; + } public void SetDiagnostics(IList diagnostics) { _Overrider.Diagnostics = diagnostics; @@ -135,6 +139,7 @@ sealed class Overrider : UIElement public ISymbol ClickAndGoSymbol; public bool LimitItemSize; public UIElement DocElement; + public UIElement ExceptionDoc; public IList Diagnostics; protected override void OnVisualParentChanged(DependencyObject oldParent) { @@ -151,7 +156,7 @@ protected override void OnVisualParentChanged(DependencyObject oldParent) { p.SetValue(TextBlock.FontFamilyProperty, ThemeHelper.ToolTipFont); p.SetValue(TextBlock.FontSizeProperty, ThemeHelper.ToolTipFontSize); } - if (DocElement != null || ClickAndGoSymbol != null || LimitItemSize) { + if (DocElement != null || ExceptionDoc != null || ClickAndGoSymbol != null || LimitItemSize) { FixQuickInfo(p); } if (LimitItemSize) { @@ -252,21 +257,37 @@ void FixQuickInfo(StackPanel infoPanel) { } // replace the default XML doc + var items = doc.Children; if (DocElement != null) { try { - if (doc.Children.Count > 1 && doc.Children[1] is TextBlock) { - doc.Children.RemoveAt(1); - doc.Children.Insert(1, DocElement); + // sequence of items in default XML Doc panel: + // 1. summary + // 2. type parameter + // 3. usage + // 4. exception + if (items.Count > 1 && items[1] is TextBlock) { + items.RemoveAt(1); + items.Insert(1, DocElement); } else { - doc.Children.Add(DocElement); + items.Add(DocElement); } } catch (InvalidOperationException) { // ignore exception: doc.Children was changed by another thread } } - + if (ExceptionDoc != null) { + try { + if (items.Count > 1 && items[items.Count - 1] is TextBlock) { + items.RemoveAt(items.Count - 1); + } + items.Add(ExceptionDoc); + } + catch (InvalidOperationException) { + // ignore exception: doc.Children was changed by another thread + } + } if (icon != null && signature != null) { // apply click and go feature @@ -307,7 +328,11 @@ static void ApplySizeLimit(StackPanel quickInfoPanel) { cp.LimitSize(); if (docPanel == c) { cp.MaxWidth += 32; + foreach (var r in docPanel.Children) { + (r as ThemedTipDocument)?.ApplySizeLimit(); + } } + (c as ThemedTipDocument)?.ApplySizeLimit(); if (c is ScrollViewer || c is Microsoft.VisualStudio.Text.Editor.IWpfTextView // skip snippet tooltip ) { @@ -404,6 +429,7 @@ public void OverrideDocumentation(UIElement newDoc) { Panel.Children.Insert(Panel.Children.IndexOf(doc), newDoc); } } + public void OverrideException(UIElement exceptionDoc) { } public void SetDiagnostics(IList diagnostics) { // not implemented for versions before 15.8 diff --git a/Codist/SmartBars/CSharpSmartBar.cs b/Codist/SmartBars/CSharpSmartBar.cs index 75b74bcf..56926e30 100644 --- a/Codist/SmartBars/CSharpSmartBar.cs +++ b/Codist/SmartBars/CSharpSmartBar.cs @@ -579,7 +579,7 @@ async Task> GetReferenceCommandsAsync(CommandContext ct r.Add(CreateCommandMenu("Find Instance as Parameter...", KnownImageIds.Parameter, t, "No instance as parameter was found", FindInstanceAsParameter)); if (t.IsSealed == false) { if (t.TypeKind == TypeKind.Class) { - r.Add(CreateCommandMenu("Find Derived Classes...", KnownImageIds.NewClass, symbol, "No derived class was found", FindDerivedClasses)); + r.Add(CreateCommandMenu("Find Derived Classes...", KnownImageIds.NewClass, t, "No derived class was found", FindDerivedClasses)); } else if (t.TypeKind == TypeKind.Interface) { r.Add(CreateCommandMenu("Find Implementations...", KnownImageIds.ImplementInterface, symbol, "No implementation was found", FindImplementations)); @@ -788,8 +788,8 @@ void SetColorPreviewIcon(ISymbol symbol) { h.Inlines.InsertBefore( h.Inlines.FirstInline, new System.Windows.Documents.InlineUIContainer(new System.Windows.Shapes.Rectangle { - Height = 16, - Width = 16, + Height = ThemeHelper.DefaultIconSize, + Width = ThemeHelper.DefaultIconSize, Fill = b, Margin = WpfHelper.GlyphMargin }) { diff --git a/Codist/SmartBars/SmartBar.CommonEdit.cs b/Codist/SmartBars/SmartBar.CommonEdit.cs index 42b8eac0..e6cb9311 100644 --- a/Codist/SmartBars/SmartBar.CommonEdit.cs +++ b/Codist/SmartBars/SmartBar.CommonEdit.cs @@ -34,14 +34,15 @@ static void ExecuteAndFind(CommandContext ctx, string command, string text) { } protected static void FindNext(CommandContext ctx, string t) { - if (t != null) { - var r = ctx.TextSearchService.Find(ctx.View.Selection.StreamSelectionSpan.End.Position, t, FindOptions.MatchCase); - if (r.HasValue) { - ctx.View.SelectSpan(r.Value); - } - else { - ctx.HideToolBar(); - } + if (String.IsNullOrEmpty(t)) { + return; + } + var r = ctx.TextSearchService.Find(ctx.View.Selection.StreamSelectionSpan.End.Position, t, FindOptions.MatchCase); + if (r.HasValue) { + ctx.View.SelectSpan(r.Value); + } + else { + ctx.HideToolBar(); } } diff --git a/TestProject/Comments.cs b/TestProject/Comments.cs index 73360e82..33251124 100644 --- a/TestProject/Comments.cs +++ b/TestProject/Comments.cs @@ -45,6 +45,7 @@ void Test() { /// The date to be formatted to string. /// The format for the date. /// The formatted . + /// is invalid for . Validate this in . public static string FormatDate(DateTime date, string format) { return date.ToString(format); } diff --git a/TestProject/TestPage.cs b/TestProject/TestPage.cs index 003dc2ec..3bdc4847 100644 --- a/TestProject/TestPage.cs +++ b/TestProject/TestPage.cs @@ -71,6 +71,7 @@ private void TestPage_MyEvent(object sender, EventArgs e) { public new void Method() { Property--; } + /// Any type public void Method() { //type parameter // unnecessary code TestProject.ExtensionClass.Log(typeof(TGeneric).Name); // extension method