// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.CodeAnalysis.DocumentationComments;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Adornments;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
namespace Microsoft.CodeAnalysis.Editor.InlineHints
{
///
/// This is the tag which implements the IntraTextAdornmentTag and is meant to create the UIElements that get shown
/// in the editor
///
internal class InlineHintsTag : IntraTextAdornmentTag
{
public const string TagId = "inline hints";
private readonly IToolTipService _toolTipService;
private readonly ITextView _textView;
private readonly SnapshotSpan _span;
private readonly SymbolKey? _key;
private readonly IThreadingContext _threadingContext;
private readonly Lazy _streamingPresenter;
private InlineHintsTag(
FrameworkElement adornment,
ITextView textView,
SnapshotSpan span,
SymbolKey? key,
InlineHintsTaggerProvider taggerProvider)
: base(adornment, removalCallback: null, PositionAffinity.Predecessor)
{
_textView = textView;
_span = span;
_key = key;
_streamingPresenter = taggerProvider.StreamingFindUsagesPresenter;
_threadingContext = taggerProvider.ThreadingContext;
_toolTipService = taggerProvider.ToolTipService;
// Sets the tooltip to a string so that the tool tip opening event can be triggered
// Tooltip value does not matter at this point because it immediately gets overwritten by the correct
// information in the Border_ToolTipOpening event handler
adornment.ToolTip = "Quick info";
adornment.ToolTipOpening += Border_ToolTipOpening;
}
///
/// Creates the UIElement on call
/// Uses PositionAffinity.Predecessor because we want the tag to be associated with the preceding character
///
/// The view of the editor
/// The span that has the location of the hint
/// The symbolkey associated with each parameter
public static InlineHintsTag Create(
string text,
TextFormattingRunProperties format,
IWpfTextView textView,
SnapshotSpan span,
SymbolKey? key,
InlineHintsTaggerProvider taggerProvider)
{
return new InlineHintsTag(CreateElement(text, textView, format), textView, span, key, taggerProvider);
}
public async Task> CreateDescriptionAsync(CancellationToken cancellationToken)
{
if (_key != null)
{
var document = _span.Snapshot.TextBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document != null)
{
var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var symbol = _key.Value.Resolve(compilation, cancellationToken: cancellationToken).Symbol;
if (symbol != null)
{
var textContentBuilder = new List();
var workspace = document.Project.Solution.Workspace;
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var symbolDisplayService = document.GetRequiredLanguageService();
var formatter = document.GetRequiredLanguageService();
var sections = await symbolDisplayService.ToDescriptionGroupsAsync(workspace, semanticModel, _span.Start, ImmutableArray.Create(symbol), cancellationToken).ConfigureAwait(false);
textContentBuilder.AddRange(sections[SymbolDescriptionGroups.MainDescription]);
if (formatter != null)
{
var documentation = symbol.GetDocumentationParts(semanticModel, _span.Start, formatter, cancellationToken);
if (documentation.Any())
{
textContentBuilder.AddLineBreak();
textContentBuilder.AddRange(documentation);
}
}
if (sections.TryGetValue(SymbolDescriptionGroups.AnonymousTypes, out var parts))
{
if (!parts.IsDefaultOrEmpty)
{
textContentBuilder.AddLineBreak();
textContentBuilder.AddLineBreak();
textContentBuilder.AddRange(parts);
}
}
var uiCollection = Implementation.IntelliSense.Helpers.BuildInteractiveTextElements(textContentBuilder.ToImmutableArray(),
document, _threadingContext, _streamingPresenter);
return uiCollection;
}
}
}
return Array.Empty