Refactor completion logic into standalone service.
- Migrated the completion item source provider and the legacy directive completion provider to use the new service. - Cleaned up duplicate tests that were both verifying common completion functionality. - Ensured that the legacy `RazorDirectiveCompletionProvider` did not result in additional Razor assembly loads when in C# scenarios. #2530
This commit is contained in:
parent
aec88e3eba
commit
572b55690d
|
|
@ -0,0 +1,98 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
[System.Composition.Shared]
|
||||||
|
[Export(typeof(RazorCompletionFactsService))]
|
||||||
|
internal class DefaultRazorCompletionFactsService : RazorCompletionFactsService
|
||||||
|
{
|
||||||
|
private static readonly IEnumerable<DirectiveDescriptor> DefaultDirectives = new[]
|
||||||
|
{
|
||||||
|
CSharpCodeParser.AddTagHelperDirectiveDescriptor,
|
||||||
|
CSharpCodeParser.RemoveTagHelperDirectiveDescriptor,
|
||||||
|
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
||||||
|
};
|
||||||
|
|
||||||
|
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree, SourceSpan location)
|
||||||
|
{
|
||||||
|
var completionItems = new List<RazorCompletionItem>();
|
||||||
|
|
||||||
|
if (AtDirectiveCompletionPoint(syntaxTree, location))
|
||||||
|
{
|
||||||
|
var directiveCompletions = GetDirectiveCompletionItems(syntaxTree);
|
||||||
|
completionItems.AddRange(directiveCompletions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return completionItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal for testing
|
||||||
|
internal static List<RazorCompletionItem> GetDirectiveCompletionItems(RazorSyntaxTree syntaxTree)
|
||||||
|
{
|
||||||
|
var directives = syntaxTree.Options.Directives.Concat(DefaultDirectives);
|
||||||
|
var completionItems = new List<RazorCompletionItem>();
|
||||||
|
foreach (var directive in directives)
|
||||||
|
{
|
||||||
|
var completionDisplayText = directive.DisplayName ?? directive.Directive;
|
||||||
|
var completionItem = new RazorCompletionItem(
|
||||||
|
completionDisplayText,
|
||||||
|
directive.Directive,
|
||||||
|
directive.Description,
|
||||||
|
RazorCompletionItemKind.Directive);
|
||||||
|
completionItems.Add(completionItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return completionItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal for testing
|
||||||
|
internal static bool AtDirectiveCompletionPoint(RazorSyntaxTree syntaxTree, SourceSpan location)
|
||||||
|
{
|
||||||
|
if (syntaxTree == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var change = new SourceChange(location, 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,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 System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
internal abstract class RazorCompletionFactsService
|
||||||
|
{
|
||||||
|
public abstract IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree, SourceSpan location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
internal sealed class RazorCompletionItem
|
||||||
|
{
|
||||||
|
public RazorCompletionItem(
|
||||||
|
string displayText,
|
||||||
|
string insertText,
|
||||||
|
string description,
|
||||||
|
RazorCompletionItemKind kind)
|
||||||
|
{
|
||||||
|
if (displayText == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(displayText));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertText == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(insertText));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(description));
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayText = displayText;
|
||||||
|
InsertText = insertText;
|
||||||
|
Description = description;
|
||||||
|
Kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayText { get; }
|
||||||
|
|
||||||
|
public string InsertText { get; }
|
||||||
|
|
||||||
|
public string Description { get; }
|
||||||
|
|
||||||
|
public RazorCompletionItemKind Kind { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// 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.Editor.Razor
|
||||||
|
{
|
||||||
|
internal enum RazorCompletionItemKind
|
||||||
|
{
|
||||||
|
Directive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,12 +36,14 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
||||||
};
|
};
|
||||||
private readonly Lazy<RazorCodeDocumentProvider> _codeDocumentProvider;
|
private readonly Lazy<RazorCodeDocumentProvider> _codeDocumentProvider;
|
||||||
|
private readonly Lazy<RazorCompletionFactsService> _completionFactsService;
|
||||||
private readonly IAsyncCompletionBroker _asyncCompletionBroker;
|
private readonly IAsyncCompletionBroker _asyncCompletionBroker;
|
||||||
private readonly RazorTextBufferProvider _textBufferProvider;
|
private readonly RazorTextBufferProvider _textBufferProvider;
|
||||||
|
|
||||||
[ImportingConstructor]
|
[ImportingConstructor]
|
||||||
public RazorDirectiveCompletionProvider(
|
public RazorDirectiveCompletionProvider(
|
||||||
[Import(typeof(RazorCodeDocumentProvider))] Lazy<RazorCodeDocumentProvider> codeDocumentProvider,
|
[Import(typeof(RazorCodeDocumentProvider))] Lazy<RazorCodeDocumentProvider> codeDocumentProvider,
|
||||||
|
[Import(typeof(RazorCompletionFactsService))] Lazy<RazorCompletionFactsService> completionFactsService,
|
||||||
IAsyncCompletionBroker asyncCompletionBroker,
|
IAsyncCompletionBroker asyncCompletionBroker,
|
||||||
RazorTextBufferProvider textBufferProvider)
|
RazorTextBufferProvider textBufferProvider)
|
||||||
{
|
{
|
||||||
|
|
@ -50,6 +52,11 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
throw new ArgumentNullException(nameof(codeDocumentProvider));
|
throw new ArgumentNullException(nameof(codeDocumentProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (completionFactsService == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(completionFactsService));
|
||||||
|
}
|
||||||
|
|
||||||
if (asyncCompletionBroker == null)
|
if (asyncCompletionBroker == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(asyncCompletionBroker));
|
throw new ArgumentNullException(nameof(asyncCompletionBroker));
|
||||||
|
|
@ -61,6 +68,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
_codeDocumentProvider = codeDocumentProvider;
|
_codeDocumentProvider = codeDocumentProvider;
|
||||||
|
_completionFactsService = completionFactsService;
|
||||||
_asyncCompletionBroker = asyncCompletionBroker;
|
_asyncCompletionBroker = asyncCompletionBroker;
|
||||||
_textBufferProvider = textBufferProvider;
|
_textBufferProvider = textBufferProvider;
|
||||||
}
|
}
|
||||||
|
|
@ -133,70 +141,41 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AtDirectiveCompletionPoint(syntaxTree, context))
|
if (!TryGetRazorSnapshotPoint(context, out var razorSnapshotPoint))
|
||||||
{
|
{
|
||||||
// Can't have a valid directive at the current location.
|
// Could not find associated Razor location.
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
var completionItems = GetCompletionItems(syntaxTree);
|
var location = new SourceSpan(razorSnapshotPoint.Position, 0);
|
||||||
context.AddItems(completionItems);
|
var razorCompletionItems = _completionFactsService.Value.GetCompletionItems(syntaxTree, location);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
foreach (var razorCompletionItem in razorCompletionItems)
|
||||||
}
|
|
||||||
|
|
||||||
// Internal virtual for testing
|
|
||||||
internal virtual IEnumerable<CompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree)
|
|
||||||
{
|
|
||||||
var directives = syntaxTree.Options.Directives.Concat(DefaultDirectives);
|
|
||||||
var completionItems = new List<CompletionItem>();
|
|
||||||
foreach (var directive in directives)
|
|
||||||
{
|
{
|
||||||
var propertyDictionary = new Dictionary<string, string>(StringComparer.Ordinal);
|
if (razorCompletionItem.Kind != RazorCompletionItemKind.Directive)
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(directive.Description))
|
|
||||||
{
|
{
|
||||||
propertyDictionary[DescriptionKey] = directive.Description;
|
// Don't support any other types of completion kinds other than directives.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var propertyDictionary = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
if (!string.IsNullOrEmpty(razorCompletionItem.Description))
|
||||||
|
{
|
||||||
|
propertyDictionary[DescriptionKey] = razorCompletionItem.Description;
|
||||||
}
|
}
|
||||||
|
|
||||||
var completionItem = CompletionItem.Create(
|
var completionItem = CompletionItem.Create(
|
||||||
directive.Directive,
|
razorCompletionItem.InsertText,
|
||||||
// This groups all Razor directives together
|
// This groups all Razor directives together
|
||||||
sortText: "_RazorDirective_",
|
sortText: "_RazorDirective_",
|
||||||
rules: CompletionItemRules.Create(formatOnCommit: false),
|
rules: CompletionItemRules.Create(formatOnCommit: false),
|
||||||
tags: ImmutableArray.Create(WellKnownTags.Intrinsic),
|
tags: ImmutableArray.Create(WellKnownTags.Intrinsic),
|
||||||
properties: propertyDictionary.ToImmutableDictionary());
|
properties: propertyDictionary.ToImmutableDictionary());
|
||||||
completionItems.Add(completionItem);
|
|
||||||
|
context.AddItem(completionItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
return completionItems;
|
return Task.CompletedTask;
|
||||||
}
|
|
||||||
|
|
||||||
// Internal for testing
|
|
||||||
internal bool AtDirectiveCompletionPoint(RazorSyntaxTree syntaxTree, CompletionContext context)
|
|
||||||
{
|
|
||||||
if (TryGetRazorSnapshotPoint(context, out var razorSnapshotPoint))
|
|
||||||
{
|
|
||||||
var change = new SourceChange(razorSnapshotPoint.Position, 0, string.Empty);
|
|
||||||
var owner = syntaxTree.Root.LocateOwner(change);
|
|
||||||
|
|
||||||
if (owner == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (owner.ChunkGenerator is ExpressionChunkGenerator &&
|
|
||||||
owner.Tokens.All(IsDirectiveCompletableSymbol) &&
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool TryGetRazorSnapshotPoint(CompletionContext context, out SnapshotPoint snapshotPoint)
|
protected virtual bool TryGetRazorSnapshotPoint(CompletionContext context, out SnapshotPoint snapshotPoint)
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
using Microsoft.VisualStudio.Core.Imaging;
|
using Microsoft.VisualStudio.Core.Imaging;
|
||||||
using Microsoft.VisualStudio.Imaging;
|
using Microsoft.VisualStudio.Imaging;
|
||||||
|
|
@ -29,33 +27,35 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
internal static readonly ImmutableArray<CompletionFilter> DirectiveCompletionFilters = new[] {
|
internal static readonly ImmutableArray<CompletionFilter> DirectiveCompletionFilters = new[] {
|
||||||
new CompletionFilter("Razor Directive", "r", DirectiveImageGlyph)
|
new CompletionFilter("Razor Directive", "r", DirectiveImageGlyph)
|
||||||
}.ToImmutableArray();
|
}.ToImmutableArray();
|
||||||
private static readonly IEnumerable<DirectiveDescriptor> DefaultDirectives = new[]
|
|
||||||
{
|
|
||||||
CSharpCodeParser.AddTagHelperDirectiveDescriptor,
|
|
||||||
CSharpCodeParser.RemoveTagHelperDirectiveDescriptor,
|
|
||||||
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal readonly VisualStudioRazorParser _parser;
|
internal readonly VisualStudioRazorParser _parser;
|
||||||
|
private readonly RazorCompletionFactsService _completionFactsService;
|
||||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||||
|
|
||||||
public RazorDirectiveCompletionSource(
|
public RazorDirectiveCompletionSource(
|
||||||
|
ForegroundDispatcher foregroundDispatcher,
|
||||||
VisualStudioRazorParser parser,
|
VisualStudioRazorParser parser,
|
||||||
ForegroundDispatcher foregroundDispatcher)
|
RazorCompletionFactsService completionFactsService)
|
||||||
{
|
{
|
||||||
if (parser == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(parser));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foregroundDispatcher == null)
|
if (foregroundDispatcher == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||||
}
|
}
|
||||||
|
|
||||||
_parser = parser;
|
if (parser == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completionFactsService == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(completionFactsService));
|
||||||
|
}
|
||||||
|
|
||||||
_foregroundDispatcher = foregroundDispatcher;
|
_foregroundDispatcher = foregroundDispatcher;
|
||||||
|
_parser = parser;
|
||||||
|
_completionFactsService = completionFactsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<CompletionContext> GetCompletionContextAsync(
|
public Task<CompletionContext> GetCompletionContextAsync(
|
||||||
|
|
@ -67,12 +67,31 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
_foregroundDispatcher.AssertBackgroundThread();
|
_foregroundDispatcher.AssertBackgroundThread();
|
||||||
|
|
||||||
var syntaxTree = _parser.CodeDocument?.GetSyntaxTree();
|
var syntaxTree = _parser.CodeDocument?.GetSyntaxTree();
|
||||||
if (!AtDirectiveCompletionPoint(syntaxTree, triggerLocation))
|
var location = new SourceSpan(applicableSpan.Start.Position, applicableSpan.Length);
|
||||||
{
|
var razorCompletionItems = _completionFactsService.GetCompletionItems(syntaxTree, location);
|
||||||
return Task.FromResult(CompletionContext.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
var completionItems = GetCompletionItems(syntaxTree);
|
var completionItems = new List<CompletionItem>();
|
||||||
|
foreach (var razorCompletionItem in razorCompletionItems)
|
||||||
|
{
|
||||||
|
if (razorCompletionItem.Kind != RazorCompletionItemKind.Directive)
|
||||||
|
{
|
||||||
|
// Don't support any other types of completion kinds other than directives.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var completionItem = new CompletionItem(
|
||||||
|
displayText: razorCompletionItem.DisplayText,
|
||||||
|
filterText: razorCompletionItem.DisplayText,
|
||||||
|
insertText: razorCompletionItem.InsertText,
|
||||||
|
source: this,
|
||||||
|
icon: DirectiveImageGlyph,
|
||||||
|
filters: DirectiveCompletionFilters,
|
||||||
|
suffix: string.Empty,
|
||||||
|
sortText: razorCompletionItem.DisplayText,
|
||||||
|
attributeIcons: ImmutableArray<ImageElement>.Empty);
|
||||||
|
completionItem.Properties.AddProperty(DescriptionKey, razorCompletionItem.Description);
|
||||||
|
completionItems.Add(completionItem);
|
||||||
|
}
|
||||||
var context = new CompletionContext(completionItems.ToImmutableArray());
|
var context = new CompletionContext(completionItems.ToImmutableArray());
|
||||||
return Task.FromResult(context);
|
return Task.FromResult(context);
|
||||||
}
|
}
|
||||||
|
|
@ -96,72 +115,5 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
applicableToSpan = default(SnapshotSpan);
|
applicableToSpan = default(SnapshotSpan);
|
||||||
return false;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,25 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
internal class RazorDirectiveCompletionSourceProvider : IAsyncCompletionSourceProvider
|
internal class RazorDirectiveCompletionSourceProvider : IAsyncCompletionSourceProvider
|
||||||
{
|
{
|
||||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||||
|
private readonly RazorCompletionFactsService _completionFactsService;
|
||||||
|
|
||||||
[ImportingConstructor]
|
[ImportingConstructor]
|
||||||
public RazorDirectiveCompletionSourceProvider(ForegroundDispatcher foregroundDispatcher)
|
public RazorDirectiveCompletionSourceProvider(
|
||||||
|
ForegroundDispatcher foregroundDispatcher,
|
||||||
|
RazorCompletionFactsService completionFactsService)
|
||||||
{
|
{
|
||||||
if (foregroundDispatcher == null)
|
if (foregroundDispatcher == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (completionFactsService == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(completionFactsService));
|
||||||
|
}
|
||||||
|
|
||||||
_foregroundDispatcher = foregroundDispatcher;
|
_foregroundDispatcher = foregroundDispatcher;
|
||||||
|
_completionFactsService = completionFactsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncCompletionSource GetOrCreate(ITextView textView)
|
public IAsyncCompletionSource GetOrCreate(ITextView textView)
|
||||||
|
|
@ -57,7 +66,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var completionSource = new RazorDirectiveCompletionSource(parser, _foregroundDispatcher);
|
var completionSource = new RazorDirectiveCompletionSource(_foregroundDispatcher, parser, _completionFactsService);
|
||||||
return completionSource;
|
return completionSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,239 @@
|
||||||
|
// 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.AspNetCore.Razor.Language.Legacy;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
public class DefaultRazorCompletionFactsServiceTest
|
||||||
|
{
|
||||||
|
private static readonly IReadOnlyList<DirectiveDescriptor> DefaultDirectives = new[]
|
||||||
|
{
|
||||||
|
CSharpCodeParser.AddTagHelperDirectiveDescriptor,
|
||||||
|
CSharpCodeParser.RemoveTagHelperDirectiveDescriptor,
|
||||||
|
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
||||||
|
};
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDirectiveCompletionItems_ReturnsDefaultDirectivesAsCompletionItems()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var syntaxTree = CreateSyntaxTree("@addTag");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var completionItems = DefaultRazorCompletionFactsService.GetDirectiveCompletionItems(syntaxTree);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
completionItems,
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[0], item),
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[1], item),
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[2], item));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDirectiveCompletionItems_ReturnsCustomDirectivesAsCompletionItems()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom", builder =>
|
||||||
|
{
|
||||||
|
builder.Description = "My Custom Directive.";
|
||||||
|
});
|
||||||
|
var syntaxTree = CreateSyntaxTree("@addTag", customDirective);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var completionItems = DefaultRazorCompletionFactsService.GetDirectiveCompletionItems(syntaxTree);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
completionItems,
|
||||||
|
item => AssertRazorCompletionItem(customDirective, item),
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[0], item),
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[1], item),
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[2], item));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDirectiveCompletionItems_UsesDisplayNamesWhenNotNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom", builder =>
|
||||||
|
{
|
||||||
|
builder.DisplayName = "different";
|
||||||
|
builder.Description = "My Custom Directive.";
|
||||||
|
});
|
||||||
|
var syntaxTree = CreateSyntaxTree("@addTag", customDirective);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var completionItems = DefaultRazorCompletionFactsService.GetDirectiveCompletionItems(syntaxTree);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
completionItems,
|
||||||
|
item => AssertRazorCompletionItem("different", customDirective, item),
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[0], item),
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[1], item),
|
||||||
|
item => AssertRazorCompletionItem(DefaultDirectives[2], item));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AtDirectiveCompletionPoint_ReturnsFalseIfSyntaxTreeNull()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.AtDirectiveCompletionPoint(syntaxTree: null, location: new SourceSpan(0, 0));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AtDirectiveCompletionPoint_ReturnsFalseIfNoOwner()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var syntaxTree = CreateSyntaxTree("@");
|
||||||
|
var location = new SourceSpan(2, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.AtDirectiveCompletionPoint(syntaxTree, location);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AtDirectiveCompletionPoint_ReturnsFalseWhenOwnerIsNotExpression()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var syntaxTree = CreateSyntaxTree("@{");
|
||||||
|
var location = new SourceSpan(2, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.AtDirectiveCompletionPoint(syntaxTree, location);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AtDirectiveCompletionPoint_ReturnsFalseWhenOwnerIsComplexExpression()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var syntaxTree = CreateSyntaxTree("@DateTime.Now");
|
||||||
|
var location = new SourceSpan(2, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.AtDirectiveCompletionPoint(syntaxTree, location);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AtDirectiveCompletionPoint_ReturnsFalseWhenOwnerIsExplicitExpression()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var syntaxTree = CreateSyntaxTree("@(something)");
|
||||||
|
var location = new SourceSpan(4, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.AtDirectiveCompletionPoint(syntaxTree, location);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AtDirectiveCompletionPoint_ReturnsTrueForSimpleImplicitExpressions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var syntaxTree = CreateSyntaxTree("@mod");
|
||||||
|
var location = new SourceSpan(2, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.AtDirectiveCompletionPoint(syntaxTree, location);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsDirectiveCompletableToken_ReturnsTrueForCSharpIdentifiers()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var csharpToken = new CSharpToken("model", CSharpTokenType.Identifier);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.IsDirectiveCompletableToken(csharpToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsDirectiveCompletableToken_ReturnsTrueForCSharpMarkerTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var csharpToken = new CSharpToken(string.Empty, CSharpTokenType.Unknown);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.IsDirectiveCompletableToken(csharpToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsDirectiveCompletableToken_ReturnsFalseForNonCSharpTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var token = Mock.Of<IToken>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.IsDirectiveCompletableToken(token);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsDirectiveCompletableToken_ReturnsFalseForInvalidCSharpTokens()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var csharpToken = new CSharpToken("~", CSharpTokenType.Tilde);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = DefaultRazorCompletionFactsService.IsDirectiveCompletableToken(csharpToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AssertRazorCompletionItem(string completionDisplayText, DirectiveDescriptor directive, RazorCompletionItem item)
|
||||||
|
{
|
||||||
|
Assert.Equal(item.DisplayText, completionDisplayText);
|
||||||
|
Assert.Equal(item.InsertText, directive.Directive);
|
||||||
|
Assert.Equal(directive.Description, item.Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AssertRazorCompletionItem(DirectiveDescriptor directive, RazorCompletionItem item) =>
|
||||||
|
AssertRazorCompletionItem(directive.Directive, directive, item);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,12 +8,9 @@ using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Completion;
|
using Microsoft.CodeAnalysis.Completion;
|
||||||
using Microsoft.CodeAnalysis.Options;
|
using Microsoft.CodeAnalysis.Options;
|
||||||
using Microsoft.CodeAnalysis.Tags;
|
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
|
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
|
||||||
using Microsoft.VisualStudio.Text;
|
using Microsoft.VisualStudio.Text;
|
||||||
|
|
@ -26,41 +23,19 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class RazorDirectiveCompletionProviderTest
|
public class RazorDirectiveCompletionProviderTest
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyList<DirectiveDescriptor> DefaultDirectives = new[]
|
|
||||||
{
|
|
||||||
CSharpCodeParser.AddTagHelperDirectiveDescriptor,
|
|
||||||
CSharpCodeParser.RemoveTagHelperDirectiveDescriptor,
|
|
||||||
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
|
||||||
};
|
|
||||||
|
|
||||||
public RazorDirectiveCompletionProviderTest()
|
public RazorDirectiveCompletionProviderTest()
|
||||||
{
|
{
|
||||||
CompletionBroker = Mock.Of<IAsyncCompletionBroker>(broker => broker.IsCompletionSupported(It.IsAny<IContentType>()) == true);
|
CompletionBroker = Mock.Of<IAsyncCompletionBroker>(broker => broker.IsCompletionSupported(It.IsAny<IContentType>()) == true);
|
||||||
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == Mock.Of<IContentType>());
|
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == Mock.Of<IContentType>());
|
||||||
TextBufferProvider = Mock.Of<RazorTextBufferProvider>(provider => provider.TryGetFromDocument(It.IsAny<TextDocument>(), out razorBuffer) == true);
|
TextBufferProvider = Mock.Of<RazorTextBufferProvider>(provider => provider.TryGetFromDocument(It.IsAny<TextDocument>(), out razorBuffer) == true);
|
||||||
|
CompletionFactsService = new DefaultRazorCompletionFactsService();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IAsyncCompletionBroker CompletionBroker { get; }
|
private IAsyncCompletionBroker CompletionBroker { get; }
|
||||||
|
|
||||||
private RazorTextBufferProvider TextBufferProvider { get; }
|
private RazorTextBufferProvider TextBufferProvider { get; }
|
||||||
|
|
||||||
[Fact]
|
private RazorCompletionFactsService CompletionFactsService { get; }
|
||||||
public void AtDirectiveCompletionPoint_ReturnsFalseIfChangeHasNoOwner()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", Enumerable.Empty<DirectiveDescriptor>());
|
|
||||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
|
||||||
var document = CreateDocument();
|
|
||||||
codeDocumentProvider.Value.TryGetFromDocument(document, out var codeDocument);
|
|
||||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
|
||||||
var completionContext = CreateContext(2, completionProvider, document);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = completionProvider.AtDirectiveCompletionPoint(syntaxTree, completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetDescriptionAsync_AddsDirectiveDescriptionIfPropertyExists()
|
public async Task GetDescriptionAsync_AddsDirectiveDescriptionIfPropertyExists()
|
||||||
|
|
@ -76,6 +51,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
||||||
var completionProvider = new RazorDirectiveCompletionProvider(
|
var completionProvider = new RazorDirectiveCompletionProvider(
|
||||||
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
||||||
|
new Lazy<RazorCompletionFactsService>(() => CompletionFactsService),
|
||||||
CompletionBroker,
|
CompletionBroker,
|
||||||
TextBufferProvider);
|
TextBufferProvider);
|
||||||
|
|
||||||
|
|
@ -98,6 +74,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
||||||
var completionProvider = new RazorDirectiveCompletionProvider(
|
var completionProvider = new RazorDirectiveCompletionProvider(
|
||||||
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
new Lazy<RazorCodeDocumentProvider>(() => codeDocumentProvider.Object),
|
||||||
|
new Lazy<RazorCompletionFactsService>(() => CompletionFactsService),
|
||||||
CompletionBroker,
|
CompletionBroker,
|
||||||
TextBufferProvider);
|
TextBufferProvider);
|
||||||
|
|
||||||
|
|
@ -126,7 +103,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
await completionProvider.ProvideCompletionsAsync(context);
|
await completionProvider.ProvideCompletionsAsync(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ProvideCompletionAsync_DoesNotProvideCompletionsForDocumentWithoutPath()
|
public async Task ProvideCompletionAsync_DoesNotProvideCompletionsForDocumentWithoutPath()
|
||||||
{
|
{
|
||||||
|
|
@ -189,145 +165,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
await completionProvider.ProvideCompletionsAsync(context);
|
await completionProvider.ProvideCompletionsAsync(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ProvideCompletionAsync_DoesNotProvideCompletionsWhenNotAtCompletionPoint()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", Enumerable.Empty<DirectiveDescriptor>());
|
|
||||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
|
||||||
var document = CreateDocument();
|
|
||||||
var context = CreateContext(0, completionProvider, document);
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
await completionProvider.ProvideCompletionsAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("DateTime.Now")]
|
|
||||||
[InlineData("SomeMethod()")]
|
|
||||||
public async Task ProvideCompletionAsync_DoesNotProvideCompletionsWhenAtComplexExpressions(string content)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var codeDocumentProvider = CreateCodeDocumentProvider("@" + content, Enumerable.Empty<DirectiveDescriptor>());
|
|
||||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
|
||||||
var document = CreateDocument();
|
|
||||||
var context = CreateContext(1, completionProvider, document);
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
await completionProvider.ProvideCompletionsAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ProvideCompletionAsync_DoesNotProvideCompletionsForExplicitExpressions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var codeDocumentProvider = CreateCodeDocumentProvider("@()", Enumerable.Empty<DirectiveDescriptor>());
|
|
||||||
var completionProvider = new FailOnGetCompletionsProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
|
||||||
var document = CreateDocument();
|
|
||||||
var context = CreateContext(2, completionProvider, document);
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
await completionProvider.ProvideCompletionsAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ProvideCompletionAsync_DoesNotProvideCompletionsForCodeDocumentWithoutSyntaxTree()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
|
||||||
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),
|
|
||||||
CompletionBroker,
|
|
||||||
TextBufferProvider);
|
|
||||||
var document = CreateDocument();
|
|
||||||
var context = CreateContext(2, completionProvider, document);
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
await completionProvider.ProvideCompletionsAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCompletionItems_ProvidesCompletionsForDefaultDirectives()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", Enumerable.Empty<DirectiveDescriptor>());
|
|
||||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
|
||||||
var document = CreateDocument();
|
|
||||||
codeDocumentProvider.Value.TryGetFromDocument(document, out var codeDocument);
|
|
||||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completionItems = completionProvider.GetCompletionItems(syntaxTree);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Collection(
|
|
||||||
completionItems,
|
|
||||||
item => AssertRazorCompletionItem(DefaultDirectives[0].Description, item),
|
|
||||||
item => AssertRazorCompletionItem(DefaultDirectives[1].Description, item),
|
|
||||||
item => AssertRazorCompletionItem(DefaultDirectives[2].Description, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCompletionItems_ProvidesCompletionsForDefaultAndExtensibleDirectives()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", new[] { SectionDirective.Directive });
|
|
||||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
|
||||||
var document = CreateDocument();
|
|
||||||
codeDocumentProvider.Value.TryGetFromDocument(document, out var codeDocument);
|
|
||||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completionItems = completionProvider.GetCompletionItems(syntaxTree);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Collection(
|
|
||||||
completionItems,
|
|
||||||
item => AssertRazorCompletionItem(SectionDirective.Directive.Description, item),
|
|
||||||
item => AssertRazorCompletionItem(DefaultDirectives[0].Description, item),
|
|
||||||
item => AssertRazorCompletionItem(DefaultDirectives[1].Description, item),
|
|
||||||
item => AssertRazorCompletionItem(DefaultDirectives[2].Description, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCompletionItems_ProvidesCompletionsForDirectivesWithoutDescription()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom");
|
|
||||||
var codeDocumentProvider = CreateCodeDocumentProvider("@", new[] { customDirective });
|
|
||||||
var completionProvider = new RazorDirectiveCompletionProvider(codeDocumentProvider, CompletionBroker, TextBufferProvider);
|
|
||||||
var document = CreateDocument();
|
|
||||||
codeDocumentProvider.Value.TryGetFromDocument(document, out var codeDocument);
|
|
||||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completionItems = completionProvider.GetCompletionItems(syntaxTree);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var customDirectiveCompletion = Assert.Single(completionItems, item => item.DisplayText == customDirective.Directive);
|
|
||||||
AssertRazorCompletionItemDefaults(customDirectiveCompletion);
|
|
||||||
Assert.DoesNotContain(customDirectiveCompletion.Properties, property => property.Key == RazorDirectiveCompletionProvider.DescriptionKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AssertRazorCompletionItem(string expectedDescription, CompletionItem item)
|
|
||||||
{
|
|
||||||
Assert.True(item.Properties.TryGetValue(RazorDirectiveCompletionProvider.DescriptionKey, out var actualDescription));
|
|
||||||
Assert.Equal(expectedDescription, actualDescription);
|
|
||||||
|
|
||||||
AssertRazorCompletionItemDefaults(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AssertRazorCompletionItemDefaults(CompletionItem item)
|
|
||||||
{
|
|
||||||
Assert.Equal("_RazorDirective_", item.SortText);
|
|
||||||
Assert.False(item.Rules.FormatOnCommit);
|
|
||||||
var tag = Assert.Single(item.Tags);
|
|
||||||
Assert.Equal(WellKnownTags.Intrinsic, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Lazy<RazorCodeDocumentProvider> CreateCodeDocumentProvider(string text, IEnumerable<DirectiveDescriptor> directives)
|
private static Lazy<RazorCodeDocumentProvider> CreateCodeDocumentProvider(string text, IEnumerable<DirectiveDescriptor> directives)
|
||||||
{
|
{
|
||||||
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
var codeDocumentProvider = new Mock<RazorCodeDocumentProvider>();
|
||||||
|
|
@ -388,17 +225,11 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
IAsyncCompletionBroker asyncCompletionBroker,
|
IAsyncCompletionBroker asyncCompletionBroker,
|
||||||
RazorTextBufferProvider textBufferProvider,
|
RazorTextBufferProvider textBufferProvider,
|
||||||
bool canGetSnapshotPoint = true)
|
bool canGetSnapshotPoint = true)
|
||||||
: base(codeDocumentProvider, asyncCompletionBroker, textBufferProvider)
|
: base(codeDocumentProvider, new Lazy<RazorCompletionFactsService>(() => new DefaultRazorCompletionFactsService()), asyncCompletionBroker, textBufferProvider)
|
||||||
{
|
{
|
||||||
_canGetSnapshotPoint = canGetSnapshotPoint;
|
_canGetSnapshotPoint = canGetSnapshotPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override IEnumerable<CompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree)
|
|
||||||
{
|
|
||||||
Assert.False(true, "Completions should not have been attempted.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool TryGetRazorSnapshotPoint(CompletionContext context, out SnapshotPoint snapshotPoint)
|
protected override bool TryGetRazorSnapshotPoint(CompletionContext context, out SnapshotPoint snapshotPoint)
|
||||||
{
|
{
|
||||||
if (!_canGetSnapshotPoint)
|
if (!_canGetSnapshotPoint)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
private IContentType NonRazorContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(It.IsAny<string>()) == false);
|
private IContentType NonRazorContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(It.IsAny<string>()) == false);
|
||||||
|
|
||||||
|
private RazorCompletionFactsService CompletionFactsService { get; } = Mock.Of<RazorCompletionFactsService>();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CreateCompletionSource_ReturnsNullIfParserHasNotBeenAssocitedWithRazorBuffer()
|
public void CreateCompletionSource_ReturnsNullIfParserHasNotBeenAssocitedWithRazorBuffer()
|
||||||
{
|
{
|
||||||
|
|
@ -27,7 +29,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var properties = new PropertyCollection();
|
var properties = new PropertyCollection();
|
||||||
properties.AddProperty(typeof(VisualStudioRazorParser), expectedParser);
|
properties.AddProperty(typeof(VisualStudioRazorParser), expectedParser);
|
||||||
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == RazorContentType && buffer.Properties == properties);
|
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == RazorContentType && buffer.Properties == properties);
|
||||||
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher);
|
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher, CompletionFactsService);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var completionSource = completionSourceProvider.CreateCompletionSource(razorBuffer);
|
var completionSource = completionSourceProvider.CreateCompletionSource(razorBuffer);
|
||||||
|
|
@ -42,7 +44,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == RazorContentType && buffer.Properties == new PropertyCollection());
|
var razorBuffer = Mock.Of<ITextBuffer>(buffer => buffer.ContentType == RazorContentType && buffer.Properties == new PropertyCollection());
|
||||||
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher);
|
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher, CompletionFactsService);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var completionSource = completionSourceProvider.CreateCompletionSource(razorBuffer);
|
var completionSource = completionSourceProvider.CreateCompletionSource(razorBuffer);
|
||||||
|
|
@ -56,7 +58,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var textView = CreateTextView(NonRazorContentType, new PropertyCollection());
|
var textView = CreateTextView(NonRazorContentType, new PropertyCollection());
|
||||||
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher);
|
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher, CompletionFactsService);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var completionSource = completionSourceProvider.GetOrCreate(textView);
|
var completionSource = completionSourceProvider.GetOrCreate(textView);
|
||||||
|
|
@ -73,7 +75,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var properties = new PropertyCollection();
|
var properties = new PropertyCollection();
|
||||||
properties.AddProperty(typeof(VisualStudioRazorParser), expectedParser);
|
properties.AddProperty(typeof(VisualStudioRazorParser), expectedParser);
|
||||||
var textView = CreateTextView(RazorContentType, properties);
|
var textView = CreateTextView(RazorContentType, properties);
|
||||||
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher);
|
var completionSourceProvider = new RazorDirectiveCompletionSourceProvider(Dispatcher, CompletionFactsService);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var completionSource1 = completionSourceProvider.GetOrCreate(textView);
|
var completionSource1 = completionSourceProvider.GetOrCreate(textView);
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,18 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private RazorCompletionFactsService CompletionFactsService { get; } = new DefaultRazorCompletionFactsService();
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public async Task GetCompletionContextAsync_DoesNotProvideCompletionsPriorToParseResults()
|
public async Task GetCompletionContextAsync_DoesNotProvideCompletionsPriorToParseResults()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var text = "@validCompletion";
|
var text = "@validCompletion";
|
||||||
var parser = Mock.Of<VisualStudioRazorParser>(); // CodeDocument will be null faking a parser without a parse.
|
var parser = Mock.Of<VisualStudioRazorParser>(); // CodeDocument will be null faking a parser without a parse.
|
||||||
var completionSource = new RazorDirectiveCompletionSource(parser, Dispatcher);
|
var completionSource = new RazorDirectiveCompletionSource(Dispatcher, parser, CompletionFactsService);
|
||||||
var documentSnapshot = new StringTextSnapshot(text);
|
var documentSnapshot = new StringTextSnapshot(text);
|
||||||
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
||||||
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(1, text.Length - 1 /* @ */));
|
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(1, text.Length - 1 /* validCompletion */));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var completionContext = await Task.Run(
|
var completionContext = await Task.Run(
|
||||||
|
|
@ -50,7 +52,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
// Arrange
|
// Arrange
|
||||||
var text = "@(NotValidCompletionLocation)";
|
var text = "@(NotValidCompletionLocation)";
|
||||||
var parser = CreateParser(text);
|
var parser = CreateParser(text);
|
||||||
var completionSource = new RazorDirectiveCompletionSource(parser, Dispatcher);
|
var completionSource = new RazorDirectiveCompletionSource(Dispatcher, parser, CompletionFactsService);
|
||||||
var documentSnapshot = new StringTextSnapshot(text);
|
var documentSnapshot = new StringTextSnapshot(text);
|
||||||
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
||||||
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(2, text.Length - 3 /* @() */));
|
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(2, text.Length - 3 /* @() */));
|
||||||
|
|
@ -70,10 +72,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
// Arrange
|
// Arrange
|
||||||
var text = "@addTag";
|
var text = "@addTag";
|
||||||
var parser = CreateParser(text, SectionDirective.Directive);
|
var parser = CreateParser(text, SectionDirective.Directive);
|
||||||
var completionSource = new RazorDirectiveCompletionSource(parser, Dispatcher);
|
var completionSource = new RazorDirectiveCompletionSource(Dispatcher, parser, CompletionFactsService);
|
||||||
var documentSnapshot = new StringTextSnapshot(text);
|
var documentSnapshot = new StringTextSnapshot(text);
|
||||||
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
var triggerLocation = new SnapshotPoint(documentSnapshot, 4);
|
||||||
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(1, text.Length - 1 /* @ */));
|
var applicableSpan = new SnapshotSpan(documentSnapshot, new Span(1, 6 /* addTag */));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var completionContext = await Task.Run(
|
var completionContext = await Task.Run(
|
||||||
|
|
@ -88,71 +90,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
item => AssertRazorCompletionItem(DefaultDirectives[2], 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]
|
[Fact]
|
||||||
public async Task GetDescriptionAsync_AddsDirectiveDescriptionIfPropertyExists()
|
public async Task GetDescriptionAsync_AddsDirectiveDescriptionIfPropertyExists()
|
||||||
{
|
{
|
||||||
|
|
@ -160,7 +97,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var completionItem = new CompletionItem("TestDirective", Mock.Of<IAsyncCompletionSource>());
|
var completionItem = new CompletionItem("TestDirective", Mock.Of<IAsyncCompletionSource>());
|
||||||
var expectedDescription = "The expected description";
|
var expectedDescription = "The expected description";
|
||||||
completionItem.Properties.AddProperty(RazorDirectiveCompletionSource.DescriptionKey, expectedDescription);
|
completionItem.Properties.AddProperty(RazorDirectiveCompletionSource.DescriptionKey, expectedDescription);
|
||||||
var completionSource = new RazorDirectiveCompletionSource(Mock.Of<VisualStudioRazorParser>(), Dispatcher);
|
var completionSource = new RazorDirectiveCompletionSource(Dispatcher, Mock.Of<VisualStudioRazorParser>(), CompletionFactsService);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptionObject = await completionSource.GetDescriptionAsync(completionItem, CancellationToken.None);
|
var descriptionObject = await completionSource.GetDescriptionAsync(completionItem, CancellationToken.None);
|
||||||
|
|
@ -175,7 +112,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var completionItem = new CompletionItem("TestDirective", Mock.Of<IAsyncCompletionSource>());
|
var completionItem = new CompletionItem("TestDirective", Mock.Of<IAsyncCompletionSource>());
|
||||||
var completionSource = new RazorDirectiveCompletionSource(Mock.Of<VisualStudioRazorParser>(), Dispatcher);
|
var completionSource = new RazorDirectiveCompletionSource(Dispatcher, Mock.Of<VisualStudioRazorParser>(), CompletionFactsService);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptionObject = await completionSource.GetDescriptionAsync(completionItem, CancellationToken.None);
|
var descriptionObject = await completionSource.GetDescriptionAsync(completionItem, CancellationToken.None);
|
||||||
|
|
@ -185,138 +122,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
Assert.Equal(string.Empty, description);
|
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)
|
private static void AssertRazorCompletionItem(string completionDisplayText, DirectiveDescriptor directive, CompletionItem item, IAsyncCompletionSource source)
|
||||||
{
|
{
|
||||||
Assert.Equal(item.DisplayText, completionDisplayText);
|
Assert.Equal(item.DisplayText, completionDisplayText);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue