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);
        }
        protected override async Task <IEnumerable <CodeActionOperation> > ComputeOperationsAsync(object options, CancellationToken cancellationToken)
        {
            if (options is ExtractClassOptions extractClassOptions)
            {
                // Map the symbols we're removing to annotations
                // so we can find them easily
                var codeGenerator = _document.GetRequiredLanguageService <ICodeGenerationService>();
                var symbolMapping = await AnnotatedSymbolMapping.CreateAsync(
                    extractClassOptions.MemberAnalysisResults.Select(m => m.Member),
                    _document.Project.Solution,
                    _selectedTypeDeclarationNode,
                    cancellationToken).ConfigureAwait(false);

                var namespaceService = _document.GetRequiredLanguageService <AbstractExtractInterfaceService>();

                // Create the symbol for the new type
                var newType = CodeGenerationSymbolFactory.CreateNamedTypeSymbol(
                    _selectedType.GetAttributes(),
                    _selectedType.DeclaredAccessibility,
                    _selectedType.GetSymbolModifiers(),
                    TypeKind.Class,
                    extractClassOptions.TypeName,
                    typeParameters: ExtractTypeHelpers.GetRequiredTypeParametersForMembers(_selectedType, extractClassOptions.MemberAnalysisResults.Select(m => m.Member)));

                var containingNamespaceDisplay = namespaceService.GetContainingNamespaceDisplay(
                    _selectedType,
                    _document.Project.CompilationOptions);

                // Add the new type to the solution. It can go in a new file or
                // be added to an existing. The returned document is always the document
                // containing the new type
                var(updatedDocument, typeAnnotation) = extractClassOptions.SameFile
                    ? await ExtractTypeHelpers.AddTypeToExistingFileAsync(
                    symbolMapping.AnnotatedSolution.GetRequiredDocument(_document.Id),
                    newType,
                    symbolMapping,
                    _fallbackOptions,
                    cancellationToken).ConfigureAwait(false)
                    : await ExtractTypeHelpers.AddTypeToNewFileAsync(
                    symbolMapping.AnnotatedSolution,
                    containingNamespaceDisplay,
                    extractClassOptions.FileName,
                    _document.Project.Id,
                    _document.Folders,
                    newType,
                    _document,
                    _fallbackOptions,
                    cancellationToken).ConfigureAwait(false);

                // Update the original type to have the new base
                var solutionWithUpdatedOriginalType = await GetSolutionWithBaseAddedAsync(
                    updatedDocument.Project.Solution,
                    symbolMapping,
                    newType,
                    extractClassOptions.MemberAnalysisResults,
                    cancellationToken).ConfigureAwait(false);

                // After all the changes, make sure we're using the most up to date symbol
                // as the destination for pulling members into
                var documentWithTypeDeclaration = solutionWithUpdatedOriginalType.GetRequiredDocument(updatedDocument.Id);
                var newTypeAfterEdits           = await GetNewTypeSymbolAsync(documentWithTypeDeclaration, typeAnnotation, cancellationToken).ConfigureAwait(false);

                // Use Members Puller to move the members to the new symbol
                var finalSolution = await PullMembersUpAsync(
                    solutionWithUpdatedOriginalType,
                    newTypeAfterEdits,
                    symbolMapping,
                    extractClassOptions.MemberAnalysisResults,
                    cancellationToken).ConfigureAwait(false);

                return(new[] { new ApplyChangesOperation(finalSolution) });
            }
            else
            {
                // If user click cancel button, options will be null and hit this branch
                return(SpecializedCollections.EmptyEnumerable <CodeActionOperation>());
            }
        }
        protected override async Task <IEnumerable <CodeActionOperation> > ComputeOperationsAsync(object options, CancellationToken cancellationToken)
        {
            if (options is not MoveStaticMembersOptions moveOptions || moveOptions.IsCancelled)
            {
                return(SpecializedCollections.EmptyEnumerable <CodeActionOperation>());
            }

            // Find the original doc root
            var syntaxFacts = _document.GetRequiredLanguageService <ISyntaxFactsService>();
            var root        = await _document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            // add annotations to the symbols that we selected so we can find them later to pull up
            // These symbols should all have (singular) definitions, but in the case that we can't find
            // any location, we just won't move that particular symbol
            var memberNodes = moveOptions.SelectedMembers
                              .Select(symbol => symbol.Locations.FirstOrDefault())
                              .WhereNotNull()
                              .SelectAsArray(loc => loc.FindNode(cancellationToken));

            root = root.TrackNodes(memberNodes);
            var sourceDoc = _document.WithSyntaxRoot(root);

            var typeParameters = ExtractTypeHelpers.GetRequiredTypeParametersForMembers(_selectedType, moveOptions.SelectedMembers);
            // which indices of the old type params should we keep for a new class reference, used for refactoring usages
            var typeArgIndices = Enumerable.Range(0, _selectedType.TypeParameters.Length)
                                 .Where(i => typeParameters.Contains(_selectedType.TypeParameters[i]))
                                 .ToImmutableArrayOrEmpty();

            // even though we can move members here, we will move them by calling PullMembersUp
            var newType = CodeGenerationSymbolFactory.CreateNamedTypeSymbol(
                ImmutableArray.Create <AttributeData>(),
                Accessibility.NotApplicable,
                DeclarationModifiers.Static,
                GetNewTypeKind(_selectedType),
                moveOptions.TypeName,
                typeParameters: typeParameters);

            var(newDoc, annotation) = await ExtractTypeHelpers.AddTypeToNewFileAsync(
                sourceDoc.Project.Solution,
                moveOptions.NamespaceDisplay,
                moveOptions.FileName,
                _document.Project.Id,
                _document.Folders,
                newType,
                _document,
                _fallbackOptions,
                cancellationToken).ConfigureAwait(false);

            // get back type declaration in the newly created file
            var destRoot = await newDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var destSemanticModel = await newDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            newType = (INamedTypeSymbol)destSemanticModel.GetRequiredDeclaredSymbol(destRoot.GetAnnotatedNodes(annotation).Single(), cancellationToken);

            // refactor references across the entire solution
            var memberReferenceLocations = await FindMemberReferencesAsync(moveOptions.SelectedMembers, newDoc.Project.Solution, cancellationToken).ConfigureAwait(false);

            var projectToLocations          = memberReferenceLocations.ToLookup(loc => loc.location.Document.Project.Id);
            var solutionWithFixedReferences = await RefactorReferencesAsync(projectToLocations, newDoc.Project.Solution, newType, typeArgIndices, cancellationToken).ConfigureAwait(false);

            sourceDoc = solutionWithFixedReferences.GetRequiredDocument(sourceDoc.Id);

            // get back nodes from our changes
            root = await sourceDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

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

            var members = memberNodes
                          .Select(node => root.GetCurrentNode(node))
                          .WhereNotNull()
                          .SelectAsArray(node => (semanticModel.GetDeclaredSymbol(node, cancellationToken), false));

            var pullMembersUpOptions = PullMembersUpOptionsBuilder.BuildPullMembersUpOptions(newType, members);
            var movedSolution        = await MembersPuller.PullMembersUpAsync(sourceDoc, pullMembersUpOptions, _fallbackOptions, cancellationToken).ConfigureAwait(false);

            return(new CodeActionOperation[] { new ApplyChangesOperation(movedSolution) });
        }