diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperAttribute.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperAttribute.cs index 16abc15b2c..c72547d82b 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperAttribute.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperAttribute.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { diff --git a/src/Microsoft.AspNet.Razor/Editor/AutoCompleteEditHandler.cs b/src/Microsoft.AspNet.Razor/Editor/AutoCompleteEditHandler.cs index 9d96d7aed2..f6e5067ca0 100644 --- a/src/Microsoft.AspNet.Razor/Editor/AutoCompleteEditHandler.cs +++ b/src/Microsoft.AspNet.Razor/Editor/AutoCompleteEditHandler.cs @@ -13,19 +13,28 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree { public class AutoCompleteEditHandler : SpanEditHandler { + private static readonly int TypeHashCode = typeof(AutoCompleteEditHandler).GetHashCode(); + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public AutoCompleteEditHandler(Func> tokenizer) : base(tokenizer) { } + public AutoCompleteEditHandler(Func> tokenizer, bool autoCompleteAtEndOfSpan) + : this(tokenizer) + { + AutoCompleteAtEndOfSpan = autoCompleteAtEndOfSpan; + } + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public AutoCompleteEditHandler(Func> tokenizer, AcceptedCharacters accepted) : base(tokenizer, accepted) { } - public bool AutoCompleteAtEndOfSpan { get; set; } + public bool AutoCompleteAtEndOfSpan { get; } + public string AutoCompleteString { get; set; } protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) @@ -48,17 +57,17 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree public override bool Equals(object obj) { var other = obj as AutoCompleteEditHandler; - return base.Equals(obj) && - other != null && - string.Equals(other.AutoCompleteString, AutoCompleteString, StringComparison.Ordinal) && - AutoCompleteAtEndOfSpan == other.AutoCompleteAtEndOfSpan; + return base.Equals(other) && + string.Equals(other.AutoCompleteString, AutoCompleteString, StringComparison.Ordinal) && + AutoCompleteAtEndOfSpan == other.AutoCompleteAtEndOfSpan; } public override int GetHashCode() { + // Hash code should include only immutable properties but Equals also checks the type. return HashCodeCombiner.Start() - .Add(base.GetHashCode()) - .Add(AutoCompleteString) + .Add(TypeHashCode) + .Add(AutoCompleteAtEndOfSpan) .CombinedHash; } } diff --git a/src/Microsoft.AspNet.Razor/Editor/ImplicitExpressionEditHandler.cs b/src/Microsoft.AspNet.Razor/Editor/ImplicitExpressionEditHandler.cs index 51c010e8c9..fc1e040cf9 100644 --- a/src/Microsoft.AspNet.Razor/Editor/ImplicitExpressionEditHandler.cs +++ b/src/Microsoft.AspNet.Razor/Editor/ImplicitExpressionEditHandler.cs @@ -18,15 +18,31 @@ namespace Microsoft.AspNet.Razor.Editor { public class ImplicitExpressionEditHandler : SpanEditHandler { + private readonly ISet _keywords; + private readonly IReadOnlyCollection _readOnlyKeywords; + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public ImplicitExpressionEditHandler(Func> tokenizer, ISet keywords, bool acceptTrailingDot) : base(tokenizer) { - Initialize(keywords, acceptTrailingDot); + _keywords = keywords ?? new HashSet(); + + // HashSet implements IReadOnlyCollection as of 4.6, but does not for 4.5.1. If the runtime cast + // succeeds, avoid creating a new collection. + _readOnlyKeywords = (_keywords as IReadOnlyCollection) ?? _keywords.ToArray(); + + AcceptTrailingDot = acceptTrailingDot; } - public bool AcceptTrailingDot { get; private set; } - public ISet Keywords { get; private set; } + public bool AcceptTrailingDot { get; } + + public IReadOnlyCollection Keywords + { + get + { + return _readOnlyKeywords; + } + } public override string ToString() { @@ -36,18 +52,17 @@ namespace Microsoft.AspNet.Razor.Editor public override bool Equals(object obj) { var other = obj as ImplicitExpressionEditHandler; - return other != null && - base.Equals(other) && - Keywords.SetEquals(other.Keywords) && - AcceptTrailingDot == other.AcceptTrailingDot; + return base.Equals(other) && + _keywords.SetEquals(other._keywords) && + AcceptTrailingDot == other.AcceptTrailingDot; } public override int GetHashCode() { + // Hash code should include only immutable properties and base has none. return HashCodeCombiner.Start() - .Add(base.GetHashCode()) - .Add(AcceptTrailingDot) .Add(Keywords) + .Add(AcceptTrailingDot) .CombinedHash; } @@ -104,12 +119,6 @@ namespace Microsoft.AspNet.Razor.Editor return PartialParseResult.Rejected; } - private void Initialize(ISet keywords, bool acceptTrailingDot) - { - Keywords = keywords ?? new HashSet(); - AcceptTrailingDot = acceptTrailingDot; - } - // A dotless commit is the process of inserting a '.' with an intellisense selection. private static bool IsDotlessCommitInsertion(Span target, TextChange change) { @@ -310,7 +319,7 @@ namespace Microsoft.AspNet.Razor.Editor { using (var reader = new StringReader(newContent)) { - return Keywords.Contains(reader.ReadWhile(ParserHelpers.IsIdentifierPart)); + return _keywords.Contains(reader.ReadWhile(ParserHelpers.IsIdentifierPart)); } } } diff --git a/src/Microsoft.AspNet.Razor/Editor/SpanEditHandler.cs b/src/Microsoft.AspNet.Razor/Editor/SpanEditHandler.cs index 94cb59dfe0..fff37a0297 100644 --- a/src/Microsoft.AspNet.Razor/Editor/SpanEditHandler.cs +++ b/src/Microsoft.AspNet.Razor/Editor/SpanEditHandler.cs @@ -8,13 +8,14 @@ using System.Linq; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Tokenizer.Symbols; -using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Editor { // Manages edits to a span public class SpanEditHandler { + private static readonly int TypeHashCode = typeof(SpanEditHandler).GetHashCode(); + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public SpanEditHandler(Func> tokenizer) : this(tokenizer, AcceptedCharacters.Any) @@ -170,16 +171,15 @@ namespace Microsoft.AspNet.Razor.Editor { var other = obj as SpanEditHandler; return other != null && - AcceptedCharacters == other.AcceptedCharacters && - EditorHints == other.EditorHints; + GetType() == other.GetType() && + AcceptedCharacters == other.AcceptedCharacters && + EditorHints == other.EditorHints; } public override int GetHashCode() { - return HashCodeCombiner.Start() - .Add(AcceptedCharacters) - .Add(EditorHints) - .CombinedHash; + // Hash code should include only immutable properties but Equals also checks the type. + return TypeHashCode; } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/AddImportCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/AddImportCodeGenerator.cs index c43fd89a11..b930091b3e 100644 --- a/src/Microsoft.AspNet.Razor/Generator/AddImportCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/AddImportCodeGenerator.cs @@ -3,7 +3,6 @@ using System; using Microsoft.AspNet.Razor.Parser.SyntaxTree; -using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Generator { @@ -15,7 +14,8 @@ namespace Microsoft.AspNet.Razor.Generator NamespaceKeywordLength = namespaceKeywordLength; } - public string Namespace { get; private set; } + public string Namespace { get; } + public int NamespaceKeywordLength { get; set; } public override void GenerateCode(Span target, CodeGeneratorContext context) @@ -39,16 +39,14 @@ namespace Microsoft.AspNet.Razor.Generator { var other = obj as AddImportCodeGenerator; return other != null && - string.Equals(Namespace, other.Namespace, StringComparison.Ordinal) && - NamespaceKeywordLength == other.NamespaceKeywordLength; + string.Equals(Namespace, other.Namespace, StringComparison.Ordinal) && + NamespaceKeywordLength == other.NamespaceKeywordLength; } public override int GetHashCode() { - return HashCodeCombiner.Start() - .Add(Namespace) - .Add(NamespaceKeywordLength) - .CombinedHash; + // Hash code should include only immutable properties. + return Namespace == null ? 0 : StringComparer.Ordinal.GetHashCode(Namespace); } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/AttributeBlockCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/AttributeBlockCodeGenerator.cs index 32f8116e75..7bee2a0ca9 100644 --- a/src/Microsoft.AspNet.Razor/Generator/AttributeBlockCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/AttributeBlockCodeGenerator.cs @@ -19,9 +19,11 @@ namespace Microsoft.AspNet.Razor.Generator Suffix = suffix; } - public string Name { get; private set; } - public LocationTagged Prefix { get; private set; } - public LocationTagged Suffix { get; private set; } + public string Name { get; } + + public LocationTagged Prefix { get; } + + public LocationTagged Suffix { get; } public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { @@ -46,15 +48,15 @@ namespace Microsoft.AspNet.Razor.Generator { var other = obj as AttributeBlockCodeGenerator; return other != null && - string.Equals(other.Name, Name, StringComparison.Ordinal) && - Equals(other.Prefix, Prefix) && - Equals(other.Suffix, Suffix); + 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(Name, StringComparer.Ordinal) .Add(Prefix) .Add(Suffix) .CombinedHash; diff --git a/src/Microsoft.AspNet.Razor/Generator/BlockCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/BlockCodeGenerator.cs index 6ba99a1b8b..8720d6558d 100644 --- a/src/Microsoft.AspNet.Razor/Generator/BlockCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/BlockCodeGenerator.cs @@ -8,6 +8,8 @@ namespace Microsoft.AspNet.Razor.Generator { public abstract class BlockCodeGenerator : IBlockCodeGenerator { + private static readonly int TypeHashCode = typeof(BlockCodeGenerator).GetHashCode(); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")] public static readonly IBlockCodeGenerator Null = new NullBlockCodeGenerator(); @@ -21,12 +23,13 @@ namespace Microsoft.AspNet.Razor.Generator public override bool Equals(object obj) { - return (obj as IBlockCodeGenerator) != null; + return obj != null && + GetType() == obj.GetType(); } public override int GetHashCode() { - return base.GetHashCode(); + return TypeHashCode; } private class NullBlockCodeGenerator : IBlockCodeGenerator diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpLineMappingWriter.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpLineMappingWriter.cs index 42dfc54718..2cc7f13238 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpLineMappingWriter.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpLineMappingWriter.cs @@ -8,16 +8,16 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp { public class CSharpLineMappingWriter : IDisposable { - private CSharpCodeWriter _writer; - private MappingLocation _documentMapping; - private SourceLocation _generatedLocation; - private int _startIndent; - private int _generatedContentLength; - private bool _writePragmas; - private bool _addLineMapping; + private readonly CSharpCodeWriter _writer; + private readonly MappingLocation _documentMapping; + private readonly int _startIndent; + private readonly bool _writePragmas; + private readonly bool _addLineMapping; - private CSharpLineMappingWriter([NotNull] CSharpCodeWriter writer, - bool addLineMappings) + private SourceLocation _generatedLocation; + private int _generatedContentLength; + + private CSharpLineMappingWriter([NotNull] CSharpCodeWriter writer, bool addLineMappings) { _writer = writer; _addLineMapping = addLineMappings; @@ -32,7 +32,11 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp _generatedLocation = _writer.GetCurrentSourceLocation(); } - public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentLocation, int contentLength, string sourceFilename) + public CSharpLineMappingWriter( + CSharpCodeWriter writer, + SourceLocation documentLocation, + int contentLength, + string sourceFilename) : this(writer, documentLocation, contentLength) { _writePragmas = true; @@ -49,9 +53,10 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp /// The to write output to. /// The of the Razor content being mapping. /// The input file path. - public CSharpLineMappingWriter([NotNull] CSharpCodeWriter writer, - [NotNull] SourceLocation documentLocation, - [NotNull] string sourceFileName) + public CSharpLineMappingWriter( + [NotNull] CSharpCodeWriter writer, + [NotNull] SourceLocation documentLocation, + [NotNull] string sourceFileName) : this(writer, addLineMappings: false) { _writePragmas = true; @@ -79,14 +84,20 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp } var generatedLocation = new MappingLocation(_generatedLocation, _generatedContentLength); - if (_documentMapping.ContentLength == -1) + var documentMapping = _documentMapping; + if (documentMapping.ContentLength == -1) { - _documentMapping.ContentLength = generatedLocation.ContentLength; + documentMapping = new MappingLocation( + location: new SourceLocation( + _documentMapping.AbsoluteIndex, + _documentMapping.LineIndex, + _documentMapping.CharacterIndex), + contentLength: _generatedContentLength); } _writer.LineMappingManager.AddMapping( - documentLocation: _documentMapping, - generatedLocation: new MappingLocation(_generatedLocation, _generatedContentLength)); + documentLocation: documentMapping, + generatedLocation: generatedLocation); } if (_writePragmas) @@ -105,7 +116,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp } _writer.WriteLineDefaultDirective() - .WriteLineHiddenDirective(); + .WriteLineHiddenDirective(); } // Reset indent back to when it was started diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMapping.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMapping.cs index 60778a34e6..6f20274f2c 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMapping.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMapping.cs @@ -2,44 +2,71 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; +using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Generator.Compiler { public class LineMapping { - public LineMapping() - : this(documentLocation: null, generatedLocation: null) - { - } - public LineMapping(MappingLocation documentLocation, MappingLocation generatedLocation) { DocumentLocation = documentLocation; GeneratedLocation = generatedLocation; } - public MappingLocation DocumentLocation { get; set; } - public MappingLocation GeneratedLocation { get; set; } + public MappingLocation DocumentLocation { get; } + + public MappingLocation GeneratedLocation { get; } public override bool Equals(object obj) { var other = obj as LineMapping; + if (ReferenceEquals(other, null)) + { + return false; + } + return DocumentLocation.Equals(other.DocumentLocation) && - GeneratedLocation.Equals(other.GeneratedLocation); + GeneratedLocation.Equals(other.GeneratedLocation); } public override int GetHashCode() { - return base.GetHashCode(); + return HashCodeCombiner.Start() + .Add(DocumentLocation) + .Add(GeneratedLocation) + .CombinedHash; } public static bool operator ==(LineMapping left, LineMapping right) { + if (ReferenceEquals(left, right)) + { + // Exact equality e.g. both objects are null. + return true; + } + + if (ReferenceEquals(left, null)) + { + return false; + } + return left.Equals(right); } public static bool operator !=(LineMapping left, LineMapping right) { + if (ReferenceEquals(left, right)) + { + // Exact equality e.g. both objects are null. + return false; + } + + if (ReferenceEquals(left, null)) + { + return true; + } + return !left.Equals(right); } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMappingManager.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMappingManager.cs index 75211f713b..1f5808a2b8 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMappingManager.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMappingManager.cs @@ -12,15 +12,11 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler Mappings = new List(); } - public List Mappings { get; private set; } + public List Mappings { get; } public void AddMapping(MappingLocation documentLocation, MappingLocation generatedLocation) { - Mappings.Add(new LineMapping - { - DocumentLocation = documentLocation, - GeneratedLocation = generatedLocation - }); + Mappings.Add(new LineMapping(documentLocation, generatedLocation)); } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/MappingLocation.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/MappingLocation.cs index 0a0319e44f..2adc8cd9f0 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/MappingLocation.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/MappingLocation.cs @@ -2,12 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; +using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Generator.Compiler { public class MappingLocation { - public MappingLocation() : base() { } + public MappingLocation() + { + } public MappingLocation(SourceLocation location, int contentLength) { @@ -17,24 +20,36 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler CharacterIndex = location.CharacterIndex; } - public int ContentLength { get; set; } - public int AbsoluteIndex { get; set; } - public int LineIndex { get; set; } - public int CharacterIndex { get; set; } + public int ContentLength { get; } + + public int AbsoluteIndex { get; } + + public int LineIndex { get; } + + public int CharacterIndex { get; } public override bool Equals(object obj) { var other = obj as MappingLocation; + if (ReferenceEquals(other, null)) + { + return false; + } return AbsoluteIndex == other.AbsoluteIndex && - ContentLength == other.ContentLength && - LineIndex == other.LineIndex && - CharacterIndex == other.CharacterIndex; + ContentLength == other.ContentLength && + LineIndex == other.LineIndex && + CharacterIndex == other.CharacterIndex; } public override int GetHashCode() { - return base.GetHashCode(); + return HashCodeCombiner.Start() + .Add(AbsoluteIndex) + .Add(ContentLength) + .Add(LineIndex) + .Add(CharacterIndex) + .CombinedHash; } public override string ToString() @@ -49,11 +64,33 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler public static bool operator ==(MappingLocation left, MappingLocation right) { + if (ReferenceEquals(left, right)) + { + // Exact equality e.g. both objects are null. + return true; + } + + if (ReferenceEquals(left, null)) + { + return false; + } + return left.Equals(right); } public static bool operator !=(MappingLocation left, MappingLocation right) { + if (ReferenceEquals(left, right)) + { + // Exact equality e.g. both objects are null. + return false; + } + + if (ReferenceEquals(left, null)) + { + return true; + } + return !left.Equals(right); } } diff --git a/src/Microsoft.AspNet.Razor/Generator/DynamicAttributeBlockCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/DynamicAttributeBlockCodeGenerator.cs index ce02a0086e..e85b26aecf 100644 --- a/src/Microsoft.AspNet.Razor/Generator/DynamicAttributeBlockCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/DynamicAttributeBlockCodeGenerator.cs @@ -1,12 +1,10 @@ // Copyright (c) Microsoft Open Technologies, Inc. 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.Globalization; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Text; -using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Generator { @@ -23,8 +21,9 @@ namespace Microsoft.AspNet.Razor.Generator ValueStart = valueStart; } - public LocationTagged Prefix { get; private set; } - public SourceLocation ValueStart { get; private set; } + public LocationTagged Prefix { get; } + + public SourceLocation ValueStart { get; } public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { @@ -47,14 +46,12 @@ namespace Microsoft.AspNet.Razor.Generator { var other = obj as DynamicAttributeBlockCodeGenerator; return other != null && - Equals(other.Prefix, Prefix); + Equals(other.Prefix, Prefix); } public override int GetHashCode() { - return HashCodeCombiner.Start() - .Add(Prefix) - .CombinedHash; + return Prefix == null ? 0 : Prefix.GetHashCode(); } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/ExpressionCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/ExpressionCodeGenerator.cs index 3476bfe3b0..7fcf4a03ab 100644 --- a/src/Microsoft.AspNet.Razor/Generator/ExpressionCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/ExpressionCodeGenerator.cs @@ -8,6 +8,8 @@ namespace Microsoft.AspNet.Razor.Generator { public class ExpressionCodeGenerator : HybridCodeGenerator { + private static readonly int TypeHashCode = typeof(ExpressionCodeGenerator).GetHashCode(); + public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { context.CodeTreeBuilder.StartChunkBlock(target); @@ -30,12 +32,13 @@ namespace Microsoft.AspNet.Razor.Generator public override bool Equals(object obj) { - return obj is ExpressionCodeGenerator; + return obj != null && + GetType() == obj.GetType(); } public override int GetHashCode() { - return base.GetHashCode(); + return TypeHashCode; } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/GeneratedClassContext.cs b/src/Microsoft.AspNet.Razor/Generator/GeneratedClassContext.cs index 3cd1f05d40..35c59b28b8 100644 --- a/src/Microsoft.AspNet.Razor/Generator/GeneratedClassContext.cs +++ b/src/Microsoft.AspNet.Razor/Generator/GeneratedClassContext.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; using Microsoft.Framework.Internal; +using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Generator { @@ -121,12 +122,12 @@ namespace Microsoft.AspNet.Razor.Generator } // Required Items - public string WriteMethodName { get; private set; } - public string WriteLiteralMethodName { get; private set; } - public string WriteToMethodName { get; private set; } - public string WriteLiteralToMethodName { get; private set; } - public string ExecuteMethodName { get; private set; } - public GeneratedTagHelperContext GeneratedTagHelperContext { get; private set; } + public string WriteMethodName { get; } + public string WriteLiteralMethodName { get; } + public string WriteToMethodName { get; } + public string WriteLiteralToMethodName { get; } + public string ExecuteMethodName { get; } + public GeneratedTagHelperContext GeneratedTagHelperContext { get; } // Optional Items public string BeginContextMethodName { get; set; } @@ -160,30 +161,29 @@ namespace Microsoft.AspNet.Razor.Generator { return false; } + var other = (GeneratedClassContext)obj; return string.Equals(DefineSectionMethodName, other.DefineSectionMethodName, StringComparison.Ordinal) && - string.Equals(WriteMethodName, other.WriteMethodName, StringComparison.Ordinal) && - string.Equals(WriteLiteralMethodName, other.WriteLiteralMethodName, StringComparison.Ordinal) && - string.Equals(WriteToMethodName, other.WriteToMethodName, StringComparison.Ordinal) && - string.Equals(WriteLiteralToMethodName, other.WriteLiteralToMethodName, StringComparison.Ordinal) && - string.Equals(ExecuteMethodName, other.ExecuteMethodName, StringComparison.Ordinal) && - string.Equals(TemplateTypeName, other.TemplateTypeName, StringComparison.Ordinal) && - string.Equals(BeginContextMethodName, other.BeginContextMethodName, StringComparison.Ordinal) && - string.Equals(EndContextMethodName, other.EndContextMethodName, StringComparison.Ordinal); + string.Equals(WriteMethodName, other.WriteMethodName, StringComparison.Ordinal) && + string.Equals(WriteLiteralMethodName, other.WriteLiteralMethodName, StringComparison.Ordinal) && + string.Equals(WriteToMethodName, other.WriteToMethodName, StringComparison.Ordinal) && + string.Equals(WriteLiteralToMethodName, other.WriteLiteralToMethodName, StringComparison.Ordinal) && + string.Equals(ExecuteMethodName, other.ExecuteMethodName, StringComparison.Ordinal) && + string.Equals(TemplateTypeName, other.TemplateTypeName, StringComparison.Ordinal) && + string.Equals(BeginContextMethodName, other.BeginContextMethodName, StringComparison.Ordinal) && + string.Equals(EndContextMethodName, other.EndContextMethodName, StringComparison.Ordinal); } public override int GetHashCode() { - // TODO: Use HashCodeCombiner - return DefineSectionMethodName.GetHashCode() ^ - WriteMethodName.GetHashCode() ^ - WriteLiteralMethodName.GetHashCode() ^ - WriteToMethodName.GetHashCode() ^ - WriteLiteralToMethodName.GetHashCode() ^ - ExecuteMethodName.GetHashCode() ^ - TemplateTypeName.GetHashCode() ^ - BeginContextMethodName.GetHashCode() ^ - EndContextMethodName.GetHashCode(); + // Hash code should include only immutable properties. + return HashCodeCombiner.Start() + .Add(WriteMethodName, StringComparer.Ordinal) + .Add(WriteLiteralMethodName, StringComparer.Ordinal) + .Add(WriteToMethodName, StringComparer.Ordinal) + .Add(WriteLiteralToMethodName, StringComparer.Ordinal) + .Add(ExecuteMethodName, StringComparer.Ordinal) + .CombinedHash; } public static bool operator ==(GeneratedClassContext left, GeneratedClassContext right) diff --git a/src/Microsoft.AspNet.Razor/Generator/LiteralAttributeCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/LiteralAttributeCodeGenerator.cs index 6fbc2983e5..26028db1f0 100644 --- a/src/Microsoft.AspNet.Razor/Generator/LiteralAttributeCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/LiteralAttributeCodeGenerator.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. 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.Globalization; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Parser.SyntaxTree; @@ -24,9 +23,11 @@ namespace Microsoft.AspNet.Razor.Generator Value = value; } - public LocationTagged Prefix { get; private set; } - public LocationTagged Value { get; private set; } - public LocationTagged ValueGenerator { get; private set; } + public LocationTagged Prefix { get; } + + public LocationTagged Value { get; } + + public LocationTagged ValueGenerator { get; } public override void GenerateCode(Span target, CodeGeneratorContext context) { @@ -62,9 +63,9 @@ namespace Microsoft.AspNet.Razor.Generator { var other = obj as LiteralAttributeCodeGenerator; return other != null && - Equals(other.Prefix, Prefix) && - Equals(other.Value, Value) && - Equals(other.ValueGenerator, ValueGenerator); + Equals(other.Prefix, Prefix) && + Equals(other.Value, Value) && + Equals(other.ValueGenerator, ValueGenerator); } public override int GetHashCode() diff --git a/src/Microsoft.AspNet.Razor/Generator/MarkupCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/MarkupCodeGenerator.cs index 958ca338e3..c5403ee3fe 100644 --- a/src/Microsoft.AspNet.Razor/Generator/MarkupCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/MarkupCodeGenerator.cs @@ -16,15 +16,5 @@ namespace Microsoft.AspNet.Razor.Generator { return "Markup"; } - - public override bool Equals(object obj) - { - return obj is MarkupCodeGenerator; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/ResolveUrlCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/ResolveUrlCodeGenerator.cs index 0f96a6d1d6..527c0ca26f 100644 --- a/src/Microsoft.AspNet.Razor/Generator/ResolveUrlCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/ResolveUrlCodeGenerator.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.Parser.SyntaxTree; namespace Microsoft.AspNet.Razor.Generator @@ -25,15 +24,5 @@ namespace Microsoft.AspNet.Razor.Generator { return "VirtualPath"; } - - public override bool Equals(object obj) - { - return obj is ResolveUrlCodeGenerator; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/SectionCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/SectionCodeGenerator.cs index 26ab0fb8a9..0359d6e6f0 100644 --- a/src/Microsoft.AspNet.Razor/Generator/SectionCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/SectionCodeGenerator.cs @@ -4,7 +4,6 @@ using System; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Parser.SyntaxTree; -using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Generator { @@ -15,7 +14,7 @@ namespace Microsoft.AspNet.Razor.Generator SectionName = sectionName; } - public string SectionName { get; private set; } + public string SectionName { get; } public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { @@ -32,17 +31,13 @@ namespace Microsoft.AspNet.Razor.Generator public override bool Equals(object obj) { var other = obj as SectionCodeGenerator; - return other != null && - base.Equals(other) && - string.Equals(SectionName, other.SectionName, StringComparison.Ordinal); + return base.Equals(other) && + string.Equals(SectionName, other.SectionName, StringComparison.Ordinal); } public override int GetHashCode() { - return HashCodeCombiner.Start() - .Add(base.GetHashCode()) - .Add(SectionName) - .CombinedHash; + return SectionName == null ? 0 : StringComparer.Ordinal.GetHashCode(SectionName); } public override string ToString() diff --git a/src/Microsoft.AspNet.Razor/Generator/SetBaseTypeCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/SetBaseTypeCodeGenerator.cs index 57d2739b59..6d70547273 100644 --- a/src/Microsoft.AspNet.Razor/Generator/SetBaseTypeCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/SetBaseTypeCodeGenerator.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Razor.Generator BaseType = baseType; } - public string BaseType { get; private set; } + public string BaseType { get; } public override void GenerateCode(Span target, CodeGeneratorContext context) { @@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Razor.Generator { var other = obj as SetBaseTypeCodeGenerator; return other != null && - string.Equals(BaseType, other.BaseType, StringComparison.Ordinal); + string.Equals(BaseType, other.BaseType, StringComparison.Ordinal); } public override int GetHashCode() diff --git a/src/Microsoft.AspNet.Razor/Generator/SpanCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/SpanCodeGenerator.cs index fc2a5e5cf2..81487e5c7a 100644 --- a/src/Microsoft.AspNet.Razor/Generator/SpanCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/SpanCodeGenerator.cs @@ -8,6 +8,8 @@ namespace Microsoft.AspNet.Razor.Generator { public abstract class SpanCodeGenerator : ISpanCodeGenerator { + private static readonly int TypeHashCode = typeof(SpanCodeGenerator).GetHashCode(); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")] public static readonly ISpanCodeGenerator Null = new NullSpanCodeGenerator(); @@ -17,12 +19,13 @@ namespace Microsoft.AspNet.Razor.Generator public override bool Equals(object obj) { - return (obj as ISpanCodeGenerator) != null; + return obj != null && + GetType() == obj.GetType(); } public override int GetHashCode() { - return base.GetHashCode(); + return TypeHashCode; } private class NullSpanCodeGenerator : ISpanCodeGenerator diff --git a/src/Microsoft.AspNet.Razor/Generator/StatementCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/StatementCodeGenerator.cs index f72b433be3..7f75adb4b9 100644 --- a/src/Microsoft.AspNet.Razor/Generator/StatementCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/StatementCodeGenerator.cs @@ -16,15 +16,5 @@ namespace Microsoft.AspNet.Razor.Generator { return "Stmt"; } - - public override bool Equals(object obj) - { - return obj is StatementCodeGenerator; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelpers/TagHelperPrefixDirectiveCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/TagHelperPrefixDirectiveCodeGenerator.cs index 7acd267395..13364114c4 100644 --- a/src/Microsoft.AspNet.Razor/Generator/TagHelpers/TagHelperPrefixDirectiveCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/TagHelperPrefixDirectiveCodeGenerator.cs @@ -6,7 +6,7 @@ using Microsoft.AspNet.Razor.Parser.SyntaxTree; namespace Microsoft.AspNet.Razor.Generator { /// - /// A responsible for generating + /// A responsible for generating /// s. /// public class TagHelperPrefixDirectiveCodeGenerator : SpanCodeGenerator diff --git a/src/Microsoft.AspNet.Razor/Generator/TypeMemberCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TypeMemberCodeGenerator.cs index 771f1a7e89..64c208c461 100644 --- a/src/Microsoft.AspNet.Razor/Generator/TypeMemberCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/TypeMemberCodeGenerator.cs @@ -16,16 +16,5 @@ namespace Microsoft.AspNet.Razor.Generator { return "TypeMember"; } - - public override bool Equals(object obj) - { - return obj is TypeMemberCodeGenerator; - } - - // C# complains at us if we don't provide an implementation, even one like this - public override int GetHashCode() - { - return base.GetHashCode(); - } } } diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs index 7042231491..47e9b529c6 100644 --- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs +++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs @@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Razor.Parser } // Set up edit handler - var editHandler = new AutoCompleteEditHandler(Language.TokenizeString) { AutoCompleteAtEndOfSpan = true }; + var editHandler = new AutoCompleteEditHandler(Language.TokenizeString, autoCompleteAtEndOfSpan: true); Span.EditHandler = editHandler; Span.Accept(CurrentSymbol); diff --git a/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/Block.cs b/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/Block.cs index 3f78ac47f5..5b5bfb9ce1 100644 --- a/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/Block.cs +++ b/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/Block.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.Linq; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Text; +using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Parser.SyntaxTree { @@ -45,11 +46,11 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree } [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Type is the most appropriate name for this property and there is little chance of confusion with GetType")] - public BlockType Type { get; private set; } + public BlockType Type { get; } - public IEnumerable Children { get; private set; } + public IEnumerable Children { get; } - public IBlockCodeGenerator CodeGenerator { get; private set; } + public IBlockCodeGenerator CodeGenerator { get; } public override bool IsBlock { @@ -111,14 +112,18 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree { var other = obj as Block; return other != null && - Type == other.Type && - Equals(CodeGenerator, other.CodeGenerator) && - ChildrenEqual(Children, other.Children); + Type == other.Type && + Equals(CodeGenerator, other.CodeGenerator) && + ChildrenEqual(Children, other.Children); } public override int GetHashCode() { - return (int)Type; + return HashCodeCombiner.Start() + .Add(Type) + .Add(CodeGenerator) + .Add(Children) + .CombinedHash; } public IEnumerable Flatten() @@ -201,7 +206,19 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree { return false; } + return Enumerable.SequenceEqual(Children, other.Children, new EquivalenceComparer()); } + + public override int GetEquivalenceHash() + { + var combiner = HashCodeCombiner.Start().Add(Type); + foreach (var child in Children) + { + combiner.Add(child.GetEquivalenceHash()); + } + + return combiner.CombinedHash; + } } } diff --git a/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/EquivalenceComparer.cs b/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/EquivalenceComparer.cs index 72a360d363..a52c6086a7 100644 --- a/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/EquivalenceComparer.cs +++ b/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/EquivalenceComparer.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree public int GetHashCode(SyntaxTreeNode obj) { - return obj.GetHashCode(); + return obj.GetEquivalenceHash(); } } } diff --git a/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/Span.cs b/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/Span.cs index 6537bb6099..3eda3908c9 100644 --- a/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/Span.cs +++ b/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/Span.cs @@ -10,12 +10,12 @@ using Microsoft.AspNet.Razor.Editor; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Tokenizer.Symbols; -using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Parser.SyntaxTree { public class Span : SyntaxTreeNode { + private static readonly int TypeHashCode = typeof(Span).GetHashCode(); private SourceLocation _start; public Span(SpanBuilder builder) @@ -126,29 +126,32 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree { var other = node as Span; return other != null && - Kind.Equals(other.Kind) && - Start.Equals(other.Start) && - EditHandler.Equals(other.EditHandler) && - string.Equals(other.Content, Content, StringComparison.Ordinal); + Kind.Equals(other.Kind) && + Start.Equals(other.Start) && + EditHandler.Equals(other.EditHandler) && + string.Equals(other.Content, Content, StringComparison.Ordinal); + } + + public override int GetEquivalenceHash() + { + // Hash code should include only immutable properties but EquivalentTo also checks the type. + return TypeHashCode; } public override bool Equals(object obj) { var other = obj as Span; return other != null && - Kind.Equals(other.Kind) && - EditHandler.Equals(other.EditHandler) && - CodeGenerator.Equals(other.CodeGenerator) && - Symbols.SequenceEqual(other.Symbols); + Kind.Equals(other.Kind) && + EditHandler.Equals(other.EditHandler) && + CodeGenerator.Equals(other.CodeGenerator) && + Symbols.SequenceEqual(other.Symbols); } public override int GetHashCode() { - return HashCodeCombiner.Start() - .Add((int)Kind) - .Add(Start) - .Add(Content) - .CombinedHash; + // Hash code should include only immutable properties but Equals also checks the type. + return TypeHashCode; } } } diff --git a/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/SyntaxTreeNode.cs b/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/SyntaxTreeNode.cs index ad78ad5c46..cbb9e762f0 100644 --- a/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/SyntaxTreeNode.cs +++ b/src/Microsoft.AspNet.Razor/Parser/SyntaxTree/SyntaxTreeNode.cs @@ -37,5 +37,15 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree /// symbols may be different. /// public abstract bool EquivalentTo(SyntaxTreeNode node); + + /// + /// Determines a hash code for the using only information relevant in + /// comparisons. + /// + /// + /// A hash code for the using only information relevant in + /// comparisons. + /// + public abstract int GetEquivalenceHash(); } } diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs index 57012f69d4..c537684f71 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs @@ -81,7 +81,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers /// /// The HTML tag name. /// - public string TagName { get; private set; } + public string TagName { get; } public override int Length { @@ -115,20 +115,18 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers /// public bool Equals(TagHelperBlock other) { - return other != null && - TagName == other.TagName && - Attributes.SequenceEqual(other.Attributes) && - base.Equals(other); + return base.Equals(other) && + string.Equals(TagName, other.TagName, StringComparison.OrdinalIgnoreCase) && + Attributes.SequenceEqual(other.Attributes); } /// public override int GetHashCode() { - return HashCodeCombiner - .Start() - .Add(TagName) - .Add(Attributes) + return HashCodeCombiner.Start() .Add(base.GetHashCode()) + .Add(TagName, StringComparer.OrdinalIgnoreCase) + .Add(Attributes) .CombinedHash; } } diff --git a/src/Microsoft.AspNet.Razor/RazorError.cs b/src/Microsoft.AspNet.Razor/RazorError.cs index d83f650d68..d2b058672d 100644 --- a/src/Microsoft.AspNet.Razor/RazorError.cs +++ b/src/Microsoft.AspNet.Razor/RazorError.cs @@ -3,6 +3,7 @@ using System; using System.Globalization; +using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor { @@ -35,8 +36,22 @@ namespace Microsoft.AspNet.Razor { } + /// + /// Gets (or sets) the message describing the error. + /// + /// Set property is only accessible for deserialization purposes. public string Message { get; set; } + + /// + /// Gets (or sets) the start position of the erroneous text. + /// + /// Set property is only accessible for deserialization purposes. public SourceLocation Location { get; set; } + + /// + /// Gets or sets the length of the erroneous text. + /// + /// Set property is only accessible for deserialization purposes. public int Length { get; set; } public override string ToString() @@ -46,19 +61,23 @@ namespace Microsoft.AspNet.Razor public override bool Equals(object obj) { - var err = obj as RazorError; - return (err != null) && Equals(err); + var error = obj as RazorError; + return Equals(error); } public override int GetHashCode() { - return base.GetHashCode(); + return HashCodeCombiner.Start() + .Add(Message, StringComparer.Ordinal) + .Add(Location) + .CombinedHash; } public bool Equals(RazorError other) { - return string.Equals(other.Message, Message, StringComparison.Ordinal) && - Location.Equals(other.Location); + return other != null && + string.Equals(other.Message, Message, StringComparison.Ordinal) && + Location.Equals(other.Location); } } } diff --git a/src/Microsoft.AspNet.Razor/SourceLocation.cs b/src/Microsoft.AspNet.Razor/SourceLocation.cs index 305091c69c..c25dccc359 100644 --- a/src/Microsoft.AspNet.Razor/SourceLocation.cs +++ b/src/Microsoft.AspNet.Razor/SourceLocation.cs @@ -90,13 +90,13 @@ namespace Microsoft.AspNet.Razor /// public override bool Equals(object obj) { - return (obj is SourceLocation) && Equals((SourceLocation)obj); + return obj is SourceLocation && + Equals((SourceLocation)obj); } /// public override int GetHashCode() { - // LineIndex and CharacterIndex can be calculated from AbsoluteIndex and the document content. return HashCodeCombiner.Start() .Add(FilePath, StringComparer.Ordinal) .Add(AbsoluteIndex) @@ -106,10 +106,8 @@ namespace Microsoft.AspNet.Razor /// public bool Equals(SourceLocation other) { - return string.Equals(FilePath, other.FilePath, StringComparison.Ordinal) && - AbsoluteIndex == other.AbsoluteIndex && - LineIndex == other.LineIndex && - CharacterIndex == other.CharacterIndex; + // LineIndex and CharacterIndex can be calculated from AbsoluteIndex and the document content. + return CompareTo(other) == 0; } /// diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs index 0f5b658dba..ca6ebea0b3 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs @@ -68,7 +68,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// . /// /// - /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available + /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available /// tag helpers. /// /// The tag name that the tag helper targets. '*' indicates a catch-all @@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers } /// - /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available + /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available /// tag helpers. /// public string Prefix { get; private set; } @@ -128,11 +128,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// /// The list of attributes the tag helper expects. /// - public IList Attributes { get; private set; } + public IReadOnlyList Attributes { get; private set; } /// /// The list of required attribute names the tag helper expects to target an element. /// - public IList RequiredAttributes { get; private set; } + public IReadOnlyList RequiredAttributes { get; private set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs index 9b59df67e3..0a909a4e0d 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs @@ -40,10 +40,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) && Enumerable.SequenceEqual( descriptorX.RequiredAttributes.OrderBy( - attribute => attribute, + attribute => attribute, StringComparer.OrdinalIgnoreCase), descriptorY.RequiredAttributes.OrderBy( - attribute => attribute, + attribute => attribute, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } @@ -62,12 +62,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers .Add(descriptor.AssemblyName, StringComparer.Ordinal); var attributes = descriptor.RequiredAttributes.OrderBy( - attribute => attribute, + attribute => attribute, StringComparer.OrdinalIgnoreCase); - foreach (var attribute in attributes) { - hashCodeCombiner.Add(attributes); + hashCodeCombiner.Add(attribute, StringComparer.OrdinalIgnoreCase); } return hashCodeCombiner.CombinedHash; diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs index cfb507bacf..6adab06c7b 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs @@ -36,16 +36,16 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// /// A used to find tag helper s. /// - public string DirectiveText { get; private set; } + public string DirectiveText { get; } /// /// The of this directive. /// - public TagHelperDirectiveType DirectiveType { get; private set; } + public TagHelperDirectiveType DirectiveType { get; } /// /// The of the directive. /// - public SourceLocation Location { get; private set; } + public SourceLocation Location { get; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Text/LocationTagged.cs b/src/Microsoft.AspNet.Razor/Text/LocationTagged.cs index da1693037c..bda6579475 100644 --- a/src/Microsoft.AspNet.Razor/Text/LocationTagged.cs +++ b/src/Microsoft.AspNet.Razor/Text/LocationTagged.cs @@ -33,15 +33,20 @@ namespace Microsoft.AspNet.Razor.Text Value = value; } - public SourceLocation Location { get; private set; } - public T Value { get; private set; } + public SourceLocation Location { get; } + + public T Value { get; } public override bool Equals(object obj) { LocationTagged other = obj as LocationTagged; - return other != null && - Equals(other.Location, Location) && - Equals(other.Value, Value); + if (ReferenceEquals(other, null)) + { + return false; + } + + return Equals(other.Location, Location) && + Equals(other.Value, Value); } public override int GetHashCode() diff --git a/src/Microsoft.AspNet.Razor/Text/TextChange.cs b/src/Microsoft.AspNet.Razor/Text/TextChange.cs index f2c7de183d..11c651df81 100644 --- a/src/Microsoft.AspNet.Razor/Text/TextChange.cs +++ b/src/Microsoft.AspNet.Razor/Text/TextChange.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Globalization; using System.Text; using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Text { @@ -58,12 +59,17 @@ namespace Microsoft.AspNet.Razor.Text OldBuffer = oldBuffer; } - public int OldPosition { get; private set; } - public int NewPosition { get; private set; } - public int OldLength { get; private set; } - public int NewLength { get; private set; } - public ITextBuffer NewBuffer { get; private set; } - public ITextBuffer OldBuffer { get; private set; } + public int OldPosition { get; } + + public int NewPosition { get; } + + public int OldLength { get; } + + public int NewLength { get; } + + public ITextBuffer NewBuffer { get; } + + public ITextBuffer OldBuffer { get; } /// /// Note: This property is not thread safe, and will move position on the textbuffer while being read. @@ -120,13 +126,26 @@ namespace Microsoft.AspNet.Razor.Text { return false; } + var change = (TextChange)obj; - return (change.OldPosition == OldPosition) && - (change.NewPosition == NewPosition) && - (change.OldLength == OldLength) && - (change.NewLength == NewLength) && - OldBuffer.Equals(change.OldBuffer) && - NewBuffer.Equals(change.NewBuffer); + return change.OldPosition == OldPosition && + change.NewPosition == NewPosition && + change.OldLength == OldLength && + change.NewLength == NewLength && + OldBuffer.Equals(change.OldBuffer) && + NewBuffer.Equals(change.NewBuffer); + } + + public override int GetHashCode() + { + return HashCodeCombiner.Start() + .Add(OldPosition) + .Add(NewPosition) + .Add(OldLength) + .Add(NewLength) + .Add(OldBuffer) + .Add(NewBuffer) + .CombinedHash; } public string ApplyChange(string content, int changeOffset) @@ -147,11 +166,6 @@ namespace Microsoft.AspNet.Razor.Text return ApplyChange(span.Content, span.Start.AbsoluteIndex); } - public override int GetHashCode() - { - return OldPosition ^ NewPosition ^ OldLength ^ NewLength ^ NewBuffer.GetHashCode() ^ OldBuffer.GetHashCode(); - } - public override string ToString() { return string.Format(CultureInfo.CurrentCulture, "({0}:{1}) \"{3}\" -> ({0}:{2}) \"{4}\"", OldPosition, OldLength, NewLength, OldText, NewText); diff --git a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/CSharpSymbol.cs b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/CSharpSymbol.cs index cc34557aa9..2bef3d29a6 100644 --- a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/CSharpSymbol.cs +++ b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/CSharpSymbol.cs @@ -35,12 +35,13 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols public override bool Equals(object obj) { var other = obj as CSharpSymbol; - return base.Equals(obj) && other.Keyword == Keyword; + return base.Equals(other) && other.Keyword == Keyword; } public override int GetHashCode() { - return base.GetHashCode() ^ Keyword.GetHashCode(); + // Hash code should include only immutable properties. + return base.GetHashCode(); } } } diff --git a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/SymbolBase.cs b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/SymbolBase.cs index 858b540ca8..8be78aade8 100644 --- a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/SymbolBase.cs +++ b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/SymbolBase.cs @@ -29,26 +29,28 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols } public SourceLocation Start { get; private set; } - public string Content { get; private set; } - public IEnumerable Errors { get; private set; } + + public string Content { get; } + + public IEnumerable Errors { get; } [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "This is the most appropriate name for this property and conflicts are unlikely")] - public TType Type { get; private set; } + public TType Type { get; } public override bool Equals(object obj) { SymbolBase other = obj as SymbolBase; return other != null && - Start.Equals(other.Start) && - string.Equals(Content, other.Content, StringComparison.Ordinal) && - Type.Equals(other.Type); + Start.Equals(other.Start) && + string.Equals(Content, other.Content, StringComparison.Ordinal) && + Type.Equals(other.Type); } public override int GetHashCode() { + // Hash code should include only immutable properties. return HashCodeCombiner.Start() - .Add(Start) - .Add(Content) + .Add(Content, StringComparer.Ordinal) .Add(Type) .CombinedHash; } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CaseSensitiveTagHelperDescriptorComparer.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CaseSensitiveTagHelperDescriptorComparer.cs index 1096ef93c5..a9d8357be7 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CaseSensitiveTagHelperDescriptorComparer.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CaseSensitiveTagHelperDescriptorComparer.cs @@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { var hashCodeCombiner = HashCodeCombiner .Start() - .Add(base.GetHashCode()) + .Add(base.GetHashCode(descriptor)) .Add(descriptor.TagName, StringComparer.Ordinal) .Add(descriptor.Prefix); diff --git a/test/Microsoft.AspNet.Razor.Test/Editor/AutoCompleteEditHandlerTest.cs b/test/Microsoft.AspNet.Razor.Test/Editor/AutoCompleteEditHandlerTest.cs new file mode 100644 index 0000000000..0938b98031 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Editor/AutoCompleteEditHandlerTest.cs @@ -0,0 +1,220 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Xunit; + +namespace Microsoft.AspNet.Razor.Parser.SyntaxTree +{ + public class AutoCompleteEditHandlerTest + { + public static TheoryData MatchingTestDataSet + { + get + { + return new TheoryData + { + { + new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + AutoCompleteString = "one string", + EditorHints = EditorHints.VirtualPath, + }, + new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + AutoCompleteString = "one string", + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Tokenizer not involved in equality check or hash code calculation. + new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.Any, + AutoCompleteString = "two string", + EditorHints = EditorHints.None, + }, + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.Any, + AutoCompleteString = "two string", + EditorHints = EditorHints.None, + } + }, + }; + } + } + + public static TheoryData NonMatchingTestDataSet + { + get + { + return new TheoryData + { + { + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: false) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + AutoCompleteString = "three string", + EditorHints = EditorHints.VirtualPath, + }, + null + }, + { + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: false) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + AutoCompleteString = "three string", + EditorHints = EditorHints.VirtualPath, + }, + new object() + }, + { + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: false) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + AutoCompleteString = "three string", + EditorHints = EditorHints.VirtualPath, + }, + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + }, + { + // Different AcceptedCharacters. + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + AutoCompleteString = "three string", + EditorHints = EditorHints.VirtualPath, + }, + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + AutoCompleteString = "three string", + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Different AutoCompleteAtEndOfSpan. + new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + AutoCompleteString = "four string", + EditorHints = EditorHints.None, + }, + new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + AutoCompleteString = "four string", + EditorHints = EditorHints.None, + } + }, + { + // Different AutoCompleteString. + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.NewLine, + AutoCompleteString = "some string", + EditorHints = EditorHints.None, + }, + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.NewLine, + AutoCompleteString = "different string", + EditorHints = EditorHints.None, + } + }, + { + // Different AutoCompleteString (case sensitive). + new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false) + { + AcceptedCharacters = AcceptedCharacters.None, + AutoCompleteString = "some string", + EditorHints = EditorHints.VirtualPath, + }, + new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false) + { + AcceptedCharacters = AcceptedCharacters.None, + AutoCompleteString = "Some String", + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Different EditorHints. + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.NonWhiteSpace, + AutoCompleteString = "five string", + EditorHints = EditorHints.VirtualPath, + }, + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.NonWhiteSpace, + AutoCompleteString = "five string", + EditorHints = EditorHints.None, + } + }, + }; + } + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void Equals_True_WhenExpected(AutoCompleteEditHandler leftObject, AutoCompleteEditHandler rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.True(result); + } + + [Theory] + [MemberData(nameof(NonMatchingTestDataSet))] + public void Equals_False_WhenExpected(AutoCompleteEditHandler leftObject, object rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.False(result); + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void GetHashCode_ReturnsSameValue_WhenEqual( + AutoCompleteEditHandler leftObject, + AutoCompleteEditHandler rightObject) + { + // Arrange & Act + var leftResult = leftObject.GetHashCode(); + var rightResult = rightObject.GetHashCode(); + + // Assert + Assert.Equal(leftResult, rightResult); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Editor/ImplicitExpressionEditHandlerTest.cs b/test/Microsoft.AspNet.Razor.Test/Editor/ImplicitExpressionEditHandlerTest.cs new file mode 100644 index 0000000000..e6a852b8c6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Editor/ImplicitExpressionEditHandlerTest.cs @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Xunit; + +namespace Microsoft.AspNet.Razor.Parser.SyntaxTree +{ + public class ImplicitExpressionEditHandlerTest + { + public static TheoryData MatchingTestDataSet + { + get + { + return new TheoryData + { + { + new ImplicitExpressionEditHandler( + tokenizer: null, + keywords: new HashSet(), + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + new ImplicitExpressionEditHandler( + tokenizer: null, + keywords: new HashSet(), + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Tokenizer not involved in equality check or hash code calculation. + new ImplicitExpressionEditHandler( + tokenizer: null, + keywords: new HashSet { "keyword 1" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.Any, + EditorHints = EditorHints.None, + }, + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 1" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.Any, + EditorHints = EditorHints.None, + } + }, + { + // Only comparers are different (HashSet's comparer does not affect GetHashCode of entries). + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 2", "keyword 3" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.Any, + EditorHints = EditorHints.None, + }, + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet(StringComparer.OrdinalIgnoreCase) { "keyword 2", "keyword 3" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.Any, + EditorHints = EditorHints.None, + } + }, + }; + } + } + + public static TheoryData NonMatchingTestDataSet + { + get + { + return new TheoryData + { + { + new ImplicitExpressionEditHandler( + tokenizer: _ => null, + keywords: new HashSet { "keyword 4" }, + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.WhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + null + }, + { + new ImplicitExpressionEditHandler( + tokenizer: _ => null, + keywords: new HashSet { "keyword 4" }, + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.WhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + new object() + }, + { + new ImplicitExpressionEditHandler( + tokenizer: _ => null, + keywords: new HashSet { "keyword 4" }, + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.WhiteSpace, + EditorHints = EditorHints.None, + }, + new SpanEditHandler( tokenizer: _ => null) + }, + { + // Different AcceptedCharacters. + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 5" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 5" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Different AcceptTrailingDot. + new ImplicitExpressionEditHandler( + tokenizer: _ => null, + keywords: new HashSet { "keyword 6" }, + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + EditorHints = EditorHints.VirtualPath, + }, + new ImplicitExpressionEditHandler( + tokenizer: _ => null, + keywords: new HashSet { "keyword 6" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Different Keywords. + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 7" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.NewLine, + EditorHints = EditorHints.None, + }, + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 8" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.NewLine, + EditorHints = EditorHints.None, + } + }, + { + // Different Keywords comparers (Equals uses left comparer). + new ImplicitExpressionEditHandler( + tokenizer: _ => null, + keywords: new HashSet { "keyword 9" }, + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.None, + EditorHints = EditorHints.VirtualPath, + }, + new ImplicitExpressionEditHandler( + tokenizer: _ => null, + keywords: new HashSet(StringComparer.OrdinalIgnoreCase) { "KEYWORD 9" }, + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.None, + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Different Keywords (count). + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 10" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.NonWhiteSpace, + EditorHints = EditorHints.None, + }, + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 10", "keyword 11" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.NonWhiteSpace, + EditorHints = EditorHints.None, + } + }, + { + // Different Keywords (count). + new ImplicitExpressionEditHandler( + tokenizer: null, + keywords: new HashSet { "keyword 12" }, + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.WhiteSpace, + EditorHints = EditorHints.None, + }, + new ImplicitExpressionEditHandler( + tokenizer: null, + keywords: new HashSet(), + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.WhiteSpace, + EditorHints = EditorHints.None, + } + }, + { + // Different EditorHints. + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 13" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + new ImplicitExpressionEditHandler( + tokenizer: _ => Enumerable.Empty(), + keywords: new HashSet { "keyword 13" }, + acceptTrailingDot: true) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.None, + } + }, + }; + } + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void Equals_True_WhenExpected( + ImplicitExpressionEditHandler leftObject, + ImplicitExpressionEditHandler rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.True(result); + } + + [Theory] + [MemberData(nameof(NonMatchingTestDataSet))] + public void Equals_False_WhenExpected(ImplicitExpressionEditHandler leftObject, object rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.False(result); + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void GetHashCode_ReturnsSameValue_WhenEqual( + ImplicitExpressionEditHandler leftObject, + ImplicitExpressionEditHandler rightObject) + { + // Arrange & Act + var leftResult = leftObject.GetHashCode(); + var rightResult = rightObject.GetHashCode(); + + // Assert + Assert.Equal(leftResult, rightResult); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Editor/SpanEditHandlerTest.cs b/test/Microsoft.AspNet.Razor.Test/Editor/SpanEditHandlerTest.cs new file mode 100644 index 0000000000..91cc6e4abe --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Editor/SpanEditHandlerTest.cs @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Xunit; + +namespace Microsoft.AspNet.Razor.Parser.SyntaxTree +{ + public class SpanEditHandlerTest + { + public static TheoryData MatchingTestDataSet + { + get + { + return new TheoryData + { + { + new SpanEditHandler(tokenizer: null) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + new SpanEditHandler(tokenizer: null) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Tokenizer not involved in equality check or hash code calculation. + new SpanEditHandler(tokenizer: null) + { + AcceptedCharacters = AcceptedCharacters.Any, + EditorHints = EditorHints.None, + }, + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + { + AcceptedCharacters = AcceptedCharacters.Any, + EditorHints = EditorHints.None, + } + }, + }; + } + } + + public static TheoryData NonMatchingTestDataSet + { + get + { + return new TheoryData + { + { + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + null + }, + { + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + new object() + }, + { + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + { + AcceptedCharacters = AcceptedCharacters.Any, + EditorHints = EditorHints.None, + }, + new AutoCompleteEditHandler( + tokenizer: _ => Enumerable.Empty(), + autoCompleteAtEndOfSpan: true) + { + AcceptedCharacters = AcceptedCharacters.Any, + AutoCompleteString = "two string", + EditorHints = EditorHints.None, + } + }, + { + new SpanEditHandler(tokenizer: null) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + EditorHints = EditorHints.VirtualPath, + }, + new ImplicitExpressionEditHandler( + tokenizer: null, + keywords: new HashSet(), + acceptTrailingDot: false) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Different AcceptedCharacters. + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + { + AcceptedCharacters = AcceptedCharacters.AllWhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + { + AcceptedCharacters = AcceptedCharacters.AnyExceptNewline, + EditorHints = EditorHints.VirtualPath, + } + }, + { + // Different EditorHints. + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + { + AcceptedCharacters = AcceptedCharacters.NonWhiteSpace, + EditorHints = EditorHints.VirtualPath, + }, + new SpanEditHandler(tokenizer: _ => Enumerable.Empty()) + { + AcceptedCharacters = AcceptedCharacters.NonWhiteSpace, + EditorHints = EditorHints.None, + } + }, + }; + } + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void Equals_True_WhenExpected(SpanEditHandler leftObject, SpanEditHandler rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.True(result); + } + + [Theory] + [MemberData(nameof(NonMatchingTestDataSet))] + public void Equals_False_WhenExpected(SpanEditHandler leftObject, object rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.False(result); + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void GetHashCode_ReturnsSameValue_WhenEqual(SpanEditHandler leftObject, SpanEditHandler rightObject) + { + // Arrange & Act + var leftResult = leftObject.GetHashCode(); + var rightResult = rightObject.GetHashCode(); + + // Assert + Assert.Equal(leftResult, rightResult); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/RawTextSymbol.cs b/test/Microsoft.AspNet.Razor.Test/Framework/RawTextSymbol.cs index 521f84b6f6..8f63b9c702 100644 --- a/test/Microsoft.AspNet.Razor.Test/Framework/RawTextSymbol.cs +++ b/test/Microsoft.AspNet.Razor.Test/Framework/RawTextSymbol.cs @@ -6,14 +6,13 @@ using System.Globalization; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Tokenizer.Symbols; -using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Test.Framework { internal class RawTextSymbol : ISymbol { public SourceLocation Start { get; private set; } - public string Content { get; private set; } + public string Content { get; } public RawTextSymbol(SourceLocation start, string content) { @@ -39,10 +38,8 @@ namespace Microsoft.AspNet.Razor.Test.Framework public override int GetHashCode() { - return HashCodeCombiner.Start() - .Add(Start) - .Add(Content) - .CombinedHash; + // Hash code should include only immutable properties. + return Content == null ? 0 : Content.GetHashCode(); } public void OffsetStart(SourceLocation documentStart) diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs b/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs index 40452ce2dd..4a60245d9e 100644 --- a/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs +++ b/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs @@ -243,7 +243,12 @@ namespace Microsoft.AspNet.Razor.Test.Framework public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString, bool atEndOfSpan) { - return self.With(new AutoCompleteEditHandler(SpanConstructor.TestTokenizer) { AutoCompleteString = autoCompleteString, AutoCompleteAtEndOfSpan = atEndOfSpan }); + return self.With(new AutoCompleteEditHandler( + SpanConstructor.TestTokenizer, + autoCompleteAtEndOfSpan: atEndOfSpan) + { + AutoCompleteString = autoCompleteString + }); } public static SpanConstructor WithEditorHints(this SpanConstructor self, EditorHints hints) diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/AddImportCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/AddImportCodeGeneratorTest.cs new file mode 100644 index 0000000000..9f9892082f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/AddImportCodeGeneratorTest.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.Generator +{ + public class AddImportCodeGeneratorTest + { + public static TheoryData MatchingTestDataSet + { + get + { + return new TheoryData + { + { + new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 3), + new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 3) + }, + { + new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23), + new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23) + }, + }; + } + } + + public static TheoryData NonMatchingTestDataSet + { + get + { + return new TheoryData + { + { + new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 0), + null + }, + { + new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23), + null + }, + { + new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23), + new object() + }, + { + new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23), + SpanCodeGenerator.Null + }, + { + new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23), + new StatementCodeGenerator() + }, + { + // Different Namespace. + new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 3), + new AddImportCodeGenerator(ns: "Ginger", namespaceKeywordLength: 3) + }, + { + // Different Namespace (case sensitive). + new AddImportCodeGenerator(ns: "fred", namespaceKeywordLength: 9), + new AddImportCodeGenerator(ns: "FRED", namespaceKeywordLength: 9) + }, + { + // Different NamespaceKeywordLength. + new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 0), + new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 23) + }, + }; + } + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void Equals_True_WhenExpected(AddImportCodeGenerator leftObject, AddImportCodeGenerator rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.True(result); + } + + [Theory] + [MemberData(nameof(NonMatchingTestDataSet))] + public void Equals_False_WhenExpected(AddImportCodeGenerator leftObject, object rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.False(result); + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void GetHashCode_ReturnsSameValue_WhenEqual( + AddImportCodeGenerator leftObject, + AddImportCodeGenerator rightObject) + { + // Arrange & Act + var leftResult = leftObject.GetHashCode(); + var rightResult = rightObject.GetHashCode(); + + // Assert + Assert.Equal(leftResult, rightResult); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/AttributeBlockCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/AttributeBlockCodeGeneratorTest.cs new file mode 100644 index 0000000000..0dea6f0c6f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/AttributeBlockCodeGeneratorTest.cs @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Text; +using Xunit; + +namespace Microsoft.AspNet.Razor.Generator +{ + public class AttributeBlockCodeGeneratorTest + { + public static TheoryData MatchingTestDataSet + { + get + { + return new TheoryData + { + { + new AttributeBlockCodeGenerator(name: null, prefix: null, suffix: null), + new AttributeBlockCodeGenerator(name: null, prefix: null, suffix: null) + }, + { + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 0, line: 0, col: 0), + suffix: new LocationTagged(value: "George", offset: 0, line: 0, col: 0)), + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 0, line: 0, col: 0), + suffix: new LocationTagged(value: "George", offset: 0, line: 0, col: 0)) + }, + { + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + suffix: new LocationTagged(value: "George", offset: 13, line: 14, col: 15)), + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + suffix: new LocationTagged(value: "George", offset: 13, line: 14, col: 15)) + }, + }; + } + } + + public static TheoryData NonMatchingTestDataSet + { + get + { + return new TheoryData + { + { + new AttributeBlockCodeGenerator(name: null, prefix: null, suffix: null), + null + }, + { + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 0, line: 0, col: 0), + suffix: new LocationTagged(value: "George", offset: 0, line: 0, col: 0)), + null + }, + { + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + suffix: new LocationTagged(value: "George", offset: 13, line: 14, col: 15)), + null + }, + { + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + suffix: new LocationTagged(value: "George", offset: 13, line: 14, col: 15)), + new object() + }, + { + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + suffix: new LocationTagged(value: "George", offset: 13, line: 14, col: 15)), + new RazorCommentCodeGenerator() + }, + { + // Different Name. + new AttributeBlockCodeGenerator(name: "Fred", prefix: null, suffix: null), + new AttributeBlockCodeGenerator(name: "Ginger", prefix: null, suffix: null) + }, + { + // Different Name (case sensitive). + new AttributeBlockCodeGenerator(name: "fred", prefix: null, suffix: null), + new AttributeBlockCodeGenerator(name: "FRED", prefix: null, suffix: null) + }, + { + // Different Prefix. + new AttributeBlockCodeGenerator( + name: null, + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + suffix: null), + new AttributeBlockCodeGenerator( + name: null, + prefix: new LocationTagged(value: "George", offset: 10, line: 11, col: 12), + suffix: null) + }, + { + // Different Suffix. + new AttributeBlockCodeGenerator( + name: null, + prefix: null, + suffix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12)), + new AttributeBlockCodeGenerator( + name: null, + prefix: null, + suffix: new LocationTagged(value: "George", offset: 10, line: 11, col: 12)) + }, + }; + } + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void Equals_True_WhenExpected( + AttributeBlockCodeGenerator leftObject, + AttributeBlockCodeGenerator rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.True(result); + } + + [Theory] + [MemberData(nameof(NonMatchingTestDataSet))] + public void Equals_False_WhenExpected(AttributeBlockCodeGenerator leftObject, object rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.False(result); + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void GetHashCode_ReturnsSameValue_WhenEqual( + AttributeBlockCodeGenerator leftObject, + AttributeBlockCodeGenerator rightObject) + { + // Arrange & Act + var leftResult = leftObject.GetHashCode(); + var rightResult = rightObject.GetHashCode(); + + // Assert + Assert.Equal(leftResult, rightResult); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/DynamicAttributeBlockCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/DynamicAttributeBlockCodeGeneratorTest.cs new file mode 100644 index 0000000000..6f7b84e733 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/DynamicAttributeBlockCodeGeneratorTest.cs @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Text; +using Xunit; + +namespace Microsoft.AspNet.Razor.Generator +{ + public class DynamicAttributeBlockCodeGeneratorTest + { + public static TheoryData MatchingTestDataSet + { + get + { + return new TheoryData + { + { + new DynamicAttributeBlockCodeGenerator(prefix: null, offset: 0, line: 0, col: 0), + new DynamicAttributeBlockCodeGenerator(prefix: null, offset: 0, line: 0, col: 0) + }, + { + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Fred", offset: 0, line: 0, col: 0), + offset: 10, + line: 11, + col: 12), + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Fred", offset: 0, line: 0, col: 0), + offset: 10, + line: 11, + col: 12) + }, + // ValueStart not involved in equality check or hash code calculation. + { + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + offset: 10, + line: 11, + col: 12), + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + offset: 100, + line: 11, + col: 12) + }, + { + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "George", offset: 10, line: 11, col: 12), + offset: 10, + line: 11, + col: 12), + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "George", offset: 10, line: 11, col: 12), + offset: 10, + line: 110, + col: 12) + }, + { + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Dean", offset: 10, line: 11, col: 12), + offset: 10, + line: 11, + col: 12), + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Dean", offset: 10, line: 11, col: 12), + offset: 10, + line: 11, + col: 120) + }, + }; + } + } + + public static TheoryData NonMatchingTestDataSet + { + get + { + return new TheoryData + { + { + new DynamicAttributeBlockCodeGenerator(prefix: null, offset: 0, line: 0, col: 0), + null + }, + { + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Ginger", offset: 0, line: 0, col: 0), + offset: 10, + line: 11, + col: 12), + null + }, + { + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + offset: 10, + line: 11, + col: 12), + new object() + }, + { + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "George", offset: 10, line: 11, col: 12), + offset: 10, + line: 11, + col: 12), + new AttributeBlockCodeGenerator( + name: "Fred", + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + suffix: new LocationTagged(value: "George", offset: 13, line: 14, col: 15)) + }, + { + // Different Prefix. + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "Ginger", offset: 10, line: 11, col: 12), + offset: 10, + line: 11, + col: 12), + new DynamicAttributeBlockCodeGenerator( + prefix: new LocationTagged(value: "George", offset: 10, line: 11, col: 12), + offset: 10, + line: 11, + col: 12) + }, + }; + } + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void Equals_True_WhenExpected( + DynamicAttributeBlockCodeGenerator leftObject, + DynamicAttributeBlockCodeGenerator rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.True(result); + } + + [Theory] + [MemberData(nameof(NonMatchingTestDataSet))] + public void Equals_False_WhenExpected(DynamicAttributeBlockCodeGenerator leftObject, object rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.False(result); + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void GetHashCode_ReturnsSameValue_WhenEqual( + DynamicAttributeBlockCodeGenerator leftObject, + DynamicAttributeBlockCodeGenerator rightObject) + { + // Arrange & Act + var leftResult = leftObject.GetHashCode(); + var rightResult = rightObject.GetHashCode(); + + // Assert + Assert.Equal(leftResult, rightResult); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/RazorCommentCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/RazorCommentCodeGeneratorTest.cs new file mode 100644 index 0000000000..a33916d7bf --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/RazorCommentCodeGeneratorTest.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.AspNet.Razor.TagHelpers; +using Xunit; + +namespace Microsoft.AspNet.Razor.Generator +{ + // Really tests underlying BlockCodeGenerator + public class RazorCommentCodeGeneratorTest + { + public static TheoryData MatchingTestDataSet + { + get + { + return new TheoryData + { + { new RazorCommentCodeGenerator(), new RazorCommentCodeGenerator() }, + }; + } + } + + public static TheoryData NonMatchingTestDataSet + { + get + { + return new TheoryData + { + { new RazorCommentCodeGenerator(), null }, + { new RazorCommentCodeGenerator(), new object() }, + { new RazorCommentCodeGenerator(), BlockCodeGenerator.Null }, + { + new RazorCommentCodeGenerator(), + new AttributeBlockCodeGenerator(name: null, prefix: null, suffix: null) + }, + { + new RazorCommentCodeGenerator(), + new DynamicAttributeBlockCodeGenerator(prefix: null, offset: 0, line: 0, col: 0) + }, + { new RazorCommentCodeGenerator(), new ExpressionCodeGenerator() }, + { new RazorCommentCodeGenerator(), new SectionCodeGenerator(sectionName: null) }, + { + new RazorCommentCodeGenerator(), + new TagHelperCodeGenerator(Enumerable.Empty()) + }, + { new RazorCommentCodeGenerator(), new TemplateBlockCodeGenerator() }, + { + new RazorCommentCodeGenerator(), + new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 0) + }, + }; + } + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void Equals_True_WhenExpected(RazorCommentCodeGenerator leftObject, IBlockCodeGenerator rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.True(result); + } + + [Theory] + [MemberData(nameof(NonMatchingTestDataSet))] + public void Equals_False_WhenExpected(IBlockCodeGenerator leftObject, object rightObject) + { + // Arrange & Act + var result = leftObject.Equals(rightObject); + + // Assert + Assert.False(result); + } + + [Theory] + [MemberData(nameof(MatchingTestDataSet))] + public void GetHashCode_ReturnsSameValue_WhenEqual( + RazorCommentCodeGenerator leftObject, + IBlockCodeGenerator rightObject) + { + // Arrange & Act + var leftResult = leftObject.GetHashCode(); + var rightResult = rightObject.GetHashCode(); + + // Assert + Assert.Equal(leftResult, rightResult); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/TestSpan.cs b/test/Microsoft.AspNet.Razor.Test/Generator/TestSpan.cs index 4d1da1a7af..0624fb26cf 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/TestSpan.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/TestSpan.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.Internal.Web.Utils; namespace Microsoft.AspNet.Razor.Test.Generator { @@ -28,9 +28,11 @@ namespace Microsoft.AspNet.Razor.Test.Generator { } - public SpanKind Kind { get; private set; } - public int Start { get; private set; } - public int End { get; private set; } + public SpanKind Kind { get; } + + public int Start { get; } + + public int End { get; } public override string ToString() { @@ -40,20 +42,19 @@ namespace Microsoft.AspNet.Razor.Test.Generator public override bool Equals(object obj) { var other = obj as TestSpan; - - if (other != null) - { - return (Kind == other.Kind) && - (Start == other.Start) && - (End == other.End); - } - - return false; + return other != null && + Kind == other.Kind && + Start == other.Start && + End == other.End; } public override int GetHashCode() { - return base.GetHashCode(); + return HashCodeCombiner.Start() + .Add(Kind) + .Add(Start) + .Add(End) + .CombinedHash; } } } diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs index 778e99b40b..4ae101c5fa 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs @@ -20,7 +20,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\"\"").AsTagHelperPrefixDirective(""))); + Factory.Code("\"\"") + .AsTagHelperPrefixDirective(string.Empty))); } [Fact] @@ -32,7 +33,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\"Foo\"").AsTagHelperPrefixDirective("Foo"))); + Factory.Code("\"Foo\"") + .AsTagHelperPrefixDirective("Foo"))); } [Fact] @@ -44,7 +46,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsTagHelperPrefixDirective(string.Empty)), + Factory.EmptyCSharp() + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.FormatParseError_DirectiveMustHaveValue( SyntaxConstants.CSharp.TagHelperPrefixKeyword), @@ -60,12 +64,13 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\"Foo").AsTagHelperPrefixDirective("Foo")), - new RazorError( - RazorResources.ParseError_Unterminated_String_Literal, - absoluteIndex: 17, lineIndex: 0, columnIndex: 17), - new RazorError( - RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( + Factory.Code("\"Foo") + .AsTagHelperPrefixDirective("Foo")), + new RazorError( + RazorResources.ParseError_Unterminated_String_Literal, + absoluteIndex: 17, lineIndex: 0, columnIndex: 17), + new RazorError( + RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( SyntaxConstants.CSharp.TagHelperPrefixKeyword), absoluteIndex: 17, lineIndex: 0, columnIndex: 17)); } @@ -79,7 +84,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("Foo\"").AsTagHelperPrefixDirective("Foo")), + Factory.Code("Foo\"") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.ParseError_Unterminated_String_Literal, absoluteIndex: 20, lineIndex: 0, columnIndex: 20), @@ -98,7 +105,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("Foo").AsTagHelperPrefixDirective("Foo")), + Factory.Code("Foo") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( SyntaxConstants.CSharp.TagHelperPrefixKeyword), @@ -113,7 +122,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\"Foo\"").AsRemoveTagHelper("Foo"))); + Factory.Code("\"Foo\"") + .AsRemoveTagHelper("Foo"))); } [Fact] @@ -124,7 +134,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\" Foo, Bar \" ").AsRemoveTagHelper(" Foo, Bar "))); + Factory.Code("\" Foo, Bar \" ") + .AsRemoveTagHelper("Foo, Bar") + .Accepts(AcceptedCharacters.AnyExceptNewline))); } [Fact] @@ -135,7 +147,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsRemoveTagHelper(string.Empty)), + Factory.EmptyCSharp() + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.FormatParseError_DirectiveMustHaveValue( SyntaxConstants.CSharp.RemoveTagHelperKeyword), @@ -150,7 +164,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\"Foo").AsRemoveTagHelper("Foo")), + Factory.Code("\"Foo") + .AsRemoveTagHelper("Foo")), new RazorError( RazorResources.ParseError_Unterminated_String_Literal, absoluteIndex: 17, lineIndex: 0, columnIndex: 17), @@ -168,7 +183,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("Foo\"").AsRemoveTagHelper("Foo")), + Factory.Code("Foo\"") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.ParseError_Unterminated_String_Literal, absoluteIndex: 20, lineIndex: 0, columnIndex: 20), @@ -186,7 +203,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("Foo").AsRemoveTagHelper("Foo")), + Factory.Code("Foo") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( SyntaxConstants.CSharp.RemoveTagHelperKeyword), @@ -201,7 +220,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\"Foo\"").AsAddTagHelper("Foo"))); + Factory.Code("\"Foo\"") + .AsAddTagHelper("Foo"))); } [Fact] @@ -212,7 +232,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\" Foo, Bar \" ").AsAddTagHelper(" Foo, Bar "))); + Factory.Code("\" Foo, Bar \" ") + .AsAddTagHelper("Foo, Bar"))); } [Fact] @@ -223,7 +244,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsAddTagHelper(string.Empty)), + Factory.EmptyCSharp() + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.FormatParseError_DirectiveMustHaveValue(SyntaxConstants.CSharp.AddTagHelperKeyword), absoluteIndex: 14, lineIndex: 0, columnIndex: 14)); @@ -237,7 +260,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("\"Foo").AsAddTagHelper("Foo")), + Factory.Code("\"Foo") + .AsAddTagHelper("Foo")), new RazorError( RazorResources.ParseError_Unterminated_String_Literal, absoluteIndex: 14, lineIndex: 0, columnIndex: 14), @@ -255,7 +279,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("Foo\"").AsAddTagHelper("Foo")), + Factory.Code("Foo\"") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.ParseError_Unterminated_String_Literal, absoluteIndex: 17, lineIndex: 0, columnIndex: 17), @@ -273,7 +299,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") .Accepts(AcceptedCharacters.None), - Factory.Code("Foo").AsAddTagHelper("Foo")), + Factory.Code("Foo") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline)), new RazorError( RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( SyntaxConstants.CSharp.AddTagHelperKeyword), @@ -349,7 +377,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {") .Accepts(AcceptedCharacters.None), Factory.Code(" foo(); bar(); ") - .AsFunctionsBody(), + .AsFunctionsBody() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}") .Accepts(AcceptedCharacters.None))); } @@ -363,7 +392,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {") .Accepts(AcceptedCharacters.None), Factory.Code(" ") - .AsFunctionsBody(), + .AsFunctionsBody() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}") .Accepts(AcceptedCharacters.None))); } diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpErrorTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpErrorTest.cs index ab87c584a7..3f2f3e0016 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpErrorTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpErrorTest.cs @@ -57,7 +57,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsStatement(), + Factory.EmptyCSharp() + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) )); } @@ -82,7 +84,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine + " ").AsStatement(), + Factory.Code(Environment.NewLine + " ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new ExpressionBlock( Factory.CodeTransition(), Factory.EmptyCSharp() @@ -101,7 +105,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + " @", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine + " ").AsStatement(), + Factory.Code(Environment.NewLine + " ") + .AsStatement() + .AutoCompleteWith("}"), new ExpressionBlock( Factory.CodeTransition(), Factory.EmptyCSharp() @@ -247,8 +253,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ var foo = bar; if(foo != null) { bar(); } ", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" var foo = bar; if(foo != null) { bar(); } ").AsStatement() - ), + Factory.Code(" var foo = bar; if(foo != null) { bar(); } ") + .AsStatement() + .AutoCompleteWith("}")), new RazorError( RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(RazorResources.BlockName_Code, '}', '{'), SourceLocation.Zero)); @@ -260,8 +267,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("functions { var foo = bar; if(foo != null) { bar(); } ", new FunctionsBlock( Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), - Factory.Code(" var foo = bar; if(foo != null) { bar(); } ").AsFunctionsBody() - ), + Factory.Code(" var foo = bar; if(foo != null) { bar(); } ") + .AsFunctionsBody() + .AutoCompleteWith("}")), new RazorError( RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", '}', '{'), SourceLocation.Zero)); @@ -592,7 +600,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.Code("string.Format(") - .AsStatement(), + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( BlockFactory.MarkupTagBlock("", AcceptedCharacters.None), BlockFactory.MarkupTagBlock("", AcceptedCharacters.None)), diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpRazorCommentsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpRazorCommentsTest.cs index b3055f8d41..766a90da5d 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpRazorCommentsTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpRazorCommentsTest.cs @@ -122,7 +122,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp new StatementBlock( Factory.CodeTransition(), Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine).AsStatement(), + Factory.Code(Environment.NewLine) + .AsStatement() + .AutoCompleteWith("}"), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -158,7 +160,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.CodeTransition(), Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.EmptyCSharp() - .AsStatement(), + .AsStatement() + .AutoCompleteWith("}"), new CommentBlock( Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) .Accepts(AcceptedCharacters.None), diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSpecialBlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSpecialBlockTest.cs index 5db86fad0b..f5843a74c4 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSpecialBlockTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSpecialBlockTest.cs @@ -53,7 +53,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ using Foo.Bar.Baz; var foo = bar; }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" using Foo.Bar.Baz; var foo = bar; ").AsStatement(), + Factory.Code(" using Foo.Bar.Baz; var foo = bar; ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) ), new RazorError( @@ -67,7 +69,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ using Foo = Bar.Baz; var foo = bar; }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" using Foo = Bar.Baz; var foo = bar; ").AsStatement(), + Factory.Code(" using Foo = Bar.Baz; var foo = bar; ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) ), new RazorError( @@ -81,7 +85,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ functions Foo; }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" functions Foo; ").AsStatement(), + Factory.Code(" functions Foo; ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) )); } @@ -94,7 +100,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code($"{Environment.NewLine} List photos = gallery.Photo.ToList();{Environment.NewLine}").AsStatement(), + Factory.Code($"{Environment.NewLine} List photos = gallery.Photo.ToList();{Environment.NewLine}") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) )); } @@ -109,7 +117,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{" + code + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(code).AsStatement(), + Factory.Code(code) + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) )); } @@ -136,7 +146,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("functions {" + code + "} zoop", new FunctionsBlock( Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), - Factory.Code(code).AsFunctionsBody(), + Factory.Code(code) + .AsFunctionsBody() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) )); } @@ -147,7 +159,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("functions { { { { { } zoop", new FunctionsBlock( Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), - Factory.Code(" { { { { } zoop").AsFunctionsBody() + Factory.Code(" { { { { } zoop") + .AsFunctionsBody() + .AutoCompleteWith("}") ), new RazorError( RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", "}", "{"), diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpTemplateTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpTemplateTest.cs index eb225b464f..dbd8039cf3 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpTemplateTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpTemplateTest.cs @@ -82,7 +82,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "; }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" var foo = ").AsStatement(), + Factory.Code(" var foo = ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new TemplateBlock( new MarkupBlock( Factory.MarkupTransition(), @@ -104,7 +106,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code("i").AsStatement(), + Factory.Code("i") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new TemplateBlock( new MarkupBlock( Factory.MarkupTransition(), @@ -210,7 +214,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp { ParseBlockTest("foreach(foo in Bar) { Html.ExecuteTemplate(foo," + TestNestedTemplateCode + "); }", new StatementBlock( - Factory.Code("foreach(foo in Bar) { Html.ExecuteTemplate(foo, ").AsStatement(), + Factory.Code("foreach(foo in Bar) { Html.ExecuteTemplate(foo, ") + .AsStatement(), TestNestedTemplate(), Factory.Code("); }") .AsStatement() @@ -225,7 +230,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestTemplateCode + "); }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), + Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), TestTemplate(), Factory.Code("); ").AsStatement(), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) @@ -238,7 +245,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestTemplateCode + "," + TestTemplateCode + "); }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), + Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), TestTemplate(), Factory.Code(", ").AsStatement(), TestTemplate(), @@ -253,7 +262,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestNestedTemplateCode + "); }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), + Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), TestNestedTemplate(), Factory.Code("); ").AsStatement(), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpToMarkupSwitchTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpToMarkupSwitchTest.cs index 49d00401a6..607b1028a9 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpToMarkupSwitchTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpToMarkupSwitchTest.cs @@ -19,7 +19,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ List< }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" List< ").AsStatement(), + Factory.Code(" List< ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); } @@ -77,7 +79,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine + " ").AsStatement(), + Factory.Code(Environment.NewLine + " ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( new MarkupTagBlock( Factory.Markup("

").Accepts(AcceptedCharacters.None)), @@ -98,7 +102,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine + " ").AsStatement(), + Factory.Code(Environment.NewLine + " ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( Factory.MarkupTransition(), new MarkupTagBlock( @@ -122,7 +128,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine + " ").AsStatement(), + Factory.Code(Environment.NewLine + " ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( Factory.MarkupTransition(), Factory.MetaMarkup(":", HtmlSymbolType.Colon), @@ -152,13 +160,15 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp new StatementBlock( Factory.CodeTransition(), Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsStatement(), + Factory.EmptyCSharp() + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}").Accepts(AcceptedCharacters.None) ), Factory.Markup(Environment.NewLine) - .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) - ) - ), + .Accepts(AcceptedCharacters.None) + ) + ), Factory.Code(")") .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) .Accepts(AcceptedCharacters.NonWhiteSpace) @@ -291,7 +301,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ if(foo) {

Bar

} else if(bar) {

Baz

} else {

Boz

} }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" if(foo) {").AsStatement(), + Factory.Code(" if(foo) {") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -421,7 +433,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "} }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code($" switch(foo) {{{Environment.NewLine} case 0:{Environment.NewLine}").AsStatement(), + Factory.Code($" switch(foo) {{{Environment.NewLine} case 0:{Environment.NewLine}") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -501,7 +515,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ for(int i = 0; i < 10; i++) {

Foo

} }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" for(int i = 0; i < 10; i++) {").AsStatement(), + Factory.Code(" for(int i = 0; i < 10; i++) {") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -541,12 +557,15 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "} } zoop", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" if(foo) {").AsStatement(), + Factory.Code(" if(foo) {") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( Factory.Markup(" "), Factory.MarkupTransition(), Factory.MetaMarkup(":", HtmlSymbolType.Colon), Factory.Markup("Bar" + Environment.NewLine).Accepts(AcceptedCharacters.None) + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) ), Factory.Code("} ").AsStatement(), Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); @@ -576,7 +595,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{ if (i > 0) { ; } }", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(" if (i > 0) {").AsStatement(), + Factory.Code(" if (i > 0) {") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -606,7 +627,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code($"{Environment.NewLine} if(true) {{{Environment.NewLine}").AsStatement(), + Factory.Code($"{Environment.NewLine} if(true) {{{Environment.NewLine}") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new MarkupBlock( Factory.Markup(" "), Factory.MarkupTransition(), diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpVerbatimBlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpVerbatimBlockTest.cs index d6b498392e..037fc5821f 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpVerbatimBlockTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpVerbatimBlockTest.cs @@ -21,7 +21,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.MetaCode("{") .Accepts(AcceptedCharacters.None), Factory.Code(" foo(); ") - .AsStatement(), + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), Factory.MetaCode("}") .Accepts(AcceptedCharacters.None) )); @@ -33,7 +34,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{@}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsStatement(), + Factory.EmptyCSharp() + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new ExpressionBlock( Factory.CodeTransition(), Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) @@ -53,7 +56,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp ParseBlockTest("{@.}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsStatement(), + Factory.EmptyCSharp() + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new ExpressionBlock( Factory.CodeTransition(), Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) @@ -75,7 +80,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine + " ").AsStatement(), + Factory.Code(Environment.NewLine + " ") + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new ExpressionBlock( Factory.CodeTransition(), Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) @@ -94,7 +101,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsStatement(), + Factory.EmptyCSharp() + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new ExpressionBlock( Factory.CodeTransition(), Factory.Code("foo.").AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)), @@ -109,7 +118,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp + "}", new StatementBlock( Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp().AsStatement(), + Factory.EmptyCSharp() + .AsStatement() + .AutoCompleteWith(autoCompleteString: null), new ExpressionBlock( Factory.CodeTransition(), Factory.Code("foo.").AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)), diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpWhitespaceHandlingTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpWhitespaceHandlingTest.cs index 788e4f6450..24bc066392 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpWhitespaceHandlingTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpWhitespaceHandlingTest.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Test.Framework; using Microsoft.AspNet.Razor.Tokenizer.Symbols; @@ -20,7 +22,10 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp Factory.MarkupTransition() .Accepts(AcceptedCharacters.None), Factory.MetaMarkup(":", HtmlSymbolType.Colon), - Factory.Markup(" "), + Factory.Markup(" ") + .With(new SingleLineMarkupEditHandler( + CSharpLanguageCharacteristics.Instance.TokenizeString, + AcceptedCharacters.Any)), new StatementBlock( Factory.CodeTransition() .Accepts(AcceptedCharacters.None), diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlBlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlBlockTest.cs index 7866b1c4db..f3a409d977 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlBlockTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlBlockTest.cs @@ -36,7 +36,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html new StatementBlock( Factory.CodeTransition(), Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine).AsStatement(), + Factory.Code(Environment.NewLine) + .AsStatement() + .AutoCompleteWith("}"), new MarkupBlock( new MarkupTagBlock( Factory.Markup("<"))))), @@ -56,7 +58,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html new StatementBlock( Factory.CodeTransition(), Factory.MetaCode("{").Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine).AsStatement(), + Factory.Code(Environment.NewLine) + .AsStatement() + .AutoCompleteWith("}"), new MarkupBlock( new MarkupTagBlock( Factory.Markup("<" + Environment.NewLine)) diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs index 355c6484ce..d45ef68da6 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs @@ -257,7 +257,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html Factory.EmptyHtml(), new SectionBlock(new SectionCodeGenerator("Foo"), Factory.CodeTransition(), - Factory.MetaCode("section Foo {"), + Factory.MetaCode("section Foo {") + .AutoCompleteWith(autoCompleteString: null, atEndOfSpan: true), new MarkupBlock( Factory.Markup(" "), BlockFactory.MarkupTagBlock("