File: Language\Legacy\CodeBlockEditHandler.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Razor.Language.Syntax;
 
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
 
internal class CodeBlockEditHandler : SpanEditHandler
{
    public static void SetupBuilder(SpanEditHandlerBuilder builder, Func<string, IEnumerable<Syntax.InternalSyntax.SyntaxToken>> tokenizer)
    {
        builder.Factory = (acceptedCharacters, tokenizer) => new CodeBlockEditHandler
        {
            AcceptedCharacters = acceptedCharacters,
            Tokenizer = tokenizer,
        };
    }
 
    protected override PartialParseResultInternal CanAcceptChange(SyntaxNode target, SourceChange change)
    {
        if (IsAcceptableDeletion(target, change))
        {
            return PartialParseResultInternal.Accepted;
        }
 
        if (IsAcceptableReplacement(target, change))
        {
            return PartialParseResultInternal.Accepted;
        }
 
        if (IsAcceptableInsertion(change))
        {
            return PartialParseResultInternal.Accepted;
        }
 
        return PartialParseResultInternal.Rejected;
    }
 
    // Internal for testing
    internal static bool IsAcceptableReplacement(SyntaxNode target, SourceChange change)
    {
        if (!change.IsReplace)
        {
            return false;
        }
 
        if (ContainsInvalidContent(change))
        {
            return false;
        }
 
        if (ModifiesInvalidContent(target, change))
        {
            return false;
        }
 
        return true;
    }
 
    // Internal for testing
    internal static bool IsAcceptableDeletion(SyntaxNode target, SourceChange change)
    {
        if (!change.IsDelete)
        {
            return false;
        }
 
        if (ModifiesInvalidContent(target, change))
        {
            return false;
        }
 
        return true;
    }
 
    // Internal for testing
    internal static bool ModifiesInvalidContent(SyntaxNode target, SourceChange change)
    {
        var relativePosition = change.Span.AbsoluteIndex - target.Position;
 
        if (target.GetContent().IndexOfAny(['{', '}', '@', '<', '*',], relativePosition, change.Span.Length) >= 0)
        {
            return true;
        }
 
        return false;
    }
 
    // Internal for testing
    internal static bool IsAcceptableInsertion(SourceChange change)
    {
        if (!change.IsInsert)
        {
            return false;
        }
 
        if (ContainsInvalidContent(change))
        {
            return false;
        }
 
        return true;
    }
 
    // Internal for testing
    internal static bool ContainsInvalidContent(SourceChange change)
    {
        if (change.NewText.IndexOfAny(new[] { '{', '}', '@', '<', '*', }) >= 0)
        {
            return true;
        }
 
        return false;
    }
 
    public override string ToString()
    {
        return string.Format(CultureInfo.InvariantCulture, "{0};CodeBlock", base.ToString());
    }
 
    public override bool Equals(object obj)
    {
        return obj is CodeBlockEditHandler other &&
            base.Equals(other);
    }
 
    public override int GetHashCode() => base.GetHashCode();
}