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