diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorSyntaxFactsService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorSyntaxFactsService.cs index 1685ae10bf..3deb20b240 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorSyntaxFactsService.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorSyntaxFactsService.cs @@ -1,6 +1,7 @@ // 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 System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; @@ -14,6 +15,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor { public override IReadOnlyList GetClassifiedSpans(RazorSyntaxTree syntaxTree) { + if (syntaxTree == null) + { + throw new ArgumentNullException(nameof(syntaxTree)); + } + var spans = Flatten(syntaxTree); var result = new ClassifiedSpan[spans.Count]; @@ -80,5 +86,54 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor } } } + + public override IReadOnlyList GetTagHelperSpans(RazorSyntaxTree syntaxTree) + { + if (syntaxTree == null) + { + throw new ArgumentNullException(nameof(syntaxTree)); + } + + var results = new List(); + + List toProcess = new List(); + List blockChildren = new List(); + toProcess.Add(syntaxTree.Root); + + for (var i = 0; i < toProcess.Count; i++) + { + var blockNode = toProcess[i]; + TagHelperBlock tagHelperNode = blockNode as TagHelperBlock; + if (tagHelperNode != null) + { + results.Add(new TagHelperSpan( + new SourceSpan( + tagHelperNode.Start.FilePath ?? syntaxTree.Source.FileName, + tagHelperNode.Start.AbsoluteIndex, + tagHelperNode.Start.LineIndex, + tagHelperNode.Start.CharacterIndex, + tagHelperNode.Length), + tagHelperNode.Binding)); + } + + // collect all child blocks and inject into toProcess as a single InsertRange + foreach (SyntaxTreeNode curNode in blockNode.Children) + { + Block curBlock = curNode as Block; + if (curBlock != null) + { + blockChildren.Add(curBlock); + } + } + + if (blockChildren.Count > 0) + { + toProcess.InsertRange(i + 1, blockChildren); + blockChildren.Clear(); + } + } + + return results; + } } } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorSyntaxFactsService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorSyntaxFactsService.cs index 4dbe40c580..cdee67db91 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorSyntaxFactsService.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorSyntaxFactsService.cs @@ -9,5 +9,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public abstract class RazorSyntaxFactsService { public abstract IReadOnlyList GetClassifiedSpans(RazorSyntaxTree syntaxTree); + + public abstract IReadOnlyList GetTagHelperSpans(RazorSyntaxTree syntaxTree); } } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperSpan.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperSpan.cs new file mode 100644 index 0000000000..3b924a242b --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperSpan.cs @@ -0,0 +1,30 @@ +// 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 System.Collections.Generic; +using Microsoft.AspNetCore.Razor.Evolution; +using Microsoft.AspNetCore.Razor.Evolution.Legacy; + +namespace Microsoft.VisualStudio.LanguageServices.Razor +{ + public struct TagHelperSpan + { + public TagHelperSpan(SourceSpan span, TagHelperBinding binding) + { + if (binding == null) + { + throw new ArgumentNullException(nameof(binding)); + } + + Span = span; + Binding = binding; + } + + public TagHelperBinding Binding { get; } + + public IEnumerable TagHelpers => Binding.Descriptors; + + public SourceSpan Span { get; } + } +}