Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Razor.Generator;
using System.Web.Razor.Parser;
using Microsoft.CSharp;
namespace System.Web.Razor
{
/// <summary>
/// Defines the C# Code Language for Razor
/// </summary>
public class CSharpRazorCodeLanguage : RazorCodeLanguage
{
private const string CSharpLanguageName = "csharp";
/// <summary>
/// Returns the name of the language: "csharp"
/// </summary>
public override string LanguageName
{
get { return CSharpLanguageName; }
}
/// <summary>
/// Returns the type of the CodeDOM provider for this language
/// </summary>
public override Type CodeDomProviderType
{
get { return typeof(CSharpCodeProvider); }
}
/// <summary>
/// Constructs a new instance of the code parser for this language
/// </summary>
public override ParserBase CreateCodeParser()
{
return new CSharpCodeParser();
}
/// <summary>
/// Constructs a new instance of the code generator for this language with the specified settings
/// </summary>
public override RazorCodeGenerator CreateCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host)
{
return new CSharpRazorCodeGenerator(className, rootNamespaceName, sourceFileName, host);
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Razor.Text;
namespace System.Web.Razor
{
/// <summary>
/// Arguments for the DocumentParseComplete event in RazorEditorParser
/// </summary>
public class DocumentParseCompleteEventArgs : EventArgs
{
/// <summary>
/// Indicates if the tree structure has actually changed since the previous reparse.
/// </summary>
public bool TreeStructureChanged { get; set; }
/// <summary>
/// The results of the code generation and parsing
/// </summary>
public GeneratorResults GeneratorResults { get; set; }
/// <summary>
/// The TextChange which triggered the reparse
/// </summary>
public TextChange SourceChange { get; set; }
}
}

View File

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Web.Razor.Editor;
using System.Web.Razor.Text;
using System.Web.Razor.Tokenizer.Symbols;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Parser.SyntaxTree
{
public class AutoCompleteEditHandler : SpanEditHandler
{
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer)
: base(tokenizer)
{
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, AcceptedCharacters accepted)
: base(tokenizer, accepted)
{
}
public bool AutoCompleteAtEndOfSpan { get; set; }
public string AutoCompleteString { get; set; }
protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange)
{
if (((AutoCompleteAtEndOfSpan && IsAtEndOfSpan(target, normalizedChange)) || IsAtEndOfFirstLine(target, normalizedChange)) &&
normalizedChange.IsInsert &&
ParserHelpers.IsNewLine(normalizedChange.NewText) &&
AutoCompleteString != null)
{
return PartialParseResult.Rejected | PartialParseResult.AutoCompleteBlock;
}
return PartialParseResult.Rejected;
}
public override string ToString()
{
return base.ToString() + ",AutoComplete:[" + (AutoCompleteString ?? "<null>") + "]" + (AutoCompleteAtEndOfSpan ? ";AtEnd" : ";AtEOL");
}
public override bool Equals(object obj)
{
AutoCompleteEditHandler other = obj as AutoCompleteEditHandler;
return base.Equals(obj) &&
other != null &&
String.Equals(other.AutoCompleteString, AutoCompleteString, StringComparison.Ordinal) &&
AutoCompleteAtEndOfSpan == other.AutoCompleteAtEndOfSpan;
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(base.GetHashCode())
.Add(AutoCompleteString)
.CombinedHash;
}
}
}

View File

@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Razor.Text;
namespace System.Web.Razor.Editor
{
internal class BackgroundParseTask : IDisposable
{
private CancellationTokenSource _cancelSource = new CancellationTokenSource();
private GeneratorResults _results;
[SuppressMessage("Microsoft.WebAPI", "CR4002:DoNotConstructTaskInstances", Justification = "This rule is not applicable to this assembly.")]
private BackgroundParseTask(RazorTemplateEngine engine, string sourceFileName, TextChange change)
{
Change = change;
Engine = engine;
SourceFileName = sourceFileName;
InnerTask = new Task(() => Run(_cancelSource.Token), _cancelSource.Token);
}
public Task InnerTask { get; private set; }
public TextChange Change { get; private set; }
public RazorTemplateEngine Engine { get; private set; }
public string SourceFileName { get; private set; }
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "It is the caller's responsibility to dispose this object, which will dispose all members of this object")]
public static BackgroundParseTask StartNew(RazorTemplateEngine engine, string sourceFileName, TextChange change)
{
BackgroundParseTask task = new BackgroundParseTask(engine, sourceFileName, change);
task.Start();
return task;
}
public void Cancel()
{
_cancelSource.Cancel();
}
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "This rule is not applicable to this assembly.")]
public void Start()
{
InnerTask.Start();
}
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "This rule is not applicable to this assembly.")]
public BackgroundParseTask ContinueWith(Action<GeneratorResults, BackgroundParseTask> continuation)
{
InnerTask.ContinueWith(t => RunContinuation(t, continuation));
return this;
}
private void RunContinuation(Task completed, Action<GeneratorResults, BackgroundParseTask> continuation)
{
if (!completed.IsCanceled)
{
continuation(_results, this);
}
}
internal virtual void Run(CancellationToken cancelToken)
{
if (!cancelToken.IsCancellationRequested)
{
// Seek the buffer to the beginning
Change.NewBuffer.Position = 0;
try
{
_results = Engine.GenerateCode(Change.NewBuffer, className: null, rootNamespace: null, sourceFileName: SourceFileName, cancelToken: cancelToken);
}
catch (OperationCanceledException ex)
{
if (ex.CancellationToken == cancelToken)
{
// We've been cancelled, so just return.
return;
}
else
{
// Exception was thrown for some other reason...
throw;
}
}
}
}
public void Dispose()
{
Dispose(disposing: true);
}
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "This rule is not applicable to this assembly.")]
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (InnerTask != null)
{
InnerTask.Dispose();
InnerTask = null;
}
if (_cancelSource != null)
{
_cancelSource.Dispose();
}
}
}
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Razor.Parser.SyntaxTree;
namespace System.Web.Razor.Editor
{
public class EditResult
{
public EditResult(PartialParseResult result, SpanBuilder editedSpan)
{
Result = result;
EditedSpan = editedSpan;
}
public PartialParseResult Result { get; set; }
public SpanBuilder EditedSpan { get; set; }
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Razor.Editor
{
/// <summary>
/// Used within <see cref="F:SpanEditHandler.EditorHints"/>.
/// </summary>
[Flags]
public enum EditorHints
{
/// <summary>
/// The default (Markup or Code) editor behavior for Statement completion should be used.
/// Editors can always use the default behavior, even if the span is labeled with a different CompletionType.
/// </summary>
None = 0, // 0000 0000
/// <summary>
/// Indicates that Virtual Path completion should be used for this span if the editor supports it.
/// Editors need not support this mode of completion, and will use the default (<see cref="F:EditorHints.None"/>) behavior
/// if they do not support it.
/// </summary>
VirtualPath = 1, // 0000 0001
/// <summary>
/// Indicates that this span's content contains the path to the layout page for this document.
/// </summary>
LayoutPage = 2, // 0000 0010
}
}

View File

@ -0,0 +1,236 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web.Razor.Parser;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Text;
using System.Web.Razor.Tokenizer.Symbols;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Editor
{
public class ImplicitExpressionEditHandler : SpanEditHandler
{
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public ImplicitExpressionEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, ISet<string> keywords, bool acceptTrailingDot)
: base(tokenizer)
{
Initialize(keywords, acceptTrailingDot);
}
public bool AcceptTrailingDot { get; private set; }
public ISet<string> Keywords { get; private set; }
public override string ToString()
{
return String.Format(CultureInfo.InvariantCulture, "{0};ImplicitExpression[{1}];K{2}", base.ToString(), AcceptTrailingDot ? "ATD" : "RTD", Keywords.Count);
}
public override bool Equals(object obj)
{
ImplicitExpressionEditHandler other = obj as ImplicitExpressionEditHandler;
return other != null &&
base.Equals(other) &&
Keywords.SetEquals(other.Keywords) &&
AcceptTrailingDot == other.AcceptTrailingDot;
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(base.GetHashCode())
.Add(AcceptTrailingDot)
.Add(Keywords)
.CombinedHash;
}
protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange)
{
if (AcceptedCharacters == AcceptedCharacters.Any)
{
return PartialParseResult.Rejected;
}
if (IsAcceptableReplace(target, normalizedChange))
{
return HandleReplacement(target, normalizedChange);
}
int changeRelativePosition = normalizedChange.OldPosition - target.Start.AbsoluteIndex;
// Get the edit context
char? lastChar = null;
if (changeRelativePosition > 0 && target.Content.Length > 0)
{
lastChar = target.Content[changeRelativePosition - 1];
}
// Don't support 0->1 length edits
if (lastChar == null)
{
return PartialParseResult.Rejected;
}
// Only support insertions at the end of the span
if (IsAcceptableInsertion(target, normalizedChange))
{
// Handle the insertion
return HandleInsertion(target, lastChar.Value, normalizedChange);
}
if (IsAcceptableDeletion(target, normalizedChange))
{
return HandleDeletion(target, lastChar.Value, normalizedChange);
}
return PartialParseResult.Rejected;
}
private void Initialize(ISet<string> keywords, bool acceptTrailingDot)
{
Keywords = keywords ?? new HashSet<string>();
AcceptTrailingDot = acceptTrailingDot;
}
private static bool IsAcceptableReplace(Span target, TextChange change)
{
return IsEndReplace(target, change) ||
(change.IsReplace && RemainingIsWhitespace(target, change));
}
private static bool IsAcceptableDeletion(Span target, TextChange change)
{
return IsEndDeletion(target, change) ||
(change.IsDelete && RemainingIsWhitespace(target, change));
}
private static bool IsAcceptableInsertion(Span target, TextChange change)
{
return IsEndInsertion(target, change) ||
(change.IsInsert && RemainingIsWhitespace(target, change));
}
private static bool RemainingIsWhitespace(Span target, TextChange change)
{
int offset = (change.OldPosition - target.Start.AbsoluteIndex) + change.OldLength;
return String.IsNullOrWhiteSpace(target.Content.Substring(offset));
}
private PartialParseResult HandleReplacement(Span target, TextChange change)
{
// Special Case for IntelliSense commits.
// When IntelliSense commits, we get two changes (for example user typed "Date", then committed "DateTime" by pressing ".")
// 1. Insert "." at the end of this span
// 2. Replace the "Date." at the end of the span with "DateTime."
// We need partial parsing to accept case #2.
string oldText = GetOldText(target, change);
PartialParseResult result = PartialParseResult.Rejected;
if (EndsWithDot(oldText) && EndsWithDot(change.NewText))
{
result = PartialParseResult.Accepted;
if (!AcceptTrailingDot)
{
result |= PartialParseResult.Provisional;
}
}
return result;
}
private PartialParseResult HandleDeletion(Span target, char previousChar, TextChange change)
{
// What's left after deleting?
if (previousChar == '.')
{
return TryAcceptChange(target, change, PartialParseResult.Accepted | PartialParseResult.Provisional);
}
else if (ParserHelpers.IsIdentifierPart(previousChar))
{
return TryAcceptChange(target, change);
}
else
{
return PartialParseResult.Rejected;
}
}
private PartialParseResult HandleInsertion(Span target, char previousChar, TextChange change)
{
// What are we inserting after?
if (previousChar == '.')
{
return HandleInsertionAfterDot(target, change);
}
else if (ParserHelpers.IsIdentifierPart(previousChar) || previousChar == ')' || previousChar == ']')
{
return HandleInsertionAfterIdPart(target, change);
}
else
{
return PartialParseResult.Rejected;
}
}
private PartialParseResult HandleInsertionAfterIdPart(Span target, TextChange change)
{
// If the insertion is a full identifier part, accept it
if (ParserHelpers.IsIdentifier(change.NewText, requireIdentifierStart: false))
{
return TryAcceptChange(target, change);
}
else if (EndsWithDot(change.NewText))
{
// Accept it, possibly provisionally
PartialParseResult result = PartialParseResult.Accepted;
if (!AcceptTrailingDot)
{
result |= PartialParseResult.Provisional;
}
return TryAcceptChange(target, change, result);
}
else
{
return PartialParseResult.Rejected;
}
}
private static bool EndsWithDot(string content)
{
return (content.Length == 1 && content[0] == '.') ||
(content[content.Length - 1] == '.' &&
content.Take(content.Length - 1).All(ParserHelpers.IsIdentifierPart));
}
private PartialParseResult HandleInsertionAfterDot(Span target, TextChange change)
{
// If the insertion is a full identifier, accept it
if (ParserHelpers.IsIdentifier(change.NewText))
{
return TryAcceptChange(target, change);
}
return PartialParseResult.Rejected;
}
private PartialParseResult TryAcceptChange(Span target, TextChange change, PartialParseResult acceptResult = PartialParseResult.Accepted)
{
string content = change.ApplyChange(target);
if (StartsWithKeyword(content))
{
return PartialParseResult.Rejected | PartialParseResult.SpanContextChanged;
}
return acceptResult;
}
private bool StartsWithKeyword(string newContent)
{
using (StringReader reader = new StringReader(newContent))
{
return Keywords.Contains(reader.ReadWhile(ParserHelpers.IsIdentifierPart));
}
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Tokenizer.Symbols;
namespace System.Web.Razor.Editor
{
public class SingleLineMarkupEditHandler : SpanEditHandler
{
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public SingleLineMarkupEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer)
: base(tokenizer)
{
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public SingleLineMarkupEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, AcceptedCharacters accepted)
: base(tokenizer, accepted)
{
}
}
}

View File

@ -0,0 +1,183 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Text;
using System.Web.Razor.Tokenizer.Symbols;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Editor
{
// Manages edits to a span
public class SpanEditHandler
{
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public SpanEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer)
: this(tokenizer, AcceptedCharacters.Any)
{
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public SpanEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, AcceptedCharacters accepted)
{
AcceptedCharacters = accepted;
Tokenizer = tokenizer;
}
public AcceptedCharacters AcceptedCharacters { get; set; }
/// <summary>
/// Provides a set of hints to editors which may be manipulating the document in which this span is located.
/// </summary>
public EditorHints EditorHints { get; set; }
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public Func<string, IEnumerable<ISymbol>> Tokenizer { get; set; }
public static SpanEditHandler CreateDefault()
{
return CreateDefault(s => Enumerable.Empty<ISymbol>());
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public static SpanEditHandler CreateDefault(Func<string, IEnumerable<ISymbol>> tokenizer)
{
return new SpanEditHandler(tokenizer);
}
public virtual EditResult ApplyChange(Span target, TextChange change)
{
return ApplyChange(target, change, force: false);
}
public virtual EditResult ApplyChange(Span target, TextChange change, bool force)
{
PartialParseResult result = PartialParseResult.Accepted;
TextChange normalized = change.Normalize();
if (!force)
{
result = CanAcceptChange(target, normalized);
}
// If the change is accepted then apply the change
if (result.HasFlag(PartialParseResult.Accepted))
{
return new EditResult(result, UpdateSpan(target, normalized));
}
return new EditResult(result, new SpanBuilder(target));
}
public virtual bool OwnsChange(Span target, TextChange change)
{
int end = target.Start.AbsoluteIndex + target.Length;
int changeOldEnd = change.OldPosition + change.OldLength;
return change.OldPosition >= target.Start.AbsoluteIndex &&
(changeOldEnd < end || (changeOldEnd == end && AcceptedCharacters != AcceptedCharacters.None));
}
protected virtual PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange)
{
return PartialParseResult.Rejected;
}
protected virtual SpanBuilder UpdateSpan(Span target, TextChange normalizedChange)
{
string newContent = normalizedChange.ApplyChange(target);
SpanBuilder newSpan = new SpanBuilder(target);
newSpan.ClearSymbols();
foreach (ISymbol sym in Tokenizer(newContent))
{
sym.OffsetStart(target.Start);
newSpan.Accept(sym);
}
if (target.Next != null)
{
SourceLocation newEnd = SourceLocationTracker.CalculateNewLocation(target.Start, newContent);
target.Next.ChangeStart(newEnd);
}
return newSpan;
}
protected internal static bool IsAtEndOfFirstLine(Span target, TextChange change)
{
int endOfFirstLine = target.Content.IndexOfAny(new char[] { (char)0x000d, (char)0x000a, (char)0x2028, (char)0x2029 });
return (endOfFirstLine == -1 || (change.OldPosition - target.Start.AbsoluteIndex) <= endOfFirstLine);
}
/// <summary>
/// Returns true if the specified change is an insertion of text at the end of this span.
/// </summary>
protected internal static bool IsEndInsertion(Span target, TextChange change)
{
return change.IsInsert && IsAtEndOfSpan(target, change);
}
/// <summary>
/// Returns true if the specified change is an insertion of text at the end of this span.
/// </summary>
protected internal static bool IsEndDeletion(Span target, TextChange change)
{
return change.IsDelete && IsAtEndOfSpan(target, change);
}
/// <summary>
/// Returns true if the specified change is a replacement of text at the end of this span.
/// </summary>
protected internal static bool IsEndReplace(Span target, TextChange change)
{
return change.IsReplace && IsAtEndOfSpan(target, change);
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method should only be used on Spans")]
protected internal static bool IsAtEndOfSpan(Span target, TextChange change)
{
return (change.OldPosition + change.OldLength) == (target.Start.AbsoluteIndex + target.Length);
}
/// <summary>
/// Returns the old text referenced by the change.
/// </summary>
/// <remarks>
/// If the content has already been updated by applying the change, this data will be _invalid_
/// </remarks>
protected internal static string GetOldText(Span target, TextChange change)
{
return target.Content.Substring(change.OldPosition - target.Start.AbsoluteIndex, change.OldLength);
}
// Is the specified span to the right of this span and immediately adjacent?
internal static bool IsAdjacentOnRight(Span target, Span other)
{
return target.Start.AbsoluteIndex < other.Start.AbsoluteIndex && target.Start.AbsoluteIndex + target.Length == other.Start.AbsoluteIndex;
}
// Is the specified span to the left of this span and immediately adjacent?
internal static bool IsAdjacentOnLeft(Span target, Span other)
{
return other.Start.AbsoluteIndex < target.Start.AbsoluteIndex && other.Start.AbsoluteIndex + other.Length == target.Start.AbsoluteIndex;
}
public override string ToString()
{
return GetType().Name + ";Accepts:" + AcceptedCharacters + ((EditorHints == EditorHints.None) ? String.Empty : (";Hints: " + EditorHints.ToString()));
}
public override bool Equals(object obj)
{
SpanEditHandler other = obj as SpanEditHandler;
return other != null &&
AcceptedCharacters == other.AcceptedCharacters &&
EditorHints == other.EditorHints;
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(AcceptedCharacters)
.Add(EditorHints)
.CombinedHash;
}
}
}

View File

@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.CodeDom;
using System.Linq;
using System.Web.Razor.Parser.SyntaxTree;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Generator
{
public class AddImportCodeGenerator : SpanCodeGenerator
{
public AddImportCodeGenerator(string ns, int namespaceKeywordLength)
{
Namespace = ns;
NamespaceKeywordLength = namespaceKeywordLength;
}
public string Namespace { get; private set; }
public int NamespaceKeywordLength { get; set; }
public override void GenerateCode(Span target, CodeGeneratorContext context)
{
// Try to find the namespace in the existing imports
string ns = Namespace;
if (!String.IsNullOrEmpty(ns) && Char.IsWhiteSpace(ns[0]))
{
ns = ns.Substring(1);
}
CodeNamespaceImport import = context.Namespace
.Imports
.OfType<CodeNamespaceImport>()
.Where(i => String.Equals(i.Namespace, ns.Trim(), StringComparison.Ordinal))
.FirstOrDefault();
if (import == null)
{
// It doesn't exist, create it
import = new CodeNamespaceImport(ns);
context.Namespace.Imports.Add(import);
}
// Attach our info to the existing/new import.
import.LinePragma = context.GenerateLinePragma(target);
}
public override string ToString()
{
return "Import:" + Namespace + ";KwdLen:" + NamespaceKeywordLength;
}
public override bool Equals(object obj)
{
AddImportCodeGenerator other = obj as AddImportCodeGenerator;
return other != null &&
String.Equals(Namespace, other.Namespace, StringComparison.Ordinal) &&
NamespaceKeywordLength == other.NamespaceKeywordLength;
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(Namespace)
.Add(NamespaceKeywordLength)
.CombinedHash;
}
}
}

View File

@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Globalization;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Text;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Generator
{
public class AttributeBlockCodeGenerator : BlockCodeGenerator
{
public AttributeBlockCodeGenerator(string name, LocationTagged<string> prefix, LocationTagged<string> suffix)
{
Name = name;
Prefix = prefix;
Suffix = suffix;
}
public string Name { get; private set; }
public LocationTagged<string> Prefix { get; private set; }
public LocationTagged<string> Suffix { get; private set; }
public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context)
{
if (context.Host.DesignTimeMode)
{
return; // Don't generate anything!
}
context.FlushBufferedStatement();
context.AddStatement(context.BuildCodeString(cw =>
{
if (!String.IsNullOrEmpty(context.TargetWriterName))
{
cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteAttributeToMethodName);
cw.WriteSnippet(context.TargetWriterName);
cw.WriteParameterSeparator();
}
else
{
cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteAttributeMethodName);
}
cw.WriteStringLiteral(Name);
cw.WriteParameterSeparator();
cw.WriteLocationTaggedString(Prefix);
cw.WriteParameterSeparator();
cw.WriteLocationTaggedString(Suffix);
// In VB, we need a line continuation
cw.WriteLineContinuation();
}));
}
public override void GenerateEndBlockCode(Block target, CodeGeneratorContext context)
{
if (context.Host.DesignTimeMode)
{
return; // Don't generate anything!
}
context.FlushBufferedStatement();
context.AddStatement(context.BuildCodeString(cw =>
{
cw.WriteEndMethodInvoke();
cw.WriteEndStatement();
}));
}
public override string ToString()
{
return String.Format(CultureInfo.CurrentCulture, "Attr:{0},{1:F},{2:F}", Name, Prefix, Suffix);
}
public override bool Equals(object obj)
{
AttributeBlockCodeGenerator other = obj as AttributeBlockCodeGenerator;
return other != null &&
String.Equals(other.Name, Name, StringComparison.Ordinal) &&
Equals(other.Prefix, Prefix) &&
Equals(other.Suffix, Suffix);
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(Name)
.Add(Prefix)
.Add(Suffix)
.CombinedHash;
}
}
}

View File

@ -0,0 +1,76 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Razor.Generator
{
internal abstract class BaseCodeWriter : CodeWriter
{
public override void WriteSnippet(string snippet)
{
InnerWriter.Write(snippet);
}
protected internal override void EmitStartMethodInvoke(string methodName)
{
EmitStartMethodInvoke(methodName, new string[0]);
}
protected internal override void EmitStartMethodInvoke(string methodName, params string[] genericArguments)
{
InnerWriter.Write(methodName);
if (genericArguments != null && genericArguments.Length > 0)
{
WriteStartGenerics();
for (int i = 0; i < genericArguments.Length; i++)
{
if (i > 0)
{
WriteParameterSeparator();
}
WriteSnippet(genericArguments[i]);
}
WriteEndGenerics();
}
InnerWriter.Write("(");
}
protected internal override void EmitEndMethodInvoke()
{
InnerWriter.Write(")");
}
protected internal override void EmitEndConstructor()
{
InnerWriter.Write(")");
}
protected internal override void EmitEndLambdaExpression()
{
}
public override void WriteParameterSeparator()
{
InnerWriter.Write(", ");
}
protected internal void WriteCommaSeparatedList<T>(T[] items, Action<T> writeItemAction)
{
for (int i = 0; i < items.Length; i++)
{
if (i > 0)
{
InnerWriter.Write(", ");
}
writeItemAction(items[i]);
}
}
protected internal virtual void WriteStartGenerics()
{
}
protected internal virtual void WriteEndGenerics()
{
}
}
}

View File

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Web.Razor.Parser.SyntaxTree;
namespace System.Web.Razor.Generator
{
public abstract class BlockCodeGenerator : CodeGeneratorBase, IBlockCodeGenerator
{
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")]
public static readonly IBlockCodeGenerator Null = new NullBlockCodeGenerator();
public virtual void GenerateStartBlockCode(Block target, CodeGeneratorContext context)
{
}
public virtual void GenerateEndBlockCode(Block target, CodeGeneratorContext context)
{
}
public override bool Equals(object obj)
{
return (obj as IBlockCodeGenerator) != null;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
private class NullBlockCodeGenerator : IBlockCodeGenerator
{
public void GenerateStartBlockCode(Block target, CodeGeneratorContext context)
{
}
public void GenerateEndBlockCode(Block target, CodeGeneratorContext context)
{
}
public override string ToString()
{
return "None";
}
}
}
}

View File

@ -0,0 +1,249 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace System.Web.Razor.Generator
{
internal class CSharpCodeWriter : BaseCodeWriter
{
protected internal override void WriteStartGenerics()
{
InnerWriter.Write("<");
}
protected internal override void WriteEndGenerics()
{
InnerWriter.Write(">");
}
public override int WriteVariableDeclaration(string type, string name, string value)
{
InnerWriter.Write(type);
InnerWriter.Write(" ");
InnerWriter.Write(name);
if (!String.IsNullOrEmpty(value))
{
InnerWriter.Write(" = ");
InnerWriter.Write(value);
}
else
{
InnerWriter.Write(" = null");
}
return 0;
}
public override void WriteDisableUnusedFieldWarningPragma()
{
InnerWriter.Write("#pragma warning disable 219");
}
public override void WriteRestoreUnusedFieldWarningPragma()
{
InnerWriter.Write("#pragma warning restore 219");
}
public override void WriteStringLiteral(string literal)
{
if (literal == null)
{
throw new ArgumentNullException("literal");
}
// From CSharpCodeProvider in CodeDOM
// If the string is short, use C style quoting (e.g "\r\n")
// Also do it if it is too long to fit in one line
// If the string contains '\0', verbatim style won't work.
if (literal.Length >= 256 && literal.Length <= 1500 && literal.IndexOf('\0') == -1)
{
WriteVerbatimStringLiteral(literal);
}
else
{
WriteCStyleStringLiteral(literal);
}
}
private void WriteVerbatimStringLiteral(string literal)
{
// From CSharpCodeGenerator.QuoteSnippetStringVerbatim in CodeDOM
InnerWriter.Write("@\"");
for (int i = 0; i < literal.Length; i++)
{
if (literal[i] == '\"')
{
InnerWriter.Write("\"\"");
}
else
{
InnerWriter.Write(literal[i]);
}
}
InnerWriter.Write("\"");
}
private void WriteCStyleStringLiteral(string literal)
{
// From CSharpCodeGenerator.QuoteSnippetStringCStyle in CodeDOM
InnerWriter.Write("\"");
for (int i = 0; i < literal.Length; i++)
{
switch (literal[i])
{
case '\r':
InnerWriter.Write("\\r");
break;
case '\t':
InnerWriter.Write("\\t");
break;
case '\"':
InnerWriter.Write("\\\"");
break;
case '\'':
InnerWriter.Write("\\\'");
break;
case '\\':
InnerWriter.Write("\\\\");
break;
case '\0':
InnerWriter.Write("\\\0");
break;
case '\n':
InnerWriter.Write("\\n");
break;
case '\u2028':
case '\u2029':
// Inlined CSharpCodeGenerator.AppendEscapedChar
InnerWriter.Write("\\u");
InnerWriter.Write(((int)literal[i]).ToString("X4", CultureInfo.InvariantCulture));
break;
default:
InnerWriter.Write(literal[i]);
break;
}
if (i > 0 && i % 80 == 0)
{
// If current character is a high surrogate and the following
// character is a low surrogate, don't break them.
// Otherwise when we write the string to a file, we might lose
// the characters.
if (Char.IsHighSurrogate(literal[i])
&& (i < literal.Length - 1)
&& Char.IsLowSurrogate(literal[i + 1]))
{
InnerWriter.Write(literal[++i]);
}
InnerWriter.Write("\" +");
InnerWriter.Write(Environment.NewLine);
InnerWriter.Write('\"');
}
}
InnerWriter.Write("\"");
}
public override void WriteEndStatement()
{
InnerWriter.WriteLine(";");
}
public override void WriteIdentifier(string identifier)
{
InnerWriter.Write("@" + identifier);
}
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Lowercase is intended here. C# boolean literals are all lowercase")]
public override void WriteBooleanLiteral(bool value)
{
WriteSnippet(value.ToString().ToLowerInvariant());
}
protected internal override void EmitStartLambdaExpression(string[] parameterNames)
{
if (parameterNames == null)
{
throw new ArgumentNullException("parameterNames");
}
if (parameterNames.Length == 0 || parameterNames.Length > 1)
{
InnerWriter.Write("(");
}
WriteCommaSeparatedList(parameterNames, InnerWriter.Write);
if (parameterNames.Length == 0 || parameterNames.Length > 1)
{
InnerWriter.Write(")");
}
InnerWriter.Write(" => ");
}
protected internal override void EmitStartLambdaDelegate(string[] parameterNames)
{
if (parameterNames == null)
{
throw new ArgumentNullException("parameterNames");
}
EmitStartLambdaExpression(parameterNames);
InnerWriter.WriteLine("{");
}
protected internal override void EmitEndLambdaDelegate()
{
InnerWriter.Write("}");
}
protected internal override void EmitStartConstructor(string typeName)
{
if (typeName == null)
{
throw new ArgumentNullException("typeName");
}
InnerWriter.Write("new ");
InnerWriter.Write(typeName);
InnerWriter.Write("(");
}
public override void WriteReturn()
{
InnerWriter.Write("return ");
}
public override void WriteLinePragma(int? lineNumber, string fileName)
{
InnerWriter.WriteLine();
if (lineNumber != null)
{
InnerWriter.Write("#line ");
InnerWriter.Write(lineNumber);
InnerWriter.Write(" \"");
InnerWriter.Write(fileName);
InnerWriter.Write("\"");
InnerWriter.WriteLine();
}
else
{
InnerWriter.WriteLine("#line default");
InnerWriter.WriteLine("#line hidden");
}
}
public override void WriteHiddenLinePragma()
{
InnerWriter.WriteLine("#line hidden");
}
public override void WriteHelperHeaderPrefix(string templateTypeName, bool isStatic)
{
InnerWriter.Write("public ");
if (isStatic)
{
InnerWriter.Write("static ");
}
InnerWriter.Write(templateTypeName);
InnerWriter.Write(" ");
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.CodeDom;
using System.Diagnostics.CodeAnalysis;
namespace System.Web.Razor.Generator
{
public class CSharpRazorCodeGenerator : RazorCodeGenerator
{
private const string HiddenLinePragma = "#line hidden";
public CSharpRazorCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host)
: base(className, rootNamespaceName, sourceFileName, host)
{
}
internal override Func<CodeWriter> CodeWriterFactory
{
get { return () => new CSharpCodeWriter(); }
}
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.CodeDom.CodeSnippetTypeMember.#ctor(System.String)", Justification = "Value is never to be localized")]
protected override void Initialize(CodeGeneratorContext context)
{
base.Initialize(context);
context.GeneratedClass.Members.Insert(0, new CodeSnippetTypeMember(HiddenLinePragma));
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.CodeDom;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Generator
{
public class CodeGenerationCompleteEventArgs : EventArgs
{
public CodeGenerationCompleteEventArgs(string virtualPath, string physicalPath, CodeCompileUnit generatedCode)
{
if (String.IsNullOrEmpty(virtualPath))
{
throw ExceptionHelper.CreateArgumentNullOrEmptyException("virtualPath");
}
if (generatedCode == null)
{
throw new ArgumentNullException("generatedCode");
}
VirtualPath = virtualPath;
PhysicalPath = physicalPath;
GeneratedCode = generatedCode;
}
public CodeCompileUnit GeneratedCode { get; private set; }
public string VirtualPath { get; private set; }
public string PhysicalPath { get; private set; }
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Web.Razor.Parser.SyntaxTree;
namespace System.Web.Razor.Generator
{
public abstract class CodeGeneratorBase
{
// Helpers
protected internal static int CalculatePadding(Span target)
{
return CalculatePadding(target, generatedStart: 0);
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method should only be used on Spans")]
protected internal static int CalculatePadding(Span target, int generatedStart)
{
int padding = target.Start.CharacterIndex - generatedStart;
if (padding < 0)
{
padding = 0;
}
return padding;
}
protected internal static string Pad(string code, Span target)
{
return Pad(code, target, 0);
}
protected internal static string Pad(string code, Span target, int generatedStart)
{
return code.PadLeft(CalculatePadding(target, generatedStart) + code.Length, ' ');
}
}
}

View File

@ -0,0 +1,329 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.CodeDom;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Resources;
using System.Web.Razor.Text;
using System.Web.Razor.Utils;
namespace System.Web.Razor.Generator
{
public class CodeGeneratorContext
{
private const string DesignTimeHelperMethodName = "__RazorDesignTimeHelpers__";
private int _nextDesignTimePragmaId = 1;
private bool _expressionHelperVariableWriten;
private CodeMemberMethod _designTimeHelperMethod;
private StatementBuffer _currentBuffer = new StatementBuffer();
private CodeGeneratorContext()
{
ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput;
}
// Internal/Private state. Technically consumers might want to use some of these but they can implement them independently if necessary.
// It's way safer to make them internal for now, especially with the code generator stuff in a bit of flux.
internal ExpressionRenderingMode ExpressionRenderingMode { get; set; }
private Action<string, CodeLinePragma> StatementCollector { get; set; }
private Func<CodeWriter> CodeWriterFactory { get; set; }
public string SourceFile { get; internal set; }
public CodeCompileUnit CompileUnit { get; internal set; }
public CodeNamespace Namespace { get; internal set; }
public CodeTypeDeclaration GeneratedClass { get; internal set; }
public RazorEngineHost Host { get; private set; }
public IDictionary<int, GeneratedCodeMapping> CodeMappings { get; private set; }
public string TargetWriterName { get; set; }
public CodeMemberMethod TargetMethod { get; set; }
public string CurrentBufferedStatement
{
get { return _currentBuffer == null ? String.Empty : _currentBuffer.Builder.ToString(); }
}
public static CodeGeneratorContext Create(RazorEngineHost host, string className, string rootNamespace, string sourceFile, bool shouldGenerateLinePragmas)
{
return Create(host, null, className, rootNamespace, sourceFile, shouldGenerateLinePragmas);
}
internal static CodeGeneratorContext Create(RazorEngineHost host, Func<CodeWriter> writerFactory, string className, string rootNamespace, string sourceFile, bool shouldGenerateLinePragmas)
{
CodeGeneratorContext context = new CodeGeneratorContext()
{
Host = host,
CodeWriterFactory = writerFactory,
SourceFile = shouldGenerateLinePragmas ? sourceFile : null,
CompileUnit = new CodeCompileUnit(),
Namespace = new CodeNamespace(rootNamespace),
GeneratedClass = new CodeTypeDeclaration(className)
{
IsClass = true
},
TargetMethod = new CodeMemberMethod()
{
Name = host.GeneratedClassContext.ExecuteMethodName,
Attributes = MemberAttributes.Override | MemberAttributes.Public
},
CodeMappings = new Dictionary<int, GeneratedCodeMapping>()
};
context.CompileUnit.Namespaces.Add(context.Namespace);
context.Namespace.Types.Add(context.GeneratedClass);
context.GeneratedClass.Members.Add(context.TargetMethod);
context.Namespace.Imports.AddRange(host.NamespaceImports
.Select(s => new CodeNamespaceImport(s))
.ToArray());
return context;
}
public void AddDesignTimeHelperStatement(CodeSnippetStatement statement)
{
if (_designTimeHelperMethod == null)
{
_designTimeHelperMethod = new CodeMemberMethod()
{
Name = DesignTimeHelperMethodName,
Attributes = MemberAttributes.Private
};
_designTimeHelperMethod.Statements.Add(
new CodeSnippetStatement(BuildCodeString(cw => cw.WriteDisableUnusedFieldWarningPragma())));
_designTimeHelperMethod.Statements.Add(
new CodeSnippetStatement(BuildCodeString(cw => cw.WriteRestoreUnusedFieldWarningPragma())));
GeneratedClass.Members.Insert(0, _designTimeHelperMethod);
}
_designTimeHelperMethod.Statements.Insert(_designTimeHelperMethod.Statements.Count - 1, statement);
}
[SuppressMessage("Microsoft.Usage", "CA2233:OperationsShouldNotOverflow", MessageId = "generatedCodeStart+1", Justification = "There is no risk of overflow in this case")]
public int AddCodeMapping(SourceLocation sourceLocation, int generatedCodeStart, int generatedCodeLength)
{
if (generatedCodeStart == Int32.MaxValue)
{
throw new ArgumentOutOfRangeException("generatedCodeStart");
}
GeneratedCodeMapping mapping = new GeneratedCodeMapping(
startOffset: sourceLocation.AbsoluteIndex,
startLine: sourceLocation.LineIndex + 1,
startColumn: sourceLocation.CharacterIndex + 1,
startGeneratedColumn: generatedCodeStart + 1,
codeLength: generatedCodeLength);
int id = _nextDesignTimePragmaId++;
CodeMappings[id] = mapping;
return id;
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method requires that a Span be provided")]
public CodeLinePragma GenerateLinePragma(Span target)
{
return GenerateLinePragma(target, 0);
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method requires that a Span be provided")]
public CodeLinePragma GenerateLinePragma(Span target, int generatedCodeStart)
{
return GenerateLinePragma(target, generatedCodeStart, target.Content.Length);
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method requires that a Span be provided")]
public CodeLinePragma GenerateLinePragma(Span target, int generatedCodeStart, int codeLength)
{
return GenerateLinePragma(target.Start, generatedCodeStart, codeLength);
}
public CodeLinePragma GenerateLinePragma(SourceLocation start, int generatedCodeStart, int codeLength)
{
if (!String.IsNullOrEmpty(SourceFile))
{
if (Host.DesignTimeMode)
{
int mappingId = AddCodeMapping(start, generatedCodeStart, codeLength);
return new CodeLinePragma(SourceFile, mappingId);
}
return new CodeLinePragma(SourceFile, start.LineIndex + 1);
}
return null;
}
public void BufferStatementFragment(Span sourceSpan)
{
BufferStatementFragment(sourceSpan.Content, sourceSpan);
}
public void BufferStatementFragment(string fragment)
{
BufferStatementFragment(fragment, null);
}
public void BufferStatementFragment(string fragment, Span sourceSpan)
{
if (sourceSpan != null && _currentBuffer.LinePragmaSpan == null)
{
_currentBuffer.LinePragmaSpan = sourceSpan;
// Pad the output as necessary
int start = _currentBuffer.Builder.Length;
if (_currentBuffer.GeneratedCodeStart != null)
{
start = _currentBuffer.GeneratedCodeStart.Value;
}
string padded = CodeGeneratorBase.Pad(_currentBuffer.Builder.ToString(), sourceSpan, start);
_currentBuffer.GeneratedCodeStart = start + (padded.Length - _currentBuffer.Builder.Length);
_currentBuffer.Builder.Clear();
_currentBuffer.Builder.Append(padded);
}
_currentBuffer.Builder.Append(fragment);
}
public void MarkStartOfGeneratedCode()
{
_currentBuffer.MarkStart();
}
public void MarkEndOfGeneratedCode()
{
_currentBuffer.MarkEnd();
}
public void FlushBufferedStatement()
{
if (_currentBuffer.Builder.Length > 0)
{
CodeLinePragma pragma = null;
if (_currentBuffer.LinePragmaSpan != null)
{
int start = _currentBuffer.Builder.Length;
if (_currentBuffer.GeneratedCodeStart != null)
{
start = _currentBuffer.GeneratedCodeStart.Value;
}
int len = _currentBuffer.Builder.Length - start;
if (_currentBuffer.CodeLength != null)
{
len = _currentBuffer.CodeLength.Value;
}
pragma = GenerateLinePragma(_currentBuffer.LinePragmaSpan, start, len);
}
AddStatement(_currentBuffer.Builder.ToString(), pragma);
_currentBuffer.Reset();
}
}
public void AddStatement(string generatedCode)
{
AddStatement(generatedCode, null);
}
public void AddStatement(string body, CodeLinePragma pragma)
{
if (StatementCollector == null)
{
TargetMethod.Statements.Add(new CodeSnippetStatement(body) { LinePragma = pragma });
}
else
{
StatementCollector(body, pragma);
}
}
public void EnsureExpressionHelperVariable()
{
if (!_expressionHelperVariableWriten)
{
GeneratedClass.Members.Insert(0,
new CodeMemberField(typeof(object), "__o")
{
Attributes = MemberAttributes.Private | MemberAttributes.Static
});
_expressionHelperVariableWriten = true;
}
}
public IDisposable ChangeStatementCollector(Action<string, CodeLinePragma> collector)
{
Action<string, CodeLinePragma> oldCollector = StatementCollector;
StatementCollector = collector;
return new DisposableAction(() =>
{
StatementCollector = oldCollector;
});
}
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We explicitly want the lower-case string here")]
public void AddContextCall(Span contentSpan, string methodName, bool isLiteral)
{
AddStatement(BuildCodeString(cw =>
{
cw.WriteStartMethodInvoke(methodName);
if (!String.IsNullOrEmpty(TargetWriterName))
{
cw.WriteSnippet(TargetWriterName);
cw.WriteParameterSeparator();
}
cw.WriteStringLiteral(Host.InstrumentedSourceFilePath);
cw.WriteParameterSeparator();
cw.WriteSnippet(contentSpan.Start.AbsoluteIndex.ToString(CultureInfo.InvariantCulture));
cw.WriteParameterSeparator();
cw.WriteSnippet(contentSpan.Content.Length.ToString(CultureInfo.InvariantCulture));
cw.WriteParameterSeparator();
cw.WriteSnippet(isLiteral.ToString().ToLowerInvariant());
cw.WriteEndMethodInvoke();
cw.WriteEndStatement();
}));
}
internal CodeWriter CreateCodeWriter()
{
Debug.Assert(CodeWriterFactory != null);
if (CodeWriterFactory == null)
{
throw new InvalidOperationException(RazorResources.CreateCodeWriter_NoCodeWriter);
}
return CodeWriterFactory();
}
internal string BuildCodeString(Action<CodeWriter> action)
{
using (CodeWriter cw = CodeWriterFactory())
{
action(cw);
return cw.Content;
}
}
private class StatementBuffer
{
public StringBuilder Builder = new StringBuilder();
public int? GeneratedCodeStart;
public int? CodeLength;
public Span LinePragmaSpan;
public void Reset()
{
Builder.Clear();
GeneratedCodeStart = null;
CodeLength = null;
LinePragmaSpan = null;
}
public void MarkStart()
{
GeneratedCodeStart = Builder.Length;
}
public void MarkEnd()
{
CodeLength = Builder.Length - GeneratedCodeStart;
}
}
}
}

View File

@ -0,0 +1,209 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.CodeDom;
using System.Globalization;
using System.IO;
namespace System.Web.Razor.Generator
{
// Utility class which helps write code snippets
internal abstract class CodeWriter : IDisposable
{
private StringWriter _writer;
protected CodeWriter()
{
}
private enum WriterMode
{
Constructor,
MethodCall,
LambdaDelegate,
LambdaExpression
}
public string Content
{
get { return InnerWriter.ToString(); }
}
public StringWriter InnerWriter
{
get
{
if (_writer == null)
{
_writer = new StringWriter(CultureInfo.InvariantCulture);
}
return _writer;
}
}
public virtual bool SupportsMidStatementLinePragmas
{
get { return true; }
}
public abstract void WriteParameterSeparator();
public abstract void WriteReturn();
public abstract void WriteLinePragma(int? lineNumber, string fileName);
public abstract void WriteHelperHeaderPrefix(string templateTypeName, bool isStatic);
public abstract void WriteSnippet(string snippet);
public abstract void WriteStringLiteral(string literal);
public abstract int WriteVariableDeclaration(string type, string name, string value);
public virtual void WriteLinePragma()
{
WriteLinePragma(null);
}
public virtual void WriteLinePragma(CodeLinePragma pragma)
{
if (pragma == null)
{
WriteLinePragma(null, null);
}
else
{
WriteLinePragma(pragma.LineNumber, pragma.FileName);
}
}
public virtual void WriteHiddenLinePragma()
{
}
public virtual void WriteDisableUnusedFieldWarningPragma()
{
}
public virtual void WriteRestoreUnusedFieldWarningPragma()
{
}
public virtual void WriteIdentifier(string identifier)
{
InnerWriter.Write(identifier);
}
public virtual void WriteHelperHeaderSuffix(string templateTypeName)
{
}
public virtual void WriteHelperTrailer()
{
}
public void WriteStartMethodInvoke(string methodName)
{
EmitStartMethodInvoke(methodName);
}
public void WriteStartMethodInvoke(string methodName, params string[] genericArguments)
{
EmitStartMethodInvoke(methodName, genericArguments);
}
public void WriteEndMethodInvoke()
{
EmitEndMethodInvoke();
}
public virtual void WriteEndStatement()
{
}
public virtual void WriteStartAssignment(string variableName)
{
InnerWriter.Write(variableName);
InnerWriter.Write(" = ");
}
public void WriteStartLambdaExpression(params string[] parameterNames)
{
EmitStartLambdaExpression(parameterNames);
}
public void WriteStartConstructor(string typeName)
{
EmitStartConstructor(typeName);
}
public void WriteStartLambdaDelegate(params string[] parameterNames)
{
EmitStartLambdaDelegate(parameterNames);
}
public void WriteEndLambdaExpression()
{
EmitEndLambdaExpression();
}
public void WriteEndConstructor()
{
EmitEndConstructor();
}
public void WriteEndLambdaDelegate()
{
EmitEndLambdaDelegate();
}
public virtual void WriteLineContinuation()
{
}
public virtual void WriteBooleanLiteral(bool value)
{
WriteSnippet(value.ToString(CultureInfo.InvariantCulture));
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Clear()
{
if (InnerWriter != null)
{
InnerWriter.GetStringBuilder().Clear();
}
}
public CodeSnippetStatement ToStatement()
{
return new CodeSnippetStatement(Content);
}
public CodeSnippetTypeMember ToTypeMember()
{
return new CodeSnippetTypeMember(Content);
}
protected internal abstract void EmitStartLambdaDelegate(string[] parameterNames);
protected internal abstract void EmitStartLambdaExpression(string[] parameterNames);
protected internal abstract void EmitStartConstructor(string typeName);
protected internal abstract void EmitStartMethodInvoke(string methodName);
protected internal virtual void EmitStartMethodInvoke(string methodName, params string[] genericArguments)
{
EmitStartMethodInvoke(methodName);
}
protected internal abstract void EmitEndLambdaDelegate();
protected internal abstract void EmitEndLambdaExpression();
protected internal abstract void EmitEndConstructor();
protected internal abstract void EmitEndMethodInvoke();
protected virtual void Dispose(bool disposing)
{
if (disposing && _writer != null)
{
_writer.Dispose();
}
}
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Globalization;
using System.Web.Razor.Text;
namespace System.Web.Razor.Generator
{
internal static class CodeWriterExtensions
{
public static void WriteLocationTaggedString(this CodeWriter writer, LocationTagged<string> value)
{
writer.WriteStartMethodInvoke("Tuple.Create");
writer.WriteStringLiteral(value.Value);
writer.WriteParameterSeparator();
writer.WriteSnippet(value.Location.AbsoluteIndex.ToString(CultureInfo.CurrentCulture));
writer.WriteEndMethodInvoke();
}
}
}

Some files were not shown because too many files have changed in this diff Show More