提交 f17f969a 编写于 作者: A Andrew Hall (METAL)

Prepare for UI review

上级 f03875bf
......@@ -80,4 +80,7 @@
<Compile Remove="ReorderParameters\ReorderParametersTests.InvocationErrors.cs" />
<Compile Remove="ReorderParameters\ReorderParametersTests.InvocationLocation.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="MoveToNamespace\" />
</ItemGroup>
</Project>
\ No newline at end of file
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities.MoveToNamespace;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MoveToNamespace
{
public class MoveToNamespaceTests : AbstractMoveToNamespaceTests
{
[WpfFact, Trait(Traits.Feature, Traits.Features.MoveToNamespace)]
public async Task MoveToNamespace_CaretOnNamespaceName()
{
var markup = @"
using System;
namespace A$$
{
class MyClass
{
void Method() { }
}
}";
var expectedMarkup = @"
using System;
namespace B
{
class MyClass
{
void Method() { }
}
}";
await TestMoveToNamespaceCommandCSharpAsync(
markup,
expectedSuccess: true,
expectedNamespace: "B",
expectedMarkup: expectedMarkup);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.MoveToNamespace)]
public async Task MoveToNamespace_CaretOnNamespaceKeyword()
{
var markup = @"
using System;
namespace$$ A
{
class MyClass
{
void Method() { }
}
}";
var expectedMarkup = @"
using System;
namespace B
{
class MyClass
{
void Method() { }
}
}";
await TestMoveToNamespaceCommandCSharpAsync(
markup,
expectedSuccess: true,
expectedNamespace: "B",
expectedMarkup: expectedMarkup);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.MoveToNamespace)]
public async Task MoveToNamespace_CaretOnNamespaceNameMultipleDeclarations()
{
var markup = @"
using System;
namespace A$$
{
class MyClass
{
void Method() { }
}
class MyOtherClass
{
void Method() { }
}
}";
var expectedMarkup = @"
using System;
namespace B
{
class MyClass
{
void Method() { }
}
class MyOtherClass
{
void Method() { }
}
}";
await TestMoveToNamespaceCommandCSharpAsync(
markup,
expectedSuccess: true,
expectedNamespace: "B",
expectedMarkup: expectedMarkup);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.MoveToNamespace)]
public async Task MoveToNamespace_CaretOnTypeDeclaration()
{
var markup = @"
using System;
namespace A
{
class MyClass$$
{
void Method() {}
}
}";
await TestMoveToNamespaceCommandCSharpAsync(
markup,
expectedSuccess: false);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.MoveToNamespace)]
public async Task MoveToNamespace_WithVariousSymbols()
{
var markup = @"
using System;
namespace A$$
{
public delegate void MyDelegate();
public enum MyEnum
{
One,
Two,
Three
}
public struct MyStruct
{ }
public interface MyInterface
{ }
class MyClass
{
void Method() { }
}
class MyOtherClass
{
void Method() { }
}
}";
var expectedMarkup = @"
using System;
namespace B
{
public delegate void MyDelegate();
public enum MyEnum
{
One,
Two,
Three
}
public struct MyStruct
{ }
public interface MyInterface
{ }
class MyClass
{
void Method() { }
}
class MyOtherClass
{
void Method() { }
}
}";
await TestMoveToNamespaceCommandCSharpAsync(
markup,
expectedSuccess: true,
expectedNamespace: "B",
expectedMarkup: expectedMarkup);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.MoveToNamespace)]
public async Task MoveToNamespace_NestedNamespace()
{
var markup = @"
using System;
namespace A$$
{
namespace C
{
class MyClass
{
void Method() { }
}
}
}";
await TestMoveToNamespaceCommandCSharpAsync(
markup,
expectedSuccess: false);
}
}
}
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.AddImports;
using Microsoft.CodeAnalysis.CSharp.ChangeNamespace;
using Microsoft.CodeAnalysis.CSharp.MoveToNamespace;
using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports;
using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.MoveToNamespace;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.Composition;
using Xunit;
using static Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.AbstractCodeActionOrUserDiagnosticTest;
namespace Microsoft.CodeAnalysis.Test.Utilities.MoveToNamespace
{
[UseExportProvider]
public abstract class AbstractMoveToNamespaceTests
{
private static readonly IExportProviderFactory CSharpExportProviderFactory =
ExportProviderCache.GetOrCreateExportProviderFactory(
TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic
.WithPart(typeof(TestMoveToNamespaceOptionsService))
.WithPart(typeof(CSharpMoveToNamespaceService))
.WithPart(typeof(CSharpChangeNamespaceService))
.WithPart(typeof(Experiments.DefaultExperimentationService))
.WithPart(typeof(CSharpAddImportsService))
.WithPart(typeof(CSharpRemoveUnnecessaryImportsService)));
public static Task TestMoveToNamespaceCommandCSharpAsync(
string markup,
bool expectedSuccess,
string expectedNamespace = null,
string expectedMarkup = null)
{
return TestMoveToNamespaceCommandAsync(
markup,
expectedSuccess,
LanguageNames.CSharp,
expectedNamespace: expectedNamespace,
expectedMarkup: expectedMarkup);
}
public static Task TestMoveToNamespaceCommandVisualBasicAsync(
string markup,
bool expectedSuccess,
string expectedNamespace = null,
string expectedMarkup = null)
{
return TestMoveToNamespaceCommandAsync(
markup,
expectedSuccess,
LanguageNames.VisualBasic,
expectedNamespace: expectedNamespace,
expectedMarkup: expectedMarkup);
}
public static async Task TestMoveToNamespaceCommandAsync(
string markup,
bool expectedSuccess,
string languageName,
string expectedNamespace = null,
string expectedMarkup = null,
CompilationOptions compilationOptions = null)
{
expectedNamespace = expectedNamespace ?? string.Empty;
var parameters = new TestParameters();
using (var workspace = CreateWorkspace(markup, languageName, compilationOptions, parameters))
{
var testDocument = workspace.Documents.Single(d => d.CursorPosition.HasValue);
var document = workspace.CurrentSolution.GetDocument(testDocument.Id);
var result = await MoveViaCommandAsync(testDocument, document, expectedNamespace);
if (expectedSuccess)
{
Assert.True(result.Succeeded);
Assert.NotNull(result.UpdatedSolution);
Assert.NotNull(result.UpdatedDocumentId);
if (expectedMarkup != null)
{
var updatedDocument = result.UpdatedSolution.GetDocument(result.UpdatedDocumentId);
var updatedText = await updatedDocument.GetTextAsync().ConfigureAwait(false);
Assert.Equal(expectedMarkup, updatedText.ToString());
}
}
else
{
Assert.False(result.Succeeded);
}
}
}
private static TestWorkspace CreateWorkspace(
string markup,
string languageName,
CompilationOptions compilationOptions,
TestParameters parameters)
{
var exportProviderFactory = GetExportProviderFactory(languageName);
var exportProvider = exportProviderFactory.CreateExportProvider();
var workspace = languageName == LanguageNames.CSharp
? TestWorkspace.CreateCSharp(markup, exportProvider: exportProvider, compilationOptions: compilationOptions as CSharpCompilationOptions)
: TestWorkspace.CreateVisualBasic(markup, exportProvider: exportProvider, compilationOptions: compilationOptions);
workspace.ApplyOptions(parameters.options);
return workspace;
}
private static IExportProviderFactory GetExportProviderFactory(string languageName)
{
return languageName == LanguageNames.CSharp
? CSharpExportProviderFactory
: throw new InvalidOperationException("VB is not currently supported");
}
private static async Task<MoveToNamespaceResult> MoveViaCommandAsync(
TestHostDocument testDocument,
Document document,
string newNamespace)
{
var cancellationToken = CancellationToken.None;
var moveToNamespaceService = document.GetLanguageService<AbstractMoveToNamespaceService>();
var analysisResult = await moveToNamespaceService.AnalyzeTypeAtPositionAsync(
document,
testDocument.CursorPosition.Value,
cancellationToken: cancellationToken).ConfigureAwait(false);
return await moveToNamespaceService.MoveToNamespaceAsync(
analysisResult,
newNamespace,
cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
extern alias WORKSPACES;
using System;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.MoveToNamespace;
using Microsoft.CodeAnalysis.Notification;
using WORKSPACES::Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Test.Utilities.MoveToNamespace
{
[Export(typeof(IMoveToNamespaceOptionsService))]
[PartNotDiscoverable]
class TestMoveToNamespaceOptionsService : IMoveToNamespaceOptionsService
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public TestMoveToNamespaceOptionsService()
{
}
public Task<MoveToNamespaceOptionsResult> GetChangeNamespaceOptionsAsync(string defaultNamespace, ImmutableArray<string> availableNamespaces, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}
......@@ -110,4 +110,7 @@
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<Folder Include="MoveToNamespace\" />
</ItemGroup>
</Project>
\ No newline at end of file
......@@ -69,5 +69,8 @@
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="$(HumanizerCoreVersion)" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<Folder Include="MoveToNamespace\" />
</ItemGroup>
<Import Project="..\..\..\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems" Label="Shared" />
</Project>
\ No newline at end of file
......@@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.MoveToNamespace
{
[ExportLanguageService(typeof(AbstractMoveToNamespaceService), LanguageNames.CSharp), Shared]
internal class CSharpMoveToNamespaceService :
AbstractMoveToNamespaceService<CompilationUnitSyntax, NamespaceDeclarationSyntax>
AbstractMoveToNamespaceService<CompilationUnitSyntax, NamespaceDeclarationSyntax, TypeDeclarationSyntax>
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
......@@ -21,8 +21,9 @@ internal class CSharpMoveToNamespaceService :
}
protected override string GetNamespaceName(NamespaceDeclarationSyntax syntax)
{
return syntax.Name.ToString();
}
=> syntax.Name.ToString();
protected override string GetNamespaceName(TypeDeclarationSyntax syntax)
=> GetNamespaceName(syntax.FirstAncestorOrSelf<NamespaceDeclarationSyntax>());
}
}
......@@ -23,12 +23,13 @@ internal abstract class AbstractMoveToNamespaceService : ILanguageService
public abstract Task<MoveToNamespaceOptionsResult> GetOptionsAsync(Document document, string defaultNamespace, CancellationToken cancellationToken);
}
internal abstract class AbstractMoveToNamespaceService<TCompilationSyntax, TNamespaceDeclarationSyntax>
internal abstract class AbstractMoveToNamespaceService<TCompilationSyntax, TNamespaceDeclarationSyntax, TNamedTypeDeclarationSyntax>
: AbstractMoveToNamespaceService
{
private IMoveToNamespaceOptionsService _moveToNamespaceOptionsService;
protected abstract string GetNamespaceName(TNamespaceDeclarationSyntax syntax);
protected abstract string GetNamespaceName(TNamedTypeDeclarationSyntax syntax);
public AbstractMoveToNamespaceService(IMoveToNamespaceOptionsService moveToNamespaceOptionsService)
{
......@@ -52,6 +53,7 @@ public AbstractMoveToNamespaceService(IMoveToNamespaceOptionsService moveToNames
int position,
CancellationToken cancellationToken)
{
#if DEBUG // TODO: remove once the feature is done
var root = await document.GetSyntaxRootAsync().ConfigureAwait(false);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
......@@ -60,71 +62,73 @@ public AbstractMoveToNamespaceService(IMoveToNamespaceOptionsService moveToNames
var symbolInfo = semanticModel.GetSymbolInfo(node, cancellationToken: cancellationToken);
var symbol = symbolInfo.Symbol;
var @namespace = symbol?.Name;
if (symbol is INamespaceSymbol namespaceSymbol)
{
node = node.FirstAncestorOrSelf<SyntaxNode>(a => a is TNamespaceDeclarationSyntax);
@namespace = GetQualifiedName(namespaceSymbol);
}
if (node is TNamespaceDeclarationSyntax declarationSyntax)
{
if (ContainsNamespaceDeclaration(node))
{
return new MoveToNamespaceAnalysisResult("Container contains nested namespace declaration");
return new MoveToNamespaceAnalysisResult("Namespace container contains nested namespace declaration");
}
var @namespace = symbol?.Name ?? GetNamespaceName(declarationSyntax);
return new MoveToNamespaceAnalysisResult(document, node, @namespace);
@namespace = @namespace ?? GetNamespaceName(declarationSyntax);
return new MoveToNamespaceAnalysisResult(document, node, @namespace, MoveToNamespaceAnalysisResult.ContainerType.Namespace);
}
if (symbol is INamedTypeSymbol namedTypeSymbol)
{
node = node.FirstAncestorOrSelf<SyntaxNode>(a => a is TNamedTypeDeclarationSyntax);
@namespace = GetQualifiedName(namedTypeSymbol.ContainingNamespace);
}
if (node is TNamedTypeDeclarationSyntax namedTypeDeclarationSyntax)
{
@namespace = @namespace ?? GetNamespaceName(namedTypeDeclarationSyntax);
return new MoveToNamespaceAnalysisResult(document, node, @namespace, MoveToNamespaceAnalysisResult.ContainerType.NamedType);
}
return new MoveToNamespaceAnalysisResult("Not a valid position");
#else
return new MoveToNamespaceAnalysisResult("Feature is not complete yet");
#endif
}
private bool ContainsNamespaceDeclaration(SyntaxNode node)
=> node.DescendantNodes(n => n is TCompilationSyntax || n is TNamespaceDeclarationSyntax)
.OfType<TNamespaceDeclarationSyntax>().Any();
public override async Task<MoveToNamespaceResult> MoveToNamespaceAsync(
public override Task<MoveToNamespaceResult> MoveToNamespaceAsync(
MoveToNamespaceAnalysisResult analysisResult,
string targetNamespace,
CancellationToken cancellationToken)
{
if (!analysisResult.CanPerform)
{
return MoveToNamespaceResult.Failed;
}
var changeNamespaceService = analysisResult.Document.GetLanguageService<IChangeNamespaceService>();
if (changeNamespaceService == null)
{
return MoveToNamespaceResult.Failed;
}
var changedSolution = await changeNamespaceService.ChangeNamespaceAsync(
analysisResult.Document,
analysisResult.Container,
targetNamespace,
cancellationToken).ConfigureAwait(false);
return new MoveToNamespaceResult(changedSolution, analysisResult.Document.Id);
// TODO: Implementation will be in a separate PR
return Task.FromResult(MoveToNamespaceResult.Failed);
}
private static SymbolDisplayFormat QualifiedNamespaceFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
protected static string GetQualifiedName(INamespaceSymbol namespaceSymbol)
=> namespaceSymbol.ToDisplayString(QualifiedNamespaceFormat);
public override async Task<MoveToNamespaceOptionsResult> GetOptionsAsync(
Document document,
string defaultNamespace,
CancellationToken cancellationToken)
{
var syntaxFactsService = document.GetLanguageService<ISyntaxFactsService>();
var notificationService = document.Project.Solution.Workspace.Services.GetService<INotificationService>();
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var namespaces = compilation.GlobalNamespace.GetAllNamespaces(cancellationToken)
.Where(n => n.NamespaceKind == NamespaceKind.Module && n.ContainingAssembly == compilation.Assembly)
.Select(n => n.ToDisplayString(QualifiedNamespaceFormat));
.Select(GetQualifiedName);
return await _moveToNamespaceOptionsService.GetChangeNamespaceOptionsAsync(
defaultNamespace,
......
......@@ -14,16 +14,19 @@ internal class MoveToNamespaceAnalysisResult
public string ErrorMessage { get; }
public SyntaxNode Container { get; }
public string OriginalNamespace { get; }
public ContainerType Type { get; }
public MoveToNamespaceAnalysisResult(
Document document,
SyntaxNode container,
string originalNamespace)
string originalNamespace,
ContainerType containerType)
{
CanPerform = true;
Document = document;
Container = container;
OriginalNamespace = originalNamespace;
Type = containerType;
}
public MoveToNamespaceAnalysisResult(string errorMessage)
......@@ -31,5 +34,12 @@ public MoveToNamespaceAnalysisResult(string errorMessage)
CanPerform = false;
ErrorMessage = errorMessage;
}
public enum ContainerType
{
Namespace,
NamedType
}
}
}
......@@ -14,7 +14,7 @@ internal partial class MoveToNamespaceDialog : DialogWindow
private readonly MoveToNamespaceDialogViewModel _viewModel;
public string MoveToNamespaceDialogTitle => ServicesVSResources.Move_to_namespace;
public string NamespaceLabelText => "Target Namespace:"; // ServicesVSResources.Namespace_colon;
public string NamespaceLabelText => ServicesVSResources.Target_Namespace_colon;
public string OK => ServicesVSResources.OK;
public string Cancel => ServicesVSResources.Cancel;
......
......@@ -24,7 +24,7 @@ class MoveToNamespaceDialogViewModel : AbstractNotifyPropertyChanged
private void MoveToNamespaceDialogViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch(e.PropertyName)
switch (e.PropertyName)
{
case nameof(NamespaceName):
OnNamespaceUpdated();
......
......@@ -1605,7 +1605,7 @@ internal class ServicesVSResources {
}
/// <summary>
/// Looks up a localized string similar to Move To Namespace.
/// Looks up a localized string similar to Move to Namespace.
/// </summary>
internal static string Move_to_namespace {
get {
......@@ -1641,11 +1641,11 @@ internal class ServicesVSResources {
}
/// <summary>
/// Looks up a localized string similar to Namespace:.
/// Looks up a localized string similar to Namespace.
/// </summary>
internal static string Namespace_colon {
internal static string Namespace {
get {
return ResourceManager.GetString("Namespace:", resourceCulture);
return ResourceManager.GetString("Namespace", resourceCulture);
}
}
......@@ -2826,6 +2826,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to Target Namespace:.
/// </summary>
internal static string Target_Namespace_colon {
get {
return ResourceManager.GetString("Target_Namespace_colon", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The analyzer assembly &apos;{0}&apos; has changed. Diagnostics may be incorrect until Visual Studio is restarted..
/// </summary>
......
......@@ -1207,4 +1207,7 @@ I agree to all of the foregoing:</value>
<data name="Namespace" xml:space="preserve">
<value>Namespace</value>
</data>
<data name="Target_Namespace_colon" xml:space="preserve">
<value>Target Namespace:</value>
</data>
</root>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册