internal async Task <Solution> SimplifyAsync(Solution solution, IEnumerable <DocumentId> documentIds, bool replacementTextValid, AnnotationTable <RenameAnnotation> renameAnnotations, CancellationToken cancellationToken)
        {
            foreach (var documentId in documentIds)
            {
                if (this.IsDocumentChanged(documentId))
                {
                    var document = solution.GetDocument(documentId);

                    if (replacementTextValid)
                    {
                        var optionSet = solution.Workspace.Options;
                        document = await Simplifier.ReduceAsync(document, Simplifier.Annotation, optionSet, cancellationToken).ConfigureAwait(false);

                        document = await Formatter.FormatAsync(document, Formatter.Annotation, cancellationToken : cancellationToken).ConfigureAwait(false);
                    }

                    // Simplification may have removed escaping and formatted whitespace.  We need to update
                    // our list of modified spans accordingly
                    if (_documentToModifiedSpansMap.ContainsKey(documentId))
                    {
                        _documentToModifiedSpansMap[documentId].Clear();
                    }

                    if (_documentToComplexifiedSpansMap.ContainsKey(documentId))
                    {
                        _documentToComplexifiedSpansMap[documentId].Clear();
                    }

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

                    // First, get all the complexified statements
                    var nodeAnnotations = renameAnnotations.GetAnnotatedNodesAndTokens <RenameNodeSimplificationAnnotation>(root)
                                          .Select(x => Tuple.Create(renameAnnotations.GetAnnotations <RenameNodeSimplificationAnnotation>(x).First(), (SyntaxNode)x));

                    HashSet <SyntaxToken> modifiedTokensInComplexifiedStatements = new HashSet <SyntaxToken>();
                    foreach (var annotationAndNode in nodeAnnotations)
                    {
                        var oldSpan = annotationAndNode.Item1.OriginalTextSpan;
                        var node    = annotationAndNode.Item2;

                        var annotationAndTokens2 = renameAnnotations.GetAnnotatedNodesAndTokens <RenameTokenSimplificationAnnotation>(node)
                                                   .Select(x => Tuple.Create(renameAnnotations.GetAnnotations <RenameTokenSimplificationAnnotation>(x).First(), (SyntaxToken)x));

                        List <ValueTuple <TextSpan, TextSpan> > modifiedSubSpans = new List <ValueTuple <TextSpan, TextSpan> >();
                        foreach (var annotationAndToken in annotationAndTokens2)
                        {
                            modifiedTokensInComplexifiedStatements.Add(annotationAndToken.Item2);
                            modifiedSubSpans.Add(ValueTuple.Create(annotationAndToken.Item1.OriginalTextSpan, annotationAndToken.Item2.Span));
                        }

                        AddComplexifiedSpan(documentId, oldSpan, node.Span, modifiedSubSpans);
                    }

                    // Now process the rest of the renamed spans
                    var annotationAndTokens = renameAnnotations.GetAnnotatedNodesAndTokens <RenameTokenSimplificationAnnotation>(root)
                                              .Where(x => !modifiedTokensInComplexifiedStatements.Contains((SyntaxToken)x))
                                              .Select(x => Tuple.Create(renameAnnotations.GetAnnotations <RenameTokenSimplificationAnnotation>(x).First(), (SyntaxToken)x));

                    foreach (var annotationAndToken in annotationAndTokens)
                    {
                        AddModifiedSpan(documentId, annotationAndToken.Item1.OriginalTextSpan, annotationAndToken.Item2.Span);
                    }

                    var annotationAndTrivias = renameAnnotations.GetAnnotatedTrivia <RenameTokenSimplificationAnnotation>(root)
                                               .Select(x => Tuple.Create(renameAnnotations.GetAnnotations <RenameTokenSimplificationAnnotation>(x).First(), x));

                    foreach (var annotationAndTrivia in annotationAndTrivias)
                    {
                        AddModifiedSpan(documentId, annotationAndTrivia.Item1.OriginalTextSpan, annotationAndTrivia.Item2.Span);
                    }

                    solution = document.Project.Solution;
                }
            }

            return(solution);
        }
示例#2
0
        public static async Task <TRoot> ReplaceSyntaxAsync <TRoot>(
            this TRoot root,
            IEnumerable <SyntaxNode> nodes,
            Func <SyntaxNode, SyntaxNode, CancellationToken, Task <SyntaxNode> > computeReplacementNodeAsync,
            IEnumerable <SyntaxToken> tokens,
            Func <SyntaxToken, SyntaxToken, CancellationToken, Task <SyntaxToken> > computeReplacementTokenAsync,
            IEnumerable <SyntaxTrivia> trivia,
            Func <SyntaxTrivia, SyntaxTrivia, CancellationToken, Task <SyntaxTrivia> > computeReplacementTriviaAsync,
            CancellationToken cancellationToken)
            where TRoot : SyntaxNode
        {
            // index all nodes, tokens and trivia by the full spans they cover
            var nodesToReplace = nodes != null?nodes.ToDictionary(n => n.FullSpan) : new Dictionary <TextSpan, SyntaxNode>();

            var tokensToReplace = tokens != null?tokens.ToDictionary(t => t.FullSpan) : new Dictionary <TextSpan, SyntaxToken>();

            var triviaToReplace = trivia != null?trivia.ToDictionary(t => t.FullSpan) : new Dictionary <TextSpan, SyntaxTrivia>();

            var nodeReplacements   = new Dictionary <SyntaxNode, SyntaxNode>();
            var tokenReplacements  = new Dictionary <SyntaxToken, SyntaxToken>();
            var triviaReplacements = new Dictionary <SyntaxTrivia, SyntaxTrivia>();

            var retryAnnotations = new AnnotationTable <object>("RetryReplace");

            var spans = new List <TextSpan>(nodesToReplace.Count + tokensToReplace.Count + triviaToReplace.Count);

            spans.AddRange(nodesToReplace.Keys);
            spans.AddRange(tokensToReplace.Keys);
            spans.AddRange(triviaToReplace.Keys);

            while (spans.Count > 0)
            {
                // sort the spans of the items to be replaced so we can tell if any overlap
                spans.Sort((x, y) =>
                {
                    // order by end offset, and then by length
                    var d = x.End - y.End;

                    if (d == 0)
                    {
                        d = x.Length - y.Length;
                    }

                    return(d);
                });

                // compute replacements for all nodes that will go in the same batch
                // only spans that do not overlap go in the same batch.
                TextSpan previous = default(TextSpan);
                foreach (var span in spans)
                {
                    // only add to replacement map if we don't intersect with the previous node. This taken with the sort order
                    // should ensure that parent nodes are not processed in the same batch as child nodes.
                    if (previous == default(TextSpan) || !previous.IntersectsWith(span))
                    {
                        if (nodesToReplace.TryGetValue(span, out var currentNode))
                        {
                            var original = (SyntaxNode)retryAnnotations.GetAnnotations(currentNode).SingleOrDefault() ?? currentNode;
                            var newNode  = await computeReplacementNodeAsync(original, currentNode, cancellationToken).ConfigureAwait(false);

                            nodeReplacements[currentNode] = newNode;
                        }
                        else if (tokensToReplace.TryGetValue(span, out var currentToken))
                        {
                            var original = (SyntaxToken)retryAnnotations.GetAnnotations(currentToken).SingleOrDefault();
                            if (original == default(SyntaxToken))
                            {
                                original = currentToken;
                            }

                            var newToken = await computeReplacementTokenAsync(original, currentToken, cancellationToken).ConfigureAwait(false);

                            tokenReplacements[currentToken] = newToken;
                        }
                        else if (triviaToReplace.TryGetValue(span, out var currentTrivia))
                        {
                            var original = (SyntaxTrivia)retryAnnotations.GetAnnotations(currentTrivia).SingleOrDefault();
                            if (original == default(SyntaxTrivia))
                            {
                                original = currentTrivia;
                            }

                            var newTrivia = await computeReplacementTriviaAsync(original, currentTrivia, cancellationToken).ConfigureAwait(false);

                            triviaReplacements[currentTrivia] = newTrivia;
                        }
                    }

                    previous = span;
                }

                bool retryNodes  = false;
                bool retryTokens = false;
                bool retryTrivia = false;

                // replace nodes in batch
                // submit all nodes so we can annotate the ones we don't replace
                root = root.ReplaceSyntax(
                    nodes: nodesToReplace.Values,
                    computeReplacementNode: (original, rewritten) =>
                {
                    if (rewritten != original || !nodeReplacements.TryGetValue(original, out var replaced))
                    {
                        // the subtree did change, or we didn't have a replacement for it in this batch
                        // so we need to add an annotation so we can find this node again for the next batch.
                        replaced   = retryAnnotations.WithAdditionalAnnotations(rewritten, original);
                        retryNodes = true;
                    }

                    return(replaced);
                },
                    tokens: tokensToReplace.Values,
                    computeReplacementToken: (original, rewritten) =>
                {
                    if (rewritten != original || !tokenReplacements.TryGetValue(original, out var replaced))
                    {
                        // the subtree did change, or we didn't have a replacement for it in this batch
                        // so we need to add an annotation so we can find this node again for the next batch.
                        replaced    = retryAnnotations.WithAdditionalAnnotations(rewritten, original);
                        retryTokens = true;
                    }

                    return(replaced);
                },
                    trivia: triviaToReplace.Values,
                    computeReplacementTrivia: (original, rewritten) =>
                {
                    if (!triviaReplacements.TryGetValue(original, out var replaced))
                    {
                        // the subtree did change, or we didn't have a replacement for it in this batch
                        // so we need to add an annotation so we can find this node again for the next batch.
                        replaced    = retryAnnotations.WithAdditionalAnnotations(rewritten, original);
                        retryTrivia = true;
                    }

                    return(replaced);
                });

                nodesToReplace.Clear();
                tokensToReplace.Clear();
                triviaToReplace.Clear();
                spans.Clear();

                // prepare next batch out of all remaining annotated nodes
                if (retryNodes)
                {
                    nodesToReplace = retryAnnotations.GetAnnotatedNodes(root).ToDictionary(n => n.FullSpan);
                    spans.AddRange(nodesToReplace.Keys);
                }

                if (retryTokens)
                {
                    tokensToReplace = retryAnnotations.GetAnnotatedTokens(root).ToDictionary(t => t.FullSpan);
                    spans.AddRange(tokensToReplace.Keys);
                }

                if (retryTrivia)
                {
                    triviaToReplace = retryAnnotations.GetAnnotatedTrivia(root).ToDictionary(t => t.FullSpan);
                    spans.AddRange(triviaToReplace.Keys);
                }
            }

            return(root);
        }
示例#3
0
        internal async Task<Solution> SimplifyAsync(Solution solution, IEnumerable<DocumentId> documentIds, bool replacementTextValid, AnnotationTable<RenameAnnotation> renameAnnotations, CancellationToken cancellationToken)
        {
            foreach (var documentId in documentIds)
            {
                if (this.IsDocumentChanged(documentId))
                {
                    var document = solution.GetDocument(documentId);

                    if (replacementTextValid)
                    {
                        var optionSet = solution.Workspace.Options;
                        document = await Simplifier.ReduceAsync(document, Simplifier.Annotation, optionSet, cancellationToken).ConfigureAwait(false);
                        document = await Formatter.FormatAsync(document, Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false);
                    }

                    // Simplification may have removed escaping and formatted whitespace.  We need to update
                    // our list of modified spans accordingly
                    if (_documentToModifiedSpansMap.ContainsKey(documentId))
                    {
                        _documentToModifiedSpansMap[documentId].Clear();
                    }

                    if (_documentToComplexifiedSpansMap.ContainsKey(documentId))
                    {
                        _documentToComplexifiedSpansMap[documentId].Clear();
                    }

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

                    // First, get all the complexified statements
                    var nodeAnnotations = renameAnnotations.GetAnnotatedNodesAndTokens<RenameNodeSimplificationAnnotation>(root)
                        .Select(x => Tuple.Create(renameAnnotations.GetAnnotations<RenameNodeSimplificationAnnotation>(x).First(), (SyntaxNode)x));

                    HashSet<SyntaxToken> modifiedTokensInComplexifiedStatements = new HashSet<SyntaxToken>();
                    foreach (var annotationAndNode in nodeAnnotations)
                    {
                        var oldSpan = annotationAndNode.Item1.OriginalTextSpan;
                        var node = annotationAndNode.Item2;

                        var annotationAndTokens2 = renameAnnotations.GetAnnotatedNodesAndTokens<RenameTokenSimplificationAnnotation>(node)
                               .Select(x => Tuple.Create(renameAnnotations.GetAnnotations<RenameTokenSimplificationAnnotation>(x).First(), (SyntaxToken)x));

                        List<ValueTuple<TextSpan, TextSpan>> modifiedSubSpans = new List<ValueTuple<TextSpan, TextSpan>>();
                        foreach (var annotationAndToken in annotationAndTokens2)
                        {
                            modifiedTokensInComplexifiedStatements.Add(annotationAndToken.Item2);
                            modifiedSubSpans.Add(ValueTuple.Create(annotationAndToken.Item1.OriginalTextSpan, annotationAndToken.Item2.Span));
                        }

                        AddComplexifiedSpan(documentId, oldSpan, node.Span, modifiedSubSpans);
                    }

                    // Now process the rest of the renamed spans
                    var annotationAndTokens = renameAnnotations.GetAnnotatedNodesAndTokens<RenameTokenSimplificationAnnotation>(root)
                        .Where(x => !modifiedTokensInComplexifiedStatements.Contains((SyntaxToken)x))
                        .Select(x => Tuple.Create(renameAnnotations.GetAnnotations<RenameTokenSimplificationAnnotation>(x).First(), (SyntaxToken)x));

                    foreach (var annotationAndToken in annotationAndTokens)
                    {
                        AddModifiedSpan(documentId, annotationAndToken.Item1.OriginalTextSpan, annotationAndToken.Item2.Span);
                    }

                    var annotationAndTrivias = renameAnnotations.GetAnnotatedTrivia<RenameTokenSimplificationAnnotation>(root)
                        .Select(x => Tuple.Create(renameAnnotations.GetAnnotations<RenameTokenSimplificationAnnotation>(x).First(), (SyntaxTrivia)x));

                    foreach (var annotationAndTrivia in annotationAndTrivias)
                    {
                        AddModifiedSpan(documentId, annotationAndTrivia.Item1.OriginalTextSpan, annotationAndTrivia.Item2.Span);
                    }

                    solution = document.Project.Solution;
                }
            }

            return solution;
        }