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); }
/// <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)); }
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; }
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); }