Roslyn-ize our tooling contracts.
- Added `ILanguageServiceFactory` types for all serviceable contracts. - Maintained binary compatibility by copy/pasting existing services into a Legacy folder. - Added a Workspaces.Test project since their implementations moved. - Updated binary incompatible version of `RazorSyntaxFactsService` to not depend on VisualStudio. Added an extension type to the VS.LanguageServices.Razor project to enable easy-access from VisualStudio. Below on refers to the binary incompatible copies. Core services that were **entirely** moved from VisualStudio.Razor => Razor.Workspaces are as follows: - `RazorSyntaxFactsService`, this included mutating the API to not depend on VisualStudio and moving some primitive types such as `AcceptedCharacters`. - `TagHelperCompletionService` - `TagHelperFactsService` These all have a `ServiceLayer` of `Editor`. Bits that were partially moved: - `RazorTemplateEngineFactoryService`. The Default implementation and its corresponding factory still live in VisualStudio.Razor. This way Razor.Workspaces can get by without a reference to Mvc.Razor.Extensions. - `ITagHelperResolver` used to exist in VisualStudio.Razor. Removed the type and replaced its usage with the already-existing `TagHelperResolver` type in Razor.Workspaces. Both contracts were nearly identical. These all have a `ServiceLayer` of `Default`. #1260
This commit is contained in:
parent
69d68ae62b
commit
aa980fc67c
13
Razor.sln
13
Razor.sln
|
|
@ -1,6 +1,6 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26428.3
|
||||
VisualStudioVersion = 15.0.26710.3002
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C0D6505-79B3-49D0-B4C3-176F0F1836ED}"
|
||||
EndProject
|
||||
|
|
@ -54,6 +54,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test.MvcShim", "test\Microsoft.AspNetCore.Razor.Test.MvcShim\Microsoft.AspNetCore.Razor.Test.MvcShim.csproj", "{8F165A3F-A18C-4649-AA08-C0E1BA5F5C90}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Razor.Workspaces.Test", "test\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.csproj", "{C61AAE12-5007-4205-A220-68F354A7F235}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -212,6 +214,14 @@ Global
|
|||
{8F165A3F-A18C-4649-AA08-C0E1BA5F5C90}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8F165A3F-A18C-4649-AA08-C0E1BA5F5C90}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8F165A3F-A18C-4649-AA08-C0E1BA5F5C90}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -236,5 +246,6 @@ Global
|
|||
{078AEF36-F319-4CE2-BAA2-5B58A6536B46} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{82C23CF8-95FF-40F7-BF50-3AD9EE21ED58} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{8F165A3F-A18C-4649-AA08-C0E1BA5F5C90} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{C61AAE12-5007-4205-A220-68F354A7F235} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[Flags]
|
||||
public enum AcceptedCharacters
|
||||
|
|
@ -5,7 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class AttributeCompletionContext
|
||||
{
|
||||
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public abstract class AttributeCompletionResult
|
||||
{
|
||||
|
|
@ -1,7 +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.
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public enum BlockKind
|
||||
{
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public struct ClassifiedSpan
|
||||
{
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Span = Microsoft.AspNetCore.Razor.Language.Legacy.Span;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal class DefaultRazorSyntaxFactsService : RazorSyntaxFactsService
|
||||
{
|
||||
public override IReadOnlyList<ClassifiedSpan> GetClassifiedSpans(RazorSyntaxTree syntaxTree)
|
||||
{
|
||||
if (syntaxTree == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(syntaxTree));
|
||||
}
|
||||
|
||||
var spans = Flatten(syntaxTree);
|
||||
|
||||
var result = new ClassifiedSpan[spans.Count];
|
||||
for (var i = 0; i < spans.Count; i++)
|
||||
{
|
||||
var span = spans[i];
|
||||
result[i] = new ClassifiedSpan(
|
||||
new SourceSpan(
|
||||
span.Start.FilePath ?? syntaxTree.Source.FilePath,
|
||||
span.Start.AbsoluteIndex,
|
||||
span.Start.LineIndex,
|
||||
span.Start.CharacterIndex,
|
||||
span.Length),
|
||||
new SourceSpan(
|
||||
span.Parent.Start.FilePath ?? syntaxTree.Source.FilePath,
|
||||
span.Parent.Start.AbsoluteIndex,
|
||||
span.Parent.Start.LineIndex,
|
||||
span.Parent.Start.CharacterIndex,
|
||||
span.Parent.Length),
|
||||
(SpanKind)span.Kind,
|
||||
(BlockKind)span.Parent.Type,
|
||||
(AcceptedCharacters)span.EditHandler.AcceptedCharacters);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Span> Flatten(RazorSyntaxTree syntaxTree)
|
||||
{
|
||||
var result = new List<Span>();
|
||||
AppendFlattenedSpans(syntaxTree.Root, result);
|
||||
return result;
|
||||
|
||||
void AppendFlattenedSpans(SyntaxTreeNode node, List<Span> foundSpans)
|
||||
{
|
||||
Span spanNode = node as Span;
|
||||
if (spanNode != null)
|
||||
{
|
||||
foundSpans.Add(spanNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
TagHelperBlock tagHelperNode = node as TagHelperBlock;
|
||||
if (tagHelperNode != null)
|
||||
{
|
||||
// These aren't in document order, sort them first and then dig in
|
||||
List<SyntaxTreeNode> attributeNodes = tagHelperNode.Attributes.Select(kvp => kvp.Value).Where(att => att != null).ToList();
|
||||
attributeNodes.Sort((x, y) => x.Start.AbsoluteIndex.CompareTo(y.Start.AbsoluteIndex));
|
||||
|
||||
foreach (SyntaxTreeNode curNode in attributeNodes)
|
||||
{
|
||||
AppendFlattenedSpans(curNode, foundSpans);
|
||||
}
|
||||
}
|
||||
|
||||
Block blockNode = node as Block;
|
||||
if (blockNode != null)
|
||||
{
|
||||
foreach (SyntaxTreeNode curNode in blockNode.Children)
|
||||
{
|
||||
AppendFlattenedSpans(curNode, foundSpans);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadOnlyList<TagHelperSpan> GetTagHelperSpans(RazorSyntaxTree syntaxTree)
|
||||
{
|
||||
if (syntaxTree == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(syntaxTree));
|
||||
}
|
||||
|
||||
var results = new List<TagHelperSpan>();
|
||||
|
||||
List<Block> toProcess = new List<Block>();
|
||||
List<Block> blockChildren = new List<Block>();
|
||||
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.FilePath,
|
||||
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;
|
||||
}
|
||||
|
||||
public override int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, int previousLineEndIndex, Func<int, string> getLineContent, int indentSize, int tabSize)
|
||||
{
|
||||
if (syntaxTree == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(syntaxTree));
|
||||
}
|
||||
|
||||
if (previousLineEndIndex < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(previousLineEndIndex));
|
||||
}
|
||||
|
||||
if (getLineContent == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(getLineContent));
|
||||
}
|
||||
|
||||
if (indentSize < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(indentSize));
|
||||
}
|
||||
|
||||
if (tabSize < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(tabSize));
|
||||
}
|
||||
|
||||
var simulatedChange = new SourceChange(previousLineEndIndex, 0, string.Empty);
|
||||
var owningSpan = LocateOwner(syntaxTree.Root, simulatedChange);
|
||||
|
||||
int? desiredIndentation = null;
|
||||
|
||||
if (owningSpan.Kind != SpanKindInternal.Code)
|
||||
{
|
||||
SyntaxTreeNode owningChild = owningSpan;
|
||||
while ((owningChild.Parent != null) && !desiredIndentation.HasValue)
|
||||
{
|
||||
Block owningParent = owningChild.Parent;
|
||||
List<SyntaxTreeNode> children = new List<SyntaxTreeNode>(owningParent.Children);
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
SyntaxTreeNode curChild = children[i];
|
||||
if (!curChild.IsBlock)
|
||||
{
|
||||
Span curSpan = curChild as Span;
|
||||
if (curSpan.Kind == SpanKindInternal.MetaCode)
|
||||
{
|
||||
var extraIndent = 0;
|
||||
|
||||
// Dev11 337312: Only indent one level deeper if the item after the metacode is a markup block
|
||||
if (i < children.Count - 1)
|
||||
{
|
||||
SyntaxTreeNode nextChild = children[i + 1];
|
||||
if (nextChild.IsBlock && ((nextChild as Block).Type == BlockKindInternal.Markup))
|
||||
{
|
||||
extraIndent = indentSize;
|
||||
}
|
||||
}
|
||||
|
||||
// We can't rely on the syntax trees representation of the source document because partial parses may have mutated
|
||||
// the underlying SyntaxTree text buffer. Because of this, if we want to provide accurate indentations we need to
|
||||
// operate on the current line representation as indicated by the provider.
|
||||
var line = getLineContent(curSpan.Start.LineIndex);
|
||||
desiredIndentation = GetIndentLevelOfLine(line, tabSize) + indentSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (curChild == owningChild)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
owningChild = owningParent;
|
||||
}
|
||||
}
|
||||
|
||||
return desiredIndentation;
|
||||
}
|
||||
|
||||
private Span LocateOwner(Block root, SourceChange change)
|
||||
{
|
||||
// Ask each child recursively
|
||||
Span owner = null;
|
||||
foreach (SyntaxTreeNode element in root.Children)
|
||||
{
|
||||
if (element.Start.AbsoluteIndex > change.Span.AbsoluteIndex)
|
||||
{
|
||||
// too far
|
||||
break;
|
||||
}
|
||||
|
||||
int elementLen = element.Length;
|
||||
if (element.Start.AbsoluteIndex + elementLen < change.Span.AbsoluteIndex)
|
||||
{
|
||||
// not far enough
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.IsBlock)
|
||||
{
|
||||
Block block = element as Block;
|
||||
|
||||
if (element.Start.AbsoluteIndex + elementLen == change.Span.AbsoluteIndex)
|
||||
{
|
||||
Span lastDescendant = block.FindLastDescendentSpan();
|
||||
if ((lastDescendant == null) && (block is TagHelperBlock))
|
||||
{
|
||||
TagHelperBlock tagHelperBlock = (TagHelperBlock)block;
|
||||
if (tagHelperBlock.SourceEndTag != null)
|
||||
{
|
||||
lastDescendant = tagHelperBlock.SourceEndTag.FindLastDescendentSpan();
|
||||
}
|
||||
else if (tagHelperBlock.SourceStartTag != null)
|
||||
{
|
||||
lastDescendant = tagHelperBlock.SourceStartTag.FindLastDescendentSpan();
|
||||
}
|
||||
}
|
||||
|
||||
// Conceptually, lastDescendant should always be non-null, but runtime errs on some
|
||||
// cases and makes empty blocks. Runtime will fix these issues as we find them, but make
|
||||
// no guarantee that they catch them all.
|
||||
if (lastDescendant == null)
|
||||
{
|
||||
owner = LocateOwner(block, change);
|
||||
if (owner != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (lastDescendant.EditHandler.OwnsChange(lastDescendant, change))
|
||||
{
|
||||
owner = lastDescendant;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
owner = LocateOwner(block, change);
|
||||
if (owner != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Span span = element as Span;
|
||||
if (span.EditHandler.OwnsChange(span, change))
|
||||
{
|
||||
owner = span;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (owner == null)
|
||||
{
|
||||
TagHelperBlock tagHelperNode = root as TagHelperBlock;
|
||||
if (tagHelperNode != null)
|
||||
{
|
||||
Block sourceStartTag = tagHelperNode.SourceStartTag;
|
||||
Block sourceEndTag = tagHelperNode.SourceEndTag;
|
||||
if ((sourceStartTag.Start.AbsoluteIndex <= change.Span.AbsoluteIndex) &&
|
||||
(sourceStartTag.Start.AbsoluteIndex + sourceStartTag.Length >= change.Span.AbsoluteIndex))
|
||||
{
|
||||
// intersects the start tag
|
||||
return LocateOwner(sourceStartTag, change);
|
||||
}
|
||||
else if ((sourceEndTag.Start.AbsoluteIndex <= change.Span.AbsoluteIndex) &&
|
||||
(sourceEndTag.Start.AbsoluteIndex + sourceEndTag.Length >= change.Span.AbsoluteIndex))
|
||||
{
|
||||
// intersects the end tag
|
||||
return LocateOwner(sourceEndTag, change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return owner;
|
||||
}
|
||||
|
||||
private int GetIndentLevelOfLine(string line, int tabSize)
|
||||
{
|
||||
var indentLevel = 0;
|
||||
|
||||
foreach (var c in line)
|
||||
{
|
||||
if (!char.IsWhiteSpace(c))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (c == '\t')
|
||||
{
|
||||
indentLevel += tabSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
indentLevel++;
|
||||
}
|
||||
}
|
||||
|
||||
return indentLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// 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 Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[ExportLanguageServiceFactory(typeof(RazorSyntaxFactsService), RazorLanguage.Name, ServiceLayer.Default)]
|
||||
internal class DefaultRazorSyntaxFactsServiceFactory : ILanguageServiceFactory
|
||||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultRazorSyntaxFactsService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,20 +3,17 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[Export(typeof(TagHelperCompletionService))]
|
||||
internal class DefaultTagHelperCompletionService : TagHelperCompletionService
|
||||
{
|
||||
private readonly TagHelperFactsService _tagHelperFactsService;
|
||||
private static readonly HashSet<TagHelperDescriptor> _emptyHashSet = new HashSet<TagHelperDescriptor>();
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultTagHelperCompletionService(TagHelperFactsService tagHelperFactsService)
|
||||
{
|
||||
_tagHelperFactsService = tagHelperFactsService;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// 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 Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[ExportLanguageServiceFactory(typeof(TagHelperCompletionService), RazorLanguage.Name, ServiceLayer.Default)]
|
||||
internal class DefaultTagHelperCompletionServiceFactory : ILanguageServiceFactory
|
||||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
var tagHelperFactsService = languageServices.GetRequiredService<TagHelperFactsService>();
|
||||
return new DefaultTagHelperCompletionService(tagHelperFactsService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[Export(typeof(TagHelperFactsService))]
|
||||
internal class DefaultTagHelperFactsService : TagHelperFactsService
|
||||
{
|
||||
public override TagHelperBinding GetTagHelperBinding(
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// 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 Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[ExportLanguageServiceFactory(typeof(TagHelperFactsService), RazorLanguage.Name, ServiceLayer.Default)]
|
||||
internal class DefaultTagHelperFactsServiceFactory : ILanguageServiceFactory
|
||||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultTagHelperFactsService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public sealed class ElementCompletionContext
|
||||
{
|
||||
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public abstract class ElementCompletionResult
|
||||
{
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Language\Microsoft.AspNetCore.Razor.Language.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.CodeAnalysis.Razor\Microsoft.CodeAnalysis.Razor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
// 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.Language;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public abstract class RazorSyntaxFactsService
|
||||
public abstract class RazorSyntaxFactsService : ILanguageService
|
||||
{
|
||||
public abstract IReadOnlyList<ClassifiedSpan> GetClassifiedSpans(RazorSyntaxTree syntaxTree);
|
||||
|
||||
public abstract IReadOnlyList<TagHelperSpan> GetTagHelperSpans(RazorSyntaxTree syntaxTree);
|
||||
|
||||
public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line, int indentSize, int tabSize);
|
||||
public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, int previousLineEnd, Func<int, string> lineProvider, int indentSize, int tabSize);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,12 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public abstract class RazorTemplateEngineFactoryService
|
||||
public abstract class RazorTemplateEngineFactoryService : ILanguageService
|
||||
{
|
||||
public abstract RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +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.
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public enum SpanKind
|
||||
{
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public abstract class TagHelperCompletionService
|
||||
public abstract class TagHelperCompletionService : ILanguageService
|
||||
{
|
||||
public abstract AttributeCompletionResult GetAttributeCompletions(AttributeCompletionContext completionContext);
|
||||
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public abstract class TagHelperFactsService
|
||||
public abstract class TagHelperFactsService : ILanguageService
|
||||
{
|
||||
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag);
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Host;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal abstract class TagHelperResolver : ILanguageService
|
||||
public abstract class TagHelperResolver : ILanguageService
|
||||
{
|
||||
public abstract TagHelperResolutionResult GetTagHelpers(Compilation compilation);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public struct TagHelperSpan
|
||||
{
|
||||
|
|
@ -17,14 +17,16 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
[Export(typeof(ITagHelperResolver))]
|
||||
internal class DefaultTagHelperResolver : ITagHelperResolver
|
||||
internal class DefaultTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
[Import]
|
||||
public VisualStudioWorkspace Workspace { get; set; }
|
||||
private readonly Workspace _workspace;
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
[Import]
|
||||
public SVsServiceProvider Services { get; set; }
|
||||
public DefaultTagHelperResolver(Workspace workspace, IServiceProvider services)
|
||||
{
|
||||
_workspace = workspace;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project)
|
||||
{
|
||||
|
|
@ -36,7 +38,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
// when it's disconnected (user stops the process).
|
||||
//
|
||||
// This will change in the future to an easier to consume API but for VS RTM this is what we have.
|
||||
var client = await RazorLanguageServiceClientFactory.CreateAsync(Workspace, CancellationToken.None);
|
||||
var client = await RazorLanguageServiceClientFactory.CreateAsync(_workspace, CancellationToken.None);
|
||||
if (client != null)
|
||||
{
|
||||
using (var session = await client.CreateSessionAsync(project.Solution))
|
||||
|
|
@ -59,7 +61,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
// The OOP host is turned off, so let's do this in process.
|
||||
var compilation = await project.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
result = GetTagHelpers(compilation, designTime: true);
|
||||
result = GetTagHelpers(compilation);
|
||||
return result;
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
|
@ -81,13 +83,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
}
|
||||
}
|
||||
|
||||
private TagHelperResolutionResult GetTagHelpers(Compilation compilation, bool designTime)
|
||||
public override TagHelperResolutionResult GetTagHelpers(Compilation compilation)
|
||||
{
|
||||
var descriptors = new List<TagHelperDescriptor>();
|
||||
|
||||
var providers = new ITagHelperDescriptorProvider[]
|
||||
{
|
||||
new DefaultTagHelperDescriptorProvider() { DesignTime = designTime, },
|
||||
new DefaultTagHelperDescriptorProvider() { DesignTime = true, },
|
||||
new ViewComponentTagHelperDescriptorProvider(),
|
||||
};
|
||||
|
||||
|
|
@ -121,8 +123,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
private IVsActivityLog GetActivityLog()
|
||||
{
|
||||
var services = (IServiceProvider)Services;
|
||||
return services.GetService(typeof(SVsActivityLog)) as IVsActivityLog;
|
||||
return _services.GetService(typeof(SVsActivityLog)) as IVsActivityLog;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
// 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.ComponentModel.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
[ExportLanguageServiceFactory(typeof(TagHelperResolver), RazorLanguage.Name, ServiceLayer.Default)]
|
||||
internal class DefaultTagHelperResolverFactory : ILanguageServiceFactory
|
||||
{
|
||||
[Import]
|
||||
public VisualStudioWorkspace Workspace { get; set; }
|
||||
|
||||
[Import]
|
||||
public SVsServiceProvider Services { get; set; }
|
||||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultTagHelperResolver(Workspace, Services);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,12 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[Export(typeof(RazorTemplateEngineFactoryService))]
|
||||
internal class DefaultRazorTemplateEngineFactoryService : RazorTemplateEngineFactoryService
|
||||
internal class DefaultTemplateEngineFactoryService : RazorTemplateEngineFactoryService
|
||||
{
|
||||
public override RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure)
|
||||
{
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// 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 Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
[ExportLanguageServiceFactory(typeof(CodeAnalysis.Razor.RazorTemplateEngineFactoryService), RazorLanguage.Name, ServiceLayer.Default)]
|
||||
internal class DefaultTemplateEngineFactoryServiceFactory : ILanguageServiceFactory
|
||||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultTemplateEngineFactoryService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
[Flags]
|
||||
public enum AcceptedCharacters
|
||||
{
|
||||
None = 0,
|
||||
NewLine = 1,
|
||||
WhiteSpace = 2,
|
||||
|
||||
NonWhiteSpace = 4,
|
||||
|
||||
AllWhiteSpace = NewLine | WhiteSpace,
|
||||
Any = AllWhiteSpace | NonWhiteSpace,
|
||||
|
||||
AnyExceptNewline = NonWhiteSpace | WhiteSpace
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// 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.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public class AttributeCompletionContext
|
||||
{
|
||||
public AttributeCompletionContext(
|
||||
TagHelperDocumentContext documentContext,
|
||||
IEnumerable<string> existingCompletions,
|
||||
string currentTagName,
|
||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
||||
string currentParentTagName,
|
||||
Func<string, bool> inHTMLSchema)
|
||||
{
|
||||
if (documentContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentContext));
|
||||
}
|
||||
|
||||
if (existingCompletions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(existingCompletions));
|
||||
}
|
||||
|
||||
if (currentTagName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(currentTagName));
|
||||
}
|
||||
|
||||
if (attributes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
if (inHTMLSchema == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(inHTMLSchema));
|
||||
}
|
||||
|
||||
DocumentContext = documentContext;
|
||||
ExistingCompletions = existingCompletions;
|
||||
CurrentTagName = currentTagName;
|
||||
Attributes = attributes;
|
||||
CurrentParentTagName = currentParentTagName;
|
||||
InHTMLSchema = inHTMLSchema;
|
||||
}
|
||||
|
||||
public TagHelperDocumentContext DocumentContext { get; }
|
||||
|
||||
public IEnumerable<string> ExistingCompletions { get; }
|
||||
|
||||
public string CurrentTagName { get; }
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> Attributes { get; }
|
||||
|
||||
public string CurrentParentTagName { get; }
|
||||
|
||||
public Func<string, bool> InHTMLSchema { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public abstract class AttributeCompletionResult
|
||||
{
|
||||
private AttributeCompletionResult()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract IReadOnlyDictionary<string, IEnumerable<BoundAttributeDescriptor>> Completions { get; }
|
||||
|
||||
internal static AttributeCompletionResult Create(Dictionary<string, HashSet<BoundAttributeDescriptor>> completions)
|
||||
{
|
||||
var readonlyCompletions = completions.ToDictionary(
|
||||
key => key.Key,
|
||||
value => (IEnumerable<BoundAttributeDescriptor>)value.Value,
|
||||
completions.Comparer);
|
||||
var result = new DefaultAttributeCompletionResult(readonlyCompletions);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private class DefaultAttributeCompletionResult : AttributeCompletionResult
|
||||
{
|
||||
private readonly IReadOnlyDictionary<string, IEnumerable<BoundAttributeDescriptor>> _completions;
|
||||
|
||||
public DefaultAttributeCompletionResult(IReadOnlyDictionary<string, IEnumerable<BoundAttributeDescriptor>> completions)
|
||||
{
|
||||
_completions = completions;
|
||||
}
|
||||
|
||||
public override IReadOnlyDictionary<string, IEnumerable<BoundAttributeDescriptor>> Completions => _completions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public enum BlockKind
|
||||
{
|
||||
// Code
|
||||
Statement,
|
||||
Directive,
|
||||
Functions,
|
||||
Expression,
|
||||
Helper,
|
||||
|
||||
// Markup
|
||||
Markup,
|
||||
Section,
|
||||
Template,
|
||||
|
||||
// Special
|
||||
Comment,
|
||||
Tag
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public struct ClassifiedSpan
|
||||
{
|
||||
public ClassifiedSpan(SourceSpan span, SourceSpan blockSpan, SpanKind spanKind, BlockKind blockKind, AcceptedCharacters acceptedCharacters)
|
||||
{
|
||||
Span = span;
|
||||
BlockSpan = blockSpan;
|
||||
SpanKind = spanKind;
|
||||
BlockKind = blockKind;
|
||||
AcceptedCharacters = acceptedCharacters;
|
||||
}
|
||||
|
||||
public AcceptedCharacters AcceptedCharacters { get; }
|
||||
|
||||
public BlockKind BlockKind { get; }
|
||||
|
||||
public SourceSpan BlockSpan { get; }
|
||||
|
||||
public SourceSpan Span { get; }
|
||||
|
||||
public SpanKind SpanKind { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,13 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Span = Microsoft.AspNetCore.Razor.Language.Legacy.Span;
|
||||
using ITextBuffer = Microsoft.AspNetCore.Razor.Language.Legacy.ITextBuffer;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
[Export(typeof(RazorSyntaxFactsService))]
|
||||
internal class DefaultRazorSyntaxFactsService : RazorSyntaxFactsService
|
||||
{
|
||||
|
|
@ -326,7 +329,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
private int GetIndentLevelOfLine(ITextSnapshotLine line, int tabSize)
|
||||
{
|
||||
var indentLevel = 0;
|
||||
|
||||
|
||||
foreach (var c in line.GetText())
|
||||
{
|
||||
if (!char.IsWhiteSpace(c))
|
||||
|
|
@ -0,0 +1,280 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
[Export(typeof(TagHelperCompletionService))]
|
||||
internal class DefaultTagHelperCompletionService : TagHelperCompletionService
|
||||
{
|
||||
private readonly TagHelperFactsService _tagHelperFactsService;
|
||||
private static readonly HashSet<TagHelperDescriptor> _emptyHashSet = new HashSet<TagHelperDescriptor>();
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultTagHelperCompletionService(TagHelperFactsService tagHelperFactsService)
|
||||
{
|
||||
_tagHelperFactsService = tagHelperFactsService;
|
||||
}
|
||||
|
||||
/*
|
||||
* This API attempts to understand a users context as they're typing in a Razor file to provide TagHelper based attribute IntelliSense.
|
||||
*
|
||||
* Scenarios for TagHelper attribute IntelliSense follows:
|
||||
* 1. TagHelperDescriptor's have matching required attribute names
|
||||
* -> Provide IntelliSense for the required attributes of those descriptors to lead users towards a TagHelperified element.
|
||||
* 2. TagHelperDescriptor entirely applies to current element. Tag name, attributes, everything is fulfilled.
|
||||
* -> Provide IntelliSense for the bound attributes for the applied descriptors.
|
||||
*
|
||||
* Within each of the above scenarios if an attribute completion has a corresponding bound attribute we associate it with the corresponding
|
||||
* BoundAttributeDescriptor. By doing this a user can see what C# type a TagHelper expects for the attribute.
|
||||
*/
|
||||
public override AttributeCompletionResult GetAttributeCompletions(AttributeCompletionContext completionContext)
|
||||
{
|
||||
if (completionContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(completionContext));
|
||||
}
|
||||
|
||||
var attributeCompletions = completionContext.ExistingCompletions.ToDictionary(
|
||||
completion => completion,
|
||||
_ => new HashSet<BoundAttributeDescriptor>(),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var documentContext = completionContext.DocumentContext;
|
||||
var descriptorsForTag = _tagHelperFactsService.GetTagHelpersGivenTag(documentContext, completionContext.CurrentTagName, completionContext.CurrentParentTagName);
|
||||
if (descriptorsForTag.Count == 0)
|
||||
{
|
||||
// If the current tag has no possible descriptors then we can't have any additional attributes.
|
||||
var defaultResult = AttributeCompletionResult.Create(attributeCompletions);
|
||||
return defaultResult;
|
||||
}
|
||||
|
||||
var prefix = documentContext.Prefix ?? string.Empty;
|
||||
Debug.Assert(completionContext.CurrentTagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var applicableTagHelperBinding = _tagHelperFactsService.GetTagHelperBinding(documentContext, completionContext.CurrentTagName, completionContext.Attributes, completionContext.CurrentParentTagName);
|
||||
var applicableDescriptors = applicableTagHelperBinding?.Descriptors ?? Enumerable.Empty<TagHelperDescriptor>();
|
||||
var unprefixedTagName = completionContext.CurrentTagName.Substring(prefix.Length);
|
||||
|
||||
if (!completionContext.InHTMLSchema(unprefixedTagName) &&
|
||||
applicableDescriptors.All(descriptor => descriptor.TagOutputHint == null))
|
||||
{
|
||||
// This isn't a known HTML tag and no descriptor has an output element hint. Remove all previous completions.
|
||||
attributeCompletions.Clear();
|
||||
}
|
||||
|
||||
for (var i = 0; i < descriptorsForTag.Count; i++)
|
||||
{
|
||||
var descriptor = descriptorsForTag[i];
|
||||
|
||||
if (applicableDescriptors.Contains(descriptor))
|
||||
{
|
||||
foreach (var attributeDescriptor in descriptor.BoundAttributes)
|
||||
{
|
||||
UpdateCompletions(attributeDescriptor.Name, attributeDescriptor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var htmlNameToBoundAttribute = descriptor.BoundAttributes.ToDictionary(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var rule in descriptor.TagMatchingRules)
|
||||
{
|
||||
foreach (var requiredAttribute in rule.Attributes)
|
||||
{
|
||||
if (htmlNameToBoundAttribute.TryGetValue(requiredAttribute.Name, out var attributeDescriptor))
|
||||
{
|
||||
UpdateCompletions(requiredAttribute.Name, attributeDescriptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCompletions(requiredAttribute.Name, possibleDescriptor: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var completionResult = AttributeCompletionResult.Create(attributeCompletions);
|
||||
return completionResult;
|
||||
|
||||
void UpdateCompletions(string attributeName, BoundAttributeDescriptor possibleDescriptor)
|
||||
{
|
||||
if (completionContext.Attributes.Any(attribute => string.Equals(attribute.Key, attributeName, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// Attribute is already present on this element it shouldn't exist in the completion list.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!attributeCompletions.TryGetValue(attributeName, out var rules))
|
||||
{
|
||||
rules = new HashSet<BoundAttributeDescriptor>();
|
||||
attributeCompletions[attributeName] = rules;
|
||||
}
|
||||
|
||||
if (possibleDescriptor != null)
|
||||
{
|
||||
rules.Add(possibleDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override ElementCompletionResult GetElementCompletions(ElementCompletionContext completionContext)
|
||||
{
|
||||
if (completionContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(completionContext));
|
||||
}
|
||||
|
||||
var elementCompletions = new Dictionary<string, HashSet<TagHelperDescriptor>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
AddAllowedChildrenCompletions(completionContext, elementCompletions);
|
||||
|
||||
if (elementCompletions.Count > 0)
|
||||
{
|
||||
// If the containing element is already a TagHelper and only allows certain children.
|
||||
var emptyResult = ElementCompletionResult.Create(elementCompletions);
|
||||
return emptyResult;
|
||||
}
|
||||
|
||||
elementCompletions = completionContext.ExistingCompletions.ToDictionary(
|
||||
completion => completion,
|
||||
_ => new HashSet<TagHelperDescriptor>(),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var catchAllDescriptors = new HashSet<TagHelperDescriptor>();
|
||||
var prefix = completionContext.DocumentContext.Prefix ?? string.Empty;
|
||||
var possibleChildDescriptors = _tagHelperFactsService.GetTagHelpersGivenParent(completionContext.DocumentContext, completionContext.ContainingTagName);
|
||||
foreach (var possibleDescriptor in possibleChildDescriptors)
|
||||
{
|
||||
var addRuleCompletions = false;
|
||||
var outputHint = possibleDescriptor.TagOutputHint;
|
||||
|
||||
foreach (var rule in possibleDescriptor.TagMatchingRules)
|
||||
{
|
||||
if (rule.TagName == TagHelperMatchingConventions.ElementCatchAllName)
|
||||
{
|
||||
catchAllDescriptors.Add(possibleDescriptor);
|
||||
}
|
||||
else if (elementCompletions.ContainsKey(rule.TagName))
|
||||
{
|
||||
addRuleCompletions = true;
|
||||
}
|
||||
else if (outputHint != null)
|
||||
{
|
||||
// If the current descriptor has an output hint we need to make sure it shows up only when its output hint would normally show up.
|
||||
// Example: We have a MyTableTagHelper that has an output hint of "table" and a MyTrTagHelper that has an output hint of "tr".
|
||||
// If we try typing in a situation like this: <body > | </body>
|
||||
// We'd expect to only get "my-table" as a completion because the "body" tag doesn't allow "tr" tags.
|
||||
addRuleCompletions = elementCompletions.ContainsKey(outputHint);
|
||||
}
|
||||
else if (!completionContext.InHTMLSchema(rule.TagName))
|
||||
{
|
||||
// If there is an unknown HTML schema tag that doesn't exist in the current completion we should add it. This happens for
|
||||
// TagHelpers that target non-schema oriented tags.
|
||||
addRuleCompletions = true;
|
||||
}
|
||||
|
||||
if (addRuleCompletions)
|
||||
{
|
||||
UpdateCompletions(prefix + rule.TagName, possibleDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We needed to track all catch-alls and update their completions after all other completions have been completed.
|
||||
// This way, any TagHelper added completions will also have catch-alls listed under their entries.
|
||||
foreach (var catchAllDescriptor in catchAllDescriptors)
|
||||
{
|
||||
foreach (var completionTagName in elementCompletions.Keys)
|
||||
{
|
||||
if (elementCompletions[completionTagName].Count > 0 ||
|
||||
!string.IsNullOrEmpty(prefix) && completionTagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// The current completion either has other TagHelper's associated with it or is prefixed with a non-empty
|
||||
// TagHelper prefix.
|
||||
UpdateCompletions(completionTagName, catchAllDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = ElementCompletionResult.Create(elementCompletions);
|
||||
return result;
|
||||
|
||||
void UpdateCompletions(string tagName, TagHelperDescriptor possibleDescriptor)
|
||||
{
|
||||
if (!elementCompletions.TryGetValue(tagName, out var existingRuleDescriptors))
|
||||
{
|
||||
existingRuleDescriptors = new HashSet<TagHelperDescriptor>();
|
||||
elementCompletions[tagName] = existingRuleDescriptors;
|
||||
}
|
||||
|
||||
existingRuleDescriptors.Add(possibleDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllowedChildrenCompletions(
|
||||
ElementCompletionContext completionContext,
|
||||
Dictionary<string, HashSet<TagHelperDescriptor>> elementCompletions)
|
||||
{
|
||||
if (completionContext.ContainingTagName == null)
|
||||
{
|
||||
// If we're at the root then there's no containing TagHelper to specify allowed children.
|
||||
return;
|
||||
}
|
||||
|
||||
var prefix = completionContext.DocumentContext.Prefix ?? string.Empty;
|
||||
var binding = _tagHelperFactsService.GetTagHelperBinding(
|
||||
completionContext.DocumentContext,
|
||||
completionContext.ContainingTagName,
|
||||
completionContext.Attributes,
|
||||
completionContext.ContainingParentTagName);
|
||||
|
||||
if (binding == null)
|
||||
{
|
||||
// Containing tag is not a TagHelper; therefore, it allows any children.
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var descriptor in binding.Descriptors)
|
||||
{
|
||||
foreach (var childTag in descriptor.AllowedChildTags)
|
||||
{
|
||||
var prefixedName = string.Concat(prefix, childTag.Name);
|
||||
var descriptors = _tagHelperFactsService.GetTagHelpersGivenTag(
|
||||
completionContext.DocumentContext,
|
||||
prefixedName,
|
||||
completionContext.ContainingTagName);
|
||||
|
||||
if (descriptors.Count == 0)
|
||||
{
|
||||
if (!elementCompletions.ContainsKey(prefixedName))
|
||||
{
|
||||
elementCompletions[prefixedName] = _emptyHashSet;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!elementCompletions.TryGetValue(prefixedName, out var existingRuleDescriptors))
|
||||
{
|
||||
existingRuleDescriptors = new HashSet<TagHelperDescriptor>();
|
||||
elementCompletions[prefixedName] = existingRuleDescriptors;
|
||||
}
|
||||
|
||||
existingRuleDescriptors.UnionWith(descriptors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
// 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;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
[Export(typeof(TagHelperFactsService))]
|
||||
internal class DefaultTagHelperFactsService : TagHelperFactsService
|
||||
{
|
||||
public override TagHelperBinding GetTagHelperBinding(
|
||||
TagHelperDocumentContext documentContext,
|
||||
string tagName,
|
||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
||||
string parentTag)
|
||||
{
|
||||
if (documentContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentContext));
|
||||
}
|
||||
|
||||
if (tagName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tagName));
|
||||
}
|
||||
|
||||
if (attributes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
var descriptors = documentContext.TagHelpers;
|
||||
if (descriptors == null || descriptors.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var prefix = documentContext.Prefix;
|
||||
var tagHelperBinder = new TagHelperBinder(prefix, descriptors);
|
||||
var binding = tagHelperBinder.GetBinding(tagName, attributes.ToList(), parentTag);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
public override IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(
|
||||
TagHelperDocumentContext documentContext,
|
||||
string attributeName,
|
||||
TagHelperBinding binding)
|
||||
{
|
||||
if (documentContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentContext));
|
||||
}
|
||||
|
||||
if (attributeName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributeName));
|
||||
}
|
||||
|
||||
if (binding == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(binding));
|
||||
}
|
||||
|
||||
var matchingBoundAttributes = new List<BoundAttributeDescriptor>();
|
||||
foreach (var descriptor in binding.Descriptors)
|
||||
{
|
||||
foreach (var boundAttributeDescriptor in descriptor.BoundAttributes)
|
||||
{
|
||||
if (TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, boundAttributeDescriptor))
|
||||
{
|
||||
matchingBoundAttributes.Add(boundAttributeDescriptor);
|
||||
|
||||
// Only one bound attribute can match an attribute
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchingBoundAttributes;
|
||||
}
|
||||
|
||||
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(
|
||||
TagHelperDocumentContext documentContext,
|
||||
string tagName,
|
||||
string parentTag)
|
||||
{
|
||||
if (documentContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentContext));
|
||||
}
|
||||
|
||||
if (tagName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tagName));
|
||||
}
|
||||
|
||||
var matchingDescriptors = new List<TagHelperDescriptor>();
|
||||
var descriptors = documentContext?.TagHelpers;
|
||||
if (descriptors?.Count == 0)
|
||||
{
|
||||
return matchingDescriptors;
|
||||
}
|
||||
|
||||
var prefix = documentContext.Prefix ?? string.Empty;
|
||||
if (!tagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Can't possibly match TagHelpers, it doesn't start with the TagHelperPrefix.
|
||||
return matchingDescriptors;
|
||||
}
|
||||
|
||||
var tagNameWithoutPrefix = tagName.Substring(prefix.Length);
|
||||
for (var i = 0; i < descriptors.Count; i++)
|
||||
{
|
||||
var descriptor = descriptors[i];
|
||||
foreach (var rule in descriptor.TagMatchingRules)
|
||||
{
|
||||
if (TagHelperMatchingConventions.SatisfiesTagName(tagNameWithoutPrefix, rule) &&
|
||||
TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
|
||||
{
|
||||
matchingDescriptors.Add(descriptor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchingDescriptors;
|
||||
}
|
||||
|
||||
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag)
|
||||
{
|
||||
if (documentContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentContext));
|
||||
}
|
||||
|
||||
var matchingDescriptors = new List<TagHelperDescriptor>();
|
||||
var descriptors = documentContext?.TagHelpers;
|
||||
if (descriptors?.Count == 0)
|
||||
{
|
||||
return matchingDescriptors;
|
||||
}
|
||||
|
||||
for (var i = 0; i < descriptors.Count; i++)
|
||||
{
|
||||
var descriptor = descriptors[i];
|
||||
foreach (var rule in descriptor.TagMatchingRules)
|
||||
{
|
||||
if (TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
|
||||
{
|
||||
matchingDescriptors.Add(descriptor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchingDescriptors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// 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.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public sealed class ElementCompletionContext
|
||||
{
|
||||
public ElementCompletionContext(
|
||||
TagHelperDocumentContext documentContext,
|
||||
IEnumerable<string> existingCompletions,
|
||||
string containingTagName,
|
||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
||||
string containingParentTagName,
|
||||
Func<string, bool> inHTMLSchema)
|
||||
{
|
||||
if (documentContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentContext));
|
||||
}
|
||||
|
||||
if (existingCompletions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(existingCompletions));
|
||||
}
|
||||
|
||||
if (inHTMLSchema == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(inHTMLSchema));
|
||||
}
|
||||
|
||||
DocumentContext = documentContext;
|
||||
ExistingCompletions = existingCompletions;
|
||||
ContainingTagName = containingTagName;
|
||||
Attributes = attributes;
|
||||
ContainingParentTagName = containingParentTagName;
|
||||
InHTMLSchema = inHTMLSchema;
|
||||
}
|
||||
|
||||
public TagHelperDocumentContext DocumentContext { get; }
|
||||
|
||||
public IEnumerable<string> ExistingCompletions { get; }
|
||||
|
||||
public string ContainingTagName { get; }
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> Attributes { get; }
|
||||
|
||||
public string ContainingParentTagName { get; }
|
||||
|
||||
public Func<string, bool> InHTMLSchema { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public abstract class ElementCompletionResult
|
||||
{
|
||||
private ElementCompletionResult()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> Completions { get; }
|
||||
|
||||
internal static ElementCompletionResult Create(Dictionary<string, HashSet<TagHelperDescriptor>> completions)
|
||||
{
|
||||
var readonlyCompletions = completions.ToDictionary(
|
||||
key => key.Key,
|
||||
value => (IEnumerable<TagHelperDescriptor>)value.Value,
|
||||
completions.Comparer);
|
||||
var result = new DefaultElementCompletionResult(readonlyCompletions);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private class DefaultElementCompletionResult : ElementCompletionResult
|
||||
{
|
||||
private readonly IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> _completions;
|
||||
|
||||
public DefaultElementCompletionResult(IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> completions)
|
||||
{
|
||||
_completions = completions;
|
||||
}
|
||||
|
||||
public override IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> Completions => _completions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,16 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public interface ITagHelperResolver
|
||||
{
|
||||
Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project);
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// 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.ComponentModel.Composition;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use TagHelperResolver.
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
[Export(typeof(ITagHelperResolver))]
|
||||
internal class LegacyTagHelperResolver : DefaultTagHelperResolver, ITagHelperResolver
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public LegacyTagHelperResolver(
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace,
|
||||
[Import(typeof(SVsServiceProvider))] IServiceProvider services) :
|
||||
base(workspace, services)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// 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.ComponentModel.Composition;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use DefaultTemplateEngineFactoryService.
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
[Export(typeof(RazorTemplateEngineFactoryService))]
|
||||
internal class LegacyTemplateEngineFactoryService : RazorTemplateEngineFactoryService
|
||||
{
|
||||
public override RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure)
|
||||
{
|
||||
if (projectPath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectPath));
|
||||
}
|
||||
|
||||
var engine = RazorEngine.CreateDesignTime(b =>
|
||||
{
|
||||
configure?.Invoke(b);
|
||||
|
||||
// For now we're hardcoded to use MVC's extensibility.
|
||||
RazorExtensions.Register(b);
|
||||
});
|
||||
|
||||
var templateEngine = new MvcRazorTemplateEngine(engine, RazorProject.Create(projectPath));
|
||||
templateEngine.Options.ImportsFileName = "_ViewImports.cshtml";
|
||||
return templateEngine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public abstract class RazorSyntaxFactsService
|
||||
{
|
||||
public abstract IReadOnlyList<ClassifiedSpan> GetClassifiedSpans(RazorSyntaxTree syntaxTree);
|
||||
|
||||
public abstract IReadOnlyList<TagHelperSpan> GetTagHelperSpans(RazorSyntaxTree syntaxTree);
|
||||
|
||||
public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line, int indentSize, int tabSize);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// 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.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public abstract class RazorTemplateEngineFactoryService
|
||||
{
|
||||
public abstract RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public enum SpanKind
|
||||
{
|
||||
Transition,
|
||||
MetaCode,
|
||||
Comment,
|
||||
Code,
|
||||
Markup
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public abstract class TagHelperCompletionService
|
||||
{
|
||||
public abstract AttributeCompletionResult GetAttributeCompletions(AttributeCompletionContext completionContext);
|
||||
|
||||
public abstract ElementCompletionResult GetElementCompletions(ElementCompletionContext completionContext);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
public abstract class TagHelperFactsService
|
||||
{
|
||||
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag);
|
||||
|
||||
public abstract IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding);
|
||||
|
||||
public abstract IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(TagHelperDocumentContext documentContext, string tagName, string parentTag);
|
||||
|
||||
public abstract IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
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<TagHelperDescriptor> TagHelpers => Binding.Descriptors;
|
||||
|
||||
public SourceSpan Span { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public static class RazorSyntaxFactsServiceExtensions
|
||||
{
|
||||
public static int? GetDesiredIndentation(
|
||||
this RazorSyntaxFactsService service,
|
||||
RazorSyntaxTree syntaxTree,
|
||||
ITextSnapshot syntaxTreeSnapshot,
|
||||
ITextSnapshotLine line,
|
||||
int indentSize,
|
||||
int tabSize)
|
||||
{
|
||||
// The tricky thing here is that line.Snapshot is very likely newer
|
||||
var previousLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);
|
||||
var trackingPoint = line.Snapshot.CreateTrackingPoint(line.End, PointTrackingMode.Negative);
|
||||
var previousLineEnd = trackingPoint.GetPosition(syntaxTreeSnapshot);
|
||||
|
||||
Func<int, string> getLineContentDelegate = (lineIndex) => line.Snapshot.GetLineFromLineNumber(lineIndex).GetText();
|
||||
|
||||
return service.GetDesiredIndentation(syntaxTree, previousLineEnd, getLineContentDelegate, indentSize, tabSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,964 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class DefaultTagHelperCompletionServiceTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_DoesNotReturnCompletionsForAlreadySuppliedAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("div")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("repeat")))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("visible")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Visible"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("class")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("Class"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["onclick"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
["visible"] = new HashSet<BoundAttributeDescriptor>()
|
||||
{
|
||||
documentDescriptors[0].BoundAttributes.Last()
|
||||
}
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "onclick" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
attributes: new Dictionary<string, string>()
|
||||
{
|
||||
["class"] = "something",
|
||||
["repeat"] = "4"
|
||||
},
|
||||
currentTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_PossibleDescriptorsReturnUnboundRequiredAttributesWithExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("div")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("repeat")))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("*")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("class")))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
["onclick"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
["repeat"] = new HashSet<BoundAttributeDescriptor>()
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "onclick", "class" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
currentTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_PossibleDescriptorsReturnBoundRequiredAttributesWithExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("div")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("repeat")))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("repeat")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Repeat"))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("visible")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Visible"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("*")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("class")))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("class")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("Class"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["class"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[1].BoundAttributes),
|
||||
["onclick"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
["repeat"] = new HashSet<BoundAttributeDescriptor>()
|
||||
{
|
||||
documentDescriptors[0].BoundAttributes.First()
|
||||
}
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "onclick" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
currentTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_AppliedDescriptorsReturnAllBoundAttributesWithExistingCompletionsForSchemaTags()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("repeat")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Repeat"))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("visible")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Visible"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("*")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("class")))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("class")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("Class"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("visible")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Visible"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["onclick"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
["class"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[1].BoundAttributes),
|
||||
["repeat"] = new HashSet<BoundAttributeDescriptor>()
|
||||
{
|
||||
documentDescriptors[0].BoundAttributes.First()
|
||||
},
|
||||
["visible"] = new HashSet<BoundAttributeDescriptor>()
|
||||
{
|
||||
documentDescriptors[0].BoundAttributes.Last(),
|
||||
documentDescriptors[2].BoundAttributes.First(),
|
||||
}
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "class", "onclick" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
currentTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_AppliedTagOutputHintDescriptorsReturnBoundAttributesWithExistingCompletionsForNonSchemaTags()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("CustomTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("custom"))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("repeat")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Repeat"))
|
||||
.TagOutputHint("div")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
["repeat"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[0].BoundAttributes)
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "class" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
currentTagName: "custom");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_AppliedDescriptorsReturnBoundAttributesCompletionsForNonSchemaTags()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("CustomTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("custom"))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("repeat")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Repeat"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["repeat"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[0].BoundAttributes)
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "class" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
currentTagName: "custom");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_AppliedDescriptorsReturnBoundAttributesWithExistingCompletionsForSchemaTags()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("repeat")
|
||||
.TypeName(typeof(bool).FullName)
|
||||
.PropertyName("Repeat"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
["repeat"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[0].BoundAttributes)
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "class" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
currentTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_NoDescriptorsReturnsExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "class" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
Enumerable.Empty<TagHelperDescriptor>(),
|
||||
existingCompletions,
|
||||
currentTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_NoDescriptorsForUnprefixedTagReturnsExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("div")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("special")))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "class" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
currentTagName: "div",
|
||||
tagHelperPrefix: "th:");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeCompletions_NoDescriptorsForTagReturnsExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("MyTableTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("table")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("special")))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||
{
|
||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "class" };
|
||||
var completionContext = BuildAttributeCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
currentTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetAttributeCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_TagOutputHintDoesNotFallThroughToSchemaCheck()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("MyTableTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("my-table"))
|
||||
.TagOutputHint("table")
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("MyTrTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("my-tr"))
|
||||
.TagOutputHint("tr")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["my-table"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["table"] = new HashSet<TagHelperDescriptor>(),
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "table" };
|
||||
var completionContext = BuildElementCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: "body",
|
||||
containingParentTagName: null);
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CatchAllsOnlyApplyToCompletionsStartingWithPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("CatchAllTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["th:li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1], documentDescriptors[0] },
|
||||
["li"] = new HashSet<TagHelperDescriptor>(),
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildElementCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: "ul",
|
||||
tagHelperPrefix: "th:");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_TagHelperPrefixIsPrependedToTagHelperCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["th:superli"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["th:li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
|
||||
["li"] = new HashSet<TagHelperDescriptor>(),
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildElementCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: "ul",
|
||||
tagHelperPrefix: "th:");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CatchAllsApplyToOnlyTagHelperCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("CatchAll", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["superli"] = new HashSet<TagHelperDescriptor>() { documentDescriptors[0], documentDescriptors[1] },
|
||||
["li"] = new HashSet<TagHelperDescriptor>(),
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildElementCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CatchAllsApplyToNonTagHelperCompletionsIfStartsWithTagHelperPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("CatchAll", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["th:superli"] = new HashSet<TagHelperDescriptor>() { documentDescriptors[0], documentDescriptors[1] },
|
||||
["th:li"] = new HashSet<TagHelperDescriptor>() { documentDescriptors[1] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "th:li" };
|
||||
var completionContext = BuildElementCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: "ul",
|
||||
tagHelperPrefix: "th:");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_AllowsMultiTargetingTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldTagHelper1", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("b"))
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("bold"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("BoldTagHelper2", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["strong"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
||||
["b"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["bold"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "strong", "b", "bold" };
|
||||
var completionContext = BuildElementCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CombinesDescriptorsOnExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper1", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper2", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_NewCompletionsForSchemaTagsNotInExistingCompletionsAreIgnored()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
||||
.TagOutputHint("strong")
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
|
||||
["superli"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_OutputHintIsCrossReferencedWithExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.TagOutputHint("li")
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
||||
.TagOutputHint("strong")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_EnsuresDescriptorsHaveSatisfiedParent()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper1", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper2", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li").RequireParentTag("ol"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_AllowedChildrenAreIgnoredWhenAtRoot()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("CatchAll", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.AllowChildTag("div")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>());
|
||||
|
||||
var existingCompletions = Enumerable.Empty<string>();
|
||||
var completionContext = BuildElementCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: null,
|
||||
containingParentTagName: null);
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_DoesNotReturnExistingCompletionsWhenAllowedChildren()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.AllowChildTag("div")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] }
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "p", "em" };
|
||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_NoneTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
||||
});
|
||||
|
||||
var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_SomeTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.AllowChildTag("div")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] }
|
||||
});
|
||||
|
||||
var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_AllTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldParentCatchAll", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.AllowChildTag("strong")
|
||||
.AllowChildTag("div")
|
||||
.AllowChildTag("b")
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["strong"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["b"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["bold"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
||||
});
|
||||
|
||||
var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
private static DefaultTagHelperCompletionService CreateTagHelperCompletionFactsService()
|
||||
{
|
||||
var tagHelperFactService = new DefaultTagHelperFactsService();
|
||||
var completionFactService = new DefaultTagHelperCompletionService(tagHelperFactService);
|
||||
|
||||
return completionFactService;
|
||||
}
|
||||
|
||||
private static void AssertCompletionsAreEquivalent(ElementCompletionResult expected, ElementCompletionResult actual)
|
||||
{
|
||||
Assert.Equal(expected.Completions.Count, actual.Completions.Count);
|
||||
|
||||
foreach (var expectedCompletion in expected.Completions)
|
||||
{
|
||||
var actualValue = actual.Completions[expectedCompletion.Key];
|
||||
Assert.NotNull(actualValue);
|
||||
Assert.Equal(expectedCompletion.Value, actualValue, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertCompletionsAreEquivalent(AttributeCompletionResult expected, AttributeCompletionResult actual)
|
||||
{
|
||||
Assert.Equal(expected.Completions.Count, actual.Completions.Count);
|
||||
|
||||
foreach (var expectedCompletion in expected.Completions)
|
||||
{
|
||||
var actualValue = actual.Completions[expectedCompletion.Key];
|
||||
Assert.NotNull(actualValue);
|
||||
Assert.Equal(expectedCompletion.Value, actualValue, BoundAttributeDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
}
|
||||
|
||||
private static ElementCompletionContext BuildElementCompletionContext(
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
IEnumerable<string> existingCompletions,
|
||||
string containingTagName,
|
||||
string containingParentTagName = "body",
|
||||
string tagHelperPrefix = "")
|
||||
{
|
||||
var documentContext = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors);
|
||||
var completionContext = new ElementCompletionContext(
|
||||
documentContext,
|
||||
existingCompletions,
|
||||
containingTagName,
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
containingParentTagName: containingParentTagName,
|
||||
inHTMLSchema: (tag) => tag == "strong" || tag == "b" || tag == "bold" || tag == "li" || tag == "div");
|
||||
|
||||
return completionContext;
|
||||
}
|
||||
|
||||
private static AttributeCompletionContext BuildAttributeCompletionContext(
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
IEnumerable<string> existingCompletions,
|
||||
string currentTagName,
|
||||
IEnumerable<KeyValuePair<string, string>> attributes = null,
|
||||
string tagHelperPrefix = "")
|
||||
{
|
||||
attributes = attributes ?? Enumerable.Empty<KeyValuePair<string, string>>();
|
||||
var documentContext = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors);
|
||||
var completionContext = new AttributeCompletionContext(
|
||||
documentContext,
|
||||
existingCompletions,
|
||||
currentTagName,
|
||||
attributes,
|
||||
currentParentTagName: "body",
|
||||
inHTMLSchema: (tag) => tag == "strong" || tag == "b" || tag == "bold" || tag == "li" || tag == "div");
|
||||
|
||||
return completionContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,388 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class DefaultTagHelperFactsServiceTest
|
||||
{
|
||||
// Purposefully not thoroughly testing DefaultTagHelperFactsService.GetTagHelperBinding because it's a pass through
|
||||
// into TagHelperDescriptorProvider.GetTagHelperBinding.
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelperBinding_DoesNotAllowOptOutCharacterPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
|
||||
|
||||
// Assert
|
||||
Assert.Null(binding);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelperBinding_WorksAsExpected()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule =>
|
||||
rule
|
||||
.RequireTagName("a")
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("asp-for")))
|
||||
.BoundAttributeDescriptor(attribute =>
|
||||
attribute
|
||||
.Name("asp-for")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("AspFor"))
|
||||
.BoundAttributeDescriptor(attribute =>
|
||||
attribute
|
||||
.Name("asp-route")
|
||||
.TypeName(typeof(IDictionary<string, string>).Namespace + "IDictionary<string, string>")
|
||||
.PropertyName("AspRoute")
|
||||
.AsDictionaryAttribute("asp-route-", typeof(string).FullName))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("input"))
|
||||
.BoundAttributeDescriptor(attribute =>
|
||||
attribute
|
||||
.Name("asp-for")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("AspFor"))
|
||||
.Build(),
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
var attributes = new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("asp-for", "Name")
|
||||
};
|
||||
|
||||
// Act
|
||||
var binding = service.GetTagHelperBinding(documentContext, "a", attributes, parentTag: "p");
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(binding.Descriptors);
|
||||
Assert.Equal(documentDescriptors[0], descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
||||
var boundRule = Assert.Single(binding.GetBoundRules(descriptor));
|
||||
Assert.Equal(documentDescriptors[0].TagMatchingRules.First(), boundRule, TagMatchingRuleDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetBoundTagHelperAttributes_MatchesPrefixedAttributeName()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("a"))
|
||||
.BoundAttributeDescriptor(attribute =>
|
||||
attribute
|
||||
.Name("asp-for")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("AspFor"))
|
||||
.BoundAttributeDescriptor(attribute =>
|
||||
attribute
|
||||
.Name("asp-route")
|
||||
.TypeName(typeof(IDictionary<string, string>).Namespace + "IDictionary<string, string>")
|
||||
.PropertyName("AspRoute")
|
||||
.AsDictionaryAttribute("asp-route-", typeof(string).FullName))
|
||||
.Build()
|
||||
};
|
||||
var expectedAttributeDescriptors = new[]
|
||||
{
|
||||
documentDescriptors[0].BoundAttributes.Last()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetBoundTagHelperAttributes(documentContext, "asp-route-something", binding);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedAttributeDescriptors, descriptors, BoundAttributeDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetBoundTagHelperAttributes_MatchesAttributeName()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("input"))
|
||||
.BoundAttributeDescriptor(attribute =>
|
||||
attribute
|
||||
.Name("asp-for")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("AspFor"))
|
||||
.BoundAttributeDescriptor(attribute =>
|
||||
attribute
|
||||
.Name("asp-extra")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("AspExtra"))
|
||||
.Build()
|
||||
};
|
||||
var expectedAttributeDescriptors = new[]
|
||||
{
|
||||
documentDescriptors[0].BoundAttributes.First()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetBoundTagHelperAttributes(documentContext, "asp-for", binding);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedAttributeDescriptors, descriptors, BoundAttributeDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenTag_DoesNotAllowOptOutCharacterPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "!strong", parentTag: null);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(descriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenTag_RequiresTagName()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "p");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(documentDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnTagName()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(
|
||||
rule => rule
|
||||
.RequireTagName("a")
|
||||
.RequireParentTag("div"))
|
||||
.Build()
|
||||
};
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
expectedDescriptors[0],
|
||||
TagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(
|
||||
rule => rule
|
||||
.RequireTagName("strong")
|
||||
.RequireParentTag("div"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "a", "div");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnTagHelperPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
|
||||
.Build()
|
||||
};
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
expectedDescriptors[0],
|
||||
TagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("thstrong"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create("th", documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "thstrong", "div");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnParent()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(
|
||||
rule => rule
|
||||
.RequireTagName("strong")
|
||||
.RequireParentTag("div"))
|
||||
.Build()
|
||||
};
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
expectedDescriptors[0],
|
||||
TagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(
|
||||
rule => rule
|
||||
.RequireTagName("strong")
|
||||
.RequireParentTag("p"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "div");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenParent_AllowsRootParentTag()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(documentDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenParent_AllowsRootParentTagForParentRestrictedTagHelperDescriptors()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("PTagHelper", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule
|
||||
.RequireTagName("p")
|
||||
.RequireParentTag("body"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(documentDescriptors[0], descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenParent_AllowsUnspecifiedParentTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, "p");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(documentDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpersGivenParent_RestrictsTagHelpersBasedOnParent()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(
|
||||
rule => rule
|
||||
.RequireTagName("p")
|
||||
.RequireParentTag("div"))
|
||||
.Build()
|
||||
};
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
expectedDescriptors[0],
|
||||
TagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
||||
.TagMatchingRuleDescriptor(
|
||||
rule => rule
|
||||
.RequireTagName("strong")
|
||||
.RequireParentTag("p"))
|
||||
.Build()
|
||||
};
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||
var service = new DefaultTagHelperFactsService();
|
||||
|
||||
// Act
|
||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, "div");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Runtime\Microsoft.AspNetCore.Razor.Runtime.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor\Microsoft.AspNetCore.Razor.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Test.Common\Microsoft.AspNetCore.Razor.Test.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(DependencyModelVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// 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.CodeAnalysis.Razor.Workspaces
|
||||
{
|
||||
public static class TestBoundAttributeDescriptorBuilderExtensions
|
||||
{
|
||||
public static BoundAttributeDescriptorBuilder Name(this BoundAttributeDescriptorBuilder builder, string name)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Name = name;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BoundAttributeDescriptorBuilder TypeName(this BoundAttributeDescriptorBuilder builder, string typeName)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.TypeName = typeName;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BoundAttributeDescriptorBuilder PropertyName(this BoundAttributeDescriptorBuilder builder, string propertyName)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.SetPropertyName(propertyName);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BoundAttributeDescriptorBuilder DisplayName(this BoundAttributeDescriptorBuilder builder, string displayName)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.DisplayName = displayName;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BoundAttributeDescriptorBuilder AsEnum(this BoundAttributeDescriptorBuilder builder)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.IsEnum = true;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BoundAttributeDescriptorBuilder AsDictionaryAttribute(
|
||||
this BoundAttributeDescriptorBuilder builder,
|
||||
string attributeNamePrefix,
|
||||
string valueTypeName)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.IsDictionary = true;
|
||||
builder.IndexerAttributeNamePrefix = attributeNamePrefix;
|
||||
builder.IndexerValueTypeName = valueTypeName;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BoundAttributeDescriptorBuilder Documentation(this BoundAttributeDescriptorBuilder builder, string documentation)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Documentation = documentation;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BoundAttributeDescriptorBuilder AddMetadata(this BoundAttributeDescriptorBuilder builder, string key, string value)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Metadata[key] = value;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BoundAttributeDescriptorBuilder AddDiagnostic(this BoundAttributeDescriptorBuilder builder, RazorDiagnostic diagnostic)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Diagnostics.Add(diagnostic);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// 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.CodeAnalysis.Razor.Workspaces
|
||||
{
|
||||
public static class TestRequiredAttributeDescriptorBuilderExtensions
|
||||
{
|
||||
public static RequiredAttributeDescriptorBuilder Name(this RequiredAttributeDescriptorBuilder builder, string name)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Name = name;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static RequiredAttributeDescriptorBuilder NameComparisonMode(
|
||||
this RequiredAttributeDescriptorBuilder builder,
|
||||
RequiredAttributeDescriptor.NameComparisonMode nameComparison)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.NameComparisonMode = nameComparison;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static RequiredAttributeDescriptorBuilder Value(this RequiredAttributeDescriptorBuilder builder, string value)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Value = value;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static RequiredAttributeDescriptorBuilder ValueComparisonMode(
|
||||
this RequiredAttributeDescriptorBuilder builder,
|
||||
RequiredAttributeDescriptor.ValueComparisonMode valueComparison)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.ValueComparisonMode = valueComparison;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static RequiredAttributeDescriptorBuilder AddDiagnostic(this RequiredAttributeDescriptorBuilder builder, RazorDiagnostic diagnostic)
|
||||
{
|
||||
builder.Diagnostics.Add(diagnostic);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// 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.CodeAnalysis.Razor.Workspaces
|
||||
{
|
||||
public static class TestTagHelperDescriptorBuilderExtensions
|
||||
{
|
||||
public static TagHelperDescriptorBuilder TypeName(this TagHelperDescriptorBuilder builder, string typeName)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.SetTypeName(typeName);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagHelperDescriptorBuilder DisplayName(this TagHelperDescriptorBuilder builder, string displayName)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.DisplayName = displayName;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagHelperDescriptorBuilder AllowChildTag(this TagHelperDescriptorBuilder builder, string allowedChild)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.AllowChildTag(childTagBuilder => childTagBuilder.Name = allowedChild);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagHelperDescriptorBuilder TagOutputHint(this TagHelperDescriptorBuilder builder, string hint)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.TagOutputHint = hint;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagHelperDescriptorBuilder Documentation(this TagHelperDescriptorBuilder builder, string documentation)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Documentation = documentation;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagHelperDescriptorBuilder AddMetadata(this TagHelperDescriptorBuilder builder, string key, string value)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Metadata[key] = value;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagHelperDescriptorBuilder AddDiagnostic(this TagHelperDescriptorBuilder builder, RazorDiagnostic diagnostic)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Diagnostics.Add(diagnostic);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagHelperDescriptorBuilder BoundAttributeDescriptor(
|
||||
this TagHelperDescriptorBuilder builder,
|
||||
Action<BoundAttributeDescriptorBuilder> configure)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.BindAttribute(configure);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagHelperDescriptorBuilder TagMatchingRuleDescriptor(
|
||||
this TagHelperDescriptorBuilder builder,
|
||||
Action<TagMatchingRuleDescriptorBuilder> configure)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.TagMatchingRule(configure);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// 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.CodeAnalysis.Razor.Workspaces
|
||||
{
|
||||
public static class TestTagMatchingRuleDescriptorBuilderExtensions
|
||||
{
|
||||
public static TagMatchingRuleDescriptorBuilder RequireTagName(this TagMatchingRuleDescriptorBuilder builder, string tagName)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.TagName = tagName;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagMatchingRuleDescriptorBuilder RequireParentTag(this TagMatchingRuleDescriptorBuilder builder, string parentTag)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.ParentTag = parentTag;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagMatchingRuleDescriptorBuilder RequireTagStructure(this TagMatchingRuleDescriptorBuilder builder, TagStructure tagStructure)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.TagStructure = tagStructure;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagMatchingRuleDescriptorBuilder AddDiagnostic(this TagMatchingRuleDescriptorBuilder builder, RazorDiagnostic diagnostic)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Diagnostics.Add(diagnostic);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TagMatchingRuleDescriptorBuilder RequireAttributeDescriptor(
|
||||
this TagMatchingRuleDescriptorBuilder builder,
|
||||
Action<RequiredAttributeDescriptorBuilder> configure)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Attribute(configure);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"methodDisplay": "method",
|
||||
"shadowCopy": false
|
||||
}
|
||||
|
|
@ -961,4 +961,4 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
return completionContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -385,4 +385,4 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,20 +4,15 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="TestFiles\**\*" />
|
||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor\Microsoft.CodeAnalysis.Razor.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.VisualStudio.LanguageServices.Razor\Microsoft.VisualStudio.LanguageServices.Razor.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Language\Microsoft.AspNetCore.Razor.Language.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Runtime\Microsoft.AspNetCore.Razor.Runtime.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor\Microsoft.AspNetCore.Razor.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Test.Common\Microsoft.AspNetCore.Razor.Test.Common.csproj" />
|
||||
|
|
@ -25,14 +20,11 @@
|
|||
|
||||
<ItemGroup>
|
||||
<!-- Temporarily disable package downgrade warnings. Revert when RoslynDevVersion >= RoslynVersion. -->
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="$(RoslynDevVersion)" NoWarn="NU1605" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="$(RoslynDevVersion)" NoWarn="NU1605" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(RoslynDevVersion)" NoWarn="NU1605" />
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(DependencyModelVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
|
||||
<PackageReference Include="Moq" Version="$(MoqVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XunitVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
|
|
@ -44,6 +45,19 @@ namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo
|
|||
public SourceTextContainer TextContainer => _documentTracker.TextContainer;
|
||||
|
||||
public Workspace Workspace => _documentTracker.Workspace;
|
||||
|
||||
public HostLanguageServices RazorLanguageServices => Workspace?.Services.GetLanguageServices(RazorLanguage.Name);
|
||||
|
||||
public TagHelperResolver TagHelperResolver => RazorLanguageServices?.GetRequiredService<TagHelperResolver>();
|
||||
|
||||
public RazorSyntaxFactsService RazorSyntaxFactsService => RazorLanguageServices?.GetRequiredService<RazorSyntaxFactsService>();
|
||||
|
||||
public RazorTemplateEngineFactoryService RazorTemplateEngineFactoryService => RazorLanguageServices?.GetRequiredService<RazorTemplateEngineFactoryService>();
|
||||
|
||||
public TagHelperCompletionService TagHelperCompletionService => RazorLanguageServices?.GetRequiredService<TagHelperCompletionService>();
|
||||
|
||||
public TagHelperFactsService TagHelperFactsService => RazorLanguageServices?.GetRequiredService<TagHelperFactsService>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@
|
|||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Label Grid.Column="0" Grid.Row="0" Content="Project"/>
|
||||
<Label Grid.Column="1" Grid.Row="0" Content="{Binding Project.Name}"/>
|
||||
|
|
@ -29,6 +35,18 @@
|
|||
<Label Grid.Column="1" Grid.Row="2" Content="{Binding TextContainer}"/>
|
||||
<Label Grid.Column="0" Grid.Row="3" Content="Workspace"/>
|
||||
<Label Grid.Column="1" Grid.Row="3" Content="{Binding Workspace}"/>
|
||||
<Label Grid.Column="0" Grid.Row="4" Content="Razor Lang"/>
|
||||
<Label Grid.Column="1" Grid.Row="4" Content="{Binding RazorLanguageServices}"/>
|
||||
<Label Grid.Column="0" Grid.Row="5" Content="Resolver"/>
|
||||
<Label Grid.Column="1" Grid.Row="5" Content="{Binding TagHelperResolver}"/>
|
||||
<Label Grid.Column="0" Grid.Row="6" Content="Syntax Facts"/>
|
||||
<Label Grid.Column="1" Grid.Row="6" Content="{Binding RazorSyntaxFactsService}"/>
|
||||
<Label Grid.Column="0" Grid.Row="7" Content="Template Eng"/>
|
||||
<Label Grid.Column="1" Grid.Row="7" Content="{Binding RazorTemplateEngineFactoryService}"/>
|
||||
<Label Grid.Column="0" Grid.Row="8" Content="Completion Service"/>
|
||||
<Label Grid.Column="1" Grid.Row="8" Content="{Binding TagHelperCompletionService}"/>
|
||||
<Label Grid.Column="0" Grid.Row="9" Content="TH Facts"/>
|
||||
<Label Grid.Column="1" Grid.Row="9" Content="{Binding TagHelperFactsService}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.ComponentModelHost;
|
||||
using Microsoft.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
|
|
@ -19,7 +20,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
private IRazorEngineAssemblyResolver _assemblyResolver;
|
||||
private IRazorEngineDocumentGenerator _documentGenerator;
|
||||
private IRazorEngineDirectiveResolver _directiveResolver;
|
||||
private ITagHelperResolver _tagHelperResolver;
|
||||
private TagHelperResolver _tagHelperResolver;
|
||||
private VisualStudioWorkspace _workspace;
|
||||
|
||||
public RazorInfoToolWindow() : base(null)
|
||||
|
|
@ -37,7 +38,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
_assemblyResolver = componentModel.GetService<IRazorEngineAssemblyResolver>();
|
||||
_documentGenerator = componentModel.GetService<IRazorEngineDocumentGenerator>();
|
||||
_directiveResolver = componentModel.GetService<IRazorEngineDirectiveResolver>();
|
||||
_tagHelperResolver = componentModel.GetService<ITagHelperResolver>();
|
||||
_tagHelperResolver = componentModel.GetService<TagHelperResolver>();
|
||||
|
||||
_workspace = componentModel.GetService<VisualStudioWorkspace>();
|
||||
_workspace.WorkspaceChanged += Workspace_WorkspaceChanged;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ using Microsoft.VisualStudio.Shell;
|
|||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
using Microsoft.VisualStudio.TextManager.Interop;
|
||||
using System.IO;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
|
|
@ -24,7 +25,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
private readonly IRazorEngineAssemblyResolver _assemblyResolver;
|
||||
private readonly IRazorEngineDirectiveResolver _directiveResolver;
|
||||
private readonly IRazorEngineDocumentGenerator _documentGenerator;
|
||||
private readonly ITagHelperResolver _tagHelperResolver;
|
||||
private readonly TagHelperResolver _tagHelperResolver;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly Workspace _workspace;
|
||||
private readonly Action<Exception> _errorHandler;
|
||||
|
|
@ -42,7 +43,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
Workspace workspace,
|
||||
IRazorEngineAssemblyResolver assemblyResolver,
|
||||
IRazorEngineDirectiveResolver directiveResolver,
|
||||
ITagHelperResolver tagHelperResolver,
|
||||
TagHelperResolver tagHelperResolver,
|
||||
IRazorEngineDocumentGenerator documentGenerator,
|
||||
Action<Exception> errorHandler)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue