aspnetcore/test/Microsoft.AspNetCore.Razor..../Legacy/TestSpanBuilder.cs

423 lines
14 KiB
C#

// 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;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal static class SpanFactoryExtensions
{
public static UnclassifiedCodeSpanConstructor EmptyCSharp(this SpanFactory self)
{
return new UnclassifiedCodeSpanConstructor(
self.Span(
SpanKind.Code,
new CSharpSymbol(string.Empty, CSharpSymbolType.Unknown)));
}
public static SpanConstructor EmptyHtml(this SpanFactory self)
{
return self
.Span(
SpanKind.Markup,
new HtmlSymbol(string.Empty, HtmlSymbolType.Unknown))
.With(new MarkupChunkGenerator());
}
public static UnclassifiedCodeSpanConstructor Code(this SpanFactory self, string content)
{
return new UnclassifiedCodeSpanConstructor(
self.Span(SpanKind.Code, content, markup: false));
}
public static SpanConstructor CodeTransition(this SpanFactory self)
{
return self
.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: false)
.Accepts(AcceptedCharacters.None);
}
public static SpanConstructor CodeTransition(this SpanFactory self, string content)
{
return self.Span(SpanKind.Transition, content, markup: false).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor CodeTransition(this SpanFactory self, CSharpSymbolType type)
{
return self
.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type)
.Accepts(AcceptedCharacters.None);
}
public static SpanConstructor CodeTransition(this SpanFactory self, string content, CSharpSymbolType type)
{
return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MarkupTransition(this SpanFactory self)
{
return self
.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: true)
.Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MarkupTransition(this SpanFactory self, string content)
{
return self.Span(SpanKind.Transition, content, markup: true).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MarkupTransition(this SpanFactory self, HtmlSymbolType type)
{
return self
.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type)
.Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MarkupTransition(this SpanFactory self, string content, HtmlSymbolType type)
{
return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MetaCode(this SpanFactory self, string content)
{
return self.Span(SpanKind.MetaCode, content, markup: false);
}
public static SpanConstructor MetaCode(this SpanFactory self, string content, CSharpSymbolType type)
{
return self.Span(SpanKind.MetaCode, content, type);
}
public static SpanConstructor MetaMarkup(this SpanFactory self, string content)
{
return self.Span(SpanKind.MetaCode, content, markup: true);
}
public static SpanConstructor MetaMarkup(this SpanFactory self, string content, HtmlSymbolType type)
{
return self.Span(SpanKind.MetaCode, content, type);
}
public static SpanConstructor Comment(this SpanFactory self, string content, CSharpSymbolType type)
{
return self.Span(SpanKind.Comment, content, type);
}
public static SpanConstructor Comment(this SpanFactory self, string content, HtmlSymbolType type)
{
return self.Span(SpanKind.Comment, content, type);
}
public static SpanConstructor BangEscape(this SpanFactory self)
{
return self
.Span(SpanKind.MetaCode, "!", markup: true)
.With(SpanChunkGenerator.Null)
.Accepts(AcceptedCharacters.None);
}
public static SpanConstructor Markup(this SpanFactory self, string content)
{
return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupChunkGenerator());
}
public static SpanConstructor Markup(this SpanFactory self, params string[] content)
{
return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupChunkGenerator());
}
public static SpanConstructor CodeMarkup(this SpanFactory self, params string[] content)
{
return self
.Span(SpanKind.Code, content, markup: true)
.AsCodeMarkup();
}
public static SpanConstructor CSharpCodeMarkup(this SpanFactory self, string content)
{
return self.Code(content)
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true)
.AsCodeMarkup();
}
public static SpanConstructor AsCodeMarkup(this SpanConstructor self)
{
return self
.With(new ImplicitExpressionEditHandler(
(content) => SpanConstructor.TestTokenizer(content),
CSharpCodeParser.DefaultKeywords,
acceptTrailingDot: true))
.With(new MarkupChunkGenerator())
.Accepts(AcceptedCharacters.AnyExceptNewline);
}
public static SourceLocation GetLocationAndAdvance(this SourceLocationTracker self, string content)
{
var ret = self.CurrentLocation;
self.UpdateLocation(content);
return ret;
}
}
internal class SpanFactory
{
public SpanFactory()
{
LocationTracker = new SourceLocationTracker();
MarkupTokenizerFactory = doc => new HtmlTokenizer(doc);
CodeTokenizerFactory = doc => new CSharpTokenizer(doc);
}
public Func<ITextDocument, HtmlTokenizer> MarkupTokenizerFactory { get; }
public Func<ITextDocument, CSharpTokenizer> CodeTokenizerFactory { get; }
public SourceLocationTracker LocationTracker { get; }
public SpanConstructor Span(SpanKind kind, string content, CSharpSymbolType type)
{
return CreateSymbolSpan(kind, content, () => new CSharpSymbol(content, type));
}
public SpanConstructor Span(SpanKind kind, string content, HtmlSymbolType type)
{
return CreateSymbolSpan(kind, content, () => new HtmlSymbol(content, type));
}
public SpanConstructor Span(SpanKind kind, string content, bool markup)
{
return new SpanConstructor(kind, LocationTracker.CurrentLocation, Tokenize(new[] { content }, markup));
}
public SpanConstructor Span(SpanKind kind, string[] content, bool markup)
{
return new SpanConstructor(kind, LocationTracker.CurrentLocation, Tokenize(content, markup));
}
public SpanConstructor Span(SpanKind kind, params ISymbol[] symbols)
{
var start = LocationTracker.CurrentLocation;
foreach (var symbol in symbols)
{
LocationTracker.UpdateLocation(symbol.Content);
}
return new SpanConstructor(kind, start, symbols);
}
private SpanConstructor CreateSymbolSpan(SpanKind kind, string content, Func<ISymbol> ctor)
{
var start = LocationTracker.CurrentLocation;
LocationTracker.UpdateLocation(content);
return new SpanConstructor(kind, start, new[] { ctor() });
}
public void Reset()
{
LocationTracker.CurrentLocation = SourceLocation.Zero;
}
private IEnumerable<ISymbol> Tokenize(IEnumerable<string> contentFragments, bool markup)
{
return contentFragments.SelectMany(fragment => Tokenize(fragment, markup));
}
private IEnumerable<ISymbol> Tokenize(string content, bool markup)
{
var tokenizer = MakeTokenizer(markup, new SeekableTextReader(content, filePath: null));
ISymbol symbol;
ISymbol last = null;
while ((symbol = tokenizer.NextSymbol()) != null)
{
last = symbol;
yield return symbol;
}
LocationTracker.UpdateLocation(content);
}
private ITokenizer MakeTokenizer(bool markup, SeekableTextReader seekableTextReader)
{
if (markup)
{
return MarkupTokenizerFactory(seekableTextReader);
}
else
{
return CodeTokenizerFactory(seekableTextReader);
}
}
}
internal static class SpanConstructorExtensions
{
public static SpanConstructor Accepts(this SpanConstructor self, AcceptedCharacters accepted)
{
return self.With(eh => eh.AcceptedCharacters = accepted);
}
public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString)
{
return AutoCompleteWith(self, autoCompleteString, atEndOfSpan: false);
}
public static SpanConstructor AutoCompleteWith(
this SpanConstructor self,
string autoCompleteString,
bool atEndOfSpan)
{
return self.With(new AutoCompleteEditHandler(
(content) => SpanConstructor.TestTokenizer(content),
autoCompleteAtEndOfSpan: atEndOfSpan)
{
AutoCompleteString = autoCompleteString
});
}
}
internal class UnclassifiedCodeSpanConstructor
{
SpanConstructor _self;
public UnclassifiedCodeSpanConstructor(SpanConstructor self)
{
_self = self;
}
public SpanConstructor AsMetaCode()
{
_self.Builder.Kind = SpanKind.MetaCode;
return _self;
}
public SpanConstructor AsStatement()
{
return _self.With(new StatementChunkGenerator());
}
public SpanConstructor AsExpression()
{
return _self.With(new ExpressionChunkGenerator());
}
public SpanConstructor AsImplicitExpression(ISet<string> keywords)
{
return AsImplicitExpression(keywords, acceptTrailingDot: false);
}
public SpanConstructor AsImplicitExpression(ISet<string> keywords, bool acceptTrailingDot)
{
return _self
.With(new ImplicitExpressionEditHandler((content) => SpanConstructor.TestTokenizer(content), keywords, acceptTrailingDot))
.With(new ExpressionChunkGenerator());
}
public SpanConstructor AsNamespaceImport(string ns)
{
return _self.With(new AddImportChunkGenerator(ns));
}
public SpanConstructor Hidden()
{
return _self.With(SpanChunkGenerator.Null);
}
public SpanConstructor AsAddTagHelper(string lookupText)
{
return _self
.With(new AddTagHelperChunkGenerator(lookupText))
.Accepts(AcceptedCharacters.AnyExceptNewline);
}
public SpanConstructor AsRemoveTagHelper(string lookupText)
{
return _self
.With(new RemoveTagHelperChunkGenerator(lookupText))
.Accepts(AcceptedCharacters.AnyExceptNewline);
}
public SpanConstructor AsTagHelperPrefixDirective(string prefix)
{
return _self
.With(new TagHelperPrefixDirectiveChunkGenerator(prefix))
.Accepts(AcceptedCharacters.AnyExceptNewline);
}
public SpanConstructor As(ISpanChunkGenerator chunkGenerator)
{
return _self.With(chunkGenerator);
}
}
internal class SpanConstructor
{
public SpanBuilder Builder { get; private set; }
internal static IEnumerable<ISymbol> TestTokenizer(string str)
{
yield return new RawTextSymbol(SourceLocation.Zero, str);
}
public SpanConstructor(SpanKind kind, SourceLocation location, IEnumerable<ISymbol> symbols)
{
Builder = new SpanBuilder(location);
Builder.Kind = kind;
Builder.EditHandler = SpanEditHandler.CreateDefault((content) => SpanConstructor.TestTokenizer(content));
foreach (ISymbol sym in symbols)
{
Builder.Accept(sym);
}
}
private Span Build()
{
return Builder.Build();
}
public SpanConstructor As(SpanKind spanKind)
{
Builder.Kind = spanKind;
return this;
}
public SpanConstructor With(ISpanChunkGenerator generator)
{
Builder.ChunkGenerator = generator;
return this;
}
public SpanConstructor With(SpanEditHandler handler)
{
Builder.EditHandler = handler;
return this;
}
public SpanConstructor With(Action<ISpanChunkGenerator> generatorConfigurer)
{
generatorConfigurer(Builder.ChunkGenerator);
return this;
}
public SpanConstructor With(Action<SpanEditHandler> handlerConfigurer)
{
handlerConfigurer(Builder.EditHandler);
return this;
}
public static implicit operator Span(SpanConstructor self)
{
return self.Build();
}
public SpanConstructor Hidden()
{
Builder.ChunkGenerator = SpanChunkGenerator.Null;
return this;
}
}
}