1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. 2 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web.Razor.Editor; 6 using System.Web.Razor.Generator; 7 using System.Web.Razor.Parser; 8 using System.Web.Razor.Parser.SyntaxTree; 9 using System.Web.Razor.Text; 10 using System.Web.Razor.Tokenizer; 11 using System.Web.Razor.Tokenizer.Symbols; 12 13 namespace System.Web.Razor.Test.Framework 14 { 15 public static class SpanFactoryExtensions 16 { EmptyCSharp(this SpanFactory self)17 public static UnclassifiedCodeSpanConstructor EmptyCSharp(this SpanFactory self) 18 { 19 return new UnclassifiedCodeSpanConstructor( 20 self.Span(SpanKind.Code, new CSharpSymbol(self.LocationTracker.CurrentLocation, String.Empty, CSharpSymbolType.Unknown))); 21 } 22 EmptyVB(this SpanFactory self)23 public static UnclassifiedCodeSpanConstructor EmptyVB(this SpanFactory self) 24 { 25 return new UnclassifiedCodeSpanConstructor( 26 self.Span(SpanKind.Code, new VBSymbol(self.LocationTracker.CurrentLocation, String.Empty, VBSymbolType.Unknown))); 27 } 28 EmptyHtml(this SpanFactory self)29 public static SpanConstructor EmptyHtml(this SpanFactory self) 30 { 31 return self.Span(SpanKind.Markup, new HtmlSymbol(self.LocationTracker.CurrentLocation, String.Empty, HtmlSymbolType.Unknown)) 32 .With(new MarkupCodeGenerator()); 33 } 34 Code(this SpanFactory self, string content)35 public static UnclassifiedCodeSpanConstructor Code(this SpanFactory self, string content) 36 { 37 return new UnclassifiedCodeSpanConstructor( 38 self.Span(SpanKind.Code, content, markup: false)); 39 } 40 CodeTransition(this SpanFactory self)41 public static SpanConstructor CodeTransition(this SpanFactory self) 42 { 43 return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: false).Accepts(AcceptedCharacters.None); 44 } 45 CodeTransition(this SpanFactory self, string content)46 public static SpanConstructor CodeTransition(this SpanFactory self, string content) 47 { 48 return self.Span(SpanKind.Transition, content, markup: false).Accepts(AcceptedCharacters.None); 49 } 50 CodeTransition(this SpanFactory self, CSharpSymbolType type)51 public static SpanConstructor CodeTransition(this SpanFactory self, CSharpSymbolType type) 52 { 53 return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None); 54 } 55 CodeTransition(this SpanFactory self, string content, CSharpSymbolType type)56 public static SpanConstructor CodeTransition(this SpanFactory self, string content, CSharpSymbolType type) 57 { 58 return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None); 59 } 60 CodeTransition(this SpanFactory self, VBSymbolType type)61 public static SpanConstructor CodeTransition(this SpanFactory self, VBSymbolType type) 62 { 63 return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None); 64 } 65 CodeTransition(this SpanFactory self, string content, VBSymbolType type)66 public static SpanConstructor CodeTransition(this SpanFactory self, string content, VBSymbolType type) 67 { 68 return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None); 69 } 70 MarkupTransition(this SpanFactory self)71 public static SpanConstructor MarkupTransition(this SpanFactory self) 72 { 73 return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: true).Accepts(AcceptedCharacters.None); 74 } 75 MarkupTransition(this SpanFactory self, string content)76 public static SpanConstructor MarkupTransition(this SpanFactory self, string content) 77 { 78 return self.Span(SpanKind.Transition, content, markup: true).Accepts(AcceptedCharacters.None); 79 } 80 MarkupTransition(this SpanFactory self, HtmlSymbolType type)81 public static SpanConstructor MarkupTransition(this SpanFactory self, HtmlSymbolType type) 82 { 83 return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None); 84 } 85 MarkupTransition(this SpanFactory self, string content, HtmlSymbolType type)86 public static SpanConstructor MarkupTransition(this SpanFactory self, string content, HtmlSymbolType type) 87 { 88 return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None); 89 } 90 MetaCode(this SpanFactory self, string content)91 public static SpanConstructor MetaCode(this SpanFactory self, string content) 92 { 93 return self.Span(SpanKind.MetaCode, content, markup: false); 94 } 95 MetaCode(this SpanFactory self, string content, CSharpSymbolType type)96 public static SpanConstructor MetaCode(this SpanFactory self, string content, CSharpSymbolType type) 97 { 98 return self.Span(SpanKind.MetaCode, content, type); 99 } 100 MetaCode(this SpanFactory self, string content, VBSymbolType type)101 public static SpanConstructor MetaCode(this SpanFactory self, string content, VBSymbolType type) 102 { 103 return self.Span(SpanKind.MetaCode, content, type); 104 } 105 MetaMarkup(this SpanFactory self, string content)106 public static SpanConstructor MetaMarkup(this SpanFactory self, string content) 107 { 108 return self.Span(SpanKind.MetaCode, content, markup: true); 109 } 110 MetaMarkup(this SpanFactory self, string content, HtmlSymbolType type)111 public static SpanConstructor MetaMarkup(this SpanFactory self, string content, HtmlSymbolType type) 112 { 113 return self.Span(SpanKind.MetaCode, content, type); 114 } 115 Comment(this SpanFactory self, string content, CSharpSymbolType type)116 public static SpanConstructor Comment(this SpanFactory self, string content, CSharpSymbolType type) 117 { 118 return self.Span(SpanKind.Comment, content, type); 119 } 120 Comment(this SpanFactory self, string content, VBSymbolType type)121 public static SpanConstructor Comment(this SpanFactory self, string content, VBSymbolType type) 122 { 123 return self.Span(SpanKind.Comment, content, type); 124 } 125 Comment(this SpanFactory self, string content, HtmlSymbolType type)126 public static SpanConstructor Comment(this SpanFactory self, string content, HtmlSymbolType type) 127 { 128 return self.Span(SpanKind.Comment, content, type); 129 } 130 Markup(this SpanFactory self, string content)131 public static SpanConstructor Markup(this SpanFactory self, string content) 132 { 133 return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupCodeGenerator()); 134 } 135 Markup(this SpanFactory self, params string[] content)136 public static SpanConstructor Markup(this SpanFactory self, params string[] content) 137 { 138 return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupCodeGenerator()); 139 } 140 GetLocationAndAdvance(this SourceLocationTracker self, string content)141 public static SourceLocation GetLocationAndAdvance(this SourceLocationTracker self, string content) 142 { 143 SourceLocation ret = self.CurrentLocation; 144 self.UpdateLocation(content); 145 return ret; 146 } 147 } 148 149 public class SpanFactory 150 { 151 public Func<ITextDocument, ITokenizer> MarkupTokenizerFactory { get; set; } 152 public Func<ITextDocument, ITokenizer> CodeTokenizerFactory { get; set; } 153 public SourceLocationTracker LocationTracker { get; private set; } 154 CreateCsHtml()155 public static SpanFactory CreateCsHtml() 156 { 157 return new SpanFactory() 158 { 159 MarkupTokenizerFactory = doc => new HtmlTokenizer(doc), 160 CodeTokenizerFactory = doc => new CSharpTokenizer(doc) 161 }; 162 } 163 CreateVbHtml()164 public static SpanFactory CreateVbHtml() 165 { 166 return new SpanFactory() 167 { 168 MarkupTokenizerFactory = doc => new HtmlTokenizer(doc), 169 CodeTokenizerFactory = doc => new VBTokenizer(doc) 170 }; 171 } 172 SpanFactory()173 public SpanFactory() 174 { 175 LocationTracker = new SourceLocationTracker(); 176 } 177 Span(SpanKind kind, string content, CSharpSymbolType type)178 public SpanConstructor Span(SpanKind kind, string content, CSharpSymbolType type) 179 { 180 return CreateSymbolSpan(kind, content, st => new CSharpSymbol(st, content, type)); 181 } 182 Span(SpanKind kind, string content, VBSymbolType type)183 public SpanConstructor Span(SpanKind kind, string content, VBSymbolType type) 184 { 185 return CreateSymbolSpan(kind, content, st => new VBSymbol(st, content, type)); 186 } 187 Span(SpanKind kind, string content, HtmlSymbolType type)188 public SpanConstructor Span(SpanKind kind, string content, HtmlSymbolType type) 189 { 190 return CreateSymbolSpan(kind, content, st => new HtmlSymbol(st, content, type)); 191 } 192 Span(SpanKind kind, string content, bool markup)193 public SpanConstructor Span(SpanKind kind, string content, bool markup) 194 { 195 return new SpanConstructor(kind, Tokenize(new[] { content }, markup)); 196 } 197 Span(SpanKind kind, string[] content, bool markup)198 public SpanConstructor Span(SpanKind kind, string[] content, bool markup) 199 { 200 return new SpanConstructor(kind, Tokenize(content, markup)); 201 } 202 Span(SpanKind kind, params ISymbol[] symbols)203 public SpanConstructor Span(SpanKind kind, params ISymbol[] symbols) 204 { 205 return new SpanConstructor(kind, symbols); 206 } 207 CreateSymbolSpan(SpanKind kind, string content, Func<SourceLocation, ISymbol> ctor)208 private SpanConstructor CreateSymbolSpan(SpanKind kind, string content, Func<SourceLocation, ISymbol> ctor) 209 { 210 SourceLocation start = LocationTracker.CurrentLocation; 211 LocationTracker.UpdateLocation(content); 212 return new SpanConstructor(kind, new[] { ctor(start) }); 213 } 214 Reset()215 public void Reset() 216 { 217 LocationTracker.CurrentLocation = SourceLocation.Zero; 218 } 219 Tokenize(IEnumerable<string> contentFragments, bool markup)220 private IEnumerable<ISymbol> Tokenize(IEnumerable<string> contentFragments, bool markup) 221 { 222 return contentFragments.SelectMany(fragment => Tokenize(fragment, markup)); 223 } 224 Tokenize(string content, bool markup)225 private IEnumerable<ISymbol> Tokenize(string content, bool markup) 226 { 227 ITokenizer tok = MakeTokenizer(markup, new SeekableTextReader(content)); 228 ISymbol sym; 229 ISymbol last = null; 230 while ((sym = tok.NextSymbol()) != null) 231 { 232 OffsetStart(sym, LocationTracker.CurrentLocation); 233 last = sym; 234 yield return sym; 235 } 236 LocationTracker.UpdateLocation(content); 237 } 238 MakeTokenizer(bool markup, SeekableTextReader seekableTextReader)239 private ITokenizer MakeTokenizer(bool markup, SeekableTextReader seekableTextReader) 240 { 241 if (markup) 242 { 243 return MarkupTokenizerFactory(seekableTextReader); 244 } 245 else 246 { 247 return CodeTokenizerFactory(seekableTextReader); 248 } 249 } 250 OffsetStart(ISymbol sym, SourceLocation sourceLocation)251 private void OffsetStart(ISymbol sym, SourceLocation sourceLocation) 252 { 253 sym.OffsetStart(sourceLocation); 254 } 255 } 256 257 public static class SpanConstructorExtensions 258 { Accepts(this SpanConstructor self, AcceptedCharacters accepted)259 public static SpanConstructor Accepts(this SpanConstructor self, AcceptedCharacters accepted) 260 { 261 return self.With(eh => eh.AcceptedCharacters = accepted); 262 } 263 AutoCompleteWith(this SpanConstructor self, string autoCompleteString)264 public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString) 265 { 266 return AutoCompleteWith(self, autoCompleteString, atEndOfSpan: false); 267 } 268 AutoCompleteWith(this SpanConstructor self, string autoCompleteString, bool atEndOfSpan)269 public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString, bool atEndOfSpan) 270 { 271 return self.With(new AutoCompleteEditHandler(SpanConstructor.TestTokenizer) { AutoCompleteString = autoCompleteString, AutoCompleteAtEndOfSpan = atEndOfSpan }); 272 } 273 WithEditorHints(this SpanConstructor self, EditorHints hints)274 public static SpanConstructor WithEditorHints(this SpanConstructor self, EditorHints hints) 275 { 276 return self.With(eh => eh.EditorHints = hints); 277 } 278 } 279 280 public class UnclassifiedCodeSpanConstructor 281 { 282 SpanConstructor _self; 283 UnclassifiedCodeSpanConstructor(SpanConstructor self)284 public UnclassifiedCodeSpanConstructor(SpanConstructor self) 285 { 286 _self = self; 287 } 288 AsMetaCode()289 public SpanConstructor AsMetaCode() 290 { 291 _self.Builder.Kind = SpanKind.MetaCode; 292 return _self; 293 } 294 AsStatement()295 public SpanConstructor AsStatement() 296 { 297 return _self.With(new StatementCodeGenerator()); 298 } 299 AsExpression()300 public SpanConstructor AsExpression() 301 { 302 return _self.With(new ExpressionCodeGenerator()); 303 } 304 AsImplicitExpression(ISet<string> keywords)305 public SpanConstructor AsImplicitExpression(ISet<string> keywords) 306 { 307 return AsImplicitExpression(keywords, acceptTrailingDot: false); 308 } 309 AsImplicitExpression(ISet<string> keywords, bool acceptTrailingDot)310 public SpanConstructor AsImplicitExpression(ISet<string> keywords, bool acceptTrailingDot) 311 { 312 return _self.With(new ImplicitExpressionEditHandler(SpanConstructor.TestTokenizer, keywords, acceptTrailingDot)) 313 .With(new ExpressionCodeGenerator()); 314 } 315 AsFunctionsBody()316 public SpanConstructor AsFunctionsBody() 317 { 318 return _self.With(new TypeMemberCodeGenerator()); 319 } 320 AsNamespaceImport(string ns, int namespaceKeywordLength)321 public SpanConstructor AsNamespaceImport(string ns, int namespaceKeywordLength) 322 { 323 return _self.With(new AddImportCodeGenerator(ns, namespaceKeywordLength)); 324 } 325 Hidden()326 public SpanConstructor Hidden() 327 { 328 return _self.With(SpanCodeGenerator.Null); 329 } 330 AsBaseType(string baseType)331 public SpanConstructor AsBaseType(string baseType) 332 { 333 return _self.With(new SetBaseTypeCodeGenerator(baseType)); 334 } 335 AsRazorDirectiveAttribute(string key, string value)336 public SpanConstructor AsRazorDirectiveAttribute(string key, string value) 337 { 338 return _self.With(new RazorDirectiveAttributeCodeGenerator(key, value)); 339 } 340 As(ISpanCodeGenerator codeGenerator)341 public SpanConstructor As(ISpanCodeGenerator codeGenerator) 342 { 343 return _self.With(codeGenerator); 344 } 345 } 346 347 public class SpanConstructor 348 { 349 public SpanBuilder Builder { get; private set; } 350 TestTokenizer(string str)351 internal static IEnumerable<ISymbol> TestTokenizer(string str) 352 { 353 yield return new RawTextSymbol(SourceLocation.Zero, str); 354 } 355 SpanConstructor(SpanKind kind, IEnumerable<ISymbol> symbols)356 public SpanConstructor(SpanKind kind, IEnumerable<ISymbol> symbols) 357 { 358 Builder = new SpanBuilder(); 359 Builder.Kind = kind; 360 Builder.EditHandler = SpanEditHandler.CreateDefault(TestTokenizer); 361 foreach (ISymbol sym in symbols) 362 { 363 Builder.Accept(sym); 364 } 365 } 366 Build()367 private Span Build() 368 { 369 return Builder.Build(); 370 } 371 With(ISpanCodeGenerator generator)372 public SpanConstructor With(ISpanCodeGenerator generator) 373 { 374 Builder.CodeGenerator = generator; 375 return this; 376 } 377 With(SpanEditHandler handler)378 public SpanConstructor With(SpanEditHandler handler) 379 { 380 Builder.EditHandler = handler; 381 return this; 382 } 383 With(Action<ISpanCodeGenerator> generatorConfigurer)384 public SpanConstructor With(Action<ISpanCodeGenerator> generatorConfigurer) 385 { 386 generatorConfigurer(Builder.CodeGenerator); 387 return this; 388 } 389 With(Action<SpanEditHandler> handlerConfigurer)390 public SpanConstructor With(Action<SpanEditHandler> handlerConfigurer) 391 { 392 handlerConfigurer(Builder.EditHandler); 393 return this; 394 } 395 operator Span(SpanConstructor self)396 public static implicit operator Span(SpanConstructor self) 397 { 398 return self.Build(); 399 } 400 Hidden()401 public SpanConstructor Hidden() 402 { 403 Builder.CodeGenerator = SpanCodeGenerator.Null; 404 return this; 405 } 406 } 407 } 408