Change Razor directive completions to use new completions API.
- Kept the same behavior as we previously had with Razor directive completions. - Attempted adding additional functionalities such as lighting up Razor directive completion when completion was invoked on top of Razor directives (non-C#) but ran into issues involving the core HTML editor not consuming the new completion APIs yet. That's something we'll have to re-visit once they move to the new completion APIs. - Added tests to validate all aspects of new completion APIs. - Made completion provider turn on and off based off of feature flag. #1743 #1813
This commit is contained in:
parent
dd62753312
commit
aec88e3eba
|
|
@ -25,6 +25,7 @@
|
|||
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.0.26606</MicrosoftVisualStudioComponentModelHostPackageVersion>
|
||||
<MicrosoftVisualStudioEditorPackageVersion>15.6.161-preview</MicrosoftVisualStudioEditorPackageVersion>
|
||||
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>15.6.161-preview</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
|
||||
<MicrosoftVisualStudioLanguagePackageVersion>15.8.519</MicrosoftVisualStudioLanguagePackageVersion>
|
||||
<MicrosoftVisualStudioOLEInteropPackageVersion>7.10.6070</MicrosoftVisualStudioOLEInteropPackageVersion>
|
||||
<MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>15.3.224</MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>
|
||||
<MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>2.0.6142705</MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>
|
||||
|
|
@ -42,7 +43,7 @@
|
|||
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
||||
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
||||
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
|
||||
<StreamJsonRpcPackageVersion>1.1.92</StreamJsonRpcPackageVersion>
|
||||
<StreamJsonRpcPackageVersion>1.3.23</StreamJsonRpcPackageVersion>
|
||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.6.0-preview1-26727-04</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||
<SystemRuntimeInteropServicesRuntimeInformationPackageVersion>4.3.0</SystemRuntimeInteropServicesRuntimeInformationPackageVersion>
|
||||
<SystemValueTuplePackageVersion>4.6.0-preview1-26727-04</SystemValueTuplePackageVersion>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="$(VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.EditorFeatures.Text" Version="$(VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Text.UI" Version="$(MicrosoftVisualStudioTextUIPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Language.Intellisense" Version="$(MicrosoftVisualStudioLanguageIntellisensePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Language" Version="$(MicrosoftVisualStudioLanguagePackageVersion)" />
|
||||
<PackageReference Include="StreamJsonRpc" Version="$(StreamJsonRpcPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Completion;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Tags;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Text.Projection;
|
||||
|
||||
|
|
@ -36,16 +36,33 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
||||
};
|
||||
private readonly Lazy<RazorCodeDocumentProvider> _codeDocumentProvider;
|
||||
private readonly IAsyncCompletionBroker _asyncCompletionBroker;
|
||||
private readonly RazorTextBufferProvider _textBufferProvider;
|
||||
|
||||
[ImportingConstructor]
|
||||
public RazorDirectiveCompletionProvider([Import(typeof(RazorCodeDocumentProvider))] Lazy<RazorCodeDocumentProvider> codeDocumentProvider)
|
||||
public RazorDirectiveCompletionProvider(
|
||||
[Import(typeof(RazorCodeDocumentProvider))] Lazy<RazorCodeDocumentProvider> codeDocumentProvider,
|
||||
IAsyncCompletionBroker asyncCompletionBroker,
|
||||
RazorTextBufferProvider textBufferProvider)
|
||||
{
|
||||
if (codeDocumentProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(codeDocumentProvider));
|
||||
}
|
||||
|
||||
if (asyncCompletionBroker == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(asyncCompletionBroker));
|
||||
}
|
||||
|
||||
if (textBufferProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textBufferProvider));
|
||||
}
|
||||
|
||||
_codeDocumentProvider = codeDocumentProvider;
|
||||
_asyncCompletionBroker = asyncCompletionBroker;
|
||||
_textBufferProvider = textBufferProvider;
|
||||
}
|
||||
|
||||
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
|
||||
|
|
@ -86,6 +103,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (!_textBufferProvider.TryGetFromDocument(context.Document, out var textBuffer) ||
|
||||
!_asyncCompletionBroker.IsCompletionSupported(textBuffer.ContentType))
|
||||
{
|
||||
// Completion is not supported.
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var result = AddCompletionItems(context);
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
// 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.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Core.Imaging;
|
||||
using Microsoft.VisualStudio.Imaging;
|
||||
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
|
||||
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Text.Adornments;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
internal class RazorDirectiveCompletionSource : IAsyncCompletionSource
|
||||
{
|
||||
// Internal for testing
|
||||
internal static readonly object DescriptionKey = new object();
|
||||
internal static readonly ImageElement DirectiveImageGlyph = new ImageElement(
|
||||
new ImageId(KnownImageIds.ImageCatalogGuid, KnownImageIds.Type),
|
||||
"Razor Directive.");
|
||||
internal static readonly ImmutableArray<CompletionFilter> DirectiveCompletionFilters = new[] {
|
||||
new CompletionFilter("Razor Directive", "r", DirectiveImageGlyph)
|
||||
}.ToImmutableArray();
|
||||
private static readonly IEnumerable<DirectiveDescriptor> DefaultDirectives = new[]
|
||||
{
|
||||
CSharpCodeParser.AddTagHelperDirectiveDescriptor,
|
||||
CSharpCodeParser.RemoveTagHelperDirectiveDescriptor,
|
||||
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
||||
};
|
||||
|
||||
// Internal for testing
|
||||
internal readonly VisualStudioRazorParser _parser;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
|
||||
public RazorDirectiveCompletionSource(
|
||||
VisualStudioRazorParser parser,
|
||||
ForegroundDispatcher foregroundDispatcher)
|
||||
{
|
||||
if (parser == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parser));
|
||||
}
|
||||
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
_parser = parser;
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
}
|
||||
|
||||
public Task<CompletionContext> GetCompletionContextAsync(
|
||||
InitialTrigger trigger,
|
||||
SnapshotPoint triggerLocation,
|
||||
SnapshotSpan applicableSpan,
|
||||
CancellationToken token)
|
||||
{
|
||||
_foregroundDispatcher.AssertBackgroundThread();
|
||||
|
||||
var syntaxTree = _parser.CodeDocument?.GetSyntaxTree();
|
||||
if (!AtDirectiveCompletionPoint(syntaxTree, triggerLocation))
|
||||
{
|
||||
return Task.FromResult(CompletionContext.Empty);
|
||||
}
|
||||
|
||||
var completionItems = GetCompletionItems(syntaxTree);
|
||||
var context = new CompletionContext(completionItems.ToImmutableArray());
|
||||
return Task.FromResult(context);
|
||||
}
|
||||
|
||||
public Task<object> GetDescriptionAsync(CompletionItem item, CancellationToken token)
|
||||
{
|
||||
if (!item.Properties.TryGetProperty<string>(DescriptionKey, out var directiveDescription))
|
||||
{
|
||||
directiveDescription = string.Empty;
|
||||
}
|
||||
|
||||
return Task.FromResult<object>(directiveDescription);
|
||||
}
|
||||
|
||||
public bool TryGetApplicableToSpan(char typeChar, SnapshotPoint triggerLocation, out SnapshotSpan applicableToSpan, CancellationToken token)
|
||||
{
|
||||
// The applicable span for completion is the piece of text a completion is for. For example:
|
||||
// @Date|Time.Now
|
||||
// If you trigger completion at the | then the applicable span is the region of 'DateTime'; however, Razor
|
||||
// doesn't know this information so we rely on Roslyn to define what the applicable span for a completion is.
|
||||
applicableToSpan = default(SnapshotSpan);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal List<CompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree)
|
||||
{
|
||||
var directives = syntaxTree.Options.Directives.Concat(DefaultDirectives);
|
||||
var completionItems = new List<CompletionItem>();
|
||||
foreach (var directive in directives)
|
||||
{
|
||||
var completionDisplayText = directive.DisplayName ?? directive.Directive;
|
||||
var completionItem = new CompletionItem(
|
||||
displayText: completionDisplayText,
|
||||
filterText: completionDisplayText,
|
||||
insertText: directive.Directive,
|
||||
source: this,
|
||||
icon: DirectiveImageGlyph,
|
||||
filters: DirectiveCompletionFilters,
|
||||
suffix: string.Empty,
|
||||
sortText: completionDisplayText,
|
||||
attributeIcons: ImmutableArray<ImageElement>.Empty);
|
||||
completionItem.Properties.AddProperty(DescriptionKey, directive.Description);
|
||||
completionItems.Add(completionItem);
|
||||
}
|
||||
|
||||
return completionItems;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool AtDirectiveCompletionPoint(RazorSyntaxTree syntaxTree, SnapshotPoint location)
|
||||
{
|
||||
if (syntaxTree == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var change = new SourceChange(location.Position, 0, string.Empty);
|
||||
var owner = syntaxTree.Root.LocateOwner(change);
|
||||
|
||||
if (owner == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (owner.ChunkGenerator is ExpressionChunkGenerator &&
|
||||
owner.Tokens.All(IsDirectiveCompletableToken) &&
|
||||
// Do not provide IntelliSense for explicit expressions. Explicit expressions will usually look like:
|
||||
// [@] [(] [DateTime.Now] [)]
|
||||
owner.Parent?.Children.Count > 1 &&
|
||||
owner.Parent.Children[1] == owner)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool IsDirectiveCompletableToken(IToken token)
|
||||
{
|
||||
if (!(token is CSharpToken csharpToken))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return csharpToken.Type == CSharpTokenType.Identifier ||
|
||||
// Marker symbol
|
||||
csharpToken.Type == CSharpTokenType.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// 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 System.Linq;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Text.Editor;
|
||||
using Microsoft.VisualStudio.Utilities;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
[System.Composition.Shared]
|
||||
[Export(typeof(IAsyncCompletionSourceProvider))]
|
||||
[Name("Razor directive completion provider.")]
|
||||
[ContentType(RazorLanguage.CoreContentType)]
|
||||
internal class RazorDirectiveCompletionSourceProvider : IAsyncCompletionSourceProvider
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
|
||||
[ImportingConstructor]
|
||||
public RazorDirectiveCompletionSourceProvider(ForegroundDispatcher foregroundDispatcher)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
}
|
||||
|
||||
public IAsyncCompletionSource GetOrCreate(ITextView textView)
|
||||
{
|
||||
if (textView == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textView));
|
||||
}
|
||||
|
||||
var razorBuffer = textView.BufferGraph.GetRazorBuffers().FirstOrDefault();
|
||||
if (!razorBuffer.Properties.TryGetProperty(typeof(RazorDirectiveCompletionSource), out IAsyncCompletionSource completionSource))
|
||||
{
|
||||
completionSource = CreateCompletionSource(razorBuffer);
|
||||
razorBuffer.Properties.AddProperty(typeof(RazorDirectiveCompletionSource), completionSource);
|
||||
}
|
||||
|
||||
return completionSource;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal IAsyncCompletionSource CreateCompletionSource(ITextBuffer razorBuffer)
|
||||
{
|
||||
if (!razorBuffer.Properties.TryGetProperty(typeof(VisualStudioRazorParser), out VisualStudioRazorParser parser))
|
||||
{
|
||||
// Parser hasn't been associated with the text buffer yet.
|
||||
return null;
|
||||
}
|
||||
|
||||
var completionSource = new RazorDirectiveCompletionSource(parser, _foregroundDispatcher);
|
||||
return completionSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -16,9 +15,12 @@ using Microsoft.CodeAnalysis.Completion;
|
|||
using Microsoft.CodeAnalysis.Options;
|
||||
using Microsoft.CodeAnalysis.Tags;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Utilities;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using ITextBuffer = Microsoft.VisualStudio.Text.ITextBuffer;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
|
|
@ -31,12 +33,23 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
||||
};
|
||||
|
||||
public RazorDirectiveCompletionProviderTest()
|
||||
{
|
||||
CompletionBroker = Mock.Of<IAsyncCompletionBroker>(broker => broker.IsCompletionSupported(It.IsAny<IContentType>()) == true);
|
||||
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == Mock.Of<IContentType>());
|
||||
TextBufferProvider = Mock.Of<RazorTextBufferProvider>(provider => provider.TryGetFromDocument(It.IsAny<TextDocument>(), out razorBuffer) == true);
|
||||
}
|
||||
|
||||
private IAsyncCompletionBroker CompletionBroker { get; }
|
||||
|
||||
private RazorTextBufferProvider TextBufferProvider { get; }
|
||||
|
||||
[Fact]
|
||||
public void AtDirectiveCompletionPoint_ReturnsFalseIfChangeHasNoOwner()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", Enumerable.Empty<DirectiveDescriptor>());
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
codeDocumentProvider.Value.TryGetFromDocument(document, out var codeDocument);
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
|
|
@ -61,7 +74,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
[RazorDirectiveCompletionProvider.DescriptionKey] = expectedDescription,
|
||||
}).ToImmutableDictionary());
|
||||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object));
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(
|
||||
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
||||
CompletionBroker,
|
||||
TextBufferProvider);
|
||||
|
||||
// Act
|
||||
var description = await completionProvider.GetDescriptionAsync(document, item, CancellationToken.None);
|
||||
|
|
@ -80,7 +96,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var document = CreateDocument();
|
||||
var item = CompletionItem.Create("TestDirective");
|
||||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object));
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(
|
||||
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
||||
CompletionBroker,
|
||||
TextBufferProvider);
|
||||
|
||||
// Act
|
||||
var description = await completionProvider.GetDescriptionAsync(document, item, CancellationToken.None);
|
||||
|
|
@ -95,7 +114,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>(MockBehavior.Strict);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object));
|
||||
var completionProvider = new FailOnGetCompletionsProvider(
|
||||
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
||||
CompletionBroker,
|
||||
TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
document = document.WithFilePath("NotRazor.cs");
|
||||
var context = CreateContext(1, completionProvider, document);
|
||||
|
|
@ -121,7 +143,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
});
|
||||
|
||||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>(MockBehavior.Strict);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object));
|
||||
var completionProvider = new FailOnGetCompletionsProvider(
|
||||
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
||||
CompletionBroker,
|
||||
TextBufferProvider);
|
||||
var context = CreateContext(1, completionProvider, document);
|
||||
|
||||
// Act & Assert
|
||||
|
|
@ -136,7 +161,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
||||
codeDocumentProvider.Setup(provider => provider.TryGetFromDocument(It.IsAny<TextDocument>(), out codeDocument))
|
||||
.Returns(false);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object));
|
||||
var completionProvider = new FailOnGetCompletionsProvider(
|
||||
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
||||
CompletionBroker,
|
||||
TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
var context = CreateContext(1, completionProvider, document);
|
||||
|
||||
|
|
@ -149,7 +177,11 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", Enumerable.Empty<DirectiveDescriptor>());
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, false);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(
|
||||
codeDocumentProvider,
|
||||
CompletionBroker,
|
||||
TextBufferProvider,
|
||||
canGetSnapshotPoint: false);
|
||||
var document = CreateDocument();
|
||||
var context = CreateContext(0, completionProvider, document);
|
||||
|
||||
|
|
@ -162,7 +194,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", Enumerable.Empty<DirectiveDescriptor>());
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
var context = CreateContext(0, completionProvider, document);
|
||||
|
||||
|
|
@ -177,7 +209,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var codeDocumentProvider = CreateCodeDocumentProvider("@" + content, Enumerable.Empty<DirectiveDescriptor>());
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
var context = CreateContext(1, completionProvider, document);
|
||||
|
||||
|
|
@ -190,7 +222,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var codeDocumentProvider = CreateCodeDocumentProvider("@()", Enumerable.Empty<DirectiveDescriptor>());
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
var context = CreateContext(2, completionProvider, document);
|
||||
|
||||
|
|
@ -206,7 +238,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
codeDocumentProvider.Setup(provider => provider.TryGetFromDocument(It.IsAny<TextDocument>(), out codeDocument))
|
||||
.Returns(true);
|
||||
var completionProvider = new FailOnGetCompletionsProvider(new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object));
|
||||
var completionProvider = new FailOnGetCompletionsProvider(
|
||||
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
||||
CompletionBroker,
|
||||
TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
var context = CreateContext(2, completionProvider, document);
|
||||
|
||||
|
|
@ -219,7 +254,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", Enumerable.Empty<DirectiveDescriptor>());
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider);
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
codeDocumentProvider.Value.TryGetFromDocument(document, out var codeDocument);
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
|
|
@ -240,7 +275,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", new[] { SectionDirective.Directive });
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider);
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
codeDocumentProvider.Value.TryGetFromDocument(document, out var codeDocument);
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
|
|
@ -263,7 +298,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
// Arrange
|
||||
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom");
|
||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", new[] { customDirective });
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider);
|
||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
||||
var document = CreateDocument();
|
||||
codeDocumentProvider.Value.TryGetFromDocument(document, out var codeDocument);
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
|
|
@ -348,8 +383,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
private readonly bool _canGetSnapshotPoint;
|
||||
|
||||
public FailOnGetCompletionsProvider(Lazy<RazorCodeDocumentProvider> codeDocumentProvider, bool canGetSnapshotPoint = true)
|
||||
: base(codeDocumentProvider)
|
||||
public FailOnGetCompletionsProvider(
|
||||
Lazy<RazorCodeDocumentProvider> codeDocumentProvider,
|
||||
IAsyncCompletionBroker asyncCompletionBroker,
|
||||
RazorTextBufferProvider textBufferProvider,
|
||||
bool canGetSnapshotPoint = true)
|
||||
: base(codeDocumentProvider, asyncCompletionBroker, textBufferProvider)
|
||||
{
|
||||
_canGetSnapshotPoint = canGetSnapshotPoint;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
// 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.ObjectModel;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Text.Editor;
|
||||
using Microsoft.VisualStudio.Text.Projection;
|
||||
using Microsoft.VisualStudio.Utilities;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
public class RazorDirectiveCompletionSourceProviderTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
private IContentType RazorContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(RazorLanguage.ContentType) == true);
|
||||
|
||||
private IContentType NonRazorContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(It.IsAny<string>()) == false);
|
||||
|
||||
[Fact]
|
||||
public void CreateCompletionSource_ReturnsNullIfParserHasNotBeenAssocitedWithRazorBuffer()
|
||||
{
|
||||
// Arrange
|
||||
var expectedParser = Mock.Of<VisualStudioRazorParser>();
|
||||
var properties = new PropertyCollection();
|
||||
properties.AddProperty(typeof(VisualStudioRazorParser), expectedParser);
|
||||
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == RazorContentType && buffer.Properties == properties);
|
||||
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher);
|
||||
|
||||
// Act
|
||||
var completionSource = completionSourceProvider.CreateCompletionSource(razorBuffer);
|
||||
|
||||
// Assert
|
||||
var completionSourceImpl = Assert.IsType<RazorDirectiveCompletionSource>(completionSource);
|
||||
Assert.Same(expectedParser, completionSourceImpl._parser);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateCompletionSource_CreatesACompletionSourceWithTextBuffersParser()
|
||||
{
|
||||
// Arrange
|
||||
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == RazorContentType && buffer.Properties == new PropertyCollection());
|
||||
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher);
|
||||
|
||||
// Act
|
||||
var completionSource = completionSourceProvider.CreateCompletionSource(razorBuffer);
|
||||
|
||||
// Assert
|
||||
Assert.Null(completionSource);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrCreate_ReturnsNullIfRazorBufferHasNotBeenAssociatedWithTextView()
|
||||
{
|
||||
// Arrange
|
||||
var textView = CreateTextView(NonRazorContentType, new PropertyCollection());
|
||||
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher);
|
||||
|
||||
// Act
|
||||
var completionSource = completionSourceProvider.GetOrCreate(textView);
|
||||
|
||||
// Assert
|
||||
Assert.Null(completionSource);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrCreate_CachesCompletionSource()
|
||||
{
|
||||
// Arrange
|
||||
var expectedParser = Mock.Of<VisualStudioRazorParser>();
|
||||
var properties = new PropertyCollection();
|
||||
properties.AddProperty(typeof(VisualStudioRazorParser), expectedParser);
|
||||
var textView = CreateTextView(RazorContentType, properties);
|
||||
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher);
|
||||
|
||||
// Act
|
||||
var completionSource1 = completionSourceProvider.GetOrCreate(textView);
|
||||
var completionSource2 = completionSourceProvider.GetOrCreate(textView);
|
||||
|
||||
// Assert
|
||||
Assert.Same(completionSource1, completionSource2);
|
||||
}
|
||||
|
||||
private static ITextView CreateTextView(IContentType contentType, PropertyCollection properties)
|
||||
{
|
||||
var bufferGraph = new Mock<IBufferGraph>();
|
||||
bufferGraph.Setup(graph => graph.GetTextBuffers(It.IsAny<Predicate<ITextBuffer>>()))
|
||||
.Returns(new Collection<ITextBuffer>()
|
||||
{
|
||||
Mock.Of<ITextBuffer>(buffer => buffer.ContentType == contentType && buffer.Properties == properties)
|
||||
});
|
||||
var textView = Mock.Of<ITextView>(view => view.BufferGraph == bufferGraph.Object);
|
||||
|
||||
return textView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
// 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;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
|
||||
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using Span = Microsoft.VisualStudio.Text.Span;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
public class RazorDirectiveCompletionSourceTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
private static readonly IReadOnlyList<DirectiveDescriptor> DefaultDirectives = new[]
|
||||
{
|
||||
CSharpCodeParser.AddTagHelperDirectiveDescriptor,
|
||||
CSharpCodeParser.RemoveTagHelperDirectiveDescriptor,
|
||||
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
||||
};
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task GetCompletionContextAsync_DoesNotProvideCompletionsPriorToParseResults()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@validCompletion";
|
||||
var parser = Mock.Of<VisualStudioRazorParser>(); // CodeDocument will be null faking a parser without a parse.
|
||||
var completionSource = new RazorDirectiveCompletionSource(parser, Dispatcher);
|
||||
var documentSnapshot = new StringTextSnapshot(text);
|
||||
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
||||
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(1, text.Length - 1 /* @ */));
|
||||
|
||||
// Act
|
||||
var completionContext = await Task.Run(
|
||||
async () => await completionSource.GetCompletionContextAsync(new InitialTrigger(), triggerLocation, applicableSpan, CancellationToken.None));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(completionContext.Items);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task GetCompletionContextAsync_DoesNotProvideCompletionsWhenNotAtCompletionPoint()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@(NotValidCompletionLocation)";
|
||||
var parser = CreateParser(text);
|
||||
var completionSource = new RazorDirectiveCompletionSource(parser, Dispatcher);
|
||||
var documentSnapshot = new StringTextSnapshot(text);
|
||||
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
||||
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(2, text.Length - 3 /* @() */));
|
||||
|
||||
// Act
|
||||
var completionContext = await Task.Run(
|
||||
async () => await completionSource.GetCompletionContextAsync(new InitialTrigger(), triggerLocation, applicableSpan, CancellationToken.None));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(completionContext.Items);
|
||||
}
|
||||
|
||||
// This is more of an integration level test validating the end-to-end completion flow.
|
||||
[ForegroundFact]
|
||||
public async Task GetCompletionContextAsync_ProvidesCompletionsWhenAtCompletionPoint()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@addTag";
|
||||
var parser = CreateParser(text, SectionDirective.Directive);
|
||||
var completionSource = new RazorDirectiveCompletionSource(parser, Dispatcher);
|
||||
var documentSnapshot = new StringTextSnapshot(text);
|
||||
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
||||
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(1, text.Length - 1 /* @ */));
|
||||
|
||||
// Act
|
||||
var completionContext = await Task.Run(
|
||||
async () => await completionSource.GetCompletionContextAsync(new InitialTrigger(), triggerLocation, applicableSpan, CancellationToken.None));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
completionContext.Items,
|
||||
item => AssertRazorCompletionItem(SectionDirective.Directive, item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[0], item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[1], item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[2], item, completionSource));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCompletionItems_ReturnsDefaultDirectivesAsCompletionItems()
|
||||
{
|
||||
// Arrange
|
||||
var syntaxTree = CreateSyntaxTree("@addTag");
|
||||
var completionSource = new RazorDirectiveCompletionSource(Mock.Of<VisualStudioRazorParser>(), Dispatcher);
|
||||
|
||||
// Act
|
||||
var completionItems = completionSource.GetCompletionItems(syntaxTree);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
completionItems,
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[0], item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[1], item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[2], item, completionSource));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCompletionItems_ReturnsCustomDirectivesAsCompletionItems()
|
||||
{
|
||||
// Arrange
|
||||
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom", builder =>
|
||||
{
|
||||
builder.Description = "My Custom Directive.";
|
||||
});
|
||||
var syntaxTree = CreateSyntaxTree("@addTag", customDirective);
|
||||
var completionSource = new RazorDirectiveCompletionSource(Mock.Of<VisualStudioRazorParser>(), Dispatcher);
|
||||
|
||||
// Act
|
||||
var completionItems = completionSource.GetCompletionItems(syntaxTree);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
completionItems,
|
||||
item => AssertRazorCompletionItem(customDirective, item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[0], item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[1], item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[2], item, completionSource));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCompletionItems_UsesDisplayNamesWhenNotNull()
|
||||
{
|
||||
// Arrange
|
||||
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom", builder =>
|
||||
{
|
||||
builder.DisplayName = "different";
|
||||
builder.Description = "My Custom Directive.";
|
||||
});
|
||||
var syntaxTree = CreateSyntaxTree("@addTag", customDirective);
|
||||
var completionSource = new RazorDirectiveCompletionSource(Mock.Of<VisualStudioRazorParser>(), Dispatcher);
|
||||
|
||||
// Act
|
||||
var completionItems = completionSource.GetCompletionItems(syntaxTree);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
completionItems,
|
||||
item => AssertRazorCompletionItem("different", customDirective, item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[0], item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[1], item, completionSource),
|
||||
item => AssertRazorCompletionItem(DefaultDirectives[2], item, completionSource));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDescriptionAsync_AddsDirectiveDescriptionIfPropertyExists()
|
||||
{
|
||||
// Arrange
|
||||
var completionItem = new CompletionItem("TestDirective", Mock.Of<IAsyncCompletionSource>());
|
||||
var expectedDescription = "The expected description";
|
||||
completionItem.Properties.AddProperty(RazorDirectiveCompletionSource.DescriptionKey, expectedDescription);
|
||||
var completionSource = new RazorDirectiveCompletionSource(Mock.Of<VisualStudioRazorParser>(), Dispatcher);
|
||||
|
||||
// Act
|
||||
var descriptionObject = await completionSource.GetDescriptionAsync(completionItem, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var description = Assert.IsType<string>(descriptionObject);
|
||||
Assert.Equal(expectedDescription, descriptionObject);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDescriptionAsync_DoesNotAddDescriptionWhenPropertyAbsent()
|
||||
{
|
||||
// Arrange
|
||||
var completionItem = new CompletionItem("TestDirective", Mock.Of<IAsyncCompletionSource>());
|
||||
var completionSource = new RazorDirectiveCompletionSource(Mock.Of<VisualStudioRazorParser>(), Dispatcher);
|
||||
|
||||
// Act
|
||||
var descriptionObject = await completionSource.GetDescriptionAsync(completionItem, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var description = Assert.IsType<string>(descriptionObject);
|
||||
Assert.Equal(string.Empty, description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AtDirectiveCompletionPoint_ReturnsFalseIfSyntaxTreeNull()
|
||||
{
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.AtDirectiveCompletionPoint(syntaxTree: null, location: new SnapshotPoint());
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AtDirectiveCompletionPoint_ReturnsFalseIfNoOwner()
|
||||
{
|
||||
// Arrange
|
||||
var syntaxTree = CreateSyntaxTree("@");
|
||||
var snapshotPoint = new SnapshotPoint(new StringTextSnapshot("@ text"), 2);
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.AtDirectiveCompletionPoint(syntaxTree, snapshotPoint);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AtDirectiveCompletionPoint_ReturnsFalseWhenOwnerIsNotExpression()
|
||||
{
|
||||
// Arrange
|
||||
var syntaxTree = CreateSyntaxTree("@{");
|
||||
var snapshotPoint = new SnapshotPoint(new StringTextSnapshot("@{"), 2);
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.AtDirectiveCompletionPoint(syntaxTree, snapshotPoint);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AtDirectiveCompletionPoint_ReturnsFalseWhenOwnerIsComplexExpression()
|
||||
{
|
||||
// Arrange
|
||||
var syntaxTree = CreateSyntaxTree("@DateTime.Now");
|
||||
var snapshotPoint = new SnapshotPoint(new StringTextSnapshot("@DateTime.Now"), 2);
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.AtDirectiveCompletionPoint(syntaxTree, snapshotPoint);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AtDirectiveCompletionPoint_ReturnsFalseWhenOwnerIsExplicitExpression()
|
||||
{
|
||||
// Arrange
|
||||
var syntaxTree = CreateSyntaxTree("@(something)");
|
||||
var snapshotPoint = new SnapshotPoint(new StringTextSnapshot("@(something)"), 4);
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.AtDirectiveCompletionPoint(syntaxTree, snapshotPoint);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AtDirectiveCompletionPoint_ReturnsTrueForSimpleImplicitExpressions()
|
||||
{
|
||||
// Arrange
|
||||
var syntaxTree = CreateSyntaxTree("@mod");
|
||||
var snapshotPoint = new SnapshotPoint(new StringTextSnapshot("@mod"), 2);
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.AtDirectiveCompletionPoint(syntaxTree, snapshotPoint);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDirectiveCompletableToken_ReturnsTrueForCSharpIdentifiers()
|
||||
{
|
||||
// Arrange
|
||||
var csharpToken = new CSharpToken("model", CSharpTokenType.Identifier);
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.IsDirectiveCompletableToken(csharpToken);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDirectiveCompletableToken_ReturnsTrueForCSharpMarkerTokens()
|
||||
{
|
||||
// Arrange
|
||||
var csharpToken = new CSharpToken(string.Empty, CSharpTokenType.Unknown);
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.IsDirectiveCompletableToken(csharpToken);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDirectiveCompletableToken_ReturnsFalseForNonCSharpTokens()
|
||||
{
|
||||
// Arrange
|
||||
var token = Mock.Of<IToken>();
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.IsDirectiveCompletableToken(token);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDirectiveCompletableToken_ReturnsFalseForInvalidCSharpTokens()
|
||||
{
|
||||
// Arrange
|
||||
var csharpToken = new CSharpToken("~", CSharpTokenType.Tilde);
|
||||
|
||||
// Act
|
||||
var result = RazorDirectiveCompletionSource.IsDirectiveCompletableToken(csharpToken);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
private static void AssertRazorCompletionItem(string completionDisplayText, DirectiveDescriptor directive, CompletionItem item, IAsyncCompletionSource source)
|
||||
{
|
||||
Assert.Equal(item.DisplayText, completionDisplayText);
|
||||
Assert.Equal(item.FilterText, completionDisplayText);
|
||||
Assert.Equal(item.InsertText, directive.Directive);
|
||||
Assert.Same(item.Source, source);
|
||||
Assert.True(item.Properties.TryGetProperty<string>(RazorDirectiveCompletionSource.DescriptionKey, out var actualDescription));
|
||||
Assert.Equal(directive.Description, actualDescription);
|
||||
|
||||
AssertRazorCompletionItemDefaults(item);
|
||||
}
|
||||
|
||||
private static void AssertRazorCompletionItem(DirectiveDescriptor directive, CompletionItem item, IAsyncCompletionSource source) =>
|
||||
AssertRazorCompletionItem(directive.Directive, directive, item, source);
|
||||
|
||||
private static void AssertRazorCompletionItemDefaults(CompletionItem item)
|
||||
{
|
||||
Assert.Equal(item.Icon.ImageId.Guid, RazorDirectiveCompletionSource.DirectiveImageGlyph.ImageId.Guid);
|
||||
var filter = Assert.Single(item.Filters);
|
||||
Assert.Same(RazorDirectiveCompletionSource.DirectiveCompletionFilters[0], filter);
|
||||
Assert.Equal(string.Empty, item.Suffix);
|
||||
Assert.Equal(item.DisplayText, item.SortText);
|
||||
Assert.Empty(item.AttributeIcons);
|
||||
}
|
||||
|
||||
private static VisualStudioRazorParser CreateParser(string text, params DirectiveDescriptor[] directives)
|
||||
{
|
||||
var syntaxTree = CreateSyntaxTree(text, directives);
|
||||
var codeDocument = TestRazorCodeDocument.Create(text);
|
||||
codeDocument.SetSyntaxTree(syntaxTree);
|
||||
var parser = Mock.Of<VisualStudioRazorParser>(p => p.CodeDocument == codeDocument);
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
private static RazorSyntaxTree CreateSyntaxTree(string text, params DirectiveDescriptor[] directives)
|
||||
{
|
||||
var sourceDocument = TestRazorSourceDocument.Create(text);
|
||||
var options = RazorParserOptions.Create(builder =>
|
||||
{
|
||||
foreach (var directive in directives)
|
||||
{
|
||||
builder.Directives.Add(directive);
|
||||
}
|
||||
});
|
||||
var syntaxTree = RazorSyntaxTree.Parse(sourceDocument, options);
|
||||
return syntaxTree;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue