private PartialParseResultInternal TryAcceptChange(Span target, SourceChange change, PartialParseResultInternal acceptResult = PartialParseResultInternal.Accepted)
        {
            var content = change.GetEditedContent(target);

            if (StartsWithKeyword(content))
            {
                return(PartialParseResultInternal.Rejected | PartialParseResultInternal.SpanContextChanged);
            }

            return(acceptResult);
        }
        private bool IsAcceptableIdentifierReplacement(SyntaxNode target, SourceChange change)
        {
            if (!change.IsReplace)
            {
                return(false);
            }

            var tokens = target.DescendantNodes().Where(n => n.IsToken).Cast <SyntaxToken>().ToArray();

            for (var i = 0; i < tokens.Length; i++)
            {
                var token = tokens[i];

                if (token == null)
                {
                    break;
                }

                var tokenStartIndex = token.Position;
                var tokenEndIndex   = token.EndPosition;

                // We're looking for the first token that contains the SourceChange.
                if (tokenEndIndex > change.Span.AbsoluteIndex)
                {
                    if (tokenEndIndex >= change.Span.AbsoluteIndex + change.Span.Length && token.Kind == SyntaxKind.Identifier)
                    {
                        // The token we're changing happens to be an identifier. Need to check if its transformed state is also one.
                        // We do this transformation logic to capture the case that the new text change happens to not be an identifier;
                        // i.e. "5". Alone, it's numeric, within an identifier it's classified as identifier.
                        var transformedContent = change.GetEditedContent(token.Content, change.Span.AbsoluteIndex - tokenStartIndex);
                        var newTokens          = Tokenizer(transformedContent);

                        if (newTokens.Count() != 1)
                        {
                            // The transformed content resulted in more than one token; we can only replace a single identifier with
                            // another single identifier.
                            break;
                        }

                        var newToken = newTokens.First();
                        if (newToken.Kind == SyntaxKind.Identifier)
                        {
                            return(true);
                        }
                    }

                    // Change is touching a non-identifier token or spans multiple tokens.

                    break;
                }
            }

            return(false);
        }
Example #3
0
        private bool IsAcceptableIdentifierReplacement(Span target, SourceChange change)
        {
            if (!change.IsReplace)
            {
                return(false);
            }

            for (var i = 0; i < target.Symbols.Count; i++)
            {
                var symbol = target.Symbols[i] as CSharpSymbol;

                if (symbol == null)
                {
                    break;
                }

                var symbolStartIndex = symbol.Start.AbsoluteIndex;
                var symbolEndIndex   = symbolStartIndex + symbol.Content.Length;

                // We're looking for the first symbol that contains the SourceChange.
                if (symbolEndIndex > change.Span.AbsoluteIndex)
                {
                    if (symbolEndIndex >= change.Span.AbsoluteIndex + change.Span.Length && symbol.Type == CSharpSymbolType.Identifier)
                    {
                        // The symbol we're changing happens to be an identifier. Need to check if its transformed state is also one.
                        // We do this transformation logic to capture the case that the new text change happens to not be an identifier;
                        // i.e. "5". Alone, it's numeric, within an identifier it's classified as identifier.
                        var transformedContent = change.GetEditedContent(symbol.Content, change.Span.AbsoluteIndex - symbolStartIndex);
                        var newSymbols         = Tokenizer(transformedContent);

                        if (newSymbols.Count() != 1)
                        {
                            // The transformed content resulted in more than one symbol; we can only replace a single identifier with
                            // another single identifier.
                            break;
                        }

                        var newSymbol = (CSharpSymbol)newSymbols.First();
                        if (newSymbol.Type == CSharpSymbolType.Identifier)
                        {
                            return(true);
                        }
                    }

                    // Change is touching a non-identifier symbol or spans multiple symbols.

                    break;
                }
            }

            return(false);
        }
Example #4
0
    public void GetEditedContent_ForReplace_ReturnsNewContent()
    {
        // Arrange
        var text = "Hello, World";

        var change = new SourceChange(2, 2, "heyo");

        // Act
        var result = change.GetEditedContent(text, 1);

        // Act
        Assert.Equal("Hheyolo, World", result);
    }
Example #5
0
    public void GetEditedContent_ForDelete_ReturnsNewContent()
    {
        // Arrange
        var text = "Hello, World";

        var change = new SourceChange(2, 2, string.Empty);

        // Act
        var result = change.GetEditedContent(text, 1);

        // Act
        Assert.Equal("Hlo, World", result);
    }
Example #6
0
        protected virtual SyntaxNode UpdateSpan(SyntaxNode target, SourceChange change)
        {
            var newContent = change.GetEditedContent(target);
            var builder    = Syntax.InternalSyntax.SyntaxListBuilder <Syntax.InternalSyntax.SyntaxToken> .Create();

            foreach (var token in Tokenizer(newContent))
            {
                builder.Add(token);
            }

            SyntaxNode newTarget = null;

            if (target is RazorMetaCodeSyntax)
            {
                newTarget = Syntax.InternalSyntax.SyntaxFactory.RazorMetaCode(builder.ToList()).CreateRed(target.Parent, target.Position);
            }
            else if (target is MarkupTextLiteralSyntax)
            {
                newTarget = Syntax.InternalSyntax.SyntaxFactory.MarkupTextLiteral(builder.ToList()).CreateRed(target.Parent, target.Position);
            }
            else if (target is MarkupEphemeralTextLiteralSyntax)
            {
                newTarget = Syntax.InternalSyntax.SyntaxFactory.MarkupEphemeralTextLiteral(builder.ToList()).CreateRed(target.Parent, target.Position);
            }
            else if (target is CSharpStatementLiteralSyntax)
            {
                newTarget = Syntax.InternalSyntax.SyntaxFactory.CSharpStatementLiteral(builder.ToList()).CreateRed(target.Parent, target.Position);
            }
            else if (target is CSharpExpressionLiteralSyntax)
            {
                newTarget = Syntax.InternalSyntax.SyntaxFactory.CSharpExpressionLiteral(builder.ToList()).CreateRed(target.Parent, target.Position);
            }
            else if (target is CSharpEphemeralTextLiteralSyntax)
            {
                newTarget = Syntax.InternalSyntax.SyntaxFactory.CSharpEphemeralTextLiteral(builder.ToList()).CreateRed(target.Parent, target.Position);
            }
            else if (target is UnclassifiedTextLiteralSyntax)
            {
                newTarget = Syntax.InternalSyntax.SyntaxFactory.UnclassifiedTextLiteral(builder.ToList()).CreateRed(target.Parent, target.Position);
            }
            else
            {
                Debug.Fail($"The type {target?.GetType().Name} is not a supported span node.");
            }

            var context = target.GetSpanContext();

            newTarget = context != null?newTarget?.WithSpanContext(context) : newTarget;

            return(newTarget);
        }
Example #7
0
        protected virtual SpanBuilder UpdateSpan(Span target, SourceChange change)
        {
            var newContent = change.GetEditedContent(target);
            var newSpan    = new SpanBuilder(target);

            newSpan.ClearTokens();
            foreach (var token in Tokenizer(newContent))
            {
                newSpan.Accept(token);
            }
            if (target.Next != null)
            {
                var newEnd = SourceLocationTracker.CalculateNewLocation(target.Start, newContent);
                target.Next.ChangeStart(newEnd);
            }
            return(newSpan);
        }
Example #8
0
    public void GetEditedContent_SyntaxNode_ReturnsNewContent()
    {
        // Arrange
        var builder = SyntaxListBuilder <SyntaxToken> .Create();

        builder.Add(SyntaxFactory.Token(SyntaxKind.Marker, "Hello, "));
        builder.Add(SyntaxFactory.Token(SyntaxKind.Marker, "World"));

        var node = SyntaxFactory.MarkupTextLiteral(builder.ToList()).CreateRed();

        var change = new SourceChange(2, 2, "heyo");

        // Act
        var result = change.GetEditedContent(node);

        // Act
        Assert.Equal("Heheyoo, World", result);
    }
    protected override PartialParseResultInternal CanAcceptChange(SyntaxNode target, SourceChange change)
    {
        if (AcceptedCharacters == AcceptedCharactersInternal.NonWhitespace)
        {
            var originalText  = change.GetOriginalText(target);
            var editedContent = change.GetEditedContent(target);

            if (!ContainsWhitespace(originalText) && !ContainsWhitespace(editedContent))
            {
                // Did not modify whitespace, directive format should be the same.
                // Return provisional so extensible IR/code gen pieces can see the full directive text
                // once the user stops editing the document.
                return(PartialParseResultInternal.Accepted | PartialParseResultInternal.Provisional);
            }
        }

        return(PartialParseResultInternal.Rejected);
    }