diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Intellisense/QuickInfo/InformationQuickInfoContentVM.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Intellisense/QuickInfo/InformationQuickInfoContentVM.cs index 11a9f548c2..65ae5af300 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Intellisense/QuickInfo/InformationQuickInfoContentVM.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Intellisense/QuickInfo/InformationQuickInfoContentVM.cs @@ -21,7 +21,7 @@ You should have received a copy of the GNU General Public License using System.Collections.Generic; using System.Collections.Immutable; using System.Text; -using System.Windows.Controls; +using System.Windows; using dnSpy.Contracts.Images; using dnSpy.Contracts.MVVM; using dnSpy.Contracts.Text.Classification; @@ -51,7 +51,7 @@ sealed class InformationQuickInfoContentVM : ViewModelBase { public object? ExceptionObject { get; } public bool HasExceptionObject => ExceptionObject is not null; - public InformationQuickInfoContentVM(ITextView textView, InformationQuickInfoContent content, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService) { + public InformationQuickInfoContentVM(ITextView textView, InformationQuickInfoContent content, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, ITextElementFactory textElementFactory) { if (textView is null) throw new ArgumentNullException(nameof(textView)); if (content is null) @@ -65,30 +65,32 @@ public InformationQuickInfoContentVM(ITextView textView, InformationQuickInfoCon SymbolImageReference = content.SymbolGlyph.Value.GetImageReference() ?? default; if (content.WarningGlyph is not null) WarningImageReference = content.WarningGlyph.Value.GetImageReference() ?? default; - MainDescriptionObject = TryCreateObject(sb, content.MainDescription, classificationFormatMap, themeClassificationTypeService); - DocumentationObject = TryCreateObject(sb, content.Documentation, classificationFormatMap, themeClassificationTypeService); - UsageObject = TryCreateObject(sb, content.UsageText, classificationFormatMap, themeClassificationTypeService); - TypeParameterMapObject = TryCreateObject(sb, content.TypeParameterMap, classificationFormatMap, themeClassificationTypeService); - AnonymousTypesObject = TryCreateObject(sb, content.AnonymousTypes, classificationFormatMap, themeClassificationTypeService); - ExceptionObject = TryCreateObject(sb, content.ExceptionText, classificationFormatMap, themeClassificationTypeService); + MainDescriptionObject = TryCreateObject(sb, content.MainDescription, classificationFormatMap, themeClassificationTypeService, textElementFactory); + DocumentationObject = TryCreateObject(sb, content.Documentation, classificationFormatMap, themeClassificationTypeService, textElementFactory); + UsageObject = TryCreateObject(sb, content.UsageText, classificationFormatMap, themeClassificationTypeService, textElementFactory); + TypeParameterMapObject = TryCreateObject(sb, content.TypeParameterMap, classificationFormatMap, themeClassificationTypeService, textElementFactory); + AnonymousTypesObject = TryCreateObject(sb, content.AnonymousTypes, classificationFormatMap, themeClassificationTypeService, textElementFactory); + ExceptionObject = TryCreateObject(sb, content.ExceptionText, classificationFormatMap, themeClassificationTypeService, textElementFactory); } - TextBlock? TryCreateObject(StringBuilder sb, ImmutableArray taggedParts, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService) { + static FrameworkElement? TryCreateObject(StringBuilder sb, ImmutableArray taggedParts, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, ITextElementFactory textElementFactory) { if (taggedParts.IsDefaultOrEmpty) return null; var text = ToString(sb, taggedParts); - var propsSpans = CreateTextRunPropertiesAndSpans(taggedParts, classificationFormatMap, themeClassificationTypeService); - return TextBlockFactory.Create(text, classificationFormatMap.DefaultTextProperties, propsSpans, TextBlockFactory.Flags.DisableSetTextBlockFontFamily | TextBlockFactory.Flags.DisableFontSize); + return textElementFactory.Create(classificationFormatMap, text, CreateTextClassificationTags(taggedParts, themeClassificationTypeService), TextElementFlags.None); } - IEnumerable CreateTextRunPropertiesAndSpans(ImmutableArray taggedParts, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService) { + static IList CreateTextClassificationTags(ImmutableArray taggedParts, IThemeClassificationTypeService themeClassificationTypeService) { int pos = 0; - foreach (var part in taggedParts) { + var result = new TextClassificationTag[taggedParts.Length]; + for (int i = 0; i < taggedParts.Length; i++) { + var part = taggedParts[i]; var color = TextTagsHelper.ToTextColor(part.Tag); var classificationType = themeClassificationTypeService.GetClassificationType(color); - yield return new TextRunPropertiesAndSpan(new Span(pos, part.Text.Length), classificationFormatMap.GetTextProperties(classificationType)); + result[i] = new TextClassificationTag(new Span(pos, part.Text.Length), classificationType); pos += part.Text.Length; } + return result; } static string ToString(StringBuilder sb, ImmutableArray taggedParts) { diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Intellisense/QuickInfo/QuickInfoContentCreator.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Intellisense/QuickInfo/QuickInfoContentCreator.cs index 4b2480437d..57de29d6bf 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Intellisense/QuickInfo/QuickInfoContentCreator.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Intellisense/QuickInfo/QuickInfoContentCreator.cs @@ -40,24 +40,28 @@ interface IQuickInfoContentCreator { sealed class QuickInfoContentCreatorProvider : IQuickInfoContentCreatorProvider { readonly IClassificationFormatMapService classificationFormatMapService; readonly IThemeClassificationTypeService themeClassificationTypeService; + readonly ITextElementFactory textElementFactory; [ImportingConstructor] - QuickInfoContentCreatorProvider(IClassificationFormatMapService classificationFormatMapService, IThemeClassificationTypeService themeClassificationTypeService) { + QuickInfoContentCreatorProvider(IClassificationFormatMapService classificationFormatMapService, IThemeClassificationTypeService themeClassificationTypeService, ITextElementFactory textElementFactory) { this.classificationFormatMapService = classificationFormatMapService; this.themeClassificationTypeService = themeClassificationTypeService; + this.textElementFactory = textElementFactory; } - public IQuickInfoContentCreator Create(ITextView textView) => new QuickInfoContentCreator(classificationFormatMapService.GetClassificationFormatMap(AppearanceCategoryConstants.UIMisc), themeClassificationTypeService, textView); + public IQuickInfoContentCreator Create(ITextView textView) => new QuickInfoContentCreator(classificationFormatMapService.GetClassificationFormatMap(AppearanceCategoryConstants.UIMisc), themeClassificationTypeService, textElementFactory, textView); } sealed class QuickInfoContentCreator : IQuickInfoContentCreator { readonly IClassificationFormatMap classificationFormatMap; readonly IThemeClassificationTypeService themeClassificationTypeService; readonly ITextView textView; + readonly ITextElementFactory textElementFactory; - public QuickInfoContentCreator(IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, ITextView textView) { + public QuickInfoContentCreator(IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, ITextElementFactory textElementFactory, ITextView textView) { this.classificationFormatMap = classificationFormatMap ?? throw new ArgumentNullException(nameof(classificationFormatMap)); this.themeClassificationTypeService = themeClassificationTypeService ?? throw new ArgumentNullException(nameof(themeClassificationTypeService)); + this.textElementFactory = textElementFactory ?? throw new ArgumentNullException(nameof(textElementFactory)); this.textView = textView ?? throw new ArgumentNullException(nameof(textView)); } @@ -80,7 +84,7 @@ public IEnumerable Create(QuickInfoItem item) { IEnumerable Create(InformationQuickInfoContent content) { yield return new InformationQuickInfoContentControl { - DataContext = new InformationQuickInfoContentVM(textView, content, classificationFormatMap, themeClassificationTypeService), + DataContext = new InformationQuickInfoContentVM(textView, content, classificationFormatMap, themeClassificationTypeService, textElementFactory), }; } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/ITaggedTextElementProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/ITaggedTextElementProvider.cs index 13fd021338..28ce7ef425 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/ITaggedTextElementProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/ITaggedTextElementProvider.cs @@ -19,22 +19,22 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Immutable; -using System.Windows.Controls; +using System.Windows; using Microsoft.CodeAnalysis; namespace dnSpy.Roslyn.Text.Classification { /// - /// Creates a . Call its method + /// Creates a which displays the given tagged text. Call its method /// to clean up its resources. /// interface ITaggedTextElementProvider : IDisposable { /// - /// Creates a + /// Creates a which displays the tagged text /// /// Tag, can be null /// Tagged parts to classify /// true if it should be colorized /// - TextBlock Create(string tag, ImmutableArray taggedParts, bool colorize); + FrameworkElement Create(string tag, ImmutableArray taggedParts, bool colorize); } } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/TaggedTextElementProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/TaggedTextElementProvider.cs index 744c9ff0f8..b2b60cab69 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/TaggedTextElementProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/TaggedTextElementProvider.cs @@ -19,8 +19,7 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Immutable; -using System.Linq; -using System.Windows.Controls; +using System.Windows; using dnSpy.Contracts.Text.Classification; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Text.Classification; @@ -28,24 +27,21 @@ You should have received a copy of the GNU General Public License namespace dnSpy.Roslyn.Text.Classification { sealed class TaggedTextElementProvider : ITaggedTextElementProvider { - readonly ITextClassifierAggregator classifierAggregator; + readonly IContentType contentType; readonly IClassificationFormatMap classificationFormatMap; + readonly ITextElementProvider textElementProvider; - public TaggedTextElementProvider(IContentType contentType, ITextClassifierAggregatorService textClassifierAggregatorService, IClassificationFormatMap classificationFormatMap) { - if (contentType is null) - throw new ArgumentNullException(nameof(contentType)); - if (textClassifierAggregatorService is null) - throw new ArgumentNullException(nameof(textClassifierAggregatorService)); - classifierAggregator = textClassifierAggregatorService.Create(contentType); + public TaggedTextElementProvider(IContentType contentType, IClassificationFormatMap classificationFormatMap, ITextElementProvider textElementProvider) { + this.contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); this.classificationFormatMap = classificationFormatMap ?? throw new ArgumentNullException(nameof(classificationFormatMap)); + this.textElementProvider = textElementProvider ?? throw new ArgumentNullException(nameof(textElementProvider)); } - public TextBlock Create(string tag, ImmutableArray taggedParts, bool colorize) { + public FrameworkElement Create(string tag, ImmutableArray taggedParts, bool colorize) { var context = TaggedTextClassifierContext.Create(tag, taggedParts, colorize); - return TextBlockFactory.Create(context.Text, classificationFormatMap.DefaultTextProperties, - classifierAggregator.GetTags(context).Select(a => new TextRunPropertiesAndSpan(a.Span, classificationFormatMap.GetTextProperties(a.ClassificationType))), TextBlockFactory.Flags.DisableSetTextBlockFontFamily | TextBlockFactory.Flags.DisableFontSize); + return textElementProvider.CreateTextElement(classificationFormatMap, context, contentType, TextElementFlags.None); } - public void Dispose() => classifierAggregator?.Dispose(); + public void Dispose() { } } } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/TaggedTextElementProviderService.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/TaggedTextElementProviderService.cs index 465271adb8..5181a81573 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/TaggedTextElementProviderService.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Text/Classification/TaggedTextElementProviderService.cs @@ -26,13 +26,13 @@ You should have received a copy of the GNU General Public License namespace dnSpy.Roslyn.Text.Classification { [Export(typeof(ITaggedTextElementProviderService))] sealed class TaggedTextElementProviderService : ITaggedTextElementProviderService { - readonly ITextClassifierAggregatorService textClassifierAggregatorService; readonly IClassificationFormatMapService classificationFormatMapService; + readonly ITextElementProvider textElementProvider; [ImportingConstructor] - TaggedTextElementProviderService(ITextClassifierAggregatorService textClassifierAggregatorService, IClassificationFormatMapService classificationFormatMapService) { - this.textClassifierAggregatorService = textClassifierAggregatorService; + TaggedTextElementProviderService(IClassificationFormatMapService classificationFormatMapService, ITextElementProvider textElementProvider) { this.classificationFormatMapService = classificationFormatMapService; + this.textElementProvider = textElementProvider; } public ITaggedTextElementProvider Create(IContentType contentType, string category) { @@ -40,7 +40,7 @@ public ITaggedTextElementProvider Create(IContentType contentType, string catego throw new ArgumentNullException(nameof(contentType)); if (category is null) throw new ArgumentNullException(nameof(category)); - return new TaggedTextElementProvider(contentType, textClassifierAggregatorService, classificationFormatMapService.GetClassificationFormatMap(category)); + return new TaggedTextElementProvider(contentType, classificationFormatMapService.GetClassificationFormatMap(category), textElementProvider); } } } diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Text/Classification/ITextElementProvider.cs b/dnSpy/dnSpy.Contracts.DnSpy/Text/Classification/ITextElementProvider.cs index b8419246dc..1a288127c0 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Text/Classification/ITextElementProvider.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Text/Classification/ITextElementProvider.cs @@ -19,6 +19,7 @@ You should have received a copy of the GNU General Public License using System.Windows; using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; namespace dnSpy.Contracts.Text.Classification { /// @@ -34,5 +35,15 @@ public interface ITextElementProvider { /// Flags /// FrameworkElement CreateTextElement(IClassificationFormatMap classificationFormatMap, TextClassifierContext context, string contentType, TextElementFlags flags); + + /// + /// Creates a WPF text element + /// + /// Classification format map + /// Text classifier context + /// Content type + /// Flags + /// + FrameworkElement CreateTextElement(IClassificationFormatMap classificationFormatMap, TextClassifierContext context, IContentType contentType, TextElementFlags flags); } } diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Text/Classification/TextBlockFactory.cs b/dnSpy/dnSpy.Contracts.DnSpy/Text/Classification/TextBlockFactory.cs index fbac060287..ecab95ba83 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Text/Classification/TextBlockFactory.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Text/Classification/TextBlockFactory.cs @@ -64,7 +64,7 @@ public enum Flags { FilterOutNewlines = 8, } - static string ToString(string s, bool filterOutNewLines) { + internal static string ToString(string s, bool filterOutNewLines) { if (!filterOutNewLines) return s; if (s.IndexOfAny(LineConstants.newLineChars) < 0) @@ -79,6 +79,20 @@ static string ToString(string s, bool filterOutNewLines) { return sb.ToString(); } + static string ToString(string s, int startIndex, int length, bool filterOutNewLines) { + if (!filterOutNewLines) + return s.Substring(startIndex, length); + var sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + var c = s[startIndex + i]; + if (Array.IndexOf(LineConstants.newLineChars, c) >= 0) + sb.Append(' '); + else + sb.Append(c); + } + return sb.ToString(); + } + /// /// Creates a /// @@ -147,12 +161,12 @@ public static TextBlock Create(string text, TextFormattingRunProperties defaultP int textOffset = 0; foreach (var tag in propsAndSpansList) { if (textOffset < tag.Span.Start) - textBlock.Inlines.Add(CreateRun(ToString(text.Substring(textOffset, tag.Span.Start - textOffset), filterOutNewlines), defaultProperties, null, flags)); - textBlock.Inlines.Add(CreateRun(ToString(text.Substring(tag.Span.Start, tag.Span.Length), filterOutNewlines), defaultProperties, tag.Properties, flags)); + textBlock.Inlines.Add(CreateRun(ToString(text, textOffset, tag.Span.Start - textOffset, filterOutNewlines), defaultProperties, null, flags)); + textBlock.Inlines.Add(CreateRun(ToString(text, tag.Span.Start, tag.Span.Length, filterOutNewlines), defaultProperties, tag.Properties, flags)); textOffset = tag.Span.End; } if (textOffset < text.Length) - textBlock.Inlines.Add(CreateRun(ToString(text.Substring(textOffset), filterOutNewlines), defaultProperties, null, flags)); + textBlock.Inlines.Add(CreateRun(ToString(text, textOffset, text.Length - textOffset, filterOutNewlines), defaultProperties, null, flags)); } propsAndSpansList.Clear(); diff --git a/dnSpy/dnSpy/Controls/TextElementFactory.cs b/dnSpy/dnSpy/Controls/TextElementFactory.cs index 5fa4f50dfc..8c681785b0 100644 --- a/dnSpy/dnSpy/Controls/TextElementFactory.cs +++ b/dnSpy/dnSpy/Controls/TextElementFactory.cs @@ -23,7 +23,6 @@ You should have received a copy of the GNU General Public License using System.Diagnostics; using System.Globalization; using System.Linq; -using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; @@ -43,21 +42,6 @@ public FrameworkElement Create(IClassificationFormatMap classificationFormatMap, } static class TextElementFactory { - static string ToString(string s, bool filterOutNewLines) { - if (!filterOutNewLines) - return s; - if (s.IndexOfAny(LineConstants.newLineChars) < 0) - return s; - var sb = new StringBuilder(s.Length); - foreach (var c in s) { - if (Array.IndexOf(LineConstants.newLineChars, c) >= 0) - sb.Append(' '); - else - sb.Append(c); - } - return sb.ToString(); - } - static TextTrimming GetTextTrimming(TextElementFlags flags) { switch (flags & TextElementFlags.TrimmingMask) { case TextElementFlags.NoTrimming: return TextTrimming.None; @@ -82,7 +66,7 @@ public static FrameworkElement Create(IClassificationFormatMap classificationFor if (tags.Count != 0) { if (useFastTextBlock) { return new FastTextBlock(new TextSrc { - text = ToString(WpfUnicodeUtils.ReplaceBadChars(text), filterOutNewLines), + text = TextBlockFactory.ToString(WpfUnicodeUtils.ReplaceBadChars(text), filterOutNewLines), classificationFormatMap = classificationFormatMap, tagsList = tags.ToArray(), }); @@ -98,12 +82,12 @@ public static FrameworkElement Create(IClassificationFormatMap classificationFor FrameworkElement fwElem; if (useFastTextBlock) { fwElem = new FastTextBlock() { - Text = ToString(WpfUnicodeUtils.ReplaceBadChars(text), filterOutNewLines) + Text = TextBlockFactory.ToString(WpfUnicodeUtils.ReplaceBadChars(text), filterOutNewLines) }; } else { fwElem = new TextBlock { - Text = ToString(WpfUnicodeUtils.ReplaceBadChars(text), filterOutNewLines), + Text = TextBlockFactory.ToString(WpfUnicodeUtils.ReplaceBadChars(text), filterOutNewLines), TextTrimming = GetTextTrimming(flags), TextWrapping = GetTextWrapping(flags), }; diff --git a/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/CodeToolTipProvider.cs b/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/CodeToolTipProvider.cs index 1a8f0e96ee..1274336490 100644 --- a/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/CodeToolTipProvider.cs +++ b/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/CodeToolTipProvider.cs @@ -42,14 +42,16 @@ sealed class CodeToolTipProvider : ICodeToolTipProvider { readonly IClassificationFormatMap classificationFormatMap; readonly IThemeClassificationTypeService themeClassificationTypeService; readonly IClassificationTypeRegistryService classificationTypeRegistryService; + readonly ITextElementFactory textElementFactory; readonly bool syntaxHighlight; - public CodeToolTipProvider(IWpfTextView wpfTextView, IDotNetImageService dotNetImageService, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, bool syntaxHighlight) { + public CodeToolTipProvider(IWpfTextView wpfTextView, IDotNetImageService dotNetImageService, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, ITextElementFactory textElementFactory, bool syntaxHighlight) { this.wpfTextView = wpfTextView ?? throw new ArgumentNullException(nameof(wpfTextView)); this.dotNetImageService = dotNetImageService ?? throw new ArgumentNullException(nameof(dotNetImageService)); this.classificationFormatMap = classificationFormatMap ?? throw new ArgumentNullException(nameof(classificationFormatMap)); this.themeClassificationTypeService = themeClassificationTypeService ?? throw new ArgumentNullException(nameof(themeClassificationTypeService)); this.classificationTypeRegistryService = classificationTypeRegistryService ?? throw new ArgumentNullException(nameof(classificationTypeRegistryService)); + this.textElementFactory = textElementFactory ?? throw new ArgumentNullException(nameof(textElementFactory)); this.syntaxHighlight = syntaxHighlight; writers = new List(); CreateNewOutput(); @@ -85,7 +87,7 @@ public object Create() { } public ICodeToolTipWriter CreateNewOutput() { - writers.Add(new CodeToolTipWriter(classificationFormatMap, themeClassificationTypeService, classificationTypeRegistryService, syntaxHighlight)); + writers.Add(new CodeToolTipWriter(classificationFormatMap, themeClassificationTypeService, classificationTypeRegistryService, textElementFactory, syntaxHighlight)); return writers[writers.Count - 1]; } diff --git a/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/CodeToolTipWriter.cs b/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/CodeToolTipWriter.cs index eee9edf11f..6980956b7d 100644 --- a/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/CodeToolTipWriter.cs +++ b/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/CodeToolTipWriter.cs @@ -30,7 +30,6 @@ You should have received a copy of the GNU General Public License using dnSpy.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; -using Microsoft.VisualStudio.Text.Formatting; namespace dnSpy.Documents.Tabs.DocViewer.ToolTips { sealed class CodeToolTipWriter : ICodeToolTipWriter, IXmlDocOutput { @@ -39,14 +38,16 @@ sealed class CodeToolTipWriter : ICodeToolTipWriter, IXmlDocOutput { readonly IClassificationFormatMap classificationFormatMap; readonly IThemeClassificationTypeService themeClassificationTypeService; readonly IClassificationTypeRegistryService classificationTypeRegistryService; + readonly ITextElementFactory textElementFactory; readonly bool syntaxHighlight; public bool IsEmpty => sb.Length == 0; - public CodeToolTipWriter(IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, bool syntaxHighlight) { + public CodeToolTipWriter(IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, ITextElementFactory textElementFactory, bool syntaxHighlight) { this.classificationFormatMap = classificationFormatMap ?? throw new ArgumentNullException(nameof(classificationFormatMap)); this.themeClassificationTypeService = themeClassificationTypeService ?? throw new ArgumentNullException(nameof(themeClassificationTypeService)); this.classificationTypeRegistryService = classificationTypeRegistryService ?? throw new ArgumentNullException(nameof(classificationTypeRegistryService)); + this.textElementFactory = textElementFactory ?? throw new ArgumentNullException(nameof(textElementFactory)); this.syntaxHighlight = syntaxHighlight; result = new List(); sb = new StringBuilder(); @@ -54,20 +55,21 @@ public CodeToolTipWriter(IClassificationFormatMap classificationFormatMap, IThem public UIElement Create() { var text = sb.ToString(); - var propsSpans = CreateTextRunPropertiesAndSpans(); - return TextBlockFactory.Create(text, classificationFormatMap.DefaultTextProperties, propsSpans, TextBlockFactory.Flags.DisableSetTextBlockFontFamily | TextBlockFactory.Flags.DisableFontSize); + return textElementFactory.Create(classificationFormatMap, text, CreateTexClassificationTags(), TextElementFlags.None); } - IEnumerable CreateTextRunPropertiesAndSpans() { + IList CreateTexClassificationTags() { int pos = 0; - foreach (var res in result) { - var props = GetTextFormattingRunProperties(res.Color); - yield return new TextRunPropertiesAndSpan(new Span(pos, res.Text.Length), props); + var tags = new TextClassificationTag[result.Count]; + for (int i = 0; i < result.Count; i++) { + var res = result[i]; + tags[i] = new TextClassificationTag(new Span(pos, res.Text.Length), GetTextClassificationType(res.Color)); pos += res.Text.Length; } + return tags; } - TextFormattingRunProperties GetTextFormattingRunProperties(object color) { + IClassificationType GetTextClassificationType(object color) { if (!syntaxHighlight) color = BoxedTextColor.Text; var classificationType = ColorUtils.GetClassificationType(classificationTypeRegistryService, themeClassificationTypeService, color); @@ -75,7 +77,7 @@ TextFormattingRunProperties GetTextFormattingRunProperties(object color) { var textColor = color as TextColor? ?? TextColor.Text; classificationType = themeClassificationTypeService.GetClassificationType(textColor); } - return classificationFormatMap.GetTextProperties(classificationType); + return classificationType; } void Add(object color, string? text) { diff --git a/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/DocumentViewerToolTipService.cs b/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/DocumentViewerToolTipService.cs index df420f92c0..0352c64c75 100644 --- a/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/DocumentViewerToolTipService.cs +++ b/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/DocumentViewerToolTipService.cs @@ -124,9 +124,10 @@ sealed class DocumentViewerToolTipServiceProvider { readonly IClassificationTypeRegistryService classificationTypeRegistryService; readonly IDecompilerService decompilerService; readonly Lazy[] documentViewerToolTipProviders; + readonly ITextElementFactory textElementFactory; [ImportingConstructor] - DocumentViewerToolTipServiceProvider(IDotNetImageService dotNetImageService, ICodeToolTipSettings codeToolTipSettings, IQuickInfoBroker quickInfoBroker, IClassificationFormatMapService classificationFormatMapService, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, IDecompilerService decompilerService, [ImportMany] IEnumerable> documentViewerToolTipProviders) { + DocumentViewerToolTipServiceProvider(IDotNetImageService dotNetImageService, ICodeToolTipSettings codeToolTipSettings, IQuickInfoBroker quickInfoBroker, IClassificationFormatMapService classificationFormatMapService, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, IDecompilerService decompilerService, [ImportMany] IEnumerable> documentViewerToolTipProviders, ITextElementFactory textElementFactory) { this.dotNetImageService = dotNetImageService; this.codeToolTipSettings = codeToolTipSettings; this.quickInfoBroker = quickInfoBroker; @@ -135,10 +136,11 @@ sealed class DocumentViewerToolTipServiceProvider { this.classificationTypeRegistryService = classificationTypeRegistryService; this.decompilerService = decompilerService; this.documentViewerToolTipProviders = documentViewerToolTipProviders.OrderBy(a => a.Metadata.Order).ToArray(); + this.textElementFactory = textElementFactory; } public DocumentViewerToolTipService GetService(IDocumentViewer documentViewer) => - documentViewer.TextView.Properties.GetOrCreateSingletonProperty(typeof(DocumentViewerToolTipService), () => new DocumentViewerToolTipService(dotNetImageService, codeToolTipSettings, documentViewerToolTipProviders, documentViewer, quickInfoBroker, classificationFormatMapService.GetClassificationFormatMap(AppearanceCategoryConstants.UIMisc), themeClassificationTypeService, classificationTypeRegistryService, decompilerService)); + documentViewer.TextView.Properties.GetOrCreateSingletonProperty(typeof(DocumentViewerToolTipService), () => new DocumentViewerToolTipService(dotNetImageService, codeToolTipSettings, documentViewerToolTipProviders, documentViewer, quickInfoBroker, classificationFormatMapService.GetClassificationFormatMap(AppearanceCategoryConstants.UIMisc), themeClassificationTypeService, classificationTypeRegistryService, decompilerService, textElementFactory)); } [Export(typeof(IQuickInfoSourceProvider))] @@ -179,8 +181,9 @@ sealed class DocumentViewerToolTipService { readonly IThemeClassificationTypeService themeClassificationTypeService; readonly IClassificationTypeRegistryService classificationTypeRegistryService; readonly IDecompilerService decompilerService; + readonly ITextElementFactory textElementFactory; - public DocumentViewerToolTipService(IDotNetImageService dotNetImageService, ICodeToolTipSettings codeToolTipSettings, Lazy[] documentViewerToolTipProviders, IDocumentViewer documentViewer, IQuickInfoBroker quickInfoBroker, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, IDecompilerService decompilerService) { + public DocumentViewerToolTipService(IDotNetImageService dotNetImageService, ICodeToolTipSettings codeToolTipSettings, Lazy[] documentViewerToolTipProviders, IDocumentViewer documentViewer, IQuickInfoBroker quickInfoBroker, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, IDecompilerService decompilerService, ITextElementFactory textElementFactory) { this.dotNetImageService = dotNetImageService ?? throw new ArgumentNullException(nameof(dotNetImageService)); this.codeToolTipSettings = codeToolTipSettings ?? throw new ArgumentNullException(nameof(codeToolTipSettings)); this.documentViewerToolTipProviders = documentViewerToolTipProviders ?? throw new ArgumentNullException(nameof(documentViewerToolTipProviders)); @@ -190,6 +193,7 @@ public DocumentViewerToolTipService(IDotNetImageService dotNetImageService, ICod this.themeClassificationTypeService = themeClassificationTypeService ?? throw new ArgumentNullException(nameof(themeClassificationTypeService)); this.classificationTypeRegistryService = classificationTypeRegistryService ?? throw new ArgumentNullException(nameof(classificationTypeRegistryService)); this.decompilerService = decompilerService ?? throw new ArgumentNullException(nameof(decompilerService)); + this.textElementFactory = textElementFactory ?? throw new ArgumentNullException(nameof(textElementFactory)); } public void AugmentQuickInfoSession(IQuickInfoSession session, IList quickInfoContent, out ITrackingSpan? applicableToSpan) { @@ -246,7 +250,7 @@ public bool TryTriggerQuickInfo() { if (@ref is null) return null; - var ctx = new ToolTipProviderContext(dotNetImageService, decompiler, codeToolTipSettings, documentViewer, classificationFormatMap, themeClassificationTypeService, classificationTypeRegistryService); + var ctx = new ToolTipProviderContext(dotNetImageService, decompiler, codeToolTipSettings, documentViewer, classificationFormatMap, themeClassificationTypeService, classificationTypeRegistryService, textElementFactory); foreach (var provider in documentViewerToolTipProviders) { var toolTipContent = provider.Value.Create(ctx, @ref); if (toolTipContent is not null) diff --git a/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/ToolTipProviderContext.cs b/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/ToolTipProviderContext.cs index 9b3af83e0b..26ac215299 100644 --- a/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/ToolTipProviderContext.cs +++ b/dnSpy/dnSpy/Documents/Tabs/DocViewer/ToolTips/ToolTipProviderContext.cs @@ -35,8 +35,9 @@ sealed class ToolTipProviderContext : IDocumentViewerToolTipProviderContext { readonly IClassificationFormatMap classificationFormatMap; readonly IThemeClassificationTypeService themeClassificationTypeService; readonly IClassificationTypeRegistryService classificationTypeRegistryService; + readonly ITextElementFactory textElementFactory; - public ToolTipProviderContext(IDotNetImageService dotNetImageService, IDecompiler decompiler, ICodeToolTipSettings codeToolTipSettings, IDocumentViewer documentViewer, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService) { + public ToolTipProviderContext(IDotNetImageService dotNetImageService, IDecompiler decompiler, ICodeToolTipSettings codeToolTipSettings, IDocumentViewer documentViewer, IClassificationFormatMap classificationFormatMap, IThemeClassificationTypeService themeClassificationTypeService, IClassificationTypeRegistryService classificationTypeRegistryService, ITextElementFactory textElementFactory) { DocumentViewer = documentViewer ?? throw new ArgumentNullException(nameof(documentViewer)); this.dotNetImageService = dotNetImageService ?? throw new ArgumentNullException(nameof(dotNetImageService)); Decompiler = decompiler ?? throw new ArgumentNullException(nameof(decompiler)); @@ -44,9 +45,10 @@ public ToolTipProviderContext(IDotNetImageService dotNetImageService, IDecompile this.classificationFormatMap = classificationFormatMap ?? throw new ArgumentNullException(nameof(classificationFormatMap)); this.themeClassificationTypeService = themeClassificationTypeService ?? throw new ArgumentNullException(nameof(themeClassificationTypeService)); this.classificationTypeRegistryService = classificationTypeRegistryService ?? throw new ArgumentNullException(nameof(classificationTypeRegistryService)); + this.textElementFactory = textElementFactory ?? throw new ArgumentNullException(nameof(textElementFactory)); } public ICodeToolTipProvider Create() => - new CodeToolTipProvider(DocumentViewer.TextView, dotNetImageService, classificationFormatMap, themeClassificationTypeService, classificationTypeRegistryService, codeToolTipSettings.SyntaxHighlight); + new CodeToolTipProvider(DocumentViewer.TextView, dotNetImageService, classificationFormatMap, themeClassificationTypeService, classificationTypeRegistryService, textElementFactory, codeToolTipSettings.SyntaxHighlight); } } diff --git a/dnSpy/dnSpy/Language/Intellisense/CompletionTextElementProvider.cs b/dnSpy/dnSpy/Language/Intellisense/CompletionTextElementProvider.cs index a71e0118c4..581fc76967 100644 --- a/dnSpy/dnSpy/Language/Intellisense/CompletionTextElementProvider.cs +++ b/dnSpy/dnSpy/Language/Intellisense/CompletionTextElementProvider.cs @@ -18,9 +18,7 @@ You should have received a copy of the GNU General Public License */ using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Windows; using dnSpy.Contracts.Language.Intellisense; using dnSpy.Contracts.Language.Intellisense.Classification; @@ -32,22 +30,14 @@ You should have received a copy of the GNU General Public License namespace dnSpy.Language.Intellisense { sealed class CompletionTextElementProvider : ICompletionTextElementProvider { - readonly ITextClassifierAggregatorService textClassifierAggregatorService; readonly IClassificationFormatMap classificationFormatMap; readonly IContentTypeRegistryService contentTypeRegistryService; - readonly Dictionary toClassifier; + readonly ITextElementProvider textElementProvider; - public CompletionTextElementProvider(ITextClassifierAggregatorService textClassifierAggregatorService, IClassificationFormatMap classificationFormatMap, IContentTypeRegistryService contentTypeRegistryService) { - this.textClassifierAggregatorService = textClassifierAggregatorService ?? throw new ArgumentNullException(nameof(textClassifierAggregatorService)); + public CompletionTextElementProvider(IClassificationFormatMap classificationFormatMap, IContentTypeRegistryService contentTypeRegistryService, ITextElementProvider textElementProvider) { this.classificationFormatMap = classificationFormatMap ?? throw new ArgumentNullException(nameof(classificationFormatMap)); this.contentTypeRegistryService = contentTypeRegistryService ?? throw new ArgumentNullException(nameof(contentTypeRegistryService)); - toClassifier = new Dictionary(); - } - - ITextClassifier GetTextClassifier(IContentType contentType) { - if (!toClassifier.TryGetValue(contentType, out var completionClassifier)) - toClassifier.Add(contentType, completionClassifier = textClassifierAggregatorService.Create(contentType)); - return completionClassifier; + this.textElementProvider = textElementProvider ?? throw new ArgumentNullException(nameof(textElementProvider)); } public FrameworkElement Create(CompletionSet completionSet, Completion completion, CompletionClassifierKind kind, bool colorize) { @@ -78,16 +68,10 @@ public FrameworkElement Create(CompletionSet completionSet, Completion completio var contentType = (completionSet as ICompletionSetContentTypeProvider)?.GetContentType(contentTypeRegistryService, kind); if (contentType is null) - contentType = contentTypeRegistryService.GetContentType(defaultContentType); - var classifier = GetTextClassifier(contentType); - return TextBlockFactory.Create(context.Text, classificationFormatMap.DefaultTextProperties, - classifier.GetTags(context).Select(a => new TextRunPropertiesAndSpan(a.Span, classificationFormatMap.GetTextProperties(a.ClassificationType))), TextBlockFactory.Flags.DisableSetTextBlockFontFamily | TextBlockFactory.Flags.DisableFontSize); + return textElementProvider.CreateTextElement(classificationFormatMap, context, defaultContentType, TextElementFlags.None); + return textElementProvider.CreateTextElement(classificationFormatMap, context, contentType, TextElementFlags.None); } - public void Dispose() { - foreach (var classifier in toClassifier.Values) - (classifier as IDisposable)?.Dispose(); - toClassifier.Clear(); - } + public void Dispose() { } } } diff --git a/dnSpy/dnSpy/Language/Intellisense/CompletionTextElementProviderService.cs b/dnSpy/dnSpy/Language/Intellisense/CompletionTextElementProviderService.cs index 7964c478f2..c21316c939 100644 --- a/dnSpy/dnSpy/Language/Intellisense/CompletionTextElementProviderService.cs +++ b/dnSpy/dnSpy/Language/Intellisense/CompletionTextElementProviderService.cs @@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ -using System; using System.ComponentModel.Composition; using dnSpy.Contracts.Settings.AppearanceCategory; using dnSpy.Contracts.Text.Classification; @@ -27,17 +26,17 @@ You should have received a copy of the GNU General Public License namespace dnSpy.Language.Intellisense { [Export(typeof(ICompletionTextElementProviderService))] sealed class CompletionTextElementProviderService : ICompletionTextElementProviderService { - readonly Lazy textClassifierAggregatorService; readonly IClassificationFormatMapService classificationFormatMapService; readonly IContentTypeRegistryService contentTypeRegistryService; + readonly ITextElementProvider textElementProvider; [ImportingConstructor] - CompletionTextElementProviderService(Lazy textClassifierAggregatorService, IClassificationFormatMapService classificationFormatMapService, IContentTypeRegistryService contentTypeRegistryService) { - this.textClassifierAggregatorService = textClassifierAggregatorService; + CompletionTextElementProviderService(IClassificationFormatMapService classificationFormatMapService, IContentTypeRegistryService contentTypeRegistryService, ITextElementProvider textElementProvider) { this.classificationFormatMapService = classificationFormatMapService; this.contentTypeRegistryService = contentTypeRegistryService; + this.textElementProvider = textElementProvider; } - public ICompletionTextElementProvider Create() => new CompletionTextElementProvider(textClassifierAggregatorService.Value, classificationFormatMapService.GetClassificationFormatMap(AppearanceCategoryConstants.UIMisc), contentTypeRegistryService); + public ICompletionTextElementProvider Create() => new CompletionTextElementProvider(classificationFormatMapService.GetClassificationFormatMap(AppearanceCategoryConstants.UIMisc), contentTypeRegistryService, textElementProvider); } } diff --git a/dnSpy/dnSpy/Language/Intellisense/SignatureHelpPresenter.cs b/dnSpy/dnSpy/Language/Intellisense/SignatureHelpPresenter.cs index b84f1dc41a..36a29b5c47 100644 --- a/dnSpy/dnSpy/Language/Intellisense/SignatureHelpPresenter.cs +++ b/dnSpy/dnSpy/Language/Intellisense/SignatureHelpPresenter.cs @@ -93,6 +93,7 @@ public double Opacity { readonly IClassifierAggregatorService classifierAggregatorService; readonly IClassificationFormatMap classificationFormatMap; readonly IContentType defaultExtendedContentType; + readonly ITextElementFactory textElementFactory; ISignature? currentSignature; IClassifier? signatureClassifier; IClassifier? otherClassifier; @@ -105,7 +106,7 @@ public double Opacity { static readonly ContentTypeDefinition? defaultContentTypeDefinition; #pragma warning restore CS0169 - public SignatureHelpPresenter(ISignatureHelpSession session, ITextBufferFactoryService textBufferFactoryService, IContentTypeRegistryService contentTypeRegistryService, IClassifierAggregatorService classifierAggregatorService, IClassificationFormatMap classificationFormatMap) { + public SignatureHelpPresenter(ISignatureHelpSession session, ITextBufferFactoryService textBufferFactoryService, IContentTypeRegistryService contentTypeRegistryService, IClassifierAggregatorService classifierAggregatorService, IClassificationFormatMap classificationFormatMap, ITextElementFactory textElementFactory) { if (textBufferFactoryService is null) throw new ArgumentNullException(nameof(textBufferFactoryService)); this.session = session ?? throw new ArgumentNullException(nameof(session)); @@ -117,6 +118,7 @@ public SignatureHelpPresenter(ISignatureHelpSession session, ITextBufferFactoryS this.contentTypeRegistryService = contentTypeRegistryService ?? throw new ArgumentNullException(nameof(contentTypeRegistryService)); this.classifierAggregatorService = classifierAggregatorService ?? throw new ArgumentNullException(nameof(classifierAggregatorService)); this.classificationFormatMap = classificationFormatMap ?? throw new ArgumentNullException(nameof(classificationFormatMap)); + this.textElementFactory = textElementFactory ?? throw new ArgumentNullException(nameof(textElementFactory)); defaultExtendedContentType = contentTypeRegistryService.GetContentType(DefaultExtendedContentTypeName); Debug2.Assert(defaultExtendedContentType is not null); classificationFormatMap.ClassificationFormatMappingChanged += ClassificationFormatMap_ClassificationFormatMappingChanged; @@ -225,9 +227,7 @@ void Signatures_CollectionChanged(object? sender, NotifyCollectionChangedEventAr if (sigIndex < 0) return null; var text = string.Format(dnSpy_Resources.SignatureHelp_Signature_N_of_TotalCount, sigIndex + 1, session.Signatures.Count); - - var propsSpans = Array.Empty(); - return TextBlockFactory.Create(text, classificationFormatMap.DefaultTextProperties, propsSpans, TextBlockFactory.Flags.DisableSetTextBlockFontFamily | TextBlockFactory.Flags.DisableFontSize); + return textElementFactory.Create(classificationFormatMap, text, Array.Empty(), TextElementFlags.None); } object? CreateSignatureDocumentationObject() { @@ -275,8 +275,8 @@ void Signatures_CollectionChanged(object? sender, NotifyCollectionChangedEventAr } var classificationSpans = signatureClassifier.GetClassificationSpans(new SnapshotSpan(signatureTextBuffer.CurrentSnapshot, 0, signatureTextBuffer.CurrentSnapshot.Length)); - var propsSpans = classificationSpans.Select(a => new TextRunPropertiesAndSpan(a.Span.Span, classificationFormatMap.GetTextProperties(a.ClassificationType))); - return TextBlockFactory.Create(text, classificationFormatMap.DefaultTextProperties, propsSpans, TextBlockFactory.Flags.DisableSetTextBlockFontFamily | TextBlockFactory.Flags.DisableFontSize); + var classificationTags = classificationSpans.Select(x => new TextClassificationTag(x.Span.Span, x.ClassificationType)).ToArray(); + return textElementFactory.Create(classificationFormatMap, text, classificationTags, TextElementFlags.None); } void RegisterSignatureClassifierEvents() { @@ -330,8 +330,8 @@ object CreateUIObject(string text, IContentType contentType, SignatureHelpClassi } var classificationSpans = otherClassifier.GetClassificationSpans(new SnapshotSpan(otherTextBuffer.CurrentSnapshot, 0, otherTextBuffer.CurrentSnapshot.Length)); - var propsSpans = classificationSpans.Select(a => new TextRunPropertiesAndSpan(a.Span.Span, classificationFormatMap.GetTextProperties(a.ClassificationType))); - var result = TextBlockFactory.Create(text, classificationFormatMap.DefaultTextProperties, propsSpans, TextBlockFactory.Flags.DisableSetTextBlockFontFamily | TextBlockFactory.Flags.DisableFontSize); + var classificationTags = classificationSpans.Select(x => new TextClassificationTag(x.Span.Span, x.ClassificationType)).ToArray(); + var result = textElementFactory.Create(classificationFormatMap, text, classificationTags, TextElementFlags.None); otherTextBuffer.Properties.RemoveProperty(SignatureHelpConstants.SignatureHelpClassifierContextBufferKey); return result; } diff --git a/dnSpy/dnSpy/Language/Intellisense/SignatureHelpPresenterProvider.cs b/dnSpy/dnSpy/Language/Intellisense/SignatureHelpPresenterProvider.cs index c7166795e8..c48b0def20 100644 --- a/dnSpy/dnSpy/Language/Intellisense/SignatureHelpPresenterProvider.cs +++ b/dnSpy/dnSpy/Language/Intellisense/SignatureHelpPresenterProvider.cs @@ -21,6 +21,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Language.Intellisense; using dnSpy.Contracts.Settings.AppearanceCategory; using dnSpy.Contracts.Text; +using dnSpy.Contracts.Text.Classification; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; @@ -35,20 +36,22 @@ sealed class SignatureHelpPresenterProvider : IIntellisensePresenterProvider { readonly IContentTypeRegistryService contentTypeRegistryService; readonly IClassifierAggregatorService classifierAggregatorService; readonly IClassificationFormatMapService classificationFormatMapService; + readonly ITextElementFactory textElementFactory; [ImportingConstructor] - SignatureHelpPresenterProvider(ITextBufferFactoryService textBufferFactoryService, IContentTypeRegistryService contentTypeRegistryService, IClassifierAggregatorService classifierAggregatorService, IClassificationFormatMapService classificationFormatMapService) { + SignatureHelpPresenterProvider(ITextBufferFactoryService textBufferFactoryService, IContentTypeRegistryService contentTypeRegistryService, IClassifierAggregatorService classifierAggregatorService, IClassificationFormatMapService classificationFormatMapService, ITextElementFactory textElementFactory) { this.textBufferFactoryService = textBufferFactoryService; this.contentTypeRegistryService = contentTypeRegistryService; this.classifierAggregatorService = classifierAggregatorService; this.classificationFormatMapService = classificationFormatMapService; + this.textElementFactory = textElementFactory; } public IIntellisensePresenter? TryCreateIntellisensePresenter(IIntellisenseSession session) { var signatureHelpSession = session as ISignatureHelpSession; if (signatureHelpSession is null) return null; - return new SignatureHelpPresenter(signatureHelpSession, textBufferFactoryService, contentTypeRegistryService, classifierAggregatorService, classificationFormatMapService.GetClassificationFormatMap(AppearanceCategoryConstants.UIMisc)); + return new SignatureHelpPresenter(signatureHelpSession, textBufferFactoryService, contentTypeRegistryService, classifierAggregatorService, classificationFormatMapService.GetClassificationFormatMap(AppearanceCategoryConstants.UIMisc), textElementFactory); } } } diff --git a/dnSpy/dnSpy/Text/Classification/TextElementProvider.cs b/dnSpy/dnSpy/Text/Classification/TextElementProvider.cs index 5d61d254ec..c814b791a7 100644 --- a/dnSpy/dnSpy/Text/Classification/TextElementProvider.cs +++ b/dnSpy/dnSpy/Text/Classification/TextElementProvider.cs @@ -52,9 +52,19 @@ public FrameworkElement CreateTextElement(IClassificationFormatMap classificatio var ct = contentTypeRegistryService.GetContentType(contentType); if (ct is null) throw new ArgumentException($"Invalid content type: {contentType}"); + return CreateTextElement(classificationFormatMap, context, ct, flags); + } + + public FrameworkElement CreateTextElement(IClassificationFormatMap classificationFormatMap, TextClassifierContext context, IContentType contentType, TextElementFlags flags) { + if (classificationFormatMap is null) + throw new ArgumentNullException(nameof(classificationFormatMap)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (contentType is null) + throw new ArgumentNullException(nameof(contentType)); - if (!toAggregator.TryGetValue(ct, out var aggregator)) - toAggregator.Add(ct, aggregator = textClassifierAggregatorService.Create(ct)); + if (!toAggregator.TryGetValue(contentType, out var aggregator)) + toAggregator.Add(contentType, aggregator = textClassifierAggregatorService.Create(contentType)); try { tagsList.AddRange(aggregator.GetTags(context)); return TextElementFactory.Create(classificationFormatMap, context.Text, tagsList, flags); diff --git a/dnSpy/dnSpy/Text/NormalizedTextChangeCollection.cs b/dnSpy/dnSpy/Text/NormalizedTextChangeCollection.cs index 23f63891a7..bb077d3982 100644 --- a/dnSpy/dnSpy/Text/NormalizedTextChangeCollection.cs +++ b/dnSpy/dnSpy/Text/NormalizedTextChangeCollection.cs @@ -50,6 +50,8 @@ public bool IncludesLineChanges { NormalizedTextChangeCollection(ITextChange[] changes) => this.changes = changes; public static INormalizedTextChangeCollection Create(IList changes) { + if (changes is INormalizedTextChangeCollection normalizedTextChangeCollection) + return normalizedTextChangeCollection; if (changes.Count == 0) return new NormalizedTextChangeCollection(Array.Empty()); return new NormalizedTextChangeCollection(CreateNormalizedList(changes).ToArray());