提交 446177b8 编写于 作者: R Ravi Chande 提交者: GitHub

Merge pull request #16861 from rchande/fix16565

Add a completion provider that suggests tuple names
......@@ -196,6 +196,7 @@
<Compile Include="CodeActions\ConvertNumericLiteral\ConvertNumericLiteralTests.cs" />
<Compile Include="CodeActions\UseNamedArguments\UseNamedArgumentsTests.cs" />
<Compile Include="Completion\CompletionProviders\OverrideCompletionProviderTests_ExpressionBody.cs" />
<Compile Include="Completion\CompletionProviders\TupleNameCompletionProviderTests.cs" />
<Compile Include="ConvertToInterpolatedString\ConvertConcatenationToInterpolatedStringTests.cs" />
<Compile Include="ConvertToInterpolatedString\ConvertPlaceholderToInterpolatedStringTests.cs" />
<Compile Include="CodeActions\EncapsulateField\EncapsulateFieldTests.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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp.Completion.Providers;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionSetSources
{
public class TupleNameCompletionProviderTests : AbstractCSharpCompletionProviderTests
{
public TupleNameCompletionProviderTests(CSharpTestWorkspaceFixture workspaceFixture) : base(workspaceFixture)
{
}
internal override CompletionProvider CreateCompletionProvider() => new TupleNameCompletionProvider();
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task AfterOpenParen()
{
await VerifyItemExistsAsync(@"
class Program
{
static void Main(string[] args)
{
(int word, int zword) t = ($$
}
}", "word:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task AfterOpenParenWithBraceCompletion()
{
await VerifyItemExistsAsync(@"
class Program
{
static void Main(string[] args)
{
(int word, int zword) t = ($$)
}
}", "word:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task AfterOpenParenInTupleExpression()
{
await VerifyItemExistsAsync(@"
class Program
{
static void Main(string[] args)
{
(int word, int zword) t = ($$, zword: 2
}
}", "word:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task AfterOpenParenInTupleExpressionWithBraceCompletion()
{
await VerifyItemExistsAsync(@"
class Program
{
static void Main(string[] args)
{
(int word, int zword) t = ($$, zword: 2
}
}", "word:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task AfterComma()
{
await VerifyItemExistsAsync(@"
class Program
{
static void Main(string[] args)
{
(int word, int zword) t = (1, $$
}
}", "zword:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task AfterCommaWithBraceCompletion()
{
await VerifyItemExistsAsync(@"
class Program
{
static void Main(string[] args)
{
(int word, int zword) t = (1, $$)
}
}", "zword:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InTupleAsArgument()
{
await VerifyItemExistsAsync(@"
class Program
{
static void Main((int word, int zword) args)
{
Main(($$))
}
}", "word:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task MultiplePossibleTuples()
{
var markup = @"
class Program
{
static void Main((int number, int znumber) args) { }
static void Main((string word, int zword) args) {
Main(($$
}
}";
await VerifyItemExistsAsync(markup, "word:");
await VerifyItemExistsAsync(markup, "number:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task MultiplePossibleTuplesAfterComma()
{
var markup = @"
class Program
{
static void Main((int number, int znumber) args) { }
static void Main((string word, int zword) args) {
Main((1, $$
}
}";
await VerifyItemExistsAsync(markup, "zword:");
await VerifyItemExistsAsync(markup, "znumber:");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task AtIndexGreaterThanNumberOfTupleElements()
{
var markup = @"
class Program
{
static void Main(string[] args)
{
(int word, int zword) t = (1, 2, 3, 4, $$
}
}";
await VerifyNoItemsExistAsync(markup);
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task ConvertCastToTupleExpression()
{
var markup = @"
class C
{
void foo()
{
(int goat, int moat) x = (g$$)1;
}
}";
await VerifyItemExistsAsync(markup, "goat:");
}
}
}
......@@ -66,6 +66,7 @@
<Compile Include="ConvertIfToSwitch\CSharpConvertIfToSwitchCodeRefactoringProvider.Pattern.cs" />
<Compile Include="ConvertNumericLiteral\CSharpConvertNumericLiteralCodeRefactoringProvider.cs" />
<Compile Include="CodeRefactorings\UseNamedArguments\CSharpUseNamedArgumentsCodeRefactoringProvider.cs" />
<Compile Include="Completion\CompletionProviders\TupleNameCompletionProvider.cs" />
<Compile Include="CSharpFeaturesResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
......
......@@ -41,7 +41,8 @@ internal class CSharpCompletionService : CommonCompletionService
new OverrideCompletionProvider(),
new PartialMethodCompletionProvider(),
new PartialTypeCompletionProvider(),
new XmlDocCommentCompletionProvider()
new XmlDocCommentCompletionProvider(),
new TupleNameCompletionProvider()
);
private readonly Workspace _workspace;
......
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Completion.Providers;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.SignatureHelp;
namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers
{
internal class TupleNameCompletionProvider : CommonCompletionProvider
{
private static readonly CompletionItemRules _cachedRules = CompletionItemRules.Default
.WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, ':'));
public override async Task ProvideCompletionsAsync(CompletionContext completionContext)
{
var document = completionContext.Document;
var position = completionContext.Position;
var cancellationToken = completionContext.CancellationToken;
var semanticModel = await document.GetSemanticModelForSpanAsync(new Text.TextSpan(position, 0), cancellationToken).ConfigureAwait(false);
var workspace = document.Project.Solution.Workspace;
var context = CSharpSyntaxContext.CreateContext(workspace, semanticModel, position, cancellationToken);
var index = GetElementIndex(context);
if (index == null)
{
return;
}
var typeInferrer = document.GetLanguageService<ITypeInferenceService>();
var inferredTypes = typeInferrer.InferTypes(semanticModel, context.TargetToken.Parent.SpanStart, cancellationToken)
.Where(t => t.IsTupleType)
.Cast<INamedTypeSymbol>()
.ToImmutableArray();
AddItems(inferredTypes, index.Value, completionContext);
}
private int? GetElementIndex(CSharpSyntaxContext context)
{
var token = context.TargetToken;
if (token.IsKind(SyntaxKind.OpenParenToken))
{
if (token.Parent.IsKind(SyntaxKind.ParenthesizedExpression,
SyntaxKind.TupleExpression,
SyntaxKind.CastExpression))
{
return 0;
}
}
if (token.IsKind(SyntaxKind.CommaToken) && token.Parent.IsKind(SyntaxKind.TupleExpression))
{
var tupleExpr = (TupleExpressionSyntax)context.TargetToken.Parent as TupleExpressionSyntax;
return (tupleExpr.Arguments.GetWithSeparators().IndexOf(context.TargetToken) + 1) / 2;
}
return null;
}
private void AddItems(ImmutableArray<INamedTypeSymbol> inferredTypes, int index, CompletionContext context)
{
foreach (var type in inferredTypes)
{
if (index >= type.TupleElements.Length)
{
return;
}
var field = type.TupleElements[index];
var item = CommonCompletionItem.Create(
field.Name + ":",
Glyph.FieldPublic,
rules: _cachedRules);
context.AddItem(item);
}
}
}
}
......@@ -1243,7 +1243,8 @@ private static SyntaxToken FindTokenOnLeftOfNode(SyntaxNode node)
return node.FindTokenOnLeftOfPosition(node.SpanStart);
}
private static bool IsPossibleTupleOpenParenOrComma(SyntaxToken possibleCommaOrParen)
public static bool IsPossibleTupleOpenParenOrComma(SyntaxToken possibleCommaOrParen)
{
if (!possibleCommaOrParen.IsKind(SyntaxKind.OpenParenToken, SyntaxKind.CommaToken))
{
......@@ -1251,10 +1252,10 @@ private static bool IsPossibleTupleOpenParenOrComma(SyntaxToken possibleCommaOrP
}
if (possibleCommaOrParen.Parent.IsKind(
SyntaxKind.ParenthesizedExpression,
SyntaxKind.TupleExpression,
SyntaxKind.TupleType,
SyntaxKind.CastExpression))
SyntaxKind.ParenthesizedExpression,
SyntaxKind.TupleExpression,
SyntaxKind.TupleType,
SyntaxKind.CastExpression))
{
return true;
}
......@@ -1268,8 +1269,8 @@ private static bool IsPossibleTupleOpenParenOrComma(SyntaxToken possibleCommaOrP
{
return true;
}
}
return false;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册