Skip to content

Commit

Permalink
review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
dibarbet committed Mar 23, 2021
1 parent 1a77506 commit 80dc0c2
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ private static async Task VerifyExpectedTextAsync(string activeDocument, string[
workspace.ApplyOptions(options!);
}

var intentSource = workspace.ExportProvider.GetExportedValue<IIntentProcessor>();
var intentSource = workspace.ExportProvider.GetExportedValue<IIntentSourceProvider>();

// The first document will be the active document.
var document = workspace.Documents.Single(d => d.Name == "test1.cs");
Expand All @@ -187,7 +187,7 @@ private static async Task VerifyExpectedTextAsync(string activeDocument, string[
ImmutableArray.Create(rewindTextChange),
TextSpan.FromBounds(rewindTextChange.Span.Start, rewindTextChange.Span.Start),
intentData: null);
var results = await intentSource.ComputeEditsAsync(intentContext, CancellationToken.None).ConfigureAwait(false);
var results = await intentSource.ComputeIntentsAsync(intentContext, CancellationToken.None).ConfigureAwait(false);

// For now, we're just taking the first result to match intellicode behavior.
var result = results.First();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@

namespace Microsoft.CodeAnalysis.ExternalAccess.IntelliCode.Api
{
internal interface IIntentProcessor
internal interface IIntentSourceProvider
{
/// <summary>
/// For an input intent, computes the edits required to apply that intent and returns them.
/// </summary>
/// <param name="intentRequestContext">the intents with the context in which the intent was found.</param>
/// <param name="context">the intents with the context in which the intent was found.</param>
/// <returns>the edits that should be applied to the current snapshot.</returns>
Task<ImmutableArray<IntentResult>> ComputeEditsAsync(IntentRequestContext intentRequestContext, CancellationToken cancellationToken);
Task<ImmutableArray<IntentSource>> ComputeIntentsAsync(IntentRequestContext context, CancellationToken cancellationToken = default);
}

/// <summary>
/// Defines the data needed to compute the code action edits from an intent.
/// </summary>
internal struct IntentRequestContext
internal readonly struct IntentRequestContext
{
/// <summary>
/// The intent name. <see cref="WellKnownIntents"/> contains all intents roslyn knows how to handle.
Expand All @@ -38,7 +38,7 @@ internal struct IntentRequestContext
public string? IntentData { get; }

/// <summary>
/// The text snapshot and selection when <see cref="IIntentProcessor.ComputeEditsAsync"/>
/// The text snapshot and selection when <see cref="IIntentSourceProvider.ComputeIntentsAsync"/>
/// was called to compute the text edits and against which the resulting text edits will be calculated.
/// </summary>
public SnapshotSpan CurrentSnapshotSpan { get; }
Expand Down Expand Up @@ -68,25 +68,26 @@ public IntentRequestContext(string intentName, SnapshotSpan currentSnapshotSpan,
/// <summary>
/// Defines the text changes needed to apply an intent.
/// </summary>
internal struct IntentResult
internal readonly struct IntentSource
{
/// <summary>
/// The text changes that should be applied to the <see cref="IntentRequestContext.CurrentSnapshotSpan"/>
/// The title associated with this intent result.
/// </summary>
public readonly ImmutableArray<TextChange> TextChanges;
public readonly string Title { get; }

/// <summary>
/// The title associated with this intent result.
/// The text changes that should be applied to the <see cref="IntentRequestContext.CurrentSnapshotSpan"/>
/// </summary>
public readonly string Title;
public readonly ImmutableArray<TextChange> TextChanges { get; }

/// <summary>
/// Contains metadata that can be used to identify the kind of sub-action these edits
/// apply to for the requested intent. Used for telemetry purposes only.
/// For example, the code action type name like FieldDelegatingCodeAction.
/// </summary>
public readonly string ActionName;
public readonly string ActionName { get; }

public IntentResult(ImmutableArray<TextChange> textChanges, string title, string actionName)
public IntentSource(string title, ImmutableArray<TextChange> textChanges, string actionName)
{
TextChanges = textChanges;
Title = title ?? throw new ArgumentNullException(nameof(title));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@

namespace Microsoft.CodeAnalysis.ExternalAccess.IntelliCode
{
[Export(typeof(IIntentProcessor)), Shared]
internal class IntentProcessor : IIntentProcessor
[Export(typeof(IIntentSourceProvider)), Shared]
internal class IntentSourceProvider : IIntentSourceProvider
{
private readonly ImmutableDictionary<(string LanguageName, string IntentName), Lazy<IIntentProvider, IIntentProviderMetadata>> _lazyIntentProviders;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public IntentProcessor([ImportMany] IEnumerable<Lazy<IIntentProvider, IIntentProviderMetadata>> lazyIntentProviders)
public IntentSourceProvider([ImportMany] IEnumerable<Lazy<IIntentProvider, IIntentProviderMetadata>> lazyIntentProviders)
{
_lazyIntentProviders = CreateProviderMap(lazyIntentProviders);
}
Expand All @@ -39,15 +39,15 @@ public IntentProcessor([ImportMany] IEnumerable<Lazy<IIntentProvider, IIntentPro
provider => provider);
}

public async Task<ImmutableArray<IntentResult>> ComputeEditsAsync(IntentRequestContext intentRequestContext, CancellationToken cancellationToken)
public async Task<ImmutableArray<IntentSource>> ComputeIntentsAsync(IntentRequestContext intentRequestContext, CancellationToken cancellationToken)
{
var currentDocument = intentRequestContext.CurrentSnapshotSpan.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
Contract.ThrowIfNull(currentDocument);

var currentText = await currentDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
var originalDocument = currentDocument.WithText(currentText.WithChanges(intentRequestContext.PriorTextEdits));
if (currentDocument == null)
{
throw new ArgumentException("could not retrieve document for request snapshot");
}

var languageName = originalDocument.Project.Language;
var languageName = currentDocument.Project.Language;
if (!_lazyIntentProviders.TryGetValue((LanguageName: languageName, IntentName: intentRequestContext.IntentName), out var provider))
{
Logger.Log(FunctionId.Intellicode_UnknownIntent, KeyValueLogMessage.Create(LogType.UserAction, m =>
Expand All @@ -56,9 +56,12 @@ public async Task<ImmutableArray<IntentResult>> ComputeEditsAsync(IntentRequestC
m["language"] = languageName;
}));

return ImmutableArray<IntentResult>.Empty;
return ImmutableArray<IntentSource>.Empty;
}

var currentText = await currentDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
var originalDocument = currentDocument.WithText(currentText.WithChanges(intentRequestContext.PriorTextEdits));

var selectionTextSpan = intentRequestContext.PriorSelection;
var results = await provider.Value.ComputeIntentAsync(
originalDocument,
Expand All @@ -68,10 +71,10 @@ public async Task<ImmutableArray<IntentResult>> ComputeEditsAsync(IntentRequestC
cancellationToken).ConfigureAwait(false);
if (results.IsDefaultOrEmpty)
{
return ImmutableArray<IntentResult>.Empty;
return ImmutableArray<IntentSource>.Empty;
}

using var _ = ArrayBuilder<IntentResult>.GetInstance(out var convertedResults);
using var _ = ArrayBuilder<IntentSource>.GetInstance(out var convertedResults);
foreach (var result in results)
{
var convertedIntent = await ConvertToIntelliCodeResultAsync(result, originalDocument, currentDocument, cancellationToken).ConfigureAwait(false);
Expand All @@ -81,9 +84,12 @@ public async Task<ImmutableArray<IntentResult>> ComputeEditsAsync(IntentRequestC
return convertedResults.ToImmutable();
}

private static async Task<IntentResult?> ConvertToIntelliCodeResultAsync(IntentProcessorResult processorResult, Document originalDocument, Document currentDocument, CancellationToken cancellationToken)
private static async Task<IntentSource?> ConvertToIntelliCodeResultAsync(
IntentProcessorResult processorResult,
Document originalDocument,
Document currentDocument,
CancellationToken cancellationToken)
{
var title = processorResult.Title;
var newSolution = processorResult.Solution;

// Merge linked file changes so all linked files have the same text changes.
Expand All @@ -100,7 +106,7 @@ public async Task<ImmutableArray<IntentResult>> ComputeEditsAsync(IntentRequestC
return null;
}

return new IntentResult(textDiffs, title, processorResult.ActionName);
return new IntentSource(processorResult.Title, textDiffs, processorResult.ActionName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ await ComputeRefactoringsAsync(

static async Task<IntentProcessorResult?> GetIntentProcessorResultAsync(CodeAction codeAction, CancellationToken cancellationToken)
{
var title = codeAction.Title;
var operations = await GetCodeActionOperationsAsync(codeAction, cancellationToken).ConfigureAwait(false);

// Generate ctor will only return an ApplyChangesOperation or potentially document navigation actions.
Expand All @@ -107,19 +106,16 @@ await ComputeRefactoringsAsync(
return null;
}

var type = typeof(CodeAction);
return new IntentProcessorResult(applyChangesOperation.ChangedSolution, title, type.Name);
var type = codeAction.GetType();
return new IntentProcessorResult(applyChangesOperation.ChangedSolution, codeAction.Title, type.Name);
}

static async Task<ImmutableArray<CodeActionOperation>> GetCodeActionOperationsAsync(
CodeAction action,
CancellationToken cancellationToken)
{
if (action is CodeActionWithOptions)
if (action is GenerateConstructorWithDialogCodeAction dialogAction)
{
// Generate ctor only returns GenerateConstructorWithDialogCodeAction for CodeActionWithOptions
var dialogAction = (GenerateConstructorWithDialogCodeAction)action;

// Usually applying this code action pops up a dialog allowing the user to choose which options.
// We can't do that here, so instead we just take the defaults until we have more intent data.
var options = new PickMembersResult(dialogAction.ViableMembers, dialogAction.PickMembersOptions);
Expand Down
1 change: 0 additions & 1 deletion src/Features/Core/Portable/Intents/IntentResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace Microsoft.CodeAnalysis.Features.Intents
/// Defines the text changes needed to apply an intent.
/// </summary>
internal struct IntentProcessorResult

{
/// <summary>
/// The changed solution for this intent result.
Expand Down

0 comments on commit 80dc0c2

Please sign in to comment.