From bc7241d87a77f8d1ad4e4620a677189ad4781832 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 17 Sep 2014 00:20:43 -0700 Subject: [PATCH] Changes to show intellisense for @inject directive --- .../InjectChunkVisitor.cs | 8 +-- .../MvcRazorCodeParser.cs | 3 +- .../InjectChunkVisitorTest.cs | 58 +++++++++++++---- .../MvcCSharpRazorCodeParserTest.cs | 65 ++++++++++++++++++- 4 files changed, 117 insertions(+), 17 deletions(-) diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/InjectChunkVisitor.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/InjectChunkVisitor.cs index 0136b2a873..95116832cd 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/InjectChunkVisitor.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/InjectChunkVisitor.cs @@ -38,10 +38,10 @@ protected override void Visit([NotNull] InjectChunk chunk) if (Context.Host.DesignTimeMode && chunk.Association != null) { Writer.WriteLine("public"); - var code = string.Format(CultureInfo.InvariantCulture, - "{0} {1}", - chunk.TypeName, - chunk.MemberName); + + var code = string.IsNullOrEmpty(chunk.MemberName) ? + chunk.TypeName : + chunk.TypeName + ' ' + chunk.MemberName; var csharpVisitor = new CSharpCodeVisitor(Writer, Context); csharpVisitor.CreateExpressionCodeMapping(code, chunk); Writer.WriteLine("{ get; private set; }"); diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorCodeParser.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorCodeParser.cs index 6601e4920c..ff40c3b42f 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorCodeParser.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorCodeParser.cs @@ -107,7 +107,7 @@ protected virtual void InjectDirective() var propertyStartLocation = CurrentLocation; AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); - if (!hasTypeError && At(CSharpSymbolType.NewLine)) + if (!hasTypeError && (EndOfFile || At(CSharpSymbolType.NewLine))) { // Add an error for the property name only if we successfully read the type name Context.OnError(propertyStartLocation, @@ -131,6 +131,7 @@ protected virtual void InjectDirective() propertyName.Trim()); // Output the span and finish the block + CompleteBlock(); Output(SpanKind.Code); } diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/InjectChunkVisitorTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/InjectChunkVisitorTest.cs index fa6bd07212..85705b0fff 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/InjectChunkVisitorTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/InjectChunkVisitorTest.cs @@ -1,6 +1,7 @@ // 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.IO; using Microsoft.AspNet.Razor; @@ -71,18 +72,54 @@ public void Visit_GeneratesProperties_ForInjectChunks() public void Visit_WithDesignTimeHost_GeneratesPropertiesAndLinePragmas_ForInjectChunks() { // Arrange - var expected = @"[Microsoft.AspNet.Mvc.ActivateAttribute] -public -#line 1 """" -MyType1 MyPropertyName1 + var expected = string.Join(Environment.NewLine, +@"[Microsoft.AspNet.Mvc.ActivateAttribute]", +@"public", +@"#line 1 """"", +@"MyType1 MyPropertyName1", +"", +@"#line default", +@"#line hidden", +@"{ get; private set; }", +@"[Microsoft.AspNet.Mvc.ActivateAttribute]", +@"public", +@"#line 1 """"", +@"MyType2 @MyPropertyName2", +"", +@"#line default", +@"#line hidden", +@"{ get; private set; }", +""); + var writer = new CSharpCodeWriter(); + var context = CreateContext(); + context.Host.DesignTimeMode = true; -#line default -#line hidden -{ get; private set; } -[Microsoft.AspNet.Mvc.ActivateAttribute] + var visitor = new InjectChunkVisitor(writer, context, "Microsoft.AspNet.Mvc.ActivateAttribute"); + var factory = SpanFactory.CreateCsHtml(); + var node = (Span)factory.Code("Some code") + .As(new InjectParameterGenerator("MyType", "MyPropertyName")); + + // Act + visitor.Accept(new Chunk[] + { + new LiteralChunk(), + new InjectChunk("MyType1", "MyPropertyName1") { Association = node }, + new InjectChunk("MyType2", "@MyPropertyName2") { Association = node } + }); + var code = writer.GenerateCode(); + + // Assert + Assert.Equal(expected, code); + } + + [Fact] + public void Visit_WithDesignTimeHost_GeneratesPropertiesAndLinePragmas_ForPartialInjectChunks() + { + // Arrange + var expected = @"[Microsoft.AspNet.Mvc.ActivateAttribute] public #line 1 """" -MyType2 @MyPropertyName2 +MyType1 #line default #line hidden @@ -101,8 +138,7 @@ MyType2 @MyPropertyName2 visitor.Accept(new Chunk[] { new LiteralChunk(), - new InjectChunk("MyType1", "MyPropertyName1") { Association = node }, - new InjectChunk("MyType2", "@MyPropertyName2") { Association = node } + new InjectChunk("MyType1", string.Empty) { Association = node }, }); var code = writer.GenerateCode(); diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcCSharpRazorCodeParserTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcCSharpRazorCodeParserTest.cs index 9c1b903fce..474c55f329 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcCSharpRazorCodeParserTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcCSharpRazorCodeParserTest.cs @@ -309,7 +309,7 @@ public void ParseInjectKeyword_ErrorOnMissingTypeName() { // Arrange var errors = new List(); - var documentContent = "@inject \r\nBar"; + var documentContent = "@inject " + Environment.NewLine + "Bar"; var factory = SpanFactory.CreateCsHtml(); var expectedSpans = new Span[] { @@ -337,6 +337,37 @@ public void ParseInjectKeyword_ErrorOnMissingTypeName() Assert.Equal(expectedErrors, errors); } + [Fact] + public void ParseInjectKeyword_ErrorOnMissingTypeName_WhenTypeNameEndsWithEOF() + { + // Arrange + var errors = new List(); + var documentContent = "@inject "; + var factory = SpanFactory.CreateCsHtml(); + var expectedSpans = new Span[] + { + factory.EmptyHtml(), + factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + factory.MetaCode("inject ") + .Accepts(AcceptedCharacters.None), + factory.Code(" ") + .As(new InjectParameterGenerator(string.Empty, string.Empty)), + }; + var expectedErrors = new[] + { + new RazorError("The 'inject' keyword must be followed by a type name on the same line.", + new SourceLocation(11, 0, 11), 1) + }; + + // Act + var spans = ParseDocument(documentContent, errors); + + // Assert + Assert.Equal(expectedSpans, spans); + Assert.Equal(expectedErrors, errors); + } + [Fact] public void ParseInjectKeyword_ErrorOnMissingPropertyName() { @@ -371,6 +402,38 @@ public void ParseInjectKeyword_ErrorOnMissingPropertyName() Assert.Equal(expectedErrors, errors); } + [Fact] + public void ParseInjectKeyword_ErrorOnMissingPropertyName_WhenTypeNameEndsWithEOF() + { + // Arrange + var errors = new List(); + var documentContent = "@inject IMyServi"; + var factory = SpanFactory.CreateCsHtml(); + var expectedSpans = new Span[] + { + factory.EmptyHtml(), + factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + factory.MetaCode("inject ") + .Accepts(AcceptedCharacters.None), + factory.Code(" IMyServi") + .As(new InjectParameterGenerator("IMyServi", string.Empty)), + }; + var expectedErrors = new[] + { + new RazorError("A property name must be specified when using the 'inject' statement. " + + "Format for a 'inject' statement is '@inject '.", + new SourceLocation(19, 0, 19), 1) + }; + + // Act + var spans = ParseDocument(documentContent, errors); + + // Assert + Assert.Equal(expectedSpans, spans); + Assert.Equal(expectedErrors, errors); + } + private static List ParseDocument(string documentContents, List errors = null, List lineMappings = null)