Add extensible directive abstractions
- Based generic directive implementation off of descriptors. - Added parsing logic to consume descriptors and parse content that's expected. - Added parsing errors to automagically detect unexpected directive pieces. - Updated visitor implementations to understand the directive bits. - Added a builder abstraction to easily create descriptors. Had to maintain the ability to manually construct a descriptor to enable convenient serialization/deserialization. - Added tests/comparers to verify correctness of parsing. #853
This commit is contained in:
parent
522f6e969d
commit
518378f499
|
|
@ -206,6 +206,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
Namespace.Children.Insert(i, @using);
|
Namespace.Children.Insert(i, @using);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
|
||||||
|
{
|
||||||
|
Builder.Add(new DirectiveTokenIRNode()
|
||||||
|
{
|
||||||
|
Content = span.Content,
|
||||||
|
Descriptor = chunkGenerator.Descriptor,
|
||||||
|
SourceLocation = span.Start,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void VisitStartDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block)
|
||||||
|
{
|
||||||
|
Builder.Push(new DirectiveIRNode()
|
||||||
|
{
|
||||||
|
Name = chunkGenerator.Descriptor.Name,
|
||||||
|
Descriptor = chunkGenerator.Descriptor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void VisitEndDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block)
|
||||||
|
{
|
||||||
|
Builder.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
private class ContainerRazorIRNode : RazorIRNode
|
private class ContainerRazorIRNode : RazorIRNode
|
||||||
{
|
{
|
||||||
private SourceLocation? _location;
|
private SourceLocation? _location;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public class DirectiveDescriptor
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public DirectiveDescriptorKind Kind { get; set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectiveTokenDescriptor> Tokens { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public static class DirectiveDescriptorBuilder
|
||||||
|
{
|
||||||
|
public static IDirectiveDescriptorBuilder Create(string name)
|
||||||
|
{
|
||||||
|
return new DefaultDirectiveDescriptorBuilder(name, DirectiveDescriptorKind.SingleLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDirectiveDescriptorBuilder CreateRazorBlock(string name)
|
||||||
|
{
|
||||||
|
return new DefaultDirectiveDescriptorBuilder(name, DirectiveDescriptorKind.RazorBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDirectiveDescriptorBuilder CreateCodeBlock(string name)
|
||||||
|
{
|
||||||
|
return new DefaultDirectiveDescriptorBuilder(name, DirectiveDescriptorKind.CodeBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultDirectiveDescriptorBuilder : IDirectiveDescriptorBuilder
|
||||||
|
{
|
||||||
|
private readonly List<DirectiveTokenDescriptor> _tokenDescriptors;
|
||||||
|
private readonly string _name;
|
||||||
|
private readonly DirectiveDescriptorKind _type;
|
||||||
|
|
||||||
|
public DefaultDirectiveDescriptorBuilder(string name, DirectiveDescriptorKind type)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_type = type;
|
||||||
|
_tokenDescriptors = new List<DirectiveTokenDescriptor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDirectiveDescriptorBuilder AddType()
|
||||||
|
{
|
||||||
|
var descriptor = new DirectiveTokenDescriptor()
|
||||||
|
{
|
||||||
|
Kind = DirectiveTokenKind.Type
|
||||||
|
};
|
||||||
|
_tokenDescriptors.Add(descriptor);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDirectiveDescriptorBuilder AddMember()
|
||||||
|
{
|
||||||
|
var descriptor = new DirectiveTokenDescriptor()
|
||||||
|
{
|
||||||
|
Kind = DirectiveTokenKind.Member
|
||||||
|
};
|
||||||
|
_tokenDescriptors.Add(descriptor);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDirectiveDescriptorBuilder AddString()
|
||||||
|
{
|
||||||
|
var descriptor = new DirectiveTokenDescriptor()
|
||||||
|
{
|
||||||
|
Kind = DirectiveTokenKind.String
|
||||||
|
};
|
||||||
|
_tokenDescriptors.Add(descriptor);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDirectiveDescriptorBuilder AddLiteral(string literal)
|
||||||
|
{
|
||||||
|
var descriptor = new DirectiveTokenDescriptor()
|
||||||
|
{
|
||||||
|
Kind = DirectiveTokenKind.Literal,
|
||||||
|
Value = literal,
|
||||||
|
};
|
||||||
|
_tokenDescriptors.Add(descriptor);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectiveDescriptor Build()
|
||||||
|
{
|
||||||
|
var descriptor = new DirectiveDescriptor
|
||||||
|
{
|
||||||
|
Name = _name,
|
||||||
|
Kind = _type,
|
||||||
|
Tokens = _tokenDescriptors,
|
||||||
|
};
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal class DirectiveDescriptorComparer : IEqualityComparer<DirectiveDescriptor>
|
||||||
|
{
|
||||||
|
public static readonly DirectiveDescriptorComparer Default = new DirectiveDescriptorComparer();
|
||||||
|
|
||||||
|
protected DirectiveDescriptorComparer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(DirectiveDescriptor descriptorX, DirectiveDescriptor descriptorY)
|
||||||
|
{
|
||||||
|
if (descriptorX == descriptorY)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptorX != null &&
|
||||||
|
string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.Ordinal) &&
|
||||||
|
descriptorX.Kind == descriptorY.Kind &&
|
||||||
|
Enumerable.SequenceEqual(
|
||||||
|
descriptorX.Tokens,
|
||||||
|
descriptorY.Tokens,
|
||||||
|
DirectiveTokenDescriptorComparer.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(DirectiveDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
var hashCodeCombiner = HashCodeCombiner.Start();
|
||||||
|
hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(descriptor.Kind);
|
||||||
|
|
||||||
|
return hashCodeCombiner.CombinedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public enum DirectiveDescriptorKind
|
||||||
|
{
|
||||||
|
SingleLine,
|
||||||
|
RazorBlock,
|
||||||
|
CodeBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public class DirectiveTokenDescriptor
|
||||||
|
{
|
||||||
|
public DirectiveTokenKind Kind { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal class DirectiveTokenDescriptorComparer : IEqualityComparer<DirectiveTokenDescriptor>
|
||||||
|
{
|
||||||
|
public static readonly DirectiveTokenDescriptorComparer Default = new DirectiveTokenDescriptorComparer();
|
||||||
|
|
||||||
|
protected DirectiveTokenDescriptorComparer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(DirectiveTokenDescriptor descriptorX, DirectiveTokenDescriptor descriptorY)
|
||||||
|
{
|
||||||
|
if (descriptorX == descriptorY)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptorX != null &&
|
||||||
|
string.Equals(descriptorX.Value, descriptorY.Value, StringComparison.Ordinal) &&
|
||||||
|
descriptorX.Kind == descriptorY.Kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(DirectiveTokenDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
var hashCodeCombiner = HashCodeCombiner.Start();
|
||||||
|
hashCodeCombiner.Add(descriptor.Value, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(descriptor.Kind);
|
||||||
|
|
||||||
|
return hashCodeCombiner.CombinedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public enum DirectiveTokenKind
|
||||||
|
{
|
||||||
|
Type,
|
||||||
|
Member,
|
||||||
|
String,
|
||||||
|
Literal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public interface IDirectiveDescriptorBuilder
|
||||||
|
{
|
||||||
|
IDirectiveDescriptorBuilder AddType();
|
||||||
|
|
||||||
|
IDirectiveDescriptorBuilder AddMember();
|
||||||
|
|
||||||
|
IDirectiveDescriptorBuilder AddString();
|
||||||
|
|
||||||
|
IDirectiveDescriptorBuilder AddLiteral(string literal);
|
||||||
|
|
||||||
|
DirectiveDescriptor Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||||
|
{
|
||||||
|
public class DirectiveIRNode : RazorIRNode
|
||||||
|
{
|
||||||
|
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
|
||||||
|
|
||||||
|
public override RazorIRNode Parent { get; set; }
|
||||||
|
|
||||||
|
internal override SourceLocation SourceLocation { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<DirectiveTokenIRNode> Tokens => Children.OfType<DirectiveTokenIRNode>();
|
||||||
|
|
||||||
|
public DirectiveDescriptor Descriptor { get; set; }
|
||||||
|
|
||||||
|
public override void Accept(RazorIRNodeVisitor visitor)
|
||||||
|
{
|
||||||
|
visitor.VisitDirective(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
|
||||||
|
{
|
||||||
|
return visitor.VisitDirective(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||||
|
{
|
||||||
|
public class DirectiveTokenIRNode : RazorIRNode
|
||||||
|
{
|
||||||
|
public override IList<RazorIRNode> Children { get; } = EmptyArray;
|
||||||
|
|
||||||
|
public override RazorIRNode Parent { get; set; }
|
||||||
|
|
||||||
|
internal override SourceLocation SourceLocation { get; set; }
|
||||||
|
|
||||||
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
public DirectiveTokenDescriptor Descriptor { get; set; }
|
||||||
|
|
||||||
|
public override void Accept(RazorIRNodeVisitor visitor)
|
||||||
|
{
|
||||||
|
visitor.VisitDirectiveToken(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
|
||||||
|
{
|
||||||
|
return visitor.VisitDirectiveToken(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void VisitDirectiveToken(DirectiveTokenIRNode node)
|
||||||
|
{
|
||||||
|
VisitDefault(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void VisitDirective(DirectiveIRNode node)
|
||||||
|
{
|
||||||
|
VisitDefault(node);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void VisitTemplate(TemplateIRNode node)
|
public virtual void VisitTemplate(TemplateIRNode node)
|
||||||
{
|
{
|
||||||
VisitDefault(node);
|
VisitDefault(node);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||||
return default(TResult);
|
return default(TResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual TResult VisitDirectiveToken(DirectiveTokenIRNode node)
|
||||||
|
{
|
||||||
|
return VisitDefault(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual TResult VisitDirective(DirectiveIRNode node)
|
||||||
|
{
|
||||||
|
return VisitDefault(node);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual TResult VisitTemplate(TemplateIRNode node)
|
public virtual TResult VisitTemplate(TemplateIRNode node)
|
||||||
{
|
{
|
||||||
return VisitDefault(node);
|
return VisitDefault(node);
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
private Dictionary<CSharpKeyword, Action<bool>> _keywordParsers = new Dictionary<CSharpKeyword, Action<bool>>();
|
private Dictionary<CSharpKeyword, Action<bool>> _keywordParsers = new Dictionary<CSharpKeyword, Action<bool>>();
|
||||||
|
|
||||||
public CSharpCodeParser(ParserContext context)
|
public CSharpCodeParser(ParserContext context)
|
||||||
|
: this (directiveDescriptors: Enumerable.Empty<DirectiveDescriptor>(), context: context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSharpCodeParser(IEnumerable<DirectiveDescriptor> directiveDescriptors, ParserContext context)
|
||||||
: base(CSharpLanguageCharacteristics.Instance, context)
|
: base(CSharpLanguageCharacteristics.Instance, context)
|
||||||
{
|
{
|
||||||
Keywords = new HashSet<string>();
|
Keywords = new HashSet<string>();
|
||||||
SetUpKeywords();
|
SetUpKeywords();
|
||||||
SetupDirectives();
|
SetupDirectives(directiveDescriptors);
|
||||||
SetUpExpressions();
|
SetUpExpressions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1404,8 +1409,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupDirectives()
|
private void SetupDirectives(IEnumerable<DirectiveDescriptor> directiveDescriptors)
|
||||||
{
|
{
|
||||||
|
foreach (var directiveDescriptor in directiveDescriptors)
|
||||||
|
{
|
||||||
|
MapDirectives(() => HandleDirective(directiveDescriptor), directiveDescriptor.Name);
|
||||||
|
}
|
||||||
|
|
||||||
MapDirectives(TagHelperPrefixDirective, SyntaxConstants.CSharp.TagHelperPrefixKeyword);
|
MapDirectives(TagHelperPrefixDirective, SyntaxConstants.CSharp.TagHelperPrefixKeyword);
|
||||||
MapDirectives(AddTagHelperDirective, SyntaxConstants.CSharp.AddTagHelperKeyword);
|
MapDirectives(AddTagHelperDirective, SyntaxConstants.CSharp.AddTagHelperKeyword);
|
||||||
MapDirectives(RemoveTagHelperDirective, SyntaxConstants.CSharp.RemoveTagHelperKeyword);
|
MapDirectives(RemoveTagHelperDirective, SyntaxConstants.CSharp.RemoveTagHelperKeyword);
|
||||||
|
|
@ -1414,6 +1424,183 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
MapDirectives(SectionDirective, SyntaxConstants.CSharp.SectionKeyword);
|
MapDirectives(SectionDirective, SyntaxConstants.CSharp.SectionKeyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleDirective(DirectiveDescriptor descriptor)
|
||||||
|
{
|
||||||
|
Context.Builder.CurrentBlock.Type = BlockType.Directive;
|
||||||
|
Context.Builder.CurrentBlock.ChunkGenerator = new DirectiveChunkGenerator(descriptor);
|
||||||
|
AssertDirective(descriptor.Name);
|
||||||
|
|
||||||
|
AcceptAndMoveNext();
|
||||||
|
Output(SpanKind.MetaCode, AcceptedCharacters.None);
|
||||||
|
|
||||||
|
for (var i = 0; i < descriptor.Tokens.Count; i++)
|
||||||
|
{
|
||||||
|
var tokenDescriptor = descriptor.Tokens[i];
|
||||||
|
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||||
|
|
||||||
|
if (tokenDescriptor.Kind == DirectiveTokenKind.Member || tokenDescriptor.Kind == DirectiveTokenKind.Type)
|
||||||
|
{
|
||||||
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
|
Output(SpanKind.Code, AcceptedCharacters.WhiteSpace);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
|
Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputKind = SpanKind.Markup;
|
||||||
|
switch (tokenDescriptor.Kind)
|
||||||
|
{
|
||||||
|
case DirectiveTokenKind.Type:
|
||||||
|
if (!NamespaceOrTypeName())
|
||||||
|
{
|
||||||
|
// Error logged for invalid type name, continue onto next piece.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputKind = SpanKind.Code;
|
||||||
|
break;
|
||||||
|
case DirectiveTokenKind.Member:
|
||||||
|
if (At(CSharpSymbolType.Identifier))
|
||||||
|
{
|
||||||
|
AcceptAndMoveNext();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Context.ErrorSink.OnError(
|
||||||
|
CurrentLocation,
|
||||||
|
LegacyResources.FormatDirectiveExpectsIdentifier(descriptor.Name),
|
||||||
|
CurrentSymbol.Content.Length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputKind = SpanKind.Code;
|
||||||
|
break;
|
||||||
|
case DirectiveTokenKind.String:
|
||||||
|
AcceptAndMoveNext();
|
||||||
|
break;
|
||||||
|
case DirectiveTokenKind.Literal:
|
||||||
|
if (string.Equals(CurrentSymbol.Content, tokenDescriptor.Value, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
AcceptAndMoveNext();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Context.ErrorSink.OnError(
|
||||||
|
CurrentLocation,
|
||||||
|
LegacyResources.FormatUnexpectedDirectiveLiteral(descriptor.Name, tokenDescriptor.Value),
|
||||||
|
CurrentSymbol.Content.Length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span.ChunkGenerator = new DirectiveTokenChunkGenerator(tokenDescriptor);
|
||||||
|
Output(outputKind, AcceptedCharacters.NonWhiteSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||||
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
|
|
||||||
|
switch (descriptor.Kind)
|
||||||
|
{
|
||||||
|
case DirectiveDescriptorKind.SingleLine:
|
||||||
|
Optional(CSharpSymbolType.Semicolon);
|
||||||
|
|
||||||
|
if (At(CSharpSymbolType.NewLine))
|
||||||
|
{
|
||||||
|
AcceptAndMoveNext();
|
||||||
|
}
|
||||||
|
else if (!EndOfFile)
|
||||||
|
{
|
||||||
|
Context.ErrorSink.OnError(
|
||||||
|
CurrentLocation,
|
||||||
|
LegacyResources.FormatUnexpectedDirectiveLiteral(descriptor.Name, Environment.NewLine),
|
||||||
|
CurrentSymbol.Content.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Output(SpanKind.Markup, AcceptedCharacters.AllWhiteSpace);
|
||||||
|
break;
|
||||||
|
case DirectiveDescriptorKind.RazorBlock:
|
||||||
|
Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace);
|
||||||
|
|
||||||
|
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
|
||||||
|
{
|
||||||
|
// When transitioning to the HTML parser we no longer want to act as if we're in a nested C# state.
|
||||||
|
// For instance, if <div>@hello.</div> is in a nested C# block we don't want the trailing '.' to be handled
|
||||||
|
// as C#; it should be handled as a period because it's wrapped in markup.
|
||||||
|
var wasNested = IsNested;
|
||||||
|
IsNested = false;
|
||||||
|
using (PushSpanConfig())
|
||||||
|
{
|
||||||
|
HtmlParser.ParseSection(Tuple.Create("{", "}"), caseSensitive: true);
|
||||||
|
}
|
||||||
|
Initialize(Span);
|
||||||
|
IsNested = wasNested;
|
||||||
|
NextToken();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case DirectiveDescriptorKind.CodeBlock:
|
||||||
|
Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace);
|
||||||
|
|
||||||
|
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
|
||||||
|
{
|
||||||
|
NextToken();
|
||||||
|
Balance(BalancingModes.NoErrorOnFailure, CSharpSymbolType.LeftBrace, CSharpSymbolType.RightBrace, startingBraceLocation);
|
||||||
|
Span.ChunkGenerator = new StatementChunkGenerator();
|
||||||
|
Output(SpanKind.Code);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseDirectiveBlock(DirectiveDescriptor descriptor, Action<SourceLocation> parseChildren)
|
||||||
|
{
|
||||||
|
if (EndOfFile)
|
||||||
|
{
|
||||||
|
Context.ErrorSink.OnError(
|
||||||
|
CurrentLocation,
|
||||||
|
LegacyResources.FormatUnexpectedEOFAfterDirective(descriptor.Name, "{"),
|
||||||
|
length: 1 /* { */);
|
||||||
|
}
|
||||||
|
else if (!At(CSharpSymbolType.LeftBrace))
|
||||||
|
{
|
||||||
|
Context.ErrorSink.OnError(
|
||||||
|
CurrentLocation,
|
||||||
|
LegacyResources.FormatUnexpectedDirectiveLiteral(descriptor.Name, "{"),
|
||||||
|
CurrentSymbol.Content.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var editHandler = new AutoCompleteEditHandler(Language.TokenizeString, autoCompleteAtEndOfSpan: true);
|
||||||
|
Span.EditHandler = editHandler;
|
||||||
|
var startingBraceLocation = CurrentLocation;
|
||||||
|
Accept(CurrentSymbol);
|
||||||
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
|
Output(SpanKind.MetaCode, AcceptedCharacters.None);
|
||||||
|
|
||||||
|
parseChildren(startingBraceLocation);
|
||||||
|
|
||||||
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
|
if (!Optional(CSharpSymbolType.RightBrace))
|
||||||
|
{
|
||||||
|
editHandler.AutoCompleteString = "}";
|
||||||
|
Context.ErrorSink.OnError(
|
||||||
|
startingBraceLocation,
|
||||||
|
LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF(descriptor.Name, "{", "}"),
|
||||||
|
length: 1 /* } */);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None;
|
||||||
|
}
|
||||||
|
CompleteBlock(insertMarkerIfNecessary: false, captureWhitespaceToEndOfLine: true);
|
||||||
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
|
Output(SpanKind.MetaCode, AcceptedCharacters.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void TagHelperPrefixDirective()
|
protected virtual void TagHelperPrefixDirective()
|
||||||
{
|
{
|
||||||
TagHelperDirective(
|
TagHelperDirective(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
{
|
||||||
|
internal class DirectiveChunkGenerator : ParentChunkGenerator
|
||||||
|
{
|
||||||
|
private static readonly Type Type = typeof(DirectiveChunkGenerator);
|
||||||
|
|
||||||
|
public DirectiveChunkGenerator(DirectiveDescriptor descriptor)
|
||||||
|
{
|
||||||
|
Descriptor = descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectiveDescriptor Descriptor { get; }
|
||||||
|
|
||||||
|
public override void AcceptStart(ParserVisitor visitor, Block block)
|
||||||
|
{
|
||||||
|
visitor.VisitStartDirectiveBlock(this, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AcceptEnd(ParserVisitor visitor, Block block)
|
||||||
|
{
|
||||||
|
visitor.VisitEndDirectiveBlock(this, block);
|
||||||
|
}
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
var other = obj as DirectiveChunkGenerator;
|
||||||
|
return base.Equals(other) &&
|
||||||
|
DirectiveDescriptorComparer.Default.Equals(Descriptor, other.Descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
var combiner = HashCodeCombiner.Start();
|
||||||
|
combiner.Add(base.GetHashCode());
|
||||||
|
combiner.Add(Type);
|
||||||
|
|
||||||
|
return combiner.CombinedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
{
|
||||||
|
internal class DirectiveTokenChunkGenerator : SpanChunkGenerator
|
||||||
|
{
|
||||||
|
private static readonly Type Type = typeof(DirectiveTokenChunkGenerator);
|
||||||
|
|
||||||
|
public DirectiveTokenChunkGenerator(DirectiveTokenDescriptor tokenDescriptor)
|
||||||
|
{
|
||||||
|
Descriptor = tokenDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectiveTokenDescriptor Descriptor { get; set; }
|
||||||
|
|
||||||
|
public override void Accept(ParserVisitor visitor, Span span)
|
||||||
|
{
|
||||||
|
visitor.VisitDirectiveToken(this, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
var other = obj as DirectiveTokenChunkGenerator;
|
||||||
|
return base.Equals(other) &&
|
||||||
|
DirectiveTokenDescriptorComparer.Default.Equals(Descriptor, other.Descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
var combiner = HashCodeCombiner.Start();
|
||||||
|
combiner.Add(base.GetHashCode());
|
||||||
|
combiner.Add(Type);
|
||||||
|
|
||||||
|
return combiner.CombinedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -89,10 +89,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span block)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void VisitEndTemplateBlock(TemplateBlockChunkGenerator chunkGenerator, Block block)
|
public virtual void VisitEndTemplateBlock(TemplateBlockChunkGenerator chunkGenerator, Block block)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void VisitStartDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void VisitEndDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void VisitStartTemplateBlock(TemplateBlockChunkGenerator chunkGenerator, Block block)
|
public virtual void VisitStartTemplateBlock(TemplateBlockChunkGenerator chunkGenerator, Block block)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,9 @@
|
||||||
<data name="CSharpSymbol_Whitespace" xml:space="preserve">
|
<data name="CSharpSymbol_Whitespace" xml:space="preserve">
|
||||||
<value><<white space>></value>
|
<value><<white space>></value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DirectiveExpectsIdentifier" xml:space="preserve">
|
||||||
|
<value>The '{0}' directive expects an identifier.</value>
|
||||||
|
</data>
|
||||||
<data name="EndBlock_Called_Without_Matching_StartBlock" xml:space="preserve">
|
<data name="EndBlock_Called_Without_Matching_StartBlock" xml:space="preserve">
|
||||||
<value>"EndBlock" was called without a matching call to "StartBlock".</value>
|
<value>"EndBlock" was called without a matching call to "StartBlock".</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -381,4 +384,10 @@ Instead, wrap the contents of the block in "{{}}":
|
||||||
<data name="TokenizerView_CannotPutBack" xml:space="preserve">
|
<data name="TokenizerView_CannotPutBack" xml:space="preserve">
|
||||||
<value>In order to put a symbol back, it must have been the symbol which ended at the current position. The specified symbol ends at {0}, but the current position is {1}</value>
|
<value>In order to put a symbol back, it must have been the symbol which ended at the current position. The specified symbol ends at {0}, but the current position is {1}</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="UnexpectedDirectiveLiteral" xml:space="preserve">
|
||||||
|
<value>Unexpected literal following the '{0}' directive. Expected '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="UnexpectedEOFAfterDirective" xml:space="preserve">
|
||||||
|
<value>Unexpected end of file following the '{0}' directive. Expected '{1}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -330,6 +330,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
return GetString("CSharpSymbol_Whitespace");
|
return GetString("CSharpSymbol_Whitespace");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The '{0}' directive expects an identifier.
|
||||||
|
/// </summary>
|
||||||
|
internal static string DirectiveExpectsIdentifier
|
||||||
|
{
|
||||||
|
get { return GetString("DirectiveExpectsIdentifier"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The '{0}' directive expects an identifier.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatDirectiveExpectsIdentifier(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveExpectsIdentifier"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "EndBlock" was called without a matching call to "StartBlock".
|
/// "EndBlock" was called without a matching call to "StartBlock".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1318,6 +1334,38 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TokenizerView_CannotPutBack"), p0, p1);
|
return string.Format(CultureInfo.CurrentCulture, GetString("TokenizerView_CannotPutBack"), p0, p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unexpected literal following the '{0}' directive. Expected '{1}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string UnexpectedDirectiveLiteral
|
||||||
|
{
|
||||||
|
get { return GetString("UnexpectedDirectiveLiteral"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unexpected literal following the '{0}' directive. Expected '{1}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatUnexpectedDirectiveLiteral(object p0, object p1)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedDirectiveLiteral"), p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unexpected end of file following the '{0}' directive. Expected '{1}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string UnexpectedEOFAfterDirective
|
||||||
|
{
|
||||||
|
get { return GetString("UnexpectedEOFAfterDirective"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unexpected end of file following the '{0}' directive. Expected '{1}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatUnexpectedEOFAfterDirective(object p0, object p1)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedEOFAfterDirective"), p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Copyright(c) .NET Foundation.All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public class DirectiveDescriptorBuilderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Create_BuildsSingleLineDirectiveDescriptor()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom").Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(DirectiveDescriptorKind.SingleLine, descriptor.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateRazorBlock_BuildsRazorBlockDirectiveDescriptor()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.CreateRazorBlock("custom").Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(DirectiveDescriptorKind.RazorBlock, descriptor.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateCodeBlock_BuildsCodeBlockDirectiveDescriptor()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.CreateCodeBlock("custom").Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(DirectiveDescriptorKind.CodeBlock, descriptor.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddType_AddsToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = DirectiveDescriptorBuilder.Create("custom");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptor = builder.AddType().Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var token = Assert.Single(descriptor.Tokens);
|
||||||
|
Assert.Equal(DirectiveTokenKind.Type, token.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddMember_AddsToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = DirectiveDescriptorBuilder.Create("custom");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptor = builder.AddMember().Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var token = Assert.Single(descriptor.Tokens);
|
||||||
|
Assert.Equal(DirectiveTokenKind.Member, token.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddString_AddsToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = DirectiveDescriptorBuilder.Create("custom");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptor = builder.AddString().Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var token = Assert.Single(descriptor.Tokens);
|
||||||
|
Assert.Equal(DirectiveTokenKind.String, token.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddLiteral_AddsToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = DirectiveDescriptorBuilder.Create("custom");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptor = builder.AddLiteral(",").Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var token = Assert.Single(descriptor.Tokens);
|
||||||
|
Assert.Equal(DirectiveTokenKind.Literal, token.Kind);
|
||||||
|
Assert.Equal(",", token.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddX_MaintainsMultipleTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = DirectiveDescriptorBuilder.Create("custom");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptor = builder
|
||||||
|
.AddType()
|
||||||
|
.AddMember()
|
||||||
|
.AddString()
|
||||||
|
.AddLiteral(",")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(descriptor.Tokens,
|
||||||
|
token => Assert.Equal(DirectiveTokenKind.Type, token.Kind),
|
||||||
|
token => Assert.Equal(DirectiveTokenKind.Member, token.Kind),
|
||||||
|
token => Assert.Equal(DirectiveTokenKind.String, token.Kind),
|
||||||
|
token =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DirectiveTokenKind.Literal, token.Kind);
|
||||||
|
Assert.Equal(",", token.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,378 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
public class CSharpDirectivesTest : CsHtmlCodeParserTestBase
|
public class CSharpDirectivesTest : CsHtmlCodeParserTestBase
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_UnderstandsTypeTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom").AddType().Build();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom System.Text.Encoding.ASCIIEncoding",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Code, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Code, "System.Text.Encoding.ASCIIEncoding", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_UnderstandsMemberTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom").AddMember().Build();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom Some_Member",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Code, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Code, "Some_Member", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_UnderstandsStringTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom").AddString().Build();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom AString",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "AString", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_UnderstandsLiteralTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom").AddLiteral("!").Build();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom !",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "!", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_UnderstandsMultipleTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom")
|
||||||
|
.AddType()
|
||||||
|
.AddMember()
|
||||||
|
.AddString()
|
||||||
|
.AddLiteral("!")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom System.Text.Encoding.ASCIIEncoding Some_Member AString !",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Code, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Code, "System.Text.Encoding.ASCIIEncoding", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Code, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Code, "Some_Member", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[1]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "AString", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[2]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "!", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[3]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_UnderstandsRazorBlocks()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.CreateRazorBlock("custom").AddString().Build();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom Header { <p>F{o}o</p> }",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "Header", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.MetaCode("{")
|
||||||
|
.AutoCompleteWith(null, atEndOfSpan: true)
|
||||||
|
.Accepts(AcceptedCharacters.None),
|
||||||
|
new MarkupBlock(
|
||||||
|
Factory.Markup(" "),
|
||||||
|
new MarkupTagBlock(
|
||||||
|
Factory.Markup("<p>")),
|
||||||
|
Factory.Markup("F", "{", "o", "}", "o"),
|
||||||
|
new MarkupTagBlock(
|
||||||
|
Factory.Markup("</p>")),
|
||||||
|
Factory.Markup(" ")),
|
||||||
|
Factory.MetaCode("}").Accepts(AcceptedCharacters.None)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_UnderstandsCodeBlocks()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.CreateCodeBlock("custom").AddString().Build();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom Name { foo(); bar(); }",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "Name", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.MetaCode("{")
|
||||||
|
.AutoCompleteWith(null, atEndOfSpan: true)
|
||||||
|
.Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Code(" foo(); bar(); ").AsStatement(),
|
||||||
|
Factory.MetaCode("}").Accepts(AcceptedCharacters.None)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_AllowsWhiteSpaceAroundTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom")
|
||||||
|
.AddType()
|
||||||
|
.AddMember()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom System.Text.Encoding.ASCIIEncoding Some_Member ",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Code, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Code, "System.Text.Encoding.ASCIIEncoding", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Code, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Code, "Some_Member", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[1]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false)
|
||||||
|
.Accepts(AcceptedCharacters.AllWhiteSpace)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_ErrorsForInvalidMemberTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom").AddMember().Build();
|
||||||
|
var expectedErorr = new RazorError(
|
||||||
|
LegacyResources.FormatDirectiveExpectsIdentifier("custom"),
|
||||||
|
new SourceLocation(8, 0, 8),
|
||||||
|
length: 1);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom -Some_Member",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Code, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace)),
|
||||||
|
expectedErorr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_ErrorsForUnmatchedLiteralTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom").AddLiteral("!").Build();
|
||||||
|
var expectedErorr = new RazorError(
|
||||||
|
LegacyResources.FormatUnexpectedDirectiveLiteral("custom", "!"),
|
||||||
|
new SourceLocation(8, 0, 8),
|
||||||
|
length: 2);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom hi",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace)),
|
||||||
|
expectedErorr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_ErrorsExtraContentAfterDirective()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.Create("custom").AddString().Build();
|
||||||
|
var expectedErorr = new RazorError(
|
||||||
|
LegacyResources.FormatUnexpectedDirectiveLiteral("custom", Environment.NewLine),
|
||||||
|
new SourceLocation(14, 0, 14),
|
||||||
|
length: 5);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom hello world",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "hello", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.AllWhiteSpace)),
|
||||||
|
expectedErorr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_ErrorsWhenExtraContentBeforeBlockStart()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.CreateCodeBlock("custom").AddString().Build();
|
||||||
|
var expectedErorr = new RazorError(
|
||||||
|
LegacyResources.FormatUnexpectedDirectiveLiteral("custom", "{"),
|
||||||
|
new SourceLocation(14, 0, 14),
|
||||||
|
length: 5);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom Hello World { foo(); bar(); }",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "Hello", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace)),
|
||||||
|
expectedErorr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_ErrorsWhenEOFBeforeDirectiveBlockStart()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.CreateCodeBlock("custom").AddString().Build();
|
||||||
|
var expectedErorr = new RazorError(
|
||||||
|
LegacyResources.FormatUnexpectedEOFAfterDirective("custom", "{"),
|
||||||
|
new SourceLocation(13, 0, 13),
|
||||||
|
length: 1);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom Hello",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "Hello", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
||||||
|
expectedErorr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_ErrorsWhenMissingEndBrace()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptorBuilder.CreateCodeBlock("custom").AddString().Build();
|
||||||
|
var expectedErorr = new RazorError(
|
||||||
|
LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("custom", "{", "}"),
|
||||||
|
new SourceLocation(14, 0, 14),
|
||||||
|
length: 1);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseCodeBlockTest(
|
||||||
|
"@custom Hello {",
|
||||||
|
new[] { descriptor },
|
||||||
|
new DirectiveBlock(
|
||||||
|
new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, "Hello", markup: false)
|
||||||
|
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
|
||||||
|
.Accepts(AcceptedCharacters.NonWhiteSpace),
|
||||||
|
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace),
|
||||||
|
Factory.MetaCode("{")
|
||||||
|
.AutoCompleteWith("}", atEndOfSpan: true)
|
||||||
|
.Accepts(AcceptedCharacters.None)),
|
||||||
|
expectedErorr);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TagHelperPrefixDirective_NoValueSucceeds()
|
public void TagHelperPrefixDirective_NoValueSucceeds()
|
||||||
{
|
{
|
||||||
|
|
@ -422,5 +788,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
Factory.MetaCode("}")
|
Factory.MetaCode("}")
|
||||||
.Accepts(AcceptedCharacters.None)));
|
.Accepts(AcceptedCharacters.None)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal virtual void ParseCodeBlockTest(
|
||||||
|
string document,
|
||||||
|
IEnumerable<DirectiveDescriptor> descriptors,
|
||||||
|
Block expected,
|
||||||
|
params RazorError[] expectedErrors)
|
||||||
|
{
|
||||||
|
var result = ParseCodeBlock(document, descriptors, designTime: false);
|
||||||
|
|
||||||
|
EvaluateResults(result, expected, expectedErrors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,20 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
internal virtual RazorSyntaxTree ParseCodeBlock(string document, bool designTime = false)
|
internal virtual RazorSyntaxTree ParseCodeBlock(string document, bool designTime = false)
|
||||||
|
{
|
||||||
|
return ParseCodeBlock(document, Enumerable.Empty<DirectiveDescriptor>(), designTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal virtual RazorSyntaxTree ParseCodeBlock(
|
||||||
|
string document,
|
||||||
|
IEnumerable<DirectiveDescriptor> descriptors,
|
||||||
|
bool designTime)
|
||||||
{
|
{
|
||||||
using (var reader = new SeekableTextReader(document))
|
using (var reader = new SeekableTextReader(document))
|
||||||
{
|
{
|
||||||
var context = new ParserContext(reader, designTime);
|
var context = new ParserContext(reader, designTime);
|
||||||
|
|
||||||
var parser = new CSharpCodeParser(context);
|
var parser = new CSharpCodeParser(descriptors, context);
|
||||||
parser.HtmlParser = new HtmlMarkupParser(context)
|
parser.HtmlParser = new HtmlMarkupParser(context)
|
||||||
{
|
{
|
||||||
CodeParser = parser,
|
CodeParser = parser,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue