From ba496bf1aff436fbb17e5b1badd35ba5d1131c59 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Wed, 29 Nov 2017 12:46:32 -0800 Subject: [PATCH] Add a way for tooling to determine if a span corresponds to a TagHelper. - This enables the editor to ask us if an HTML parent happens to be a TagHelper. #1778 --- .../RazorSyntaxFactsServiceExtensions.cs | 37 +++++++++ .../RazorSyntaxFactsServiceExtensionsTest.cs | 82 +++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/RazorSyntaxFactsServiceExtensions.cs create mode 100644 test/Microsoft.VisualStudio.Editor.Razor.Test/RazorSyntaxFactsServiceExtensionsTest.cs diff --git a/src/Microsoft.VisualStudio.Editor.Razor/RazorSyntaxFactsServiceExtensions.cs b/src/Microsoft.VisualStudio.Editor.Razor/RazorSyntaxFactsServiceExtensions.cs new file mode 100644 index 0000000000..c64a0210d1 --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/RazorSyntaxFactsServiceExtensions.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation. 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.AspNetCore.Razor.Language; + +namespace Microsoft.VisualStudio.Editor.Razor +{ + public static class RazorSyntaxFactsServiceExtensions + { + public static bool IsTagHelperSpan(this RazorSyntaxFactsService syntaxFactsService, RazorSyntaxTree syntaxTree, SourceSpan span) + { + if (syntaxFactsService == null) + { + throw new ArgumentNullException(nameof(syntaxFactsService)); + } + + if (syntaxTree == null) + { + // Extra hardening for the case that tooling hasn't retrieved a SyntaxTree yet. + return false; + } + + var tagHelperSpans = syntaxFactsService.GetTagHelperSpans(syntaxTree); + for (var i = 0; i < tagHelperSpans.Count; i++) + { + var tagHelperSpan = tagHelperSpans[i].Span; + if (tagHelperSpan.AbsoluteIndex == span.AbsoluteIndex && tagHelperSpan.Length == span.Length) + { + return true; + } + } + + return false; + } + } +} diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorSyntaxFactsServiceExtensionsTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorSyntaxFactsServiceExtensionsTest.cs new file mode 100644 index 0000000000..96e4b7b2ec --- /dev/null +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorSyntaxFactsServiceExtensionsTest.cs @@ -0,0 +1,82 @@ +// Copyright (c) .NET Foundation. 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.AspNetCore.Razor.Language; +using Xunit; + +namespace Microsoft.VisualStudio.Editor.Razor +{ + public class RazorSyntaxFactsServiceExtensionsTest + { + [Fact] + public void IsTagHelperSpan_ReturnsTrue() + { + // Arrange + var syntaxTree = GetSyntaxTree( +@"
+ +
"); + var location = new SourceSpan(9 + Environment.NewLine.Length, 13); + var service = new DefaultRazorSyntaxFactsService(); + + // Act + var result = service.IsTagHelperSpan(syntaxTree, location); + + // Assert + Assert.True(result); + } + + [Fact] + public void IsTagHelperSpan_ReturnsFalse() + { + // Arrange + var syntaxTree = GetSyntaxTree( +@"
+ +
"); + var location = new SourceSpan(0, 4); + var service = new DefaultRazorSyntaxFactsService(); + + // Act + var result = service.IsTagHelperSpan(syntaxTree, location); + + // Assert + Assert.False(result); + } + + [Fact] + public void IsTagHelperSpan_NullSyntaxTree_ReturnsFalse() + { + // Arrange + var location = new SourceSpan(0, 4); + var service = new DefaultRazorSyntaxFactsService(); + + // Act + var result = service.IsTagHelperSpan(null, location); + + // Assert + Assert.False(result); + } + + private static RazorSyntaxTree GetSyntaxTree(string source) + { + var taghelper = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly") + .TagMatchingRuleDescriptor(rule => rule.RequireTagName("taghelper")) + .TypeName("TestTagHelper") + .Build(); + var engine = RazorEngine.CreateDesignTime(builder => + { + builder.AddTagHelpers(taghelper); + }); + + var sourceDocument = RazorSourceDocument.Create(source, "test.cshtml"); + var addTagHelperImport = RazorSourceDocument.Create("@addTagHelper *, TestAssembly", "import.cshtml"); + var codeDocument = RazorCodeDocument.Create(sourceDocument, new[] { addTagHelperImport }); + + engine.Process(codeDocument); + + return codeDocument.GetSyntaxTree(); + } + } +}