Esempio n. 1
0
        private bool CleanupWholeNode(List <ValueTuple <SyntaxAnnotation, SyntaxAnnotation> > annotations)
        {
            if (annotations.Count != 1)
            {
                return(false);
            }

            var startMarker = SpanMarker.FromAnnotation(annotations[0].Item1);
            var endMarker   = SpanMarker.FromAnnotation(annotations[0].Item2);

            return(startMarker.Type == SpanMarkerType.BeginningOfFile && endMarker.Type == SpanMarkerType.EndOfFile);
        }
Esempio n. 2
0
        /// <summary>
        /// Inject annotations into the node so that it can re-calculate spans for each code cleaner after each tree transformation.
        /// </summary>
        private ValueTuple <SyntaxNode, List <ValueTuple <SyntaxAnnotation, SyntaxAnnotation> > > AnnotateNodeForTextSpans(
            ISyntaxFactsService syntaxFactsService, SyntaxNode root, IEnumerable <TextSpan> spans, CancellationToken cancellationToken)
        {
            // Get spans where the tokens around the spans are not overlapping with the spans.
            var nonOverlappingSpans = GetNonOverlappingSpans(syntaxFactsService, root, spans, cancellationToken);

            // Build token annotation map
            var tokenAnnotationMap = new Dictionary <SyntaxToken, List <SyntaxAnnotation> >();
            var annotations        = new List <ValueTuple <SyntaxAnnotation, SyntaxAnnotation> >();

            foreach (var span in nonOverlappingSpans)
            {
                cancellationToken.ThrowIfCancellationRequested();

                SyntaxToken previousToken;
                SyntaxToken startToken;
                SyntaxToken endToken;
                SyntaxToken nextToken;
                GetTokensAroundSpan(root, span, out previousToken, out startToken, out endToken, out nextToken);

                // Create marker to insert
                SpanMarker startMarker = new SpanMarker(type: (previousToken.RawKind == 0) ? SpanMarkerType.BeginningOfFile : SpanMarkerType.Normal,
                                                        oppositeMarkerType: (nextToken.RawKind == 0) ? SpanMarkerType.EndOfFile : SpanMarkerType.Normal);

                SpanMarker endMarker = new SpanMarker(type: (nextToken.RawKind == 0) ? SpanMarkerType.EndOfFile : SpanMarkerType.Normal,
                                                      oppositeMarkerType: (previousToken.RawKind == 0) ? SpanMarkerType.BeginningOfFile : SpanMarkerType.Normal);

                // Set proper tokens
                previousToken = (previousToken.RawKind == 0) ? root.GetFirstToken(includeZeroWidth: true) : previousToken;
                nextToken     = (nextToken.RawKind == 0) ? root.GetLastToken(includeZeroWidth: true) : nextToken;

                // Build token to marker map
                tokenAnnotationMap.GetOrAdd(previousToken, _ => new List <SyntaxAnnotation>()).Add(startMarker.Annotation);
                tokenAnnotationMap.GetOrAdd(nextToken, _ => new List <SyntaxAnnotation>()).Add(endMarker.Annotation);

                // Remember markers
                annotations.Add(new ValueTuple <SyntaxAnnotation, SyntaxAnnotation>(startMarker.Annotation, endMarker.Annotation));
            }

            // Do a quick check.
            // If, after all merges, the spans are merged into one span that covers the whole tree, return right away.
            if (CleanupWholeNode(annotations))
            {
                // This will indicate that no annotation is needed.
                return(default(ValueTuple <SyntaxNode, List <ValueTuple <SyntaxAnnotation, SyntaxAnnotation> > >));
            }

            // Inject annotations
            var newNode = InjectAnnotations(root, tokenAnnotationMap);

            return(ValueTuple.Create(newNode, annotations));
        }
Esempio n. 3
0
        private IEnumerable <TextSpan> GetTextSpansFromAnnotation(SyntaxNode node, List <ValueTuple <SyntaxAnnotation, SyntaxAnnotation> > annotations, CancellationToken cancellationToken)
        {
            // Now try to retrieve the text span from the annotations injected into the node.
            foreach (var annotationPair in annotations)
            {
                cancellationToken.ThrowIfCancellationRequested();

                var previousMarkerAnnotation = annotationPair.Item1;
                var nextMarkerAnnotation     = annotationPair.Item2;

                var previousTokenMarker = SpanMarker.FromAnnotation(previousMarkerAnnotation);
                var nextTokenMarker     = SpanMarker.FromAnnotation(nextMarkerAnnotation);

                var previousTokens = node.GetAnnotatedNodesAndTokens(previousMarkerAnnotation).Where(n => n.IsToken).Select(n => n.AsToken());
                var nextTokens     = node.GetAnnotatedNodesAndTokens(nextMarkerAnnotation).Where(n => n.IsToken).Select(n => n.AsToken());

                // Check whether we can use the tokens we got back from the node.
                TextSpan span;
                if (TryGetTextSpanFromAnnotation(previousTokenMarker, nextTokenMarker, node, previousTokens, nextTokens, out span))
                {
                    yield return(span);
                }
            }
        }
        /// <summary>
        /// inject annotations to the node so that it can re-calculate spans for each code cleaners after each tree transformation
        /// </summary>
        private ValueTuple<SyntaxNode, List<ValueTuple<SyntaxAnnotation, SyntaxAnnotation>>> AnnotateNodeForTextSpans(
            ISyntaxFactsService syntaxFactsService, SyntaxNode root, IEnumerable<TextSpan> spans, CancellationToken cancellationToken)
        {
            // get spans where tokens around the spans are not overlapping with spans
            var nonOverlappingSpans = GetNonOverlappingSpans(syntaxFactsService, root, spans, cancellationToken);

            // build token annotation map
            var tokenAnnotationMap = new Dictionary<SyntaxToken, List<SyntaxAnnotation>>();
            var annotations = new List<ValueTuple<SyntaxAnnotation, SyntaxAnnotation>>();
            foreach (var span in nonOverlappingSpans)
            {
                cancellationToken.ThrowIfCancellationRequested();

                SyntaxToken previousToken;
                SyntaxToken startToken;
                SyntaxToken endToken;
                SyntaxToken nextToken;
                GetTokensAroundSpan(root, span, out previousToken, out startToken, out endToken, out nextToken);

                // create marker to insert
                SpanMarker startMarker = new SpanMarker(type: (previousToken.RawKind == 0) ? SpanMarkerType.BeginningOfFile : SpanMarkerType.Normal,
                                                        oppositeMarkerType: (nextToken.RawKind == 0) ? SpanMarkerType.EndOfFile : SpanMarkerType.Normal);

                SpanMarker endMarker = new SpanMarker(type: (nextToken.RawKind == 0) ? SpanMarkerType.EndOfFile : SpanMarkerType.Normal,
                                                      oppositeMarkerType: (previousToken.RawKind == 0) ? SpanMarkerType.BeginningOfFile : SpanMarkerType.Normal);

                // set proper tokens
                previousToken = (previousToken.RawKind == 0) ? root.GetFirstToken(includeZeroWidth: true) : previousToken;
                nextToken = (nextToken.RawKind == 0) ? root.GetLastToken(includeZeroWidth: true) : nextToken;

                // build token to marker map
                tokenAnnotationMap.GetOrAdd(previousToken, _ => new List<SyntaxAnnotation>()).Add(startMarker.Annotation);
                tokenAnnotationMap.GetOrAdd(nextToken, _ => new List<SyntaxAnnotation>()).Add(endMarker.Annotation);

                // remember markers
                annotations.Add(new ValueTuple<SyntaxAnnotation, SyntaxAnnotation>(startMarker.Annotation, endMarker.Annotation));
            }

            // do a quick check
            // if, after all merges, spans are merged into one span that covers whole tree, return right away
            if (CleanupWholeNode(annotations))
            {
                // this will indicate that no annotation is needed
                return default(ValueTuple<SyntaxNode, List<ValueTuple<SyntaxAnnotation, SyntaxAnnotation>>>);
            }

            // inject annotations
            var newNode = InjectAnnotations(root, tokenAnnotationMap);
            return ValueTuple.Create(newNode, annotations);
        }
        private bool TryGetTextSpanFromAnnotation(
            SpanMarker previousTokenMarker,
            SpanMarker nextTokenMarker,
            SyntaxNode node,
            IEnumerable<SyntaxToken> previousTokens,
            IEnumerable<SyntaxToken> nextTokens,
            out TextSpan span)
        {
            // set initial value
            span = default(TextSpan);

            var previousToken = previousTokens.FirstOrDefault();
            var nextToken = nextTokens.FirstOrDefault();

            // define all variables required to determine state
            var hasNoPreviousToken = previousToken.RawKind == 0;
            var hasNoNextToken = nextToken.RawKind == 0;

            var hasMultiplePreviousToken = previousTokens.Skip(1).Any();
            var hasMultipleNextToken = nextTokens.Skip(1).Any();

            var hasOnePreviousToken = !hasNoPreviousToken && !hasMultiplePreviousToken;
            var hasOneNextToken = !hasNoNextToken && !hasMultipleNextToken;

            // normal case
            if (hasOnePreviousToken && hasOneNextToken)
            {
                return TryCreateTextSpan(GetPreviousTokenStartPosition(previousTokenMarker.Type, previousToken),
                                         GetNextTokenEndPosition(nextTokenMarker.Type, nextToken),
                                         out span);
            }

            // quick error check * we don't have any token, ignore this one.
            // this can't happen unless one of cleaners violated contract of only changing things inside of the provided span
            if (hasNoPreviousToken && hasNoNextToken)
            {
                return false;
            }

            // we don't have previous token, but we do have one next token
            // nextTokenMarker has hint to how to find opposite marker
            // if we can't find the other side, then it means one of cleaners has violated contract. ignore span.
            if (hasNoPreviousToken && hasOneNextToken)
            {
                if (nextTokenMarker.OppositeMarkerType == SpanMarkerType.BeginningOfFile)
                {
                    // okay, found right span
                    return TryCreateTextSpan(node.SpanStart, GetNextTokenEndPosition(nextTokenMarker.Type, nextToken), out span);
                }

                return false;
            }

            // one previous token but no next token with hint case
            if (hasOnePreviousToken && hasNoNextToken)
            {
                if (previousTokenMarker.OppositeMarkerType == SpanMarkerType.EndOfFile)
                {
                    // okay, found right span
                    return TryCreateTextSpan(GetPreviousTokenStartPosition(previousTokenMarker.Type, previousToken), node.Span.End, out span);
                }

                return false;
            }

            // now simple cases are done. now we need to deal with cases where annotation found more than one corresponding tokens
            // mostly it means one of cleaners violated contract. so we can just ignore the span except one cases where it involves beginning and end of tree
            Contract.ThrowIfFalse(hasMultiplePreviousToken || hasMultipleNextToken);

            // check whether it is one of special cases or not
            if (hasMultiplePreviousToken && previousTokenMarker.Type == SpanMarkerType.BeginningOfFile)
            {
                // okay, it is edge case, let's use start of node as beginning of the span
                span = TextSpan.FromBounds(node.SpanStart, GetNextTokenEndPosition(nextTokenMarker.Type, nextToken));
                return true;
            }

            if (hasMultipleNextToken && nextTokenMarker.Type == SpanMarkerType.EndOfFile)
            {
                // okay, it is edge case, let's use end of node as end of the span
                span = TextSpan.FromBounds(GetPreviousTokenStartPosition(previousTokenMarker.Type, previousToken), node.Span.End);
                return true;
            }

            // all other cases are invalid cases where one of code cleaner should have messed up things by moving around things it shouldn't move
            return false;
        }
Esempio n. 6
0
        private bool TryGetTextSpanFromAnnotation(
            SpanMarker previousTokenMarker,
            SpanMarker nextTokenMarker,
            SyntaxNode node,
            IEnumerable <SyntaxToken> previousTokens,
            IEnumerable <SyntaxToken> nextTokens,
            out TextSpan span)
        {
            // Set initial value
            span = default(TextSpan);

            var previousToken = previousTokens.FirstOrDefault();
            var nextToken     = nextTokens.FirstOrDefault();

            // Define all variables required to determine state
            var hasNoPreviousToken = previousToken.RawKind == 0;
            var hasNoNextToken     = nextToken.RawKind == 0;

            var hasMultiplePreviousToken = previousTokens.Skip(1).Any();
            var hasMultipleNextToken     = nextTokens.Skip(1).Any();

            var hasOnePreviousToken = !hasNoPreviousToken && !hasMultiplePreviousToken;
            var hasOneNextToken     = !hasNoNextToken && !hasMultipleNextToken;

            // Normal case
            if (hasOnePreviousToken && hasOneNextToken)
            {
                return(TryCreateTextSpan(GetPreviousTokenStartPosition(previousTokenMarker.Type, previousToken),
                                         GetNextTokenEndPosition(nextTokenMarker.Type, nextToken),
                                         out span));
            }

            // Quick error check * we don't have any token, ignore this one.
            // This can't happen unless one of cleaners violated the contract of only changing things inside of the provided span.
            if (hasNoPreviousToken && hasNoNextToken)
            {
                return(false);
            }

            // We don't have the previous token, but we do have one next token.
            // nextTokenMarker has hint to how to find the opposite marker.
            // If we can't find the other side, then it means one of cleaners has violated the contract. Ignore span.
            if (hasNoPreviousToken && hasOneNextToken)
            {
                if (nextTokenMarker.OppositeMarkerType == SpanMarkerType.BeginningOfFile)
                {
                    // Okay, found right span
                    return(TryCreateTextSpan(node.SpanStart, GetNextTokenEndPosition(nextTokenMarker.Type, nextToken), out span));
                }

                return(false);
            }

            // One previous token but no next token with hint case
            if (hasOnePreviousToken && hasNoNextToken)
            {
                if (previousTokenMarker.OppositeMarkerType == SpanMarkerType.EndOfFile)
                {
                    // Okay, found right span
                    return(TryCreateTextSpan(GetPreviousTokenStartPosition(previousTokenMarker.Type, previousToken), node.Span.End, out span));
                }

                return(false);
            }

            // Now the simple cases are done. Now we need to deal with cases where annotations found more than one corresponding token.
            // Mostly it means one of cleaners violated the contract, so we can just ignore the span except in one cases where it involves the beginning and end of the tree.
            Contract.ThrowIfFalse(hasMultiplePreviousToken || hasMultipleNextToken);

            // Check whether it is one of special cases or not
            if (hasMultiplePreviousToken && previousTokenMarker.Type == SpanMarkerType.BeginningOfFile)
            {
                // Okay, it is an edge case. Let's use the start of the node as the beginning of the span
                span = TextSpan.FromBounds(node.SpanStart, GetNextTokenEndPosition(nextTokenMarker.Type, nextToken));
                return(true);
            }

            if (hasMultipleNextToken && nextTokenMarker.Type == SpanMarkerType.EndOfFile)
            {
                // Okay, it is an edge case. Let's use the end of the node as the end of the span
                span = TextSpan.FromBounds(GetPreviousTokenStartPosition(previousTokenMarker.Type, previousToken), node.Span.End);
                return(true);
            }

            // All other cases are invalid cases where one of code cleaners messed things up by moving around things it shouldn't move.
            return(false);
        }