public async Task <ExtractClassOptions?> GetExtractClassOptionsAsync(Document document, INamedTypeSymbol selectedType, ISymbol?selectedMember)
        {
            var notificationService = document.Project.Solution.Workspace.Services.GetRequiredService <INotificationService>();

            var membersInType = selectedType.GetMembers().
                                WhereAsArray(member => MemberAndDestinationValidator.IsMemberValid(member));

            var memberViewModels = membersInType
                                   .SelectAsArray(member =>
                                                  new PullMemberUpSymbolViewModel(member, _glyphService)
            {
                // The member user selected will be checked at the beginning.
                IsChecked               = SymbolEquivalenceComparer.Instance.Equals(selectedMember, member),
                MakeAbstract            = false,
                IsMakeAbstractCheckable = !member.IsKind(SymbolKind.Field) && !member.IsAbstract,
                IsCheckable             = true
            });

            using var cancellationTokenSource = new CancellationTokenSource();
            var memberToDependentsMap = SymbolDependentsBuilder.FindMemberToDependentsMap(membersInType, document.Project, cancellationTokenSource.Token);

            var conflictingTypeNames = selectedType.ContainingNamespace.GetAllTypes(cancellationTokenSource.Token).Select(t => t.Name);
            var candidateName        = selectedType.Name + "Base";
            var defaultTypeName      = NameGenerator.GenerateUniqueName(candidateName, name => !conflictingTypeNames.Contains(name));

            var containingNamespaceDisplay = selectedType.ContainingNamespace.IsGlobalNamespace
                ? string.Empty
                : selectedType.ContainingNamespace.ToDisplayString();

            var generatedNameTypeParameterSuffix = ExtractTypeHelpers.GetTypeParameterSuffix(document, selectedType, membersInType);

            var viewModel = new ExtractClassViewModel(
                _waitIndicator,
                notificationService,
                memberViewModels,
                memberToDependentsMap,
                defaultTypeName,
                containingNamespaceDisplay,
                document.Project.Language,
                generatedNameTypeParameterSuffix,
                conflictingTypeNames.ToImmutableArray(),
                document.GetRequiredLanguageService <ISyntaxFactsService>());

            await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync();

            var dialog = new ExtractClassDialog(viewModel);

            var result = dialog.ShowModal();

            if (result.GetValueOrDefault())
            {
                return(new ExtractClassOptions(
                           viewModel.DestinationViewModel.FileName,
                           viewModel.DestinationViewModel.TypeName,
                           viewModel.DestinationViewModel.Destination == CommonControls.NewTypeDestination.CurrentFile,
                           viewModel.MemberSelectionViewModel.CheckedMembers.SelectAsArray(m => new ExtractClassMemberAnalysisResult(m.Symbol, m.MakeAbstract))));
            }

            return(null);
        }
Beispiel #2
0
        private async Task <ExtractClassWithDialogCodeAction?> TryGetMemberActionAsync(
            CodeRefactoringContext context,
            IExtractClassOptionsService optionsService
            )
        {
            var selectedMemberNode = await GetSelectedNodeAsync(context).ConfigureAwait(false);

            if (selectedMemberNode is null)
            {
                return(null);
            }

            var(document, span, cancellationToken) = context;
            var semanticModel = await document
                                .GetRequiredSemanticModelAsync(cancellationToken)
                                .ConfigureAwait(false);

            var selectedMember = semanticModel.GetDeclaredSymbol(
                selectedMemberNode,
                cancellationToken
                );

            if (selectedMember is null || selectedMember.ContainingType is null)
            {
                return(null);
            }

            // Use same logic as pull members up for determining if a selected member
            // is valid to be moved into a base
            if (!MemberAndDestinationValidator.IsMemberValid(selectedMember))
            {
                return(null);
            }

            var containingType = selectedMember.ContainingType;

            // Can't extract to a new type if there's already a base. Maybe
            // in the future we could inject a new type inbetween base and
            // current
            if (containingType.BaseType?.SpecialType != SpecialType.System_Object)
            {
                return(null);
            }

            var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();
            var containingTypeDeclarationNode = selectedMemberNode.FirstAncestorOrSelf <SyntaxNode>(
                syntaxFacts.IsTypeDeclaration
                );

            return(new ExtractClassWithDialogCodeAction(
                       document,
                       span,
                       optionsService,
                       containingType,
                       containingTypeDeclarationNode !,
                       selectedMember
                       ));
        }
Beispiel #3
0
        private async Task <ExtractClassWithDialogCodeAction?> TryGetMemberActionAsync(CodeRefactoringContext context, IExtractClassOptionsService optionsService)
        {
            var selectedMemberNodes = await GetSelectedNodesAsync(context).ConfigureAwait(false);

            if (selectedMemberNodes.IsEmpty)
            {
                return(null);
            }

            var(document, span, cancellationToken) = context;
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var memberNodeSymbolPairs = selectedMemberNodes
                                        .SelectAsArray(m => (node: m, symbol: semanticModel.GetRequiredDeclaredSymbol(m, cancellationToken)))
                                        // Use same logic as pull members up for determining if a selected member
                                        // is valid to be moved into a base
                                        .WhereAsArray(pair => MemberAndDestinationValidator.IsMemberValid(pair.symbol));

            if (memberNodeSymbolPairs.IsEmpty)
            {
                return(null);
            }

            var selectedMembers = memberNodeSymbolPairs.SelectAsArray(pair => pair.symbol);

            var containingType = selectedMembers.First().ContainingType;

            Contract.ThrowIfNull(containingType);

            // Treat the entire nodes' span as the span of interest here.  That way if the user's location is closer to
            // a refactoring with a narrower span (for example, a span just on the name/parameters of a member, then it
            // will take precedence over us).
            var memberSpan = TextSpan.FromBounds(
                memberNodeSymbolPairs.First().node.FullSpan.Start,
                memberNodeSymbolPairs.Last().node.FullSpan.End);

            // Can't extract to a new type if there's already a base. Maybe
            // in the future we could inject a new type inbetween base and
            // current
            if (containingType.BaseType?.SpecialType != SpecialType.System_Object)
            {
                return(null);
            }

            var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();
            var containingTypeDeclarationNode = selectedMemberNodes.First().FirstAncestorOrSelf <SyntaxNode>(syntaxFacts.IsTypeDeclaration);

            Contract.ThrowIfNull(containingTypeDeclarationNode);
            if (selectedMemberNodes.Any(m => m.FirstAncestorOrSelf <SyntaxNode>(syntaxFacts.IsTypeDeclaration) != containingTypeDeclarationNode))
            {
                return(null);
            }

            return(new ExtractClassWithDialogCodeAction(
                       document, memberSpan, optionsService, containingType, containingTypeDeclarationNode, context.Options, selectedMembers));
        }
        public PullMembersUpOptions GetPullMemberUpOptions(
            Document document,
            ISymbol selectedMember
            )
        {
            var membersInType = selectedMember.ContainingType
                                .GetMembers()
                                .WhereAsArray(member => MemberAndDestinationValidator.IsMemberValid(member));
            var memberViewModels = membersInType.SelectAsArray(
                member =>
                new PullMemberUpSymbolViewModel(member, _glyphService)
            {
                // The member user selected will be checked at the beginning.
                IsChecked = SymbolEquivalenceComparer.Instance.Equals(
                    selectedMember,
                    member
                    ),
                MakeAbstract            = false,
                IsMakeAbstractCheckable =
                    !member.IsKind(SymbolKind.Field) && !member.IsAbstract,
                IsCheckable = true
            }
                );

            using var cancellationTokenSource = new CancellationTokenSource();
            var baseTypeRootViewModel = BaseTypeTreeNodeViewModel.CreateBaseTypeTree(
                _glyphService,
                document.Project.Solution,
                selectedMember.ContainingType,
                cancellationTokenSource.Token
                );
            var memberToDependentsMap = SymbolDependentsBuilder.FindMemberToDependentsMap(
                membersInType,
                document.Project,
                cancellationTokenSource.Token
                );
            var viewModel = new PullMemberUpDialogViewModel(
                _waitIndicator,
                memberViewModels,
                baseTypeRootViewModel,
                memberToDependentsMap
                );
            var dialog = new PullMemberUpDialog(viewModel);
            var result = dialog.ShowModal();

            // Dialog has finshed its work, cancel finding dependents task.
            cancellationTokenSource.Cancel();
            if (result.GetValueOrDefault())
            {
                return(dialog.ViewModel.CreatePullMemberUpOptions());
            }
            else
            {
                return(null);
            }
        }
Beispiel #5
0
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, span, cancellationToken) = context;

            var service = document.Project.Solution.Services.GetService <IMoveStaticMembersOptionsService>();

            if (service == null)
            {
                return;
            }

            var selectedMemberNodes = await GetSelectedNodesAsync(context).ConfigureAwait(false);

            if (selectedMemberNodes.IsEmpty)
            {
                return;
            }

            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            if (semanticModel == null)
            {
                return;
            }

            var memberNodeSymbolPairs = selectedMemberNodes
                                        .SelectAsArray(m => (node: m, symbol: semanticModel.GetRequiredDeclaredSymbol(m, cancellationToken)))
                                        // Use same logic as pull members up for determining if a selected member
                                        // is valid to be moved into a base
                                        .WhereAsArray(pair => MemberAndDestinationValidator.IsMemberValid(pair.symbol) && pair.symbol.IsStatic);

            if (memberNodeSymbolPairs.IsEmpty)
            {
                return;
            }

            var selectedMembers = memberNodeSymbolPairs.SelectAsArray(pair => pair.symbol);

            var containingType = selectedMembers.First().ContainingType;

            Contract.ThrowIfNull(containingType);
            if (selectedMembers.Any(m => !m.ContainingType.Equals(containingType)))
            {
                return;
            }

            // we want to use a span which covers all the selected viable member nodes, so that more specific nodes have priority
            var memberSpan = TextSpan.FromBounds(
                memberNodeSymbolPairs.First().node.FullSpan.Start,
                memberNodeSymbolPairs.Last().node.FullSpan.End);

            var action = new MoveStaticMembersWithDialogCodeAction(document, service, containingType, context.Options, selectedMembers);

            context.RegisterRefactoring(action, memberSpan);
        }
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            // Currently support to pull field, method, event, property and indexer up,
            // constructor, operator and finalizer are excluded.
            var document          = context.Document;
            var cancellationToken = context.CancellationToken;
            var semanticModel     = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var selectedMemberNode = await GetMatchedNodeAsync(document, context.Span, cancellationToken).ConfigureAwait(false);

            if (selectedMemberNode == null)
            {
                return;
            }

            var selectedMember = semanticModel.GetDeclaredSymbol(selectedMemberNode);

            if (selectedMember == null || selectedMember.ContainingType == null)
            {
                return;
            }

            if (!MemberAndDestinationValidator.IsMemberValid(selectedMember))
            {
                return;
            }

            if (!IsSelectionValid(context.Span, selectedMemberNode))
            {
                return;
            }

            var allDestinations = FindAllValidDestinations(
                selectedMember,
                document.Project.Solution,
                cancellationToken);

            if (allDestinations.Length == 0)
            {
                return;
            }

            var allActions = allDestinations.Select(destination => MembersPuller.TryComputeCodeAction(document, selectedMember, destination))
                             .WhereNotNull().Concat(new PullMemberUpWithDialogCodeAction(document, selectedMember, _service))
                             .ToImmutableArray();

            var nestedCodeAction = new CodeActionWithNestedActions(
                string.Format(FeaturesResources.Pull_0_up, selectedMember.ToNameDisplayString()),
                allActions, isInlinable: true);

            context.RegisterRefactoring(nestedCodeAction);
        }
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, span, cancellationToken) = context;

            var service = document.Project.Solution.Workspace.Services.GetService <IMoveStaticMembersOptionsService>();

            if (service == null)
            {
                return;
            }

            var memberDeclaration = await GetSelectedNodeAsync(context).ConfigureAwait(false);

            if (memberDeclaration == null)
            {
                return;
            }

            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            if (semanticModel == null)
            {
                return;
            }

            var selectedType = semanticModel.GetEnclosingNamedType(span.Start, cancellationToken);

            if (selectedType == null)
            {
                return;
            }

            var selectedMembers = selectedType.GetMembers()
                                  .WhereAsArray(m => m.IsStatic &&
                                                MemberAndDestinationValidator.IsMemberValid(m) &&
                                                m.DeclaringSyntaxReferences.Any(sr => memberDeclaration.FullSpan.Contains(sr.Span)));

            if (selectedMembers.IsEmpty)
            {
                return;
            }

            var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();

            var action = new MoveStaticMembersWithDialogCodeAction(document, span, service, selectedType, context.Options, selectedMember: selectedMembers[0]);

            context.RegisterRefactoring(action, selectedMembers[0].DeclaringSyntaxReferences[0].Span);
        }
        private async Task <ExtractClassWithDialogCodeAction?> TryGetMemberActionAsync(CodeRefactoringContext context, IExtractClassOptionsService optionsService)
        {
            var selectedMemberNode = await GetSelectedNodeAsync(context).ConfigureAwait(false);

            if (selectedMemberNode is null)
            {
                return(null);
            }

            var(document, span, cancellationToken) = context;
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var selectedMember = semanticModel.GetDeclaredSymbol(selectedMemberNode, cancellationToken);

            if (selectedMember is null || selectedMember.ContainingType is null)
            {
                return(null);
            }

            // Use same logic as pull members up for determining if a selected member
            // is valid to be moved into a base
            if (!MemberAndDestinationValidator.IsMemberValid(selectedMember))
            {
                return(null);
            }

            var containingType = selectedMember.ContainingType;

            // Can't extract to a new type if there's already a base. Maybe
            // in the future we could inject a new type inbetween base and
            // current
            if (containingType.BaseType?.SpecialType != SpecialType.System_Object)
            {
                return(null);
            }

            var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();
            var containingTypeDeclarationNode = selectedMemberNode.FirstAncestorOrSelf <SyntaxNode>(syntaxFacts.IsTypeDeclaration);

            Contract.ThrowIfNull(containingTypeDeclarationNode);

            // Treat the entire node's span as the span of interest here.  That way if the user's location is closer to
            // a refactoring with a narrower span (for example, a span just on the name/parameters of a member, then it
            // will take precedence over us).
            return(new ExtractClassWithDialogCodeAction(
                       document, selectedMemberNode.Span, optionsService, containingType, containingTypeDeclarationNode, context.Options, selectedMember));
        }
        // internal for testing purposes, get the view model
        internal static MoveStaticMembersDialogViewModel GetViewModel(
            Document document,
            INamedTypeSymbol selectedType,
            ISymbol?selectedNodeSymbol,
            IGlyphService?glyphService,
            IUIThreadOperationExecutor uiThreadOperationExecutor)
        {
            var membersInType = selectedType.GetMembers().
                                WhereAsArray(member => MemberAndDestinationValidator.IsMemberValid(member) && member.IsStatic);

            var memberViewModels = membersInType
                                   .SelectAsArray(member =>
                                                  new SymbolViewModel <ISymbol>(member, glyphService)
            {
                // The member user selected will be checked at the beginning.
                IsChecked = SymbolEquivalenceComparer.Instance.Equals(selectedNodeSymbol, member),
            });

            using var cancellationTokenSource = new CancellationTokenSource();
            var memberToDependentsMap = SymbolDependentsBuilder.FindMemberToDependentsMap(membersInType, document.Project, cancellationTokenSource.Token);

            var existingTypeNames = selectedType.ContainingNamespace.GetAllTypes(cancellationTokenSource.Token).SelectAsArray(t => t.ToDisplayString());
            var candidateName     = selectedType.Name + "Helpers";
            var defaultTypeName   = NameGenerator.GenerateUniqueName(candidateName, name => !existingTypeNames.Contains(name));

            var containingNamespaceDisplay = selectedType.ContainingNamespace.IsGlobalNamespace
                ? string.Empty
                : selectedType.ContainingNamespace.ToDisplayString();

            var selectMembersViewModel = new StaticMemberSelectionViewModel(
                uiThreadOperationExecutor,
                memberViewModels,
                memberToDependentsMap);

            return(new MoveStaticMembersDialogViewModel(selectMembersViewModel,
                                                        defaultTypeName,
                                                        existingTypeNames,
                                                        containingNamespaceDisplay,
                                                        document.GetRequiredLanguageService <ISyntaxFactsService>()));
        }
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, span, cancellationToken) = context;

            var service = document.Project.Solution.Workspace.Services.GetService <IMoveStaticMembersOptionsService>();

            if (service == null)
            {
                return;
            }

            var memberDeclaration = await GetSelectedNodeAsync(context).ConfigureAwait(false);

            if (memberDeclaration == null)
            {
                return;
            }

            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            if (semanticModel == null)
            {
                return;
            }

            var selectedType = semanticModel.GetEnclosingNamedType(span.Start, cancellationToken);

            if (selectedType == null)
            {
                return;
            }

            var selectedMembers = selectedType.GetMembers()
                                  .WhereAsArray(m => m.IsStatic &&
                                                MemberAndDestinationValidator.IsMemberValid(m) &&
                                                m.DeclaringSyntaxReferences.Any(static (sr, memberDeclaration) => memberDeclaration.FullSpan.Contains(sr.Span), memberDeclaration));