提交 153ae5b9 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #16617 from CyrusNajmabadi/lightbulbCleanup

Filter out light bulb actions with duplicate titles.
Fixes #16024
......@@ -112,6 +112,7 @@
<Compile Include="Implementation\Intellisense\Completion\CompletionFilterReason.cs" />
<Compile Include="Implementation\Intellisense\Completion\FilterResult.cs" />
<Compile Include="Implementation\Structure\BlockTagState.cs" />
<Compile Include="Implementation\Suggestions\SuggestedActionSetComparer.cs" />
<Compile Include="Tags\ExportImageMonikerServiceAttribute.cs" />
<Compile Include="Implementation\NavigateTo\AbstractNavigateToItemDisplay.cs" />
<Compile Include="CommandArgs.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Editor
{
internal interface ICodeRefactoringProducer
{
Task<IEnumerable<CodeRefactoring>> GetCodeRefactoringsAsync(Document document, TextSpan state, CancellationToken cancellationToken);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions
{
internal class SuggestedActionSetComparer : IComparer<SuggestedActionSet>
{
private readonly SnapshotPoint? _targetPoint;
public SuggestedActionSetComparer(SnapshotPoint? targetPoint)
{
_targetPoint = targetPoint;
}
private static int Distance(Span? textSpan, SnapshotPoint? targetPoint)
{
// If we don't have a text span or target point we cannot calculate the distance between them
if (textSpan == null || !textSpan.HasValue || targetPoint == null || !targetPoint.HasValue)
{
return int.MaxValue;
}
var span = textSpan.Value;
var position = targetPoint.Value.Position;
if (position < span.Start)
{
return span.Start - position;
}
else if (position > span.End)
{
return position - span.End;
}
else
{
return 0;
}
}
public int Compare(SuggestedActionSet x, SuggestedActionSet y)
{
if (!_targetPoint.HasValue || !x.ApplicableToSpan.HasValue || !y.ApplicableToSpan.HasValue)
{
// Not enough data to compare, consider them equal
return 0;
}
var distanceX = Distance(x.ApplicableToSpan, _targetPoint);
var distanceY = Distance(y.ApplicableToSpan, _targetPoint);
if (distanceX != 0 || distanceY != 0)
{
return distanceX.CompareTo(distanceY);
}
// This is the case when both actions sets' spans contain the trigger point.
// Now we compare first by start position then by end position.
var targetPosition = _targetPoint.Value.Position;
var distanceToStartX = targetPosition - x.ApplicableToSpan.Value.Start;
var distanceToStartY = targetPosition - y.ApplicableToSpan.Value.Start;
if (distanceToStartX != distanceToStartY)
{
return distanceToStartX.CompareTo(distanceToStartY);
}
var distanceToEndX = x.ApplicableToSpan.Value.End - targetPosition;
var distanceToEndY = y.ApplicableToSpan.Value.End - targetPosition;
return distanceToEndX.CompareTo(distanceToEndY);
}
}
}
\ No newline at end of file
......@@ -159,7 +159,10 @@ public bool TryGetTelemetryId(out Guid telemetryId)
}
}
public IEnumerable<SuggestedActionSet> GetSuggestedActions(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken)
public IEnumerable<SuggestedActionSet> GetSuggestedActions(
ISuggestedActionCategorySet requestedActionCategories,
SnapshotSpan range,
CancellationToken cancellationToken)
{
AssertIsForeground();
......@@ -187,7 +190,61 @@ public IEnumerable<SuggestedActionSet> GetSuggestedActions(ISuggestedActionCateg
}
var allActionSets = InlineActionSetsIfDesirable(result);
return allActionSets;
var orderedActionSets = OrderActionSets(allActionSets);
var filteredSets = FilterActionSetsByTitle(orderedActionSets);
return filteredSets;
}
}
private ImmutableArray<SuggestedActionSet> OrderActionSets(
ImmutableArray<SuggestedActionSet> actionSets)
{
var caretPoint = _textView.GetCaretPoint(_subjectBuffer);
return actionSets.OrderByDescending(s => s.Priority)
.ThenBy(s => s, new SuggestedActionSetComparer(caretPoint))
.ToImmutableArray();
}
private ImmutableArray<SuggestedActionSet> FilterActionSetsByTitle(ImmutableArray<SuggestedActionSet> allActionSets)
{
var result = ArrayBuilder<SuggestedActionSet>.GetInstance();
var seenTitles = new HashSet<string>();
foreach (var set in allActionSets)
{
var filteredSet = FilterActionSetByTitle(set, seenTitles);
if (filteredSet != null)
{
result.Add(filteredSet);
}
}
return result.ToImmutableAndFree();
}
private SuggestedActionSet FilterActionSetByTitle(SuggestedActionSet set, HashSet<string> seenTitles)
{
var actions = ArrayBuilder<ISuggestedAction>.GetInstance();
foreach (var action in set.Actions)
{
if (seenTitles.Add(action.DisplayText))
{
actions.Add(action);
}
}
try
{
return actions.Count == 0
? null
: new SuggestedActionSet(actions.ToImmutable(), set.Title, set.Priority, set.ApplicableToSpan);
}
finally
{
actions.Free();
}
}
......@@ -240,7 +297,7 @@ private SuggestedActionSet InlineActions(SuggestedActionSet actionSet)
supportsFeatureService.SupportsCodeFixes(document) &&
requestedActionCategories.Contains(PredefinedSuggestedActionCategoryNames.CodeFix))
{
// We only include suppressions if lightbulb is asking for everything.
// We only include suppressions if light bulb is asking for everything.
// If the light bulb is only asking for code fixes, then we don't include suppressions.
var includeSuppressionFixes = requestedActionCategories.Contains(PredefinedSuggestedActionCategoryNames.Any);
......@@ -537,7 +594,7 @@ private static SuggestedActionSetPriority GetSuggestedActionSetPriority(CodeActi
// It may seem strange that we kick off a task, but then immediately 'Wait' on
// it. However, it's deliberate. We want to make sure that the code runs on
// the background so that no one takes an accidently dependency on running on
// the background so that no one takes an accidentally dependency on running on
// the UI thread.
var refactorings = Task.Run(
() => _owner._codeRefactoringService.GetRefactoringsAsync(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册