Enable using directive IntelliSense auto-completion.

- Allow identical `@using` directives in the primary document. This means that if a user types `@using System` twice in the primary document (i.e. `Index.cshtml`) then we'll generate that using statement twice; this enables proper C# IntelliSense to light up on both using statements and allows auto-completion to work because there's no more magical distinction logic for Razor code that is in the current document.
- Prior to this change if you had two identical using statements in the same document one would be in no-mans land mapped to nothing (not C#, not HTML) and the other would be mapped to C#; the second a user differentiated the two statements (i.e. adding a `.`) we'd distinctify the using statements resulting in an invalid completion.
- This PR has an end-user impact where they will now receive the normal C# warning about having duplicate using. I treated this prior behavior more as a bug because we threw away the first using directive instance which impacted editing and in general was a silly thing to correct for the user.
- Added new integration test showing how using directives are not de-duplicated in the primary document.

#1255
This commit is contained in:
N. Taylor Mullen 2018-06-13 11:55:10 -07:00
parent af63afdae7
commit d6f3a1bd22
9 changed files with 321 additions and 29 deletions

View File

@ -34,13 +34,13 @@ namespace Microsoft.AspNetCore.Razor.Language
document.Options = codeDocument.GetCodeGenerationOptions() ?? _optionsFeature.GetOptions();
var namespaces = new Dictionary<string, SourceSpan?>(StringComparer.Ordinal);
IReadOnlyList<UsingReference> importedUsings = Array.Empty<UsingReference>();
// The import documents should be inserted logically before the main document.
var imports = codeDocument.GetImportSyntaxTrees();
if (imports != null)
{
var importsVisitor = new ImportsVisitor(document, builder, namespaces, syntaxTree.Options.FeatureFlags);
var importsVisitor = new ImportsVisitor(document, builder, syntaxTree.Options.FeatureFlags);
for (var j = 0; j < imports.Count; j++)
{
@ -49,26 +49,40 @@ namespace Microsoft.AspNetCore.Razor.Language
importsVisitor.FilePath = import.Source.FilePath;
importsVisitor.VisitBlock(import.Root);
}
importedUsings = importsVisitor.Usings;
}
var tagHelperPrefix = tagHelperContext?.Prefix;
var visitor = new MainSourceVisitor(document, builder, namespaces, tagHelperPrefix, syntaxTree.Options.FeatureFlags)
var visitor = new MainSourceVisitor(document, builder, tagHelperPrefix, syntaxTree.Options.FeatureFlags)
{
FilePath = syntaxTree.Source.FilePath,
};
visitor.VisitBlock(syntaxTree.Root);
// 1. Prioritize non-imported usings over imported ones.
// 2. Don't import usings that already exist in primary document.
// 3. Allow duplicate usings in primary document (C# warning).
var usingReferences = new List<UsingReference>(visitor.Usings);
for (var j = importedUsings.Count - 1; j >= 0; j--)
{
if (!usingReferences.Contains(importedUsings[j]))
{
usingReferences.Insert(0, importedUsings[j]);
}
}
// In each lowering piece above, namespaces were tracked. We render them here to ensure every
// lowering action has a chance to add a source location to a namespace. Ultimately, closest wins.
var i = 0;
foreach (var @namespace in namespaces)
foreach (var reference in usingReferences)
{
var @using = new UsingDirectiveIntermediateNode()
{
Content = @namespace.Key,
Source = @namespace.Value,
Content = reference.Namespace,
Source = reference.Source,
};
builder.Insert(i++, @using);
@ -147,21 +161,51 @@ namespace Microsoft.AspNetCore.Razor.Language
}
}
private struct UsingReference : IEquatable<UsingReference>
{
public UsingReference(string @namespace, SourceSpan? source)
{
Namespace = @namespace;
Source = source;
}
public string Namespace { get; }
public SourceSpan? Source { get; }
public override bool Equals(object other)
{
if (other is UsingReference reference)
{
return Equals(reference);
}
return false;
}
public bool Equals(UsingReference other)
{
return string.Equals(Namespace, other.Namespace, StringComparison.Ordinal);
}
public override int GetHashCode() => Namespace.GetHashCode();
}
private class LoweringVisitor : ParserVisitor
{
protected readonly IntermediateNodeBuilder _builder;
protected readonly DocumentIntermediateNode _document;
protected readonly Dictionary<string, SourceSpan?> _namespaces;
protected readonly List<UsingReference> _usings;
protected readonly RazorParserFeatureFlags _featureFlags;
public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces, RazorParserFeatureFlags featureFlags)
public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, RazorParserFeatureFlags featureFlags)
{
_document = document;
_builder = builder;
_namespaces = namespaces;
_usings = new List<UsingReference>();
_featureFlags = featureFlags;
}
public IReadOnlyList<UsingReference> Usings => _usings;
public string FilePath { get; set; }
public override void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
@ -212,7 +256,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{
var namespaceImport = chunkGenerator.Namespace.Trim();
var namespaceSpan = BuildSourceSpanFromNode(span);
_namespaces[namespaceImport] = namespaceSpan;
_usings.Add(new UsingReference(namespaceImport, namespaceSpan));
}
public override void VisitAddTagHelperSpan(AddTagHelperChunkGenerator chunkGenerator, Span span)
@ -353,8 +397,8 @@ namespace Microsoft.AspNetCore.Razor.Language
{
private readonly string _tagHelperPrefix;
public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces, string tagHelperPrefix, RazorParserFeatureFlags featureFlags)
: base(document, builder, namespaces, featureFlags)
public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, string tagHelperPrefix, RazorParserFeatureFlags featureFlags)
: base(document, builder, featureFlags)
{
_tagHelperPrefix = tagHelperPrefix;
}
@ -731,8 +775,8 @@ namespace Microsoft.AspNetCore.Razor.Language
private class ImportsVisitor : LoweringVisitor
{
public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces, RazorParserFeatureFlags featureFlags)
: base(document, new ImportBuilder(builder), namespaces, featureFlags)
public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, RazorParserFeatureFlags featureFlags)
: base(document, new ImportBuilder(builder), featureFlags)
{
}

View File

@ -23,6 +23,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.IntegrationTests
private CSharpCompilation BaseCompilation => MvcShim.BaseCompilation.WithAssemblyName("AppCode");
#region Runtime
[Fact]
public void UsingDirectives_Runtime()
{
var compilation = BaseCompilation;
RunRuntimeTest(compilation, new[] { "The using directive for 'System' appeared previously in this namespace" });
}
[Fact]
public void InvalidNamespaceAtEOF_Runtime()
{
@ -301,6 +310,15 @@ public class AllTagHelper : {typeof(TagHelper).FullName}
#endregion
#region DesignTime
[Fact]
public void UsingDirectives_DesignTime()
{
var compilation = BaseCompilation;
RunDesignTimeTest(compilation, new[] { "The using directive for 'System' appeared previously in this namespace" });
}
[Fact]
public void InvalidNamespaceAtEOF_DesignTime()
{
@ -597,7 +615,7 @@ public class AllTagHelper : {typeof(TagHelper).FullName}
private void RunRuntimeTest(
CSharpCompilation baseCompilation,
IEnumerable<string> expectedErrors = null)
IEnumerable<string> expectedWarnings = null)
{
Assert.Empty(baseCompilation.GetDiagnostics());
@ -611,12 +629,12 @@ public class AllTagHelper : {typeof(TagHelper).FullName}
// Assert
AssertDocumentNodeMatchesBaseline(document.GetDocumentIntermediateNode());
AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument());
AssertDocumentCompiles(document, baseCompilation, expectedErrors);
AssertDocumentCompiles(document, baseCompilation, expectedWarnings);
}
private void RunDesignTimeTest(
CSharpCompilation baseCompilation,
IEnumerable<string> expectedErrors = null)
IEnumerable<string> expectedWarnings = null)
{
Assert.Empty(baseCompilation.GetDiagnostics());
@ -631,13 +649,13 @@ public class AllTagHelper : {typeof(TagHelper).FullName}
AssertDocumentNodeMatchesBaseline(document.GetDocumentIntermediateNode());
AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument());
AssertSourceMappingsMatchBaseline(document);
AssertDocumentCompiles(document, baseCompilation, expectedErrors);
AssertDocumentCompiles(document, baseCompilation, expectedWarnings);
}
private void AssertDocumentCompiles(
RazorCodeDocument document,
CSharpCompilation baseCompilation,
IEnumerable<string> expectedErrors = null)
IEnumerable<string> expectedWarnings = null)
{
var cSharp = document.GetCSharpDocument().GeneratedCode;
@ -649,15 +667,15 @@ public class AllTagHelper : {typeof(TagHelper).FullName}
var diagnostics = compilation.GetDiagnostics();
var errors = diagnostics.Where(d => d.Severity >= DiagnosticSeverity.Warning);
var warnings = diagnostics.Where(d => d.Severity >= DiagnosticSeverity.Warning);
if (expectedErrors == null)
if (expectedWarnings == null)
{
Assert.Empty(errors.Select(e => e.GetMessage()));
Assert.Empty(warnings);
}
else
{
Assert.Equal(expectedErrors, errors.Select(e => e.GetMessage()));
Assert.Equal(expectedWarnings, warnings.Select(e => e.GetMessage()));
}
}

View File

@ -0,0 +1,4 @@
@using System.ComponentModel
@using System.Collections
@using System
@using System

View File

@ -0,0 +1,59 @@
// <auto-generated/>
#pragma warning disable 1591
namespace AspNetCore
{
#line hidden
using TModel = global::System.Object;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
#line 1 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml"
using System.ComponentModel;
#line default
#line hidden
#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml"
using System.Collections;
#line default
#line hidden
#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml"
using System;
#line default
#line hidden
#line 4 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml"
using System;
#line default
#line hidden
public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_UsingDirectives : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
{
#pragma warning disable 219
private void __RazorDirectiveTokenHelpers__() {
}
#pragma warning restore 219
#pragma warning disable 0414
private static System.Object __o = null;
#pragma warning restore 0414
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
}
#pragma warning restore 1998
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<dynamic> Html { get; private set; }
}
}
#pragma warning restore 1591

View File

@ -0,0 +1,46 @@
Document -
NamespaceDeclaration - - AspNetCore
UsingDirective - - TModel = global::System.Object
UsingDirective - (16:1,1 [32] ) - System.Collections.Generic
UsingDirective - (51:2,1 [17] ) - System.Linq
UsingDirective - (71:3,1 [28] ) - System.Threading.Tasks
UsingDirective - (102:4,1 [30] ) - Microsoft.AspNetCore.Mvc
UsingDirective - (135:5,1 [40] ) - Microsoft.AspNetCore.Mvc.Rendering
UsingDirective - (178:6,1 [43] ) - Microsoft.AspNetCore.Mvc.ViewFeatures
UsingDirective - (1:0,1 [27] UsingDirectives.cshtml) - System.ComponentModel
UsingDirective - (31:1,1 [24] UsingDirectives.cshtml) - System.Collections
UsingDirective - (58:2,1 [12] UsingDirectives.cshtml) - System
UsingDirective - (73:3,1 [12] UsingDirectives.cshtml) - System
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_UsingDirectives - global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic> -
DesignTimeDirective -
DirectiveToken - (231:7,8 [62] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<TModel>
DirectiveToken - (294:7,71 [4] ) - Html
DirectiveToken - (308:8,8 [54] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper
DirectiveToken - (363:8,63 [4] ) - Json
DirectiveToken - (377:9,8 [53] ) - global::Microsoft.AspNetCore.Mvc.IViewComponentHelper
DirectiveToken - (431:9,62 [9] ) - Component
DirectiveToken - (450:10,8 [43] ) - global::Microsoft.AspNetCore.Mvc.IUrlHelper
DirectiveToken - (494:10,52 [3] ) - Url
DirectiveToken - (507:11,8 [70] ) - global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider
DirectiveToken - (578:11,79 [23] ) - ModelExpressionProvider
DirectiveToken - (617:12,14 [96] ) - Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor
DirectiveToken - (729:13,14 [87] ) - Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor
DirectiveToken - (832:14,14 [87] ) - Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor
CSharpCode -
IntermediateToken - - CSharp - #pragma warning disable 0414
CSharpCode -
IntermediateToken - - CSharp - private static System.Object __o = null;
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - public async override - global::System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (28:0,28 [2] UsingDirectives.cshtml)
IntermediateToken - (28:0,28 [2] UsingDirectives.cshtml) - Html - \n
HtmlContent - (55:1,25 [2] UsingDirectives.cshtml)
IntermediateToken - (55:1,25 [2] UsingDirectives.cshtml) - Html - \n
HtmlContent - (70:2,13 [2] UsingDirectives.cshtml)
IntermediateToken - (70:2,13 [2] UsingDirectives.cshtml) - Html - \n
Inject -
Inject -
Inject -
Inject -
Inject -

View File

@ -0,0 +1,20 @@
Source Location: (1:0,1 [27] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml)
|using System.ComponentModel|
Generated Location: (461:13,0 [27] )
|using System.ComponentModel|
Source Location: (31:1,1 [24] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml)
|using System.Collections|
Generated Location: (613:18,0 [24] )
|using System.Collections|
Source Location: (58:2,1 [12] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml)
|using System|
Generated Location: (762:23,0 [12] )
|using System|
Source Location: (73:3,1 [12] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml)
|using System|
Generated Location: (899:28,0 [12] )
|using System|

View File

@ -0,0 +1,55 @@
#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "452979e8e4dd77a84a4c50425dd3a162e265990d"
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_UsingDirectives), @"mvc.1.0.view", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml")]
[assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(@"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml", typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_UsingDirectives))]
namespace AspNetCore
{
#line hidden
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
#line 1 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml"
using System.ComponentModel;
#line default
#line hidden
#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml"
using System.Collections;
#line default
#line hidden
#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml"
using System;
#line default
#line hidden
#line 4 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml"
using System;
#line default
#line hidden
[global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"452979e8e4dd77a84a4c50425dd3a162e265990d", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml")]
public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_UsingDirectives : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
}
#pragma warning restore 1998
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<dynamic> Html { get; private set; }
}
}
#pragma warning restore 1591

View File

@ -0,0 +1,23 @@
Document -
RazorCompiledItemAttribute -
CSharpCode -
IntermediateToken - - CSharp - [assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(@"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/UsingDirectives.cshtml", typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_UsingDirectives))]
NamespaceDeclaration - - AspNetCore
UsingDirective - (16:1,1 [34] ) - System.Collections.Generic
UsingDirective - (51:2,1 [19] ) - System.Linq
UsingDirective - (71:3,1 [30] ) - System.Threading.Tasks
UsingDirective - (102:4,1 [32] ) - Microsoft.AspNetCore.Mvc
UsingDirective - (135:5,1 [42] ) - Microsoft.AspNetCore.Mvc.Rendering
UsingDirective - (178:6,1 [45] ) - Microsoft.AspNetCore.Mvc.ViewFeatures
UsingDirective - (1:0,1 [29] UsingDirectives.cshtml) - System.ComponentModel
UsingDirective - (31:1,1 [26] UsingDirectives.cshtml) - System.Collections
UsingDirective - (58:2,1 [14] UsingDirectives.cshtml) - System
UsingDirective - (73:3,1 [12] UsingDirectives.cshtml) - System
RazorSourceChecksumAttribute -
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_UsingDirectives - global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic> -
MethodDeclaration - - public async override - global::System.Threading.Tasks.Task - ExecuteAsync
Inject -
Inject -
Inject -
Inject -
Inject -

View File

@ -3,12 +3,12 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Moq;
using Xunit;
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
using Moq;
using Microsoft.AspNetCore.Razor.Language.Extensions;
namespace Microsoft.AspNetCore.Razor.Language
{
@ -358,7 +358,7 @@ namespace Microsoft.AspNetCore.Razor.Language
TestRazorSourceDocument.Create("@using System.Globalization"),
TestRazorSourceDocument.Create("@using System.Text"),
};
var codeDocument = TestRazorCodeDocument.Create(source, imports);
// Act
@ -373,6 +373,29 @@ namespace Microsoft.AspNetCore.Razor.Language
n => Html("<p>Hi!</p>", n));
}
[Fact]
public void Lower_WithImports_AllowsIdenticalNamespacesInPrimaryDocument()
{
// Arrange
var source = TestRazorSourceDocument.Create(@"@using System.Threading.Tasks
@using System.Threading.Tasks");
var imports = new[]
{
TestRazorSourceDocument.Create("@using System.Threading.Tasks"),
};
var codeDocument = TestRazorCodeDocument.Create(source, imports);
// Act
var documentNode = Lower(codeDocument);
// Assert
Children(
documentNode,
n => Using("System.Threading.Tasks", n),
n => Using("System.Threading.Tasks", n));
}
[Fact]
public void Lower_WithMultipleImports_SingleLineFileScopedSinglyOccurring()
{
@ -390,8 +413,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var documentNode = Lower(codeDocument, b =>
{
b.AddDirective(DirectiveDescriptor.CreateDirective(
"test",
DirectiveKind.SingleLine,
"test",
DirectiveKind.SingleLine,
builder =>
{
builder.AddMemberToken();