-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #73277 from dotnet/merges/release/dev17.11-to-main
Merge release/dev17.11 to main
- Loading branch information
Showing
43 changed files
with
1,100 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/EditorFeatures/Core.Wpf/QuickInfo/ClassificationFormatDefinitions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// 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.ComponentModel.Composition; | ||
using Microsoft.CodeAnalysis.Host.Mef; | ||
using Microsoft.VisualStudio.Language.StandardClassification; | ||
using Microsoft.VisualStudio.Text.Classification; | ||
using Microsoft.VisualStudio.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.Editor.QuickInfo; | ||
|
||
internal sealed class ClassificationFormatDefinitions | ||
{ | ||
[Export(typeof(EditorFormatDefinition))] | ||
[ClassificationType(ClassificationTypeNames = ClassificationTypeDefinitions.ReducedEmphasisText)] | ||
[Name(ClassificationTypeDefinitions.ReducedEmphasisText)] | ||
[Order(After = Priority.High)] | ||
[UserVisible(false)] | ||
private class ReducedEmphasisTextFormat : ClassificationFormatDefinition | ||
{ | ||
[ImportingConstructor] | ||
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] | ||
public ReducedEmphasisTextFormat() | ||
{ | ||
this.ForegroundOpacity = 0.65f; | ||
} | ||
} | ||
} | ||
|
||
internal sealed class ClassificationTypeDefinitions | ||
{ | ||
// Only used for theming, does not need localized | ||
public const string ReducedEmphasisText = "Reduced Emphasis Text"; | ||
|
||
[Export] | ||
[Name(ReducedEmphasisText)] | ||
[BaseDefinition(PredefinedClassificationTypeNames.Text)] | ||
internal readonly ClassificationTypeDefinition? ReducedEmphasisTextTypeDefinition; | ||
} |
26 changes: 26 additions & 0 deletions
26
src/EditorFeatures/Core.Wpf/QuickInfo/OnTheFlyDocsState.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// 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. | ||
|
||
namespace Microsoft.CodeAnalysis.Editor.QuickInfo; | ||
|
||
/// <summary> | ||
/// Represents the potential states of the view. | ||
/// </summary> | ||
internal enum OnTheFlyDocsState | ||
{ | ||
/// <summary> | ||
/// The view is displaying the on-demand hyperlink. | ||
/// </summary> | ||
OnDemandLink, | ||
|
||
/// <summary> | ||
/// The view is in the loading state. | ||
/// </summary> | ||
Loading, | ||
|
||
/// <summary> | ||
/// The view is displaying computed results. | ||
/// </summary> | ||
Finished, | ||
} |
32 changes: 32 additions & 0 deletions
32
src/EditorFeatures/Core.Wpf/QuickInfo/OnTheFlyDocsView.xaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<UserControl x:Class="Microsoft.CodeAnalysis.Editor.QuickInfo.OnTheFlyDocsView" | ||
x:ClassModifier="internal" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:local="clr-namespace:Microsoft.CodeAnalysis.Editor.QuickInfo" | ||
DataContext="{Binding RelativeSource={RelativeSource Self}}" | ||
AutomationProperties.Name="{Binding OnTheFlyDocumentation}"> | ||
<UserControl.Resources> | ||
<local:OnTheFlyDocsViewStateVisibilityConverter x:Key="VisibilityConverter"></local:OnTheFlyDocsViewStateVisibilityConverter> | ||
</UserControl.Resources> | ||
|
||
<Grid> | ||
<ContentControl | ||
Visibility="{Binding Path=CurrentState, | ||
Converter={StaticResource VisibilityConverter}, | ||
ConverterParameter={x:Static local:OnTheFlyDocsState.OnDemandLink}}" | ||
Content="{Binding Path=OnDemandLinkContent}" /> | ||
<ContentControl | ||
Visibility="{Binding Path=CurrentState, | ||
Converter={StaticResource VisibilityConverter}, | ||
ConverterParameter={x:Static local:OnTheFlyDocsState.Loading}}" | ||
Content="{Binding Path=LoadingContent}" /> | ||
<ContentControl | ||
Visibility="{Binding Path=CurrentState, | ||
Converter={StaticResource VisibilityConverter}, | ||
ConverterParameter={x:Static local:OnTheFlyDocsState.Finished}}" | ||
Content="{Binding Path=ResultsContent}" /> | ||
|
||
</Grid> | ||
</UserControl> |
232 changes: 232 additions & 0 deletions
232
src/EditorFeatures/Core.Wpf/QuickInfo/OnTheFlyDocsView.xaml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
// 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.ComponentModel; | ||
using System.Runtime.CompilerServices; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using System.Windows; | ||
using System.Windows.Controls; | ||
using Microsoft.CodeAnalysis.Classification; | ||
using Microsoft.CodeAnalysis.Copilot; | ||
using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; | ||
using Microsoft.CodeAnalysis.Editor.Shared.Utilities; | ||
using Microsoft.CodeAnalysis.ErrorReporting; | ||
using Microsoft.CodeAnalysis.Internal.Log; | ||
using Microsoft.CodeAnalysis.QuickInfo; | ||
using Microsoft.CodeAnalysis.Shared.Extensions; | ||
using Microsoft.CodeAnalysis.Shared.TestHooks; | ||
using Microsoft.VisualStudio.Language.Intellisense; | ||
using Microsoft.VisualStudio.PlatformUI; | ||
using Microsoft.VisualStudio.Text.Adornments; | ||
using Microsoft.VisualStudio.Text.Editor; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.Editor.QuickInfo; | ||
|
||
/// <summary> | ||
/// Interaction logic for OnTheFlyDocsView.xaml. | ||
/// </summary> | ||
internal sealed partial class OnTheFlyDocsView : UserControl, INotifyPropertyChanged | ||
{ | ||
private readonly ITextView _textView; | ||
private readonly IViewElementFactoryService _viewElementFactoryService; | ||
private readonly IAsynchronousOperationListener _asyncListener; | ||
private readonly IAsyncQuickInfoSession _asyncQuickInfoSession; | ||
private readonly IThreadingContext _threadingContext; | ||
private readonly Document _document; | ||
private readonly OnTheFlyDocsElement _onTheFlyDocsElement; | ||
private readonly ContentControl _responseControl = new(); | ||
private readonly CancellationTokenSource _cancellationTokenSource = new(); | ||
|
||
private OnTheFlyDocsState _currentState = OnTheFlyDocsState.OnDemandLink; | ||
|
||
/// <inheritdoc/> | ||
public event PropertyChangedEventHandler? PropertyChanged; | ||
|
||
/// <summary> | ||
/// Event that fires when the user requests results. | ||
/// </summary> | ||
public event EventHandler ResultsRequested; | ||
|
||
#pragma warning disable CA1822 // Mark members as static | ||
/// <summary> | ||
/// Used to display the "On the fly documentation" directly in the associated XAML file. | ||
/// </summary> | ||
public string OnTheFlyDocumentation => EditorFeaturesResources.On_the_fly_documentation; | ||
#pragma warning restore CA1822 // Mark members as static | ||
|
||
public OnTheFlyDocsView(ITextView textView, IViewElementFactoryService viewElementFactoryService, IAsynchronousOperationListenerProvider listenerProvider, IAsyncQuickInfoSession asyncQuickInfoSession, IThreadingContext threadingContext, EditorFeaturesOnTheFlyDocsElement editorFeaturesOnTheFlyDocsElement) | ||
{ | ||
_textView = textView; | ||
_viewElementFactoryService = viewElementFactoryService; | ||
_asyncListener = listenerProvider.GetListener(FeatureAttribute.OnTheFlyDocs); | ||
_asyncQuickInfoSession = asyncQuickInfoSession; | ||
_threadingContext = threadingContext; | ||
_onTheFlyDocsElement = editorFeaturesOnTheFlyDocsElement.OnTheFlyDocsElement; | ||
_document = editorFeaturesOnTheFlyDocsElement.Document; | ||
|
||
var sparkle = new ImageElement(new VisualStudio.Core.Imaging.ImageId(CopilotConstants.CopilotIconMonikerGuid, CopilotConstants.CopilotIconSparkleId)); | ||
|
||
OnDemandLinkContent = ToUIElement( | ||
new ContainerElement( | ||
ContainerElementStyle.Wrapped, | ||
new object[] | ||
{ | ||
sparkle, | ||
ClassifiedTextElement.CreateHyperlink(EditorFeaturesResources.Tell_me_more, EditorFeaturesResources.Show_an_AI_generated_summary_of_this_code, () => | ||
RequestResults()), | ||
})); | ||
|
||
LoadingContent = ToUIElement( | ||
new ContainerElement( | ||
ContainerElementStyle.Stacked, | ||
new object[] | ||
{ | ||
new ClassifiedTextElement(new ClassifiedTextRun( | ||
ClassificationTypeDefinitions.ReducedEmphasisText, EditorFeaturesResources.GitHub_Copilot_thinking)), | ||
new SmoothProgressBar { IsIndeterminate = true, Height = 2, Margin = new Thickness { Top = 2 } }, | ||
})); | ||
|
||
// Ensure the loading content stretches so that the progress bar | ||
// takes the entire width of the quick info tooltip. | ||
if (LoadingContent is FrameworkElement element) | ||
{ | ||
element.HorizontalAlignment = HorizontalAlignment.Stretch; | ||
} | ||
|
||
ResultsContent = ToUIElement( | ||
new ContainerElement( | ||
ContainerElementStyle.Stacked, | ||
new object[] | ||
{ | ||
new ContainerElement( | ||
ContainerElementStyle.Wrapped, | ||
new object[] | ||
{ | ||
sparkle, | ||
ClassifiedTextElement.CreatePlainText(EditorFeaturesResources.GitHub_Copilot), | ||
}), | ||
new ThematicBreakElement(), | ||
_responseControl, | ||
new ThematicBreakElement(), | ||
new ClassifiedTextElement(new ClassifiedTextRun( | ||
ClassificationTypeDefinitions.ReducedEmphasisText, EditorFeaturesResources.AI_generated_content_may_be_inaccurate)), | ||
})); | ||
|
||
ResultsRequested += (_, _) => PopulateAIDocumentationElements(_cancellationTokenSource.Token); | ||
_asyncQuickInfoSession.StateChanged += (_, _) => OnQuickInfoSessionChanged(); | ||
InitializeComponent(); | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the documentation for the given symbol from the Copilot service and displays it in the view. | ||
/// </summary> | ||
private void PopulateAIDocumentationElements(CancellationToken cancellationToken) | ||
{ | ||
var token = _asyncListener.BeginAsyncOperation(nameof(SetResultTextAsync)); | ||
var copilotService = _document.GetLanguageService<ICopilotCodeAnalysisService>(); | ||
if (copilotService is not null) | ||
{ | ||
_ = SetResultTextAsync(copilotService, cancellationToken).CompletesAsyncOperation(token); | ||
} | ||
} | ||
|
||
private async Task SetResultTextAsync(ICopilotCodeAnalysisService copilotService, CancellationToken cancellationToken) | ||
{ | ||
var stopwatch = SharedStopwatch.StartNew(); | ||
|
||
try | ||
{ | ||
var response = await copilotService.GetOnTheFlyDocsAsync(_onTheFlyDocsElement.SymbolSignature, _onTheFlyDocsElement.DeclarationCode, _onTheFlyDocsElement.Language, cancellationToken).ConfigureAwait(false); | ||
var copilotRequestTime = stopwatch.Elapsed; | ||
|
||
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); | ||
|
||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
if (response is null || response.Length == 0) | ||
{ | ||
SetResultText(EditorFeaturesResources.An_error_occurred_while_generating_documentation_for_this_code); | ||
CurrentState = OnTheFlyDocsState.Finished; | ||
Logger.Log(FunctionId.Copilot_On_The_Fly_Docs_Error_Displayed, KeyValueLogMessage.Create(m => | ||
{ | ||
m["ElapsedTime"] = copilotRequestTime; | ||
}, LogLevel.Information)); | ||
} | ||
else | ||
{ | ||
SetResultText(response); | ||
CurrentState = OnTheFlyDocsState.Finished; | ||
|
||
Logger.Log(FunctionId.Copilot_On_The_Fly_Docs_Results_Displayed, KeyValueLogMessage.Create(m => | ||
{ | ||
m["ElapsedTime"] = copilotRequestTime; | ||
m["ResponseLength"] = response.Length; | ||
}, LogLevel.Information)); | ||
} | ||
} | ||
catch (OperationCanceledException) | ||
{ | ||
Logger.Log(FunctionId.Copilot_On_The_Fly_Docs_Results_Canceled, KeyValueLogMessage.Create(m => | ||
{ | ||
m["ElapsedTime"] = stopwatch.Elapsed; | ||
}, LogLevel.Information)); | ||
} | ||
catch (Exception e) when (FatalError.ReportAndCatch(e)) | ||
{ | ||
} | ||
} | ||
|
||
private void OnQuickInfoSessionChanged() | ||
{ | ||
if (_asyncQuickInfoSession.State == QuickInfoSessionState.Dismissed) | ||
{ | ||
_cancellationTokenSource.Cancel(); | ||
} | ||
} | ||
|
||
public OnTheFlyDocsState CurrentState | ||
{ | ||
get => _currentState; | ||
set => OnPropertyChanged(ref _currentState, value); | ||
} | ||
|
||
public UIElement OnDemandLinkContent { get; } | ||
|
||
public UIElement LoadingContent { get; } | ||
|
||
public UIElement ResultsContent { get; } | ||
|
||
public void RequestResults() | ||
{ | ||
CurrentState = OnTheFlyDocsState.Loading; | ||
Logger.Log(FunctionId.Copilot_On_The_Fly_Docs_Loading_State_Entered, KeyValueLogMessage.Create(m => | ||
{ | ||
m["SymbolHeaderText"] = _onTheFlyDocsElement.SymbolSignature; | ||
}, LogLevel.Information)); | ||
|
||
ResultsRequested?.Invoke(this, EventArgs.Empty); | ||
} | ||
|
||
/// <summary> | ||
/// Sets the text of the AI-generated response. | ||
/// </summary> | ||
/// <param name="text">Response text to display.</param> | ||
public void SetResultText(string text) | ||
{ | ||
_responseControl.Content = ToUIElement( | ||
new ContainerElement(ContainerElementStyle.Wrapped, new ClassifiedTextElement([new ClassifiedTextRun(ClassificationTypeNames.Text, text)]))); | ||
} | ||
|
||
private void OnPropertyChanged<T>(ref T member, T value, [CallerMemberName] string? name = null) | ||
{ | ||
member = value; | ||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); | ||
} | ||
|
||
private UIElement ToUIElement(object model) | ||
=> _viewElementFactoryService.CreateViewElement<UIElement>(_textView, model); | ||
} |
Oops, something went wrong.