Merge branch 'dev' into rel/vs15.6
This commit is contained in:
commit
fa2d79296a
|
|
@ -4,7 +4,9 @@
|
||||||
<RestoreDependsOn Condition="'$(OS)'=='Windows_NT' AND '$(BuildVSIX)' == 'true'">$(RestoreDependsOn);RestoreVSIX</RestoreDependsOn>
|
<RestoreDependsOn Condition="'$(OS)'=='Windows_NT' AND '$(BuildVSIX)' == 'true'">$(RestoreDependsOn);RestoreVSIX</RestoreDependsOn>
|
||||||
<PackageDependsOn Condition="'$(OS)'=='Windows_NT' AND '$(BuildVSIX)' == 'true'">$(PackageDependsOn);PackageVSIX</PackageDependsOn>
|
<PackageDependsOn Condition="'$(OS)'=='Windows_NT' AND '$(BuildVSIX)' == 'true'">$(PackageDependsOn);PackageVSIX</PackageDependsOn>
|
||||||
<VSIXName>Microsoft.VisualStudio.RazorExtension</VSIXName>
|
<VSIXName>Microsoft.VisualStudio.RazorExtension</VSIXName>
|
||||||
|
<VSIXOutputPath>$(BuildDir)$(VSIXName).vsix</VSIXOutputPath>
|
||||||
<VSIXProject>$(RepositoryRoot)tooling\$(VSIXName)\$(VSIXName).csproj</VSIXProject>
|
<VSIXProject>$(RepositoryRoot)tooling\$(VSIXName)\$(VSIXName).csproj</VSIXProject>
|
||||||
|
<MSBuildArtifactsDir>$(ArtifactsDir)msbuild\</MSBuildArtifactsDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Target
|
<Target
|
||||||
|
|
@ -16,7 +18,32 @@
|
||||||
VisualStudioMSBuildx86Path is set by the GetToolsets target in KoreBuild if a version of VS matching the requirements in korebuild.json is found.
|
VisualStudioMSBuildx86Path is set by the GetToolsets target in KoreBuild if a version of VS matching the requirements in korebuild.json is found.
|
||||||
-->
|
-->
|
||||||
<Target Name="RestoreVSIX" DependsOnTargets="GetToolsets">
|
<Target Name="RestoreVSIX" DependsOnTargets="GetToolsets">
|
||||||
<Exec Command=""$(VisualStudioMSBuildx86Path)" "$(VSIXProject)" /t:Restore /v:m /p:BuildNumber=$(BuildNumber)"
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<VSIXResponseFilePath>$(MSBuildArtifactsDir)vsix-restore.rsp</VSIXResponseFilePath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<MSBuildArguments Remove="@(MSBuildArguments)" />
|
||||||
|
<MSBuildArguments Include="
|
||||||
|
$(VSIXProject);
|
||||||
|
/t:Restore;
|
||||||
|
/m;
|
||||||
|
/v:m;
|
||||||
|
/p:Configuration=$(Configuration);
|
||||||
|
/p:BuildNumber=$(BuildNumber);" />
|
||||||
|
<MSBuildArguments Include="/p:DotNetPackageVersionPropsPath=$(DotNetPackageVersionPropsPath)" Condition="'$(DotNetPackageVersionPropsPath)' != ''" />
|
||||||
|
<MSBuildArguments Include="/p:DotNetRestoreSourcePropsPath=$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<MakeDir Directories="$(MSBuildArtifactsDir)" />
|
||||||
|
|
||||||
|
<WriteLinesToFile
|
||||||
|
File="$(VSIXResponseFilePath)"
|
||||||
|
Lines="@(MSBuildArguments)"
|
||||||
|
Overwrite="true" />
|
||||||
|
|
||||||
|
<Exec Command=""$(VisualStudioMSBuildx86Path)" @"$(VSIXResponseFilePath)""
|
||||||
Condition="'$(VisualStudioMSBuildx86Path)' != ''" />
|
Condition="'$(VisualStudioMSBuildx86Path)' != ''" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
|
@ -26,13 +53,12 @@
|
||||||
Condition="'$(VisualStudioMSBuildx86Path)' == ''" />
|
Condition="'$(VisualStudioMSBuildx86Path)' == ''" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<MSBuildArtifactsDir>$(ArtifactsDir)msbuild\</MSBuildArtifactsDir>
|
|
||||||
<VSIXLogFilePath>$(MSBuildArtifactsDir)vsix.log</VSIXLogFilePath>
|
<VSIXLogFilePath>$(MSBuildArtifactsDir)vsix.log</VSIXLogFilePath>
|
||||||
<VSIXResponseFilePath>$(MSBuildArtifactsDir)vsix.rsp</VSIXResponseFilePath>
|
<VSIXResponseFilePath>$(MSBuildArtifactsDir)vsix-build.rsp</VSIXResponseFilePath>
|
||||||
<VSIXOutputPath>$(BuildDir)$(VSIXName).vsix</VSIXOutputPath>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<MSBuildArguments Remove="@(MSBuildArguments)" />
|
||||||
<MSBuildArguments Include="
|
<MSBuildArguments Include="
|
||||||
$(VSIXProject);
|
$(VSIXProject);
|
||||||
/m;
|
/m;
|
||||||
|
|
@ -44,7 +70,7 @@
|
||||||
/p:Configuration=$(Configuration);" />
|
/p:Configuration=$(Configuration);" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<MakeDir Directories="$(MSBuildArtifactsDir)" Condition="!Exists('$(MSBuildArtifactsDir)')" />
|
<MakeDir Directories="$(MSBuildArtifactsDir)" />
|
||||||
|
|
||||||
<WriteLinesToFile
|
<WriteLinesToFile
|
||||||
File="$(VSIXResponseFilePath)"
|
File="$(VSIXResponseFilePath)"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<MicrosoftCodeAnalysisCommonPackageVersion>2.3.1</MicrosoftCodeAnalysisCommonPackageVersion>
|
<MicrosoftCodeAnalysisCommonPackageVersion>2.3.1</MicrosoftCodeAnalysisCommonPackageVersion>
|
||||||
<MicrosoftCodeAnalysisCSharpPackageVersion>2.3.1</MicrosoftCodeAnalysisCSharpPackageVersion>
|
<MicrosoftCodeAnalysisCSharpPackageVersion>2.3.1</MicrosoftCodeAnalysisCSharpPackageVersion>
|
||||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.0-preview1-27498</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.0-preview1-27498</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyModelPackageVersion>2.0.0</MicrosoftExtensionsDependencyModelPackageVersion>
|
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0-preview2-25711-01</MicrosoftExtensionsDependencyModelPackageVersion>
|
||||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.0-preview1-27498</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.0-preview1-27498</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.0-preview1-27498</MicrosoftExtensionsWebEncodersPackageVersion>
|
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.0-preview1-27498</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
version:2.1.0-preview1-15551
|
version:2.1.0-preview1-15567
|
||||||
commithash:8fad9553b48533fddbb16a423ea55b9710ea2e63
|
commithash:903e3104807b1bb8cddd28bdef205b1e2dc021d1
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
// 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.Composition;
|
|
||||||
using Microsoft.CodeAnalysis.Host;
|
|
||||||
using Microsoft.CodeAnalysis.Host.Mef;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
|
||||||
{
|
|
||||||
[Shared]
|
|
||||||
[ExportLanguageServiceFactory(typeof(RazorIndentationFactsService), RazorLanguage.Name, ServiceLayer.Default)]
|
|
||||||
internal class DefaultRazorIndentationFactsServiceFactory : ILanguageServiceFactory
|
|
||||||
{
|
|
||||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
|
||||||
{
|
|
||||||
return new DefaultRazorIndentationFactsService();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis.Host;
|
|
||||||
using Microsoft.CodeAnalysis.Host.Mef;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
|
||||||
{
|
|
||||||
[ExportLanguageServiceFactory(typeof(TagHelperCompletionService), RazorLanguage.Name, ServiceLayer.Default)]
|
|
||||||
internal class DefaultTagHelperCompletionServiceFactory : ILanguageServiceFactory
|
|
||||||
{
|
|
||||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
|
||||||
{
|
|
||||||
var tagHelperFactsService = languageServices.GetRequiredService<TagHelperFactsService>();
|
|
||||||
return new DefaultTagHelperCompletionService(tagHelperFactsService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis.Host;
|
|
||||||
using Microsoft.CodeAnalysis.Host.Mef;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
|
||||||
{
|
|
||||||
[ExportLanguageServiceFactory(typeof(TagHelperFactsService), RazorLanguage.Name, ServiceLayer.Default)]
|
|
||||||
internal class DefaultTagHelperFactsServiceFactory : ILanguageServiceFactory
|
|
||||||
{
|
|
||||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
|
||||||
{
|
|
||||||
return new DefaultTagHelperFactsService();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
internal class DefaultTagHelperFactsService : TagHelperFactsService
|
internal class DefaultTagHelperFactsServiceInternal : TagHelperFactsServiceInternal
|
||||||
{
|
{
|
||||||
public override TagHelperBinding GetTagHelperBinding(
|
public override TagHelperBinding GetTagHelperBinding(
|
||||||
TagHelperDocumentContext documentContext,
|
TagHelperDocumentContext documentContext,
|
||||||
|
|
@ -6,12 +6,12 @@ using Microsoft.CodeAnalysis.Host.Mef;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
[ExportLanguageServiceFactory(typeof(RazorSyntaxFactsService), RazorLanguage.Name, ServiceLayer.Default)]
|
[ExportLanguageServiceFactory(typeof(TagHelperFactsServiceInternal), RazorLanguage.Name, ServiceLayer.Default)]
|
||||||
internal class DefaultRazorSyntaxFactsServiceFactory : ILanguageServiceFactory
|
internal class DefaultTagHelperFactsServiceInternalFactory : ILanguageServiceFactory
|
||||||
{
|
{
|
||||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||||
{
|
{
|
||||||
return new DefaultRazorSyntaxFactsService();
|
return new DefaultTagHelperFactsServiceInternal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,14 +5,14 @@ using System;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Editor
|
namespace Microsoft.CodeAnalysis.Razor.Editor
|
||||||
{
|
{
|
||||||
internal class DefaultEditorSettingsManager : EditorSettingsManager
|
internal class DefaultEditorSettingsManagerInternal : EditorSettingsManagerInternal
|
||||||
{
|
{
|
||||||
public override event EventHandler<EditorSettingsChangedEventArgs> Changed;
|
public override event EventHandler<EditorSettingsChangedEventArgs> Changed;
|
||||||
|
|
||||||
private readonly object SettingsAccessorLock = new object();
|
private readonly object SettingsAccessorLock = new object();
|
||||||
private EditorSettings _settings;
|
private EditorSettings _settings;
|
||||||
|
|
||||||
public DefaultEditorSettingsManager()
|
public DefaultEditorSettingsManagerInternal()
|
||||||
{
|
{
|
||||||
_settings = EditorSettings.Default;
|
_settings = EditorSettings.Default;
|
||||||
}
|
}
|
||||||
|
|
@ -9,8 +9,8 @@ using Microsoft.CodeAnalysis.Host.Mef;
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Editor
|
namespace Microsoft.CodeAnalysis.Razor.Editor
|
||||||
{
|
{
|
||||||
[Shared]
|
[Shared]
|
||||||
[ExportLanguageServiceFactory(typeof(EditorSettingsManager), RazorLanguage.Name)]
|
[ExportLanguageServiceFactory(typeof(EditorSettingsManagerInternal), RazorLanguage.Name)]
|
||||||
internal class DefaultEditorSettingsManagerFactory : ILanguageServiceFactory
|
internal class DefaultEditorSettingsManagerInternalFactory : ILanguageServiceFactory
|
||||||
{
|
{
|
||||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor
|
||||||
throw new ArgumentNullException(nameof(languageServices));
|
throw new ArgumentNullException(nameof(languageServices));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DefaultEditorSettingsManager();
|
return new DefaultEditorSettingsManagerInternal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ using Microsoft.CodeAnalysis.Host;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Editor
|
namespace Microsoft.CodeAnalysis.Razor.Editor
|
||||||
{
|
{
|
||||||
public abstract class EditorSettingsManager : ILanguageService
|
internal abstract class EditorSettingsManagerInternal : ILanguageService
|
||||||
{
|
{
|
||||||
public abstract event EventHandler<EditorSettingsChangedEventArgs> Changed;
|
public abstract event EventHandler<EditorSettingsChangedEventArgs> Changed;
|
||||||
|
|
||||||
|
|
@ -8,5 +8,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
Added,
|
Added,
|
||||||
Removed,
|
Removed,
|
||||||
Changed,
|
Changed,
|
||||||
|
TagHelpersChanged,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Host;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
public abstract class RazorTemplateEngineFactoryService : ILanguageService
|
internal abstract class RazorTemplateEngineFactoryService : ILanguageService
|
||||||
{
|
{
|
||||||
public abstract RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure);
|
public abstract RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Host;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
public abstract class TagHelperFactsService : ILanguageService
|
internal abstract class TagHelperFactsServiceInternal : ILanguageService
|
||||||
{
|
{
|
||||||
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag, bool parentIsTagHelper);
|
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag, bool parentIsTagHelper);
|
||||||
|
|
||||||
|
|
@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Host;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
public abstract class TagHelperResolver : ILanguageService
|
internal abstract class TagHelperResolver : ILanguageService
|
||||||
{
|
{
|
||||||
public abstract TagHelperResolutionResult GetTagHelpers(Compilation compilation);
|
public abstract TagHelperResolutionResult GetTagHelpers(Compilation compilation);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum AcceptedCharacters
|
public enum AcceptedCharacters
|
||||||
|
|
@ -5,7 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class AttributeCompletionContext
|
public class AttributeCompletionContext
|
||||||
{
|
{
|
||||||
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public abstract class AttributeCompletionResult
|
public abstract class AttributeCompletionResult
|
||||||
{
|
{
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public enum BlockKind
|
public enum BlockKind
|
||||||
{
|
{
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public struct ClassifiedSpan
|
public struct ClassifiedSpan
|
||||||
{
|
{
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
public sealed class ContextChangeEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public ContextChangeEventArgs(ContextChangeKind kind)
|
||||||
|
{
|
||||||
|
Kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContextChangeKind Kind { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
public enum ContextChangeKind
|
||||||
|
{
|
||||||
|
ProjectChanged,
|
||||||
|
EditorSettingsChanged,
|
||||||
|
TagHelpersChanged,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
[System.Composition.Shared]
|
||||||
|
[Export(typeof(EditorSettingsManager))]
|
||||||
|
internal class DefaultEditorSettingsManager : EditorSettingsManager
|
||||||
|
{
|
||||||
|
private readonly EditorSettingsManagerInternal _editorSettingsManager;
|
||||||
|
|
||||||
|
[ImportingConstructor]
|
||||||
|
public DefaultEditorSettingsManager(VisualStudioWorkspaceAccessor workspaceAccessor)
|
||||||
|
{
|
||||||
|
var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name);
|
||||||
|
_editorSettingsManager = razorLanguageServices.GetRequiredService<EditorSettingsManagerInternal>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override event EventHandler<EditorSettingsChangedEventArgs> Changed
|
||||||
|
{
|
||||||
|
add => _editorSettingsManager.Changed += value;
|
||||||
|
remove => _editorSettingsManager.Changed -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override EditorSettings Current => _editorSettingsManager.Current;
|
||||||
|
|
||||||
|
public override void Update(EditorSettings updateSettings)
|
||||||
|
{
|
||||||
|
_editorSettingsManager.Update(updateSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,28 +3,38 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||||
|
using Microsoft.VisualStudio.Text;
|
||||||
|
using Span = Microsoft.AspNetCore.Razor.Language.Legacy.Span;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
|
[System.Composition.Shared]
|
||||||
|
[Export(typeof(RazorIndentationFactsService))]
|
||||||
internal class DefaultRazorIndentationFactsService : RazorIndentationFactsService
|
internal class DefaultRazorIndentationFactsService : RazorIndentationFactsService
|
||||||
{
|
{
|
||||||
public override int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, int previousLineEndIndex, Func<int, string> getLineContent, int indentSize, int tabSize)
|
public override int? GetDesiredIndentation(
|
||||||
|
RazorSyntaxTree syntaxTree,
|
||||||
|
ITextSnapshot syntaxTreeSnapshot,
|
||||||
|
ITextSnapshotLine line,
|
||||||
|
int indentSize,
|
||||||
|
int tabSize)
|
||||||
{
|
{
|
||||||
if (syntaxTree == null)
|
if (syntaxTree == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(syntaxTree));
|
throw new ArgumentNullException(nameof(syntaxTree));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previousLineEndIndex < 0)
|
if (syntaxTreeSnapshot == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(previousLineEndIndex));
|
throw new ArgumentNullException(nameof(syntaxTreeSnapshot));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getLineContent == null)
|
if (line == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(getLineContent));
|
throw new ArgumentNullException(nameof(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indentSize < 0)
|
if (indentSize < 0)
|
||||||
|
|
@ -37,6 +47,10 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
throw new ArgumentOutOfRangeException(nameof(tabSize));
|
throw new ArgumentOutOfRangeException(nameof(tabSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var previousLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);
|
||||||
|
var trackingPoint = previousLine.Snapshot.CreateTrackingPoint(previousLine.End, PointTrackingMode.Negative);
|
||||||
|
var previousLineEndIndex = trackingPoint.GetPosition(syntaxTreeSnapshot);
|
||||||
|
|
||||||
var simulatedChange = new SourceChange(previousLineEndIndex, 0, string.Empty);
|
var simulatedChange = new SourceChange(previousLineEndIndex, 0, string.Empty);
|
||||||
var owningSpan = LocateOwner(syntaxTree.Root, simulatedChange);
|
var owningSpan = LocateOwner(syntaxTree.Root, simulatedChange);
|
||||||
if (owningSpan.Kind == SpanKindInternal.Code)
|
if (owningSpan.Kind == SpanKindInternal.Code)
|
||||||
|
|
@ -80,8 +94,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
// We can't rely on the syntax trees representation of the source document because partial parses may have mutated
|
// We can't rely on the syntax trees representation of the source document because partial parses may have mutated
|
||||||
// the underlying SyntaxTree text buffer. Because of this, if we want to provide accurate indentations we need to
|
// the underlying SyntaxTree text buffer. Because of this, if we want to provide accurate indentations we need to
|
||||||
// operate on the current line representation as indicated by the provider.
|
// operate on the current line representation as indicated by the provider.
|
||||||
var line = getLineContent(currentSpan.Start.LineIndex);
|
var lineText = line.Snapshot.GetLineFromLineNumber(currentSpan.Start.LineIndex).GetText();
|
||||||
desiredIndentation = GetIndentLevelOfLine(line, tabSize) + indentSize;
|
desiredIndentation = GetIndentLevelOfLine(lineText, tabSize) + indentSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3,13 +3,16 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||||
using Span = Microsoft.AspNetCore.Razor.Language.Legacy.Span;
|
using Span = Microsoft.AspNetCore.Razor.Language.Legacy.Span;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
|
[System.Composition.Shared]
|
||||||
|
[Export(typeof(RazorSyntaxFactsService))]
|
||||||
internal class DefaultRazorSyntaxFactsService : RazorSyntaxFactsService
|
internal class DefaultRazorSyntaxFactsService : RazorSyntaxFactsService
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<ClassifiedSpan> GetClassifiedSpans(RazorSyntaxTree syntaxTree)
|
public override IReadOnlyList<ClassifiedSpan> GetClassifiedSpans(RazorSyntaxTree syntaxTree)
|
||||||
|
|
@ -3,18 +3,30 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
|
[System.Composition.Shared]
|
||||||
|
[Export(typeof(TagHelperCompletionService))]
|
||||||
internal class DefaultTagHelperCompletionService : TagHelperCompletionService
|
internal class DefaultTagHelperCompletionService : TagHelperCompletionService
|
||||||
{
|
{
|
||||||
private readonly TagHelperFactsService _tagHelperFactsService;
|
private readonly TagHelperFactsServiceInternal _tagHelperFactsService;
|
||||||
private static readonly HashSet<TagHelperDescriptor> _emptyHashSet = new HashSet<TagHelperDescriptor>();
|
private static readonly HashSet<TagHelperDescriptor> _emptyHashSet = new HashSet<TagHelperDescriptor>();
|
||||||
|
|
||||||
public DefaultTagHelperCompletionService(TagHelperFactsService tagHelperFactsService)
|
[ImportingConstructor]
|
||||||
|
public DefaultTagHelperCompletionService(VisualStudioWorkspaceAccessor workspaceAccessor)
|
||||||
|
{
|
||||||
|
var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name);
|
||||||
|
_tagHelperFactsService = razorLanguageServices.GetRequiredService<TagHelperFactsServiceInternal>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal for testing
|
||||||
|
internal DefaultTagHelperCompletionService(TagHelperFactsServiceInternal tagHelperFactsService)
|
||||||
{
|
{
|
||||||
_tagHelperFactsService = tagHelperFactsService;
|
_tagHelperFactsService = tagHelperFactsService;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
[System.Composition.Shared]
|
||||||
|
[Export(typeof(TagHelperFactsService))]
|
||||||
|
internal class DefaultTagHelperFactsService : TagHelperFactsService
|
||||||
|
{
|
||||||
|
private readonly TagHelperFactsServiceInternal _tagHelperFactsService;
|
||||||
|
|
||||||
|
[ImportingConstructor]
|
||||||
|
public DefaultTagHelperFactsService(VisualStudioWorkspaceAccessor workspaceAccessor)
|
||||||
|
{
|
||||||
|
var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name);
|
||||||
|
_tagHelperFactsService = razorLanguageServices.GetRequiredService<TagHelperFactsServiceInternal>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override TagHelperBinding GetTagHelperBinding(
|
||||||
|
TagHelperDocumentContext documentContext,
|
||||||
|
string tagName,
|
||||||
|
IEnumerable<KeyValuePair<string, string>> attributes,
|
||||||
|
string parentTag,
|
||||||
|
bool parentIsTagHelper)
|
||||||
|
{
|
||||||
|
return _tagHelperFactsService.GetTagHelperBinding(documentContext, tagName, attributes, parentTag, parentIsTagHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(
|
||||||
|
TagHelperDocumentContext documentContext,
|
||||||
|
string attributeName,
|
||||||
|
TagHelperBinding binding)
|
||||||
|
{
|
||||||
|
return _tagHelperFactsService.GetBoundTagHelperAttributes(documentContext, attributeName, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(
|
||||||
|
TagHelperDocumentContext documentContext,
|
||||||
|
string tagName,
|
||||||
|
string parentTag)
|
||||||
|
{
|
||||||
|
return _tagHelperFactsService.GetTagHelpersGivenTag(documentContext, tagName, parentTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag)
|
||||||
|
{
|
||||||
|
return _tagHelperFactsService.GetTagHelpersGivenParent(documentContext, parentTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -133,7 +132,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal void DocumentTracker_ContextChanged(object sender, EventArgs args)
|
internal void DocumentTracker_ContextChanged(object sender, ContextChangeEventArgs args)
|
||||||
{
|
{
|
||||||
_dispatcher.AssertForegroundThread();
|
_dispatcher.AssertForegroundThread();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
public abstract class EditorSettingsManager
|
||||||
|
{
|
||||||
|
public abstract event EventHandler<EditorSettingsChangedEventArgs> Changed;
|
||||||
|
|
||||||
|
public abstract EditorSettings Current { get; }
|
||||||
|
|
||||||
|
public abstract void Update(EditorSettings updateSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public sealed class ElementCompletionContext
|
public sealed class ElementCompletionContext
|
||||||
{
|
{
|
||||||
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public abstract class ElementCompletionResult
|
public abstract class ElementCompletionResult
|
||||||
{
|
{
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis.Host;
|
using Microsoft.CodeAnalysis.Host;
|
||||||
|
using Microsoft.VisualStudio.Text;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public abstract class RazorIndentationFactsService : ILanguageService
|
public abstract class RazorIndentationFactsService : ILanguageService
|
||||||
{
|
{
|
||||||
public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, int previousLineEnd, Func<int, string> lineProvider, int indentSize, int tabSize);
|
public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line, int indentSize, int tabSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.VisualStudio.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
|
||||||
{
|
|
||||||
public static class RazorIndentationFactsServiceExtensions
|
|
||||||
{
|
|
||||||
public static int? GetDesiredIndentation(
|
|
||||||
this RazorIndentationFactsService service,
|
|
||||||
RazorSyntaxTree syntaxTree,
|
|
||||||
ITextSnapshot syntaxTreeSnapshot,
|
|
||||||
ITextSnapshotLine line,
|
|
||||||
int indentSize,
|
|
||||||
int tabSize)
|
|
||||||
{
|
|
||||||
// The tricky thing here is that line.Snapshot is very likely newer
|
|
||||||
var previousLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);
|
|
||||||
var trackingPoint = line.Snapshot.CreateTrackingPoint(line.End, PointTrackingMode.Negative);
|
|
||||||
var previousLineEnd = trackingPoint.GetPosition(syntaxTreeSnapshot);
|
|
||||||
|
|
||||||
Func<int, string> getLineContentDelegate = (lineIndex) => line.Snapshot.GetLineFromLineNumber(lineIndex).GetText();
|
|
||||||
|
|
||||||
return service.GetDesiredIndentation(syntaxTree, previousLineEnd, getLineContentDelegate, indentSize, tabSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,8 +4,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis.Host;
|
using Microsoft.CodeAnalysis.Host;
|
||||||
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public abstract class RazorSyntaxFactsService : ILanguageService
|
public abstract class RazorSyntaxFactsService : ILanguageService
|
||||||
{
|
{
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public enum SpanKind
|
public enum SpanKind
|
||||||
{
|
{
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis.Host;
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
|
||||||
{
|
{
|
||||||
public abstract class TagHelperCompletionService : ILanguageService
|
public abstract class TagHelperCompletionService
|
||||||
{
|
{
|
||||||
public abstract AttributeCompletionResult GetAttributeCompletions(AttributeCompletionContext completionContext);
|
public abstract AttributeCompletionResult GetAttributeCompletions(AttributeCompletionContext completionContext);
|
||||||
|
|
||||||
|
|
@ -4,15 +4,11 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public abstract class TagHelperFactsService
|
public abstract class TagHelperFactsService
|
||||||
{
|
{
|
||||||
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag);
|
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag, bool parentIsTagHelper);
|
||||||
|
|
||||||
public abstract IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding);
|
public abstract IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding);
|
||||||
|
|
||||||
|
|
@ -5,7 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public struct TagHelperSpan
|
public struct TagHelperSpan
|
||||||
{
|
{
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor.Editor;
|
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
|
|
@ -13,12 +14,14 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public abstract class VisualStudioDocumentTracker
|
public abstract class VisualStudioDocumentTracker
|
||||||
{
|
{
|
||||||
public abstract event EventHandler ContextChanged;
|
public abstract event EventHandler<ContextChangeEventArgs> ContextChanged;
|
||||||
|
|
||||||
internal abstract ProjectExtensibilityConfiguration Configuration { get; }
|
internal abstract ProjectExtensibilityConfiguration Configuration { get; }
|
||||||
|
|
||||||
public abstract EditorSettings EditorSettings { get; }
|
public abstract EditorSettings EditorSettings { get; }
|
||||||
|
|
||||||
|
public abstract IReadOnlyList<TagHelperDescriptor> TagHelpers { get; }
|
||||||
|
|
||||||
public abstract bool IsSupportedProject { get; }
|
public abstract bool IsSupportedProject { get; }
|
||||||
|
|
||||||
public abstract string FilePath { get; }
|
public abstract string FilePath { get; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
{
|
||||||
|
internal abstract class VisualStudioWorkspaceAccessor
|
||||||
|
{
|
||||||
|
public abstract Workspace Workspace { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,9 +29,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
throw new RazorLanguageServiceException(
|
throw new InvalidOperationException(
|
||||||
typeof(DefaultRazorEngineDirectiveResolver).FullName,
|
Resources.FormatUnexpectedException(
|
||||||
nameof(GetRazorEngineDirectivesAsync),
|
typeof(DefaultRazorEngineDirectiveResolver).FullName,
|
||||||
|
nameof(GetRazorEngineDirectivesAsync)),
|
||||||
exception);
|
exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
throw new RazorLanguageServiceException(
|
throw new InvalidOperationException(
|
||||||
typeof(DefaultRazorEngineDocumentGenerator).FullName,
|
Resources.FormatUnexpectedException(
|
||||||
nameof(GenerateDocumentAsync),
|
typeof(DefaultRazorEngineDocumentGenerator).FullName,
|
||||||
|
nameof(GenerateDocumentAsync)),
|
||||||
exception);
|
exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
{
|
{
|
||||||
_errorReporter.ReportError(exception, project);
|
_errorReporter.ReportError(exception, project);
|
||||||
|
|
||||||
throw new RazorLanguageServiceException(
|
throw new InvalidOperationException(
|
||||||
typeof(DefaultTagHelperResolver).FullName,
|
Resources.FormatUnexpectedException(
|
||||||
nameof(GetTagHelpersAsync),
|
typeof(DefaultTagHelperResolver).FullName,
|
||||||
|
nameof(GetTagHelpersAsync)),
|
||||||
exception);
|
exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.VisualStudio.Editor.Razor;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
{
|
||||||
|
[System.Composition.Shared]
|
||||||
|
[Export(typeof(VisualStudioWorkspaceAccessor))]
|
||||||
|
internal class DefaultVisualStudioWorkspaceAccessor : VisualStudioWorkspaceAccessor
|
||||||
|
{
|
||||||
|
[ImportingConstructor]
|
||||||
|
public DefaultVisualStudioWorkspaceAccessor([Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||||
|
{
|
||||||
|
Workspace = workspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Workspace Workspace { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
using Microsoft.CodeAnalysis.Razor.Editor;
|
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||||
|
|
@ -18,7 +19,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
{
|
{
|
||||||
private readonly string _filePath;
|
private readonly string _filePath;
|
||||||
private readonly ProjectSnapshotManager _projectManager;
|
private readonly ProjectSnapshotManager _projectManager;
|
||||||
private readonly EditorSettingsManager _editorSettingsManager;
|
private readonly EditorSettingsManagerInternal _editorSettingsManager;
|
||||||
private readonly TextBufferProjectService _projectService;
|
private readonly TextBufferProjectService _projectService;
|
||||||
private readonly ITextBuffer _textBuffer;
|
private readonly ITextBuffer _textBuffer;
|
||||||
private readonly List<ITextView> _textViews;
|
private readonly List<ITextView> _textViews;
|
||||||
|
|
@ -27,13 +28,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
private ProjectSnapshot _project;
|
private ProjectSnapshot _project;
|
||||||
private string _projectPath;
|
private string _projectPath;
|
||||||
|
|
||||||
public override event EventHandler ContextChanged;
|
public override event EventHandler<ContextChangeEventArgs> ContextChanged;
|
||||||
|
|
||||||
public DefaultVisualStudioDocumentTracker(
|
public DefaultVisualStudioDocumentTracker(
|
||||||
string filePath,
|
string filePath,
|
||||||
ProjectSnapshotManager projectManager,
|
ProjectSnapshotManager projectManager,
|
||||||
TextBufferProjectService projectService,
|
TextBufferProjectService projectService,
|
||||||
EditorSettingsManager editorSettingsManager,
|
EditorSettingsManagerInternal editorSettingsManager,
|
||||||
Workspace workspace,
|
Workspace workspace,
|
||||||
ITextBuffer textBuffer)
|
ITextBuffer textBuffer)
|
||||||
{
|
{
|
||||||
|
|
@ -81,6 +82,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
|
|
||||||
public override EditorSettings EditorSettings => _editorSettingsManager.Current;
|
public override EditorSettings EditorSettings => _editorSettingsManager.Current;
|
||||||
|
|
||||||
|
public override IReadOnlyList<TagHelperDescriptor> TagHelpers => Array.Empty<TagHelperDescriptor>();
|
||||||
|
|
||||||
public override bool IsSupportedProject => _isSupportedProject;
|
public override bool IsSupportedProject => _isSupportedProject;
|
||||||
|
|
||||||
public override Project Project => _workspace.CurrentSolution.GetProject(_project.UnderlyingProject.Id);
|
public override Project Project => _workspace.CurrentSolution.GetProject(_project.UnderlyingProject.Id);
|
||||||
|
|
@ -176,7 +179,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
_projectManager.Changed += ProjectManager_Changed;
|
_projectManager.Changed += ProjectManager_Changed;
|
||||||
_editorSettingsManager.Changed += EditorSettingsManager_Changed;
|
_editorSettingsManager.Changed += EditorSettingsManager_Changed;
|
||||||
|
|
||||||
OnContextChanged(_project);
|
OnContextChanged(_project, ContextChangeKind.ProjectChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Unsubscribe()
|
private void Unsubscribe()
|
||||||
|
|
@ -187,17 +190,17 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
// Detached from project.
|
// Detached from project.
|
||||||
_isSupportedProject = false;
|
_isSupportedProject = false;
|
||||||
_project = null;
|
_project = null;
|
||||||
OnContextChanged(project: null);
|
OnContextChanged(project: null, kind: ContextChangeKind.ProjectChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnContextChanged(ProjectSnapshot project)
|
private void OnContextChanged(ProjectSnapshot project, ContextChangeKind kind)
|
||||||
{
|
{
|
||||||
_project = project;
|
_project = project;
|
||||||
|
|
||||||
var handler = ContextChanged;
|
var handler = ContextChanged;
|
||||||
if (handler != null)
|
if (handler != null)
|
||||||
{
|
{
|
||||||
handler(this, EventArgs.Empty);
|
handler(this, new ContextChangeEventArgs(kind));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,14 +209,21 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
if (_projectPath != null &&
|
if (_projectPath != null &&
|
||||||
string.Equals(_projectPath, e.Project.UnderlyingProject.FilePath, StringComparison.OrdinalIgnoreCase))
|
string.Equals(_projectPath, e.Project.UnderlyingProject.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
OnContextChanged(e.Project);
|
if (e.Kind == ProjectChangeKind.TagHelpersChanged)
|
||||||
|
{
|
||||||
|
OnContextChanged(e.Project, ContextChangeKind.TagHelpersChanged);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnContextChanged(e.Project, ContextChangeKind.ProjectChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal void EditorSettingsManager_Changed(object sender, EditorSettingsChangedEventArgs args)
|
internal void EditorSettingsManager_Changed(object sender, EditorSettingsChangedEventArgs args)
|
||||||
{
|
{
|
||||||
OnContextChanged(_project);
|
OnContextChanged(_project, ContextChangeKind.EditorSettingsChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
private readonly Workspace _workspace;
|
private readonly Workspace _workspace;
|
||||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||||
private readonly ProjectSnapshotManager _projectManager;
|
private readonly ProjectSnapshotManager _projectManager;
|
||||||
private readonly EditorSettingsManager _editorSettingsManager;
|
private readonly EditorSettingsManagerInternal _editorSettingsManager;
|
||||||
|
|
||||||
[ImportingConstructor]
|
[ImportingConstructor]
|
||||||
public DefaultVisualStudioDocumentTrackerFactory(
|
public DefaultVisualStudioDocumentTrackerFactory(
|
||||||
|
|
@ -52,7 +52,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
_foregroundDispatcher = workspace.Services.GetRequiredService<ForegroundDispatcher>();
|
_foregroundDispatcher = workspace.Services.GetRequiredService<ForegroundDispatcher>();
|
||||||
var razorLanguageServices = workspace.Services.GetLanguageServices(RazorLanguage.Name);
|
var razorLanguageServices = workspace.Services.GetLanguageServices(RazorLanguage.Name);
|
||||||
_projectManager = razorLanguageServices.GetRequiredService<ProjectSnapshotManager>();
|
_projectManager = razorLanguageServices.GetRequiredService<ProjectSnapshotManager>();
|
||||||
_editorSettingsManager = razorLanguageServices.GetRequiredService<EditorSettingsManager>();
|
_editorSettingsManager = razorLanguageServices.GetRequiredService<EditorSettingsManagerInternal>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override VisualStudioDocumentTracker Create(ITextBuffer textBuffer)
|
public override VisualStudioDocumentTracker Create(ITextBuffer textBuffer)
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
[Flags]
|
|
||||||
public enum AcceptedCharacters
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
NewLine = 1,
|
|
||||||
WhiteSpace = 2,
|
|
||||||
|
|
||||||
NonWhiteSpace = 4,
|
|
||||||
|
|
||||||
AllWhiteSpace = NewLine | WhiteSpace,
|
|
||||||
Any = AllWhiteSpace | NonWhiteSpace,
|
|
||||||
|
|
||||||
AnyExceptNewline = NonWhiteSpace | WhiteSpace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public class AttributeCompletionContext
|
|
||||||
{
|
|
||||||
public AttributeCompletionContext(
|
|
||||||
TagHelperDocumentContext documentContext,
|
|
||||||
IEnumerable<string> existingCompletions,
|
|
||||||
string currentTagName,
|
|
||||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
|
||||||
string currentParentTagName,
|
|
||||||
Func<string, bool> inHTMLSchema)
|
|
||||||
{
|
|
||||||
if (documentContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(documentContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingCompletions == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(existingCompletions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentTagName == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(currentTagName));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(attributes));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inHTMLSchema == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(inHTMLSchema));
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentContext = documentContext;
|
|
||||||
ExistingCompletions = existingCompletions;
|
|
||||||
CurrentTagName = currentTagName;
|
|
||||||
Attributes = attributes;
|
|
||||||
CurrentParentTagName = currentParentTagName;
|
|
||||||
InHTMLSchema = inHTMLSchema;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TagHelperDocumentContext DocumentContext { get; }
|
|
||||||
|
|
||||||
public IEnumerable<string> ExistingCompletions { get; }
|
|
||||||
|
|
||||||
public string CurrentTagName { get; }
|
|
||||||
|
|
||||||
public IEnumerable<KeyValuePair<string, string>> Attributes { get; }
|
|
||||||
|
|
||||||
public string CurrentParentTagName { get; }
|
|
||||||
|
|
||||||
public Func<string, bool> InHTMLSchema { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public abstract class AttributeCompletionResult
|
|
||||||
{
|
|
||||||
private AttributeCompletionResult()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract IReadOnlyDictionary<string, IEnumerable<BoundAttributeDescriptor>> Completions { get; }
|
|
||||||
|
|
||||||
internal static AttributeCompletionResult Create(Dictionary<string, HashSet<BoundAttributeDescriptor>> completions)
|
|
||||||
{
|
|
||||||
var readonlyCompletions = completions.ToDictionary(
|
|
||||||
key => key.Key,
|
|
||||||
value => (IEnumerable<BoundAttributeDescriptor>)value.Value,
|
|
||||||
completions.Comparer);
|
|
||||||
var result = new DefaultAttributeCompletionResult(readonlyCompletions);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DefaultAttributeCompletionResult : AttributeCompletionResult
|
|
||||||
{
|
|
||||||
private readonly IReadOnlyDictionary<string, IEnumerable<BoundAttributeDescriptor>> _completions;
|
|
||||||
|
|
||||||
public DefaultAttributeCompletionResult(IReadOnlyDictionary<string, IEnumerable<BoundAttributeDescriptor>> completions)
|
|
||||||
{
|
|
||||||
_completions = completions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyDictionary<string, IEnumerable<BoundAttributeDescriptor>> Completions => _completions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public enum BlockKind
|
|
||||||
{
|
|
||||||
// Code
|
|
||||||
Statement,
|
|
||||||
Directive,
|
|
||||||
Functions,
|
|
||||||
Expression,
|
|
||||||
Helper,
|
|
||||||
|
|
||||||
// Markup
|
|
||||||
Markup,
|
|
||||||
Section,
|
|
||||||
Template,
|
|
||||||
|
|
||||||
// Special
|
|
||||||
Comment,
|
|
||||||
Tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public struct ClassifiedSpan
|
|
||||||
{
|
|
||||||
public ClassifiedSpan(SourceSpan span, SourceSpan blockSpan, SpanKind spanKind, BlockKind blockKind, AcceptedCharacters acceptedCharacters)
|
|
||||||
{
|
|
||||||
Span = span;
|
|
||||||
BlockSpan = blockSpan;
|
|
||||||
SpanKind = spanKind;
|
|
||||||
BlockKind = blockKind;
|
|
||||||
AcceptedCharacters = acceptedCharacters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AcceptedCharacters AcceptedCharacters { get; }
|
|
||||||
|
|
||||||
public BlockKind BlockKind { get; }
|
|
||||||
|
|
||||||
public SourceSpan BlockSpan { get; }
|
|
||||||
|
|
||||||
public SourceSpan Span { get; }
|
|
||||||
|
|
||||||
public SpanKind SpanKind { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,352 +0,0 @@
|
||||||
// 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;
|
|
||||||
using Microsoft.VisualStudio.Text;
|
|
||||||
using Span = Microsoft.AspNetCore.Razor.Language.Legacy.Span;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
[Export(typeof(RazorSyntaxFactsService))]
|
|
||||||
internal class DefaultRazorSyntaxFactsService : RazorSyntaxFactsService
|
|
||||||
{
|
|
||||||
public override IReadOnlyList<ClassifiedSpan> GetClassifiedSpans(RazorSyntaxTree syntaxTree)
|
|
||||||
{
|
|
||||||
if (syntaxTree == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(syntaxTree));
|
|
||||||
}
|
|
||||||
|
|
||||||
var spans = Flatten(syntaxTree);
|
|
||||||
|
|
||||||
var result = new ClassifiedSpan[spans.Count];
|
|
||||||
for (var i = 0; i < spans.Count; i++)
|
|
||||||
{
|
|
||||||
var span = spans[i];
|
|
||||||
result[i] = new ClassifiedSpan(
|
|
||||||
new SourceSpan(
|
|
||||||
span.Start.FilePath ?? syntaxTree.Source.FilePath,
|
|
||||||
span.Start.AbsoluteIndex,
|
|
||||||
span.Start.LineIndex,
|
|
||||||
span.Start.CharacterIndex,
|
|
||||||
span.Length),
|
|
||||||
new SourceSpan(
|
|
||||||
span.Parent.Start.FilePath ?? syntaxTree.Source.FilePath,
|
|
||||||
span.Parent.Start.AbsoluteIndex,
|
|
||||||
span.Parent.Start.LineIndex,
|
|
||||||
span.Parent.Start.CharacterIndex,
|
|
||||||
span.Parent.Length),
|
|
||||||
(SpanKind)span.Kind,
|
|
||||||
(BlockKind)span.Parent.Type,
|
|
||||||
(AcceptedCharacters)span.EditHandler.AcceptedCharacters);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Span> Flatten(RazorSyntaxTree syntaxTree)
|
|
||||||
{
|
|
||||||
var result = new List<Span>();
|
|
||||||
AppendFlattenedSpans(syntaxTree.Root, result);
|
|
||||||
return result;
|
|
||||||
|
|
||||||
void AppendFlattenedSpans(SyntaxTreeNode node, List<Span> foundSpans)
|
|
||||||
{
|
|
||||||
Span spanNode = node as Span;
|
|
||||||
if (spanNode != null)
|
|
||||||
{
|
|
||||||
foundSpans.Add(spanNode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TagHelperBlock tagHelperNode = node as TagHelperBlock;
|
|
||||||
if (tagHelperNode != null)
|
|
||||||
{
|
|
||||||
// These aren't in document order, sort them first and then dig in
|
|
||||||
List<SyntaxTreeNode> attributeNodes = tagHelperNode.Attributes.Select(kvp => kvp.Value).Where(att => att != null).ToList();
|
|
||||||
attributeNodes.Sort((x, y) => x.Start.AbsoluteIndex.CompareTo(y.Start.AbsoluteIndex));
|
|
||||||
|
|
||||||
foreach (SyntaxTreeNode curNode in attributeNodes)
|
|
||||||
{
|
|
||||||
AppendFlattenedSpans(curNode, foundSpans);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Block blockNode = node as Block;
|
|
||||||
if (blockNode != null)
|
|
||||||
{
|
|
||||||
foreach (SyntaxTreeNode curNode in blockNode.Children)
|
|
||||||
{
|
|
||||||
AppendFlattenedSpans(curNode, foundSpans);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyList<TagHelperSpan> GetTagHelperSpans(RazorSyntaxTree syntaxTree)
|
|
||||||
{
|
|
||||||
if (syntaxTree == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(syntaxTree));
|
|
||||||
}
|
|
||||||
|
|
||||||
var results = new List<TagHelperSpan>();
|
|
||||||
|
|
||||||
List<Block> toProcess = new List<Block>();
|
|
||||||
List<Block> blockChildren = new List<Block>();
|
|
||||||
toProcess.Add(syntaxTree.Root);
|
|
||||||
|
|
||||||
for (var i = 0; i < toProcess.Count; i++)
|
|
||||||
{
|
|
||||||
var blockNode = toProcess[i];
|
|
||||||
TagHelperBlock tagHelperNode = blockNode as TagHelperBlock;
|
|
||||||
if (tagHelperNode != null)
|
|
||||||
{
|
|
||||||
results.Add(new TagHelperSpan(
|
|
||||||
new SourceSpan(
|
|
||||||
tagHelperNode.Start.FilePath ?? syntaxTree.Source.FilePath,
|
|
||||||
tagHelperNode.Start.AbsoluteIndex,
|
|
||||||
tagHelperNode.Start.LineIndex,
|
|
||||||
tagHelperNode.Start.CharacterIndex,
|
|
||||||
tagHelperNode.Length),
|
|
||||||
tagHelperNode.Binding));
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect all child blocks and inject into toProcess as a single InsertRange
|
|
||||||
foreach (SyntaxTreeNode curNode in blockNode.Children)
|
|
||||||
{
|
|
||||||
Block curBlock = curNode as Block;
|
|
||||||
if (curBlock != null)
|
|
||||||
{
|
|
||||||
blockChildren.Add(curBlock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blockChildren.Count > 0)
|
|
||||||
{
|
|
||||||
toProcess.InsertRange(i + 1, blockChildren);
|
|
||||||
blockChildren.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line, int indentSize, int tabSize)
|
|
||||||
{
|
|
||||||
if (syntaxTree == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(syntaxTree));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syntaxTreeSnapshot == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(syntaxTreeSnapshot));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(line));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indentSize < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(indentSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tabSize < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(tabSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The tricky thing here is that line.Snapshot is very likely newer
|
|
||||||
var previousLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);
|
|
||||||
var trackingPoint = line.Snapshot.CreateTrackingPoint(line.End, PointTrackingMode.Negative);
|
|
||||||
var previousLineEnd = trackingPoint.GetPosition(syntaxTreeSnapshot);
|
|
||||||
|
|
||||||
var simulatedChange = new SourceChange(previousLineEnd, 0, string.Empty);
|
|
||||||
var owningSpan = LocateOwner(syntaxTree.Root, simulatedChange);
|
|
||||||
|
|
||||||
int? desiredIndentation = null;
|
|
||||||
|
|
||||||
if (owningSpan.Kind != SpanKindInternal.Code)
|
|
||||||
{
|
|
||||||
SyntaxTreeNode owningChild = owningSpan;
|
|
||||||
while ((owningChild.Parent != null) && !desiredIndentation.HasValue)
|
|
||||||
{
|
|
||||||
Block owningParent = owningChild.Parent;
|
|
||||||
List<SyntaxTreeNode> children = new List<SyntaxTreeNode>(owningParent.Children);
|
|
||||||
for (int i = 0; i < children.Count; i++)
|
|
||||||
{
|
|
||||||
SyntaxTreeNode curChild = children[i];
|
|
||||||
if (!curChild.IsBlock)
|
|
||||||
{
|
|
||||||
Span curSpan = curChild as Span;
|
|
||||||
if (curSpan.Kind == SpanKindInternal.MetaCode)
|
|
||||||
{
|
|
||||||
// yay! We want to use the start of this span to determine the indent level.
|
|
||||||
var startLine = line.Snapshot.GetLineFromLineNumber(curSpan.Start.LineIndex);
|
|
||||||
var extraIndent = 0;
|
|
||||||
|
|
||||||
// Dev11 337312: Only indent one level deeper if the item after the metacode is a markup block
|
|
||||||
if (i < children.Count - 1)
|
|
||||||
{
|
|
||||||
SyntaxTreeNode nextChild = children[i + 1];
|
|
||||||
if (nextChild.IsBlock && ((nextChild as Block).Type == BlockKindInternal.Markup))
|
|
||||||
{
|
|
||||||
extraIndent = indentSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
desiredIndentation = GetIndentLevelOfLine(startLine, tabSize) + indentSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curChild == owningChild)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
owningChild = owningParent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return desiredIndentation;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Span LocateOwner(Block root, SourceChange change)
|
|
||||||
{
|
|
||||||
// Ask each child recursively
|
|
||||||
Span owner = null;
|
|
||||||
foreach (SyntaxTreeNode element in root.Children)
|
|
||||||
{
|
|
||||||
if (element.Start.AbsoluteIndex > change.Span.AbsoluteIndex)
|
|
||||||
{
|
|
||||||
// too far
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int elementLen = element.Length;
|
|
||||||
if (element.Start.AbsoluteIndex + elementLen < change.Span.AbsoluteIndex)
|
|
||||||
{
|
|
||||||
// not far enough
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.IsBlock)
|
|
||||||
{
|
|
||||||
Block block = element as Block;
|
|
||||||
|
|
||||||
if (element.Start.AbsoluteIndex + elementLen == change.Span.AbsoluteIndex)
|
|
||||||
{
|
|
||||||
Span lastDescendant = block.FindLastDescendentSpan();
|
|
||||||
if ((lastDescendant == null) && (block is TagHelperBlock))
|
|
||||||
{
|
|
||||||
TagHelperBlock tagHelperBlock = (TagHelperBlock)block;
|
|
||||||
if (tagHelperBlock.SourceEndTag != null)
|
|
||||||
{
|
|
||||||
lastDescendant = tagHelperBlock.SourceEndTag.FindLastDescendentSpan();
|
|
||||||
}
|
|
||||||
else if (tagHelperBlock.SourceStartTag != null)
|
|
||||||
{
|
|
||||||
lastDescendant = tagHelperBlock.SourceStartTag.FindLastDescendentSpan();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conceptually, lastDescendant should always be non-null, but runtime errs on some
|
|
||||||
// cases and makes empty blocks. Runtime will fix these issues as we find them, but make
|
|
||||||
// no guarantee that they catch them all.
|
|
||||||
if (lastDescendant == null)
|
|
||||||
{
|
|
||||||
owner = LocateOwner(block, change);
|
|
||||||
if (owner != null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (lastDescendant.EditHandler.OwnsChange(lastDescendant, change))
|
|
||||||
{
|
|
||||||
owner = lastDescendant;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
owner = LocateOwner(block, change);
|
|
||||||
if (owner != null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Span span = element as Span;
|
|
||||||
if (span.EditHandler.OwnsChange(span, change))
|
|
||||||
{
|
|
||||||
owner = span;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (owner == null)
|
|
||||||
{
|
|
||||||
TagHelperBlock tagHelperNode = root as TagHelperBlock;
|
|
||||||
if (tagHelperNode != null)
|
|
||||||
{
|
|
||||||
Block sourceStartTag = tagHelperNode.SourceStartTag;
|
|
||||||
Block sourceEndTag = tagHelperNode.SourceEndTag;
|
|
||||||
if ((sourceStartTag.Start.AbsoluteIndex <= change.Span.AbsoluteIndex) &&
|
|
||||||
(sourceStartTag.Start.AbsoluteIndex + sourceStartTag.Length >= change.Span.AbsoluteIndex))
|
|
||||||
{
|
|
||||||
// intersects the start tag
|
|
||||||
return LocateOwner(sourceStartTag, change);
|
|
||||||
}
|
|
||||||
else if ((sourceEndTag.Start.AbsoluteIndex <= change.Span.AbsoluteIndex) &&
|
|
||||||
(sourceEndTag.Start.AbsoluteIndex + sourceEndTag.Length >= change.Span.AbsoluteIndex))
|
|
||||||
{
|
|
||||||
// intersects the end tag
|
|
||||||
return LocateOwner(sourceEndTag, change);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetIndentLevelOfLine(ITextSnapshotLine line, int tabSize)
|
|
||||||
{
|
|
||||||
var indentLevel = 0;
|
|
||||||
|
|
||||||
foreach (var c in line.GetText())
|
|
||||||
{
|
|
||||||
if (!char.IsWhiteSpace(c))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (c == '\t')
|
|
||||||
{
|
|
||||||
indentLevel += tabSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
indentLevel++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return indentLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,285 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.Composition;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
[Export(typeof(TagHelperCompletionService))]
|
|
||||||
internal class DefaultTagHelperCompletionService : TagHelperCompletionService
|
|
||||||
{
|
|
||||||
private readonly TagHelperFactsService _tagHelperFactsService;
|
|
||||||
private static readonly HashSet<TagHelperDescriptor> _emptyHashSet = new HashSet<TagHelperDescriptor>();
|
|
||||||
|
|
||||||
[ImportingConstructor]
|
|
||||||
public DefaultTagHelperCompletionService(TagHelperFactsService tagHelperFactsService)
|
|
||||||
{
|
|
||||||
_tagHelperFactsService = tagHelperFactsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This API attempts to understand a users context as they're typing in a Razor file to provide TagHelper based attribute IntelliSense.
|
|
||||||
*
|
|
||||||
* Scenarios for TagHelper attribute IntelliSense follows:
|
|
||||||
* 1. TagHelperDescriptor's have matching required attribute names
|
|
||||||
* -> Provide IntelliSense for the required attributes of those descriptors to lead users towards a TagHelperified element.
|
|
||||||
* 2. TagHelperDescriptor entirely applies to current element. Tag name, attributes, everything is fulfilled.
|
|
||||||
* -> Provide IntelliSense for the bound attributes for the applied descriptors.
|
|
||||||
*
|
|
||||||
* Within each of the above scenarios if an attribute completion has a corresponding bound attribute we associate it with the corresponding
|
|
||||||
* BoundAttributeDescriptor. By doing this a user can see what C# type a TagHelper expects for the attribute.
|
|
||||||
*/
|
|
||||||
public override AttributeCompletionResult GetAttributeCompletions(AttributeCompletionContext completionContext)
|
|
||||||
{
|
|
||||||
if (completionContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(completionContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
var attributeCompletions = completionContext.ExistingCompletions.ToDictionary(
|
|
||||||
completion => completion,
|
|
||||||
_ => new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var documentContext = completionContext.DocumentContext;
|
|
||||||
var descriptorsForTag = _tagHelperFactsService.GetTagHelpersGivenTag(documentContext, completionContext.CurrentTagName, completionContext.CurrentParentTagName);
|
|
||||||
if (descriptorsForTag.Count == 0)
|
|
||||||
{
|
|
||||||
// If the current tag has no possible descriptors then we can't have any additional attributes.
|
|
||||||
var defaultResult = AttributeCompletionResult.Create(attributeCompletions);
|
|
||||||
return defaultResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
var prefix = documentContext.Prefix ?? string.Empty;
|
|
||||||
Debug.Assert(completionContext.CurrentTagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
var applicableTagHelperBinding = _tagHelperFactsService.GetTagHelperBinding(
|
|
||||||
documentContext,
|
|
||||||
completionContext.CurrentTagName,
|
|
||||||
completionContext.Attributes,
|
|
||||||
completionContext.CurrentParentTagName);
|
|
||||||
|
|
||||||
var applicableDescriptors = applicableTagHelperBinding?.Descriptors ?? Enumerable.Empty<TagHelperDescriptor>();
|
|
||||||
var unprefixedTagName = completionContext.CurrentTagName.Substring(prefix.Length);
|
|
||||||
|
|
||||||
if (!completionContext.InHTMLSchema(unprefixedTagName) &&
|
|
||||||
applicableDescriptors.All(descriptor => descriptor.TagOutputHint == null))
|
|
||||||
{
|
|
||||||
// This isn't a known HTML tag and no descriptor has an output element hint. Remove all previous completions.
|
|
||||||
attributeCompletions.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < descriptorsForTag.Count; i++)
|
|
||||||
{
|
|
||||||
var descriptor = descriptorsForTag[i];
|
|
||||||
|
|
||||||
if (applicableDescriptors.Contains(descriptor))
|
|
||||||
{
|
|
||||||
foreach (var attributeDescriptor in descriptor.BoundAttributes)
|
|
||||||
{
|
|
||||||
UpdateCompletions(attributeDescriptor.Name, attributeDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var htmlNameToBoundAttribute = descriptor.BoundAttributes.ToDictionary(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
foreach (var rule in descriptor.TagMatchingRules)
|
|
||||||
{
|
|
||||||
foreach (var requiredAttribute in rule.Attributes)
|
|
||||||
{
|
|
||||||
if (htmlNameToBoundAttribute.TryGetValue(requiredAttribute.Name, out var attributeDescriptor))
|
|
||||||
{
|
|
||||||
UpdateCompletions(requiredAttribute.Name, attributeDescriptor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UpdateCompletions(requiredAttribute.Name, possibleDescriptor: null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var completionResult = AttributeCompletionResult.Create(attributeCompletions);
|
|
||||||
return completionResult;
|
|
||||||
|
|
||||||
void UpdateCompletions(string attributeName, BoundAttributeDescriptor possibleDescriptor)
|
|
||||||
{
|
|
||||||
if (completionContext.Attributes.Any(attribute => string.Equals(attribute.Key, attributeName, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
// Attribute is already present on this element it shouldn't exist in the completion list.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!attributeCompletions.TryGetValue(attributeName, out var rules))
|
|
||||||
{
|
|
||||||
rules = new HashSet<BoundAttributeDescriptor>();
|
|
||||||
attributeCompletions[attributeName] = rules;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (possibleDescriptor != null)
|
|
||||||
{
|
|
||||||
rules.Add(possibleDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ElementCompletionResult GetElementCompletions(ElementCompletionContext completionContext)
|
|
||||||
{
|
|
||||||
if (completionContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(completionContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
var elementCompletions = new Dictionary<string, HashSet<TagHelperDescriptor>>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
AddAllowedChildrenCompletions(completionContext, elementCompletions);
|
|
||||||
|
|
||||||
if (elementCompletions.Count > 0)
|
|
||||||
{
|
|
||||||
// If the containing element is already a TagHelper and only allows certain children.
|
|
||||||
var emptyResult = ElementCompletionResult.Create(elementCompletions);
|
|
||||||
return emptyResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
elementCompletions = completionContext.ExistingCompletions.ToDictionary(
|
|
||||||
completion => completion,
|
|
||||||
_ => new HashSet<TagHelperDescriptor>(),
|
|
||||||
StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var catchAllDescriptors = new HashSet<TagHelperDescriptor>();
|
|
||||||
var prefix = completionContext.DocumentContext.Prefix ?? string.Empty;
|
|
||||||
var possibleChildDescriptors = _tagHelperFactsService.GetTagHelpersGivenParent(completionContext.DocumentContext, completionContext.ContainingTagName);
|
|
||||||
foreach (var possibleDescriptor in possibleChildDescriptors)
|
|
||||||
{
|
|
||||||
var addRuleCompletions = false;
|
|
||||||
var outputHint = possibleDescriptor.TagOutputHint;
|
|
||||||
|
|
||||||
foreach (var rule in possibleDescriptor.TagMatchingRules)
|
|
||||||
{
|
|
||||||
if (rule.TagName == TagHelperMatchingConventions.ElementCatchAllName)
|
|
||||||
{
|
|
||||||
catchAllDescriptors.Add(possibleDescriptor);
|
|
||||||
}
|
|
||||||
else if (elementCompletions.ContainsKey(rule.TagName))
|
|
||||||
{
|
|
||||||
addRuleCompletions = true;
|
|
||||||
}
|
|
||||||
else if (outputHint != null)
|
|
||||||
{
|
|
||||||
// If the current descriptor has an output hint we need to make sure it shows up only when its output hint would normally show up.
|
|
||||||
// Example: We have a MyTableTagHelper that has an output hint of "table" and a MyTrTagHelper that has an output hint of "tr".
|
|
||||||
// If we try typing in a situation like this: <body > | </body>
|
|
||||||
// We'd expect to only get "my-table" as a completion because the "body" tag doesn't allow "tr" tags.
|
|
||||||
addRuleCompletions = elementCompletions.ContainsKey(outputHint);
|
|
||||||
}
|
|
||||||
else if (!completionContext.InHTMLSchema(rule.TagName))
|
|
||||||
{
|
|
||||||
// If there is an unknown HTML schema tag that doesn't exist in the current completion we should add it. This happens for
|
|
||||||
// TagHelpers that target non-schema oriented tags.
|
|
||||||
addRuleCompletions = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addRuleCompletions)
|
|
||||||
{
|
|
||||||
UpdateCompletions(prefix + rule.TagName, possibleDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We needed to track all catch-alls and update their completions after all other completions have been completed.
|
|
||||||
// This way, any TagHelper added completions will also have catch-alls listed under their entries.
|
|
||||||
foreach (var catchAllDescriptor in catchAllDescriptors)
|
|
||||||
{
|
|
||||||
foreach (var completionTagName in elementCompletions.Keys)
|
|
||||||
{
|
|
||||||
if (elementCompletions[completionTagName].Count > 0 ||
|
|
||||||
!string.IsNullOrEmpty(prefix) && completionTagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
// The current completion either has other TagHelper's associated with it or is prefixed with a non-empty
|
|
||||||
// TagHelper prefix.
|
|
||||||
UpdateCompletions(completionTagName, catchAllDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = ElementCompletionResult.Create(elementCompletions);
|
|
||||||
return result;
|
|
||||||
|
|
||||||
void UpdateCompletions(string tagName, TagHelperDescriptor possibleDescriptor)
|
|
||||||
{
|
|
||||||
if (!elementCompletions.TryGetValue(tagName, out var existingRuleDescriptors))
|
|
||||||
{
|
|
||||||
existingRuleDescriptors = new HashSet<TagHelperDescriptor>();
|
|
||||||
elementCompletions[tagName] = existingRuleDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
existingRuleDescriptors.Add(possibleDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddAllowedChildrenCompletions(
|
|
||||||
ElementCompletionContext completionContext,
|
|
||||||
Dictionary<string, HashSet<TagHelperDescriptor>> elementCompletions)
|
|
||||||
{
|
|
||||||
if (completionContext.ContainingTagName == null)
|
|
||||||
{
|
|
||||||
// If we're at the root then there's no containing TagHelper to specify allowed children.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var prefix = completionContext.DocumentContext.Prefix ?? string.Empty;
|
|
||||||
var binding = _tagHelperFactsService.GetTagHelperBinding(
|
|
||||||
completionContext.DocumentContext,
|
|
||||||
completionContext.ContainingTagName,
|
|
||||||
completionContext.Attributes,
|
|
||||||
completionContext.ContainingParentTagName);
|
|
||||||
|
|
||||||
if (binding == null)
|
|
||||||
{
|
|
||||||
// Containing tag is not a TagHelper; therefore, it allows any children.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var descriptor in binding.Descriptors)
|
|
||||||
{
|
|
||||||
foreach (var childTag in descriptor.AllowedChildTags)
|
|
||||||
{
|
|
||||||
var prefixedName = string.Concat(prefix, childTag.Name);
|
|
||||||
var descriptors = _tagHelperFactsService.GetTagHelpersGivenTag(
|
|
||||||
completionContext.DocumentContext,
|
|
||||||
prefixedName,
|
|
||||||
completionContext.ContainingTagName);
|
|
||||||
|
|
||||||
if (descriptors.Count == 0)
|
|
||||||
{
|
|
||||||
if (!elementCompletions.ContainsKey(prefixedName))
|
|
||||||
{
|
|
||||||
elementCompletions[prefixedName] = _emptyHashSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!elementCompletions.TryGetValue(prefixedName, out var existingRuleDescriptors))
|
|
||||||
{
|
|
||||||
existingRuleDescriptors = new HashSet<TagHelperDescriptor>();
|
|
||||||
elementCompletions[prefixedName] = existingRuleDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
existingRuleDescriptors.UnionWith(descriptors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.Composition;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
[Export(typeof(TagHelperFactsService))]
|
|
||||||
internal class DefaultTagHelperFactsService : TagHelperFactsService
|
|
||||||
{
|
|
||||||
public override TagHelperBinding GetTagHelperBinding(
|
|
||||||
TagHelperDocumentContext documentContext,
|
|
||||||
string tagName,
|
|
||||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
|
||||||
string parentTag)
|
|
||||||
{
|
|
||||||
if (documentContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(documentContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagName == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(tagName));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(attributes));
|
|
||||||
}
|
|
||||||
|
|
||||||
var descriptors = documentContext.TagHelpers;
|
|
||||||
if (descriptors == null || descriptors.Count == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var prefix = documentContext.Prefix;
|
|
||||||
var tagHelperBinder = new TagHelperBinder(prefix, descriptors);
|
|
||||||
var binding = tagHelperBinder.GetBinding(tagName, attributes.ToList(), parentTag, parentIsTagHelper: false);
|
|
||||||
|
|
||||||
return binding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(
|
|
||||||
TagHelperDocumentContext documentContext,
|
|
||||||
string attributeName,
|
|
||||||
TagHelperBinding binding)
|
|
||||||
{
|
|
||||||
if (documentContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(documentContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributeName == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(attributeName));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (binding == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(binding));
|
|
||||||
}
|
|
||||||
|
|
||||||
var matchingBoundAttributes = new List<BoundAttributeDescriptor>();
|
|
||||||
foreach (var descriptor in binding.Descriptors)
|
|
||||||
{
|
|
||||||
foreach (var boundAttributeDescriptor in descriptor.BoundAttributes)
|
|
||||||
{
|
|
||||||
if (TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, boundAttributeDescriptor))
|
|
||||||
{
|
|
||||||
matchingBoundAttributes.Add(boundAttributeDescriptor);
|
|
||||||
|
|
||||||
// Only one bound attribute can match an attribute
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchingBoundAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(
|
|
||||||
TagHelperDocumentContext documentContext,
|
|
||||||
string tagName,
|
|
||||||
string parentTag)
|
|
||||||
{
|
|
||||||
if (documentContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(documentContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagName == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(tagName));
|
|
||||||
}
|
|
||||||
|
|
||||||
var matchingDescriptors = new List<TagHelperDescriptor>();
|
|
||||||
var descriptors = documentContext?.TagHelpers;
|
|
||||||
if (descriptors?.Count == 0)
|
|
||||||
{
|
|
||||||
return matchingDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
var prefix = documentContext.Prefix ?? string.Empty;
|
|
||||||
if (!tagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
// Can't possibly match TagHelpers, it doesn't start with the TagHelperPrefix.
|
|
||||||
return matchingDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
var tagNameWithoutPrefix = tagName.Substring(prefix.Length);
|
|
||||||
for (var i = 0; i < descriptors.Count; i++)
|
|
||||||
{
|
|
||||||
var descriptor = descriptors[i];
|
|
||||||
foreach (var rule in descriptor.TagMatchingRules)
|
|
||||||
{
|
|
||||||
if (TagHelperMatchingConventions.SatisfiesTagName(tagNameWithoutPrefix, rule) &&
|
|
||||||
TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
|
|
||||||
{
|
|
||||||
matchingDescriptors.Add(descriptor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchingDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag)
|
|
||||||
{
|
|
||||||
if (documentContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(documentContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
var matchingDescriptors = new List<TagHelperDescriptor>();
|
|
||||||
var descriptors = documentContext?.TagHelpers;
|
|
||||||
if (descriptors?.Count == 0)
|
|
||||||
{
|
|
||||||
return matchingDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < descriptors.Count; i++)
|
|
||||||
{
|
|
||||||
var descriptor = descriptors[i];
|
|
||||||
foreach (var rule in descriptor.TagMatchingRules)
|
|
||||||
{
|
|
||||||
if (TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
|
|
||||||
{
|
|
||||||
matchingDescriptors.Add(descriptor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchingDescriptors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.VisualStudio.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Arguments for the <see cref="RazorEditorParser.DocumentParseComplete"/> event in <see cref="RazorEditorParser"/>.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class DocumentParseCompleteEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public DocumentParseCompleteEventArgs(
|
|
||||||
SourceChange change,
|
|
||||||
ITextSnapshot buffer,
|
|
||||||
bool treeStructureChanged,
|
|
||||||
RazorCodeDocument codeDocument)
|
|
||||||
{
|
|
||||||
SourceChange = change;
|
|
||||||
Buffer = buffer;
|
|
||||||
TreeStructureChanged = treeStructureChanged;
|
|
||||||
CodeDocument = codeDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="AspNetCore.Razor.Language.SourceChange"/> which triggered the re-parse.
|
|
||||||
/// </summary>
|
|
||||||
public SourceChange SourceChange { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The text snapshot used in the re-parse.
|
|
||||||
/// </summary>
|
|
||||||
public ITextSnapshot Buffer { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates if the tree structure has actually changed since the previous re-parse.
|
|
||||||
/// </summary>
|
|
||||||
public bool TreeStructureChanged { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The result of the parsing and code generation.
|
|
||||||
/// </summary>
|
|
||||||
public RazorCodeDocument CodeDocument { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public sealed class ElementCompletionContext
|
|
||||||
{
|
|
||||||
public ElementCompletionContext(
|
|
||||||
TagHelperDocumentContext documentContext,
|
|
||||||
IEnumerable<string> existingCompletions,
|
|
||||||
string containingTagName,
|
|
||||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
|
||||||
string containingParentTagName,
|
|
||||||
Func<string, bool> inHTMLSchema)
|
|
||||||
{
|
|
||||||
if (documentContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(documentContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingCompletions == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(existingCompletions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inHTMLSchema == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(inHTMLSchema));
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentContext = documentContext;
|
|
||||||
ExistingCompletions = existingCompletions;
|
|
||||||
ContainingTagName = containingTagName;
|
|
||||||
Attributes = attributes;
|
|
||||||
ContainingParentTagName = containingParentTagName;
|
|
||||||
InHTMLSchema = inHTMLSchema;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TagHelperDocumentContext DocumentContext { get; }
|
|
||||||
|
|
||||||
public IEnumerable<string> ExistingCompletions { get; }
|
|
||||||
|
|
||||||
public string ContainingTagName { get; }
|
|
||||||
|
|
||||||
public IEnumerable<KeyValuePair<string, string>> Attributes { get; }
|
|
||||||
|
|
||||||
public string ContainingParentTagName { get; }
|
|
||||||
|
|
||||||
public Func<string, bool> InHTMLSchema { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public abstract class ElementCompletionResult
|
|
||||||
{
|
|
||||||
private ElementCompletionResult()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> Completions { get; }
|
|
||||||
|
|
||||||
internal static ElementCompletionResult Create(Dictionary<string, HashSet<TagHelperDescriptor>> completions)
|
|
||||||
{
|
|
||||||
var readonlyCompletions = completions.ToDictionary(
|
|
||||||
key => key.Key,
|
|
||||||
value => (IEnumerable<TagHelperDescriptor>)value.Value,
|
|
||||||
completions.Comparer);
|
|
||||||
var result = new DefaultElementCompletionResult(readonlyCompletions);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DefaultElementCompletionResult : ElementCompletionResult
|
|
||||||
{
|
|
||||||
private readonly IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> _completions;
|
|
||||||
|
|
||||||
public DefaultElementCompletionResult(IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> completions)
|
|
||||||
{
|
|
||||||
_completions = completions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> Completions => _completions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -15,4 +15,4 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
{
|
{
|
||||||
Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project);
|
Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -21,4 +21,4 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel.Composition;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
|
||||||
using Inner = Microsoft.CodeAnalysis.Razor.RazorTemplateEngineFactoryService;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use DefaultTemplateEngineFactoryService.
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
[Export(typeof(RazorTemplateEngineFactoryService))]
|
|
||||||
internal class LegacyTemplateEngineFactoryService : RazorTemplateEngineFactoryService
|
|
||||||
{
|
|
||||||
private readonly Inner _inner;
|
|
||||||
private readonly Workspace _workspace;
|
|
||||||
|
|
||||||
[ImportingConstructor]
|
|
||||||
public LegacyTemplateEngineFactoryService([Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
|
||||||
{
|
|
||||||
if (workspace == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(workspace));
|
|
||||||
}
|
|
||||||
|
|
||||||
_workspace = workspace;
|
|
||||||
_inner = workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<Inner>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal for testing
|
|
||||||
internal LegacyTemplateEngineFactoryService(Workspace workspace, Inner inner)
|
|
||||||
{
|
|
||||||
if (workspace == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(workspace));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inner == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(inner));
|
|
||||||
}
|
|
||||||
|
|
||||||
_workspace = workspace;
|
|
||||||
_inner = inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure)
|
|
||||||
{
|
|
||||||
if (projectPath == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(projectPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _inner.Create(projectPath, configure);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel.Composition;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.VisualStudio.Editor.Razor;
|
|
||||||
using Microsoft.VisualStudio.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|
||||||
{
|
|
||||||
[System.Composition.Shared]
|
|
||||||
[Export(typeof(TextBufferCodeDocumentProvider))]
|
|
||||||
internal class LegacyTextBufferCodeDocumentProvider : TextBufferCodeDocumentProvider
|
|
||||||
{
|
|
||||||
public override bool TryGetFromBuffer(ITextBuffer textBuffer, out RazorCodeDocument codeDocument)
|
|
||||||
{
|
|
||||||
if (textBuffer == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(textBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textBuffer.Properties.TryGetProperty(typeof(VisualStudioRazorParser), out VisualStudioRazorParser parser) && parser.CodeDocument != null)
|
|
||||||
{
|
|
||||||
codeDocument = parser.CodeDocument;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support the legacy parser for code document extraction.
|
|
||||||
if (textBuffer.Properties.TryGetProperty(typeof(RazorEditorParser), out RazorEditorParser legacyParser) && legacyParser.CodeDocument != null)
|
|
||||||
{
|
|
||||||
codeDocument = legacyParser.CodeDocument;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
codeDocument = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
// 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.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum PartialParseResult
|
|
||||||
{
|
|
||||||
Rejected = 1,
|
|
||||||
|
|
||||||
Accepted = 2,
|
|
||||||
|
|
||||||
Provisional = 4,
|
|
||||||
|
|
||||||
SpanContextChanged = 8,
|
|
||||||
|
|
||||||
AutoCompleteBlock = 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,623 +0,0 @@
|
||||||
// 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.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
|
||||||
using Microsoft.VisualStudio.Editor.Razor;
|
|
||||||
using Microsoft.VisualStudio.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
public class RazorEditorParser : IDisposable
|
|
||||||
{
|
|
||||||
private RazorTemplateEngine _templateEngine;
|
|
||||||
|
|
||||||
private AspNetCore.Razor.Language.Legacy.Span _lastChangeOwner;
|
|
||||||
private AspNetCore.Razor.Language.Legacy.Span _lastAutoCompleteSpan;
|
|
||||||
private BackgroundParser _parser;
|
|
||||||
|
|
||||||
// For testing only.
|
|
||||||
internal RazorEditorParser(RazorCodeDocument codeDocument)
|
|
||||||
{
|
|
||||||
CodeDocument = codeDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RazorEditorParser(RazorTemplateEngine templateEngine, string filePath)
|
|
||||||
{
|
|
||||||
if (templateEngine == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(templateEngine));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(filePath))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
|
||||||
AspNetCore.Razor.Language.Resources.ArgumentCannotBeNullOrEmpty,
|
|
||||||
nameof(filePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
TemplateEngine = templateEngine;
|
|
||||||
FilePath = filePath;
|
|
||||||
_parser = new BackgroundParser(this, filePath);
|
|
||||||
_parser.ResultsReady += (sender, args) => OnDocumentParseComplete(args);
|
|
||||||
_parser.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event fired when a full reparse of the document completes.
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<DocumentParseCompleteEventArgs> DocumentParseComplete;
|
|
||||||
|
|
||||||
public RazorTemplateEngine TemplateEngine
|
|
||||||
{
|
|
||||||
get => _templateEngine;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_templateEngine = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FilePath { get; }
|
|
||||||
|
|
||||||
// Internal for testing.
|
|
||||||
internal RazorSyntaxTree CurrentSyntaxTree { get; private set; }
|
|
||||||
|
|
||||||
internal RazorCodeDocument CodeDocument { get; private set; }
|
|
||||||
|
|
||||||
// Internal for testing.
|
|
||||||
internal bool LastResultProvisional { get; private set; }
|
|
||||||
|
|
||||||
public virtual string GetAutoCompleteString()
|
|
||||||
{
|
|
||||||
if (_lastAutoCompleteSpan?.EditHandler is AutoCompleteEditHandler editHandler)
|
|
||||||
{
|
|
||||||
return editHandler.AutoCompleteString;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual PartialParseResult CheckForStructureChanges(SourceChange change, ITextSnapshot snapshot)
|
|
||||||
{
|
|
||||||
if (snapshot == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(snapshot));
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = PartialParseResultInternal.Rejected;
|
|
||||||
|
|
||||||
using (_parser.SynchronizeMainThreadState())
|
|
||||||
{
|
|
||||||
// Check if we can partial-parse
|
|
||||||
if (CurrentSyntaxTree != null && _parser.IsIdle)
|
|
||||||
{
|
|
||||||
result = TryPartialParse(change);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If partial parsing failed or there were outstanding parser tasks, start a full reparse
|
|
||||||
if ((result & PartialParseResultInternal.Rejected) == PartialParseResultInternal.Rejected)
|
|
||||||
{
|
|
||||||
_parser.QueueChange(change, snapshot);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, remember if this was provisionally accepted for next partial parse
|
|
||||||
LastResultProvisional = (result & PartialParseResultInternal.Provisional) == PartialParseResultInternal.Provisional;
|
|
||||||
VerifyFlagsAreValid(result);
|
|
||||||
|
|
||||||
return (PartialParseResult)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disposes of this parser. Should be called when the editor window is closed and the document is unloaded.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_parser.Dispose();
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PartialParseResultInternal TryPartialParse(SourceChange change)
|
|
||||||
{
|
|
||||||
var result = PartialParseResultInternal.Rejected;
|
|
||||||
|
|
||||||
// Try the last change owner
|
|
||||||
if (_lastChangeOwner != null && _lastChangeOwner.EditHandler.OwnsChange(_lastChangeOwner, change))
|
|
||||||
{
|
|
||||||
var editResult = _lastChangeOwner.EditHandler.ApplyChange(_lastChangeOwner, change);
|
|
||||||
result = editResult.Result;
|
|
||||||
if ((editResult.Result & PartialParseResultInternal.Rejected) != PartialParseResultInternal.Rejected)
|
|
||||||
{
|
|
||||||
_lastChangeOwner.ReplaceWith(editResult.EditedSpan);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locate the span responsible for this change
|
|
||||||
_lastChangeOwner = CurrentSyntaxTree.Root.LocateOwner(change);
|
|
||||||
|
|
||||||
if (LastResultProvisional)
|
|
||||||
{
|
|
||||||
// Last change owner couldn't accept this, so we must do a full reparse
|
|
||||||
result = PartialParseResultInternal.Rejected;
|
|
||||||
}
|
|
||||||
else if (_lastChangeOwner != null)
|
|
||||||
{
|
|
||||||
var editResult = _lastChangeOwner.EditHandler.ApplyChange(_lastChangeOwner, change);
|
|
||||||
result = editResult.Result;
|
|
||||||
if ((editResult.Result & PartialParseResultInternal.Rejected) != PartialParseResultInternal.Rejected)
|
|
||||||
{
|
|
||||||
_lastChangeOwner.ReplaceWith(editResult.EditedSpan);
|
|
||||||
}
|
|
||||||
if ((result & PartialParseResultInternal.AutoCompleteBlock) == PartialParseResultInternal.AutoCompleteBlock)
|
|
||||||
{
|
|
||||||
_lastAutoCompleteSpan = _lastChangeOwner;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_lastAutoCompleteSpan = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDocumentParseComplete(DocumentParseCompleteEventArgs args)
|
|
||||||
{
|
|
||||||
using (_parser.SynchronizeMainThreadState())
|
|
||||||
{
|
|
||||||
CurrentSyntaxTree = args.CodeDocument.GetSyntaxTree();
|
|
||||||
CodeDocument = args.CodeDocument;
|
|
||||||
_lastChangeOwner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(args != null, "Event arguments cannot be null");
|
|
||||||
EventHandler<DocumentParseCompleteEventArgs> handler = DocumentParseComplete;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
handler(this, args);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine("[RzEd] Document Parse Complete Handler Threw: " + ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
|
||||||
private static void VerifyFlagsAreValid(PartialParseResultInternal result)
|
|
||||||
{
|
|
||||||
Debug.Assert(((result & PartialParseResultInternal.Accepted) == PartialParseResultInternal.Accepted) ||
|
|
||||||
((result & PartialParseResultInternal.Rejected) == PartialParseResultInternal.Rejected),
|
|
||||||
"Partial Parse result does not have either of Accepted or Rejected flags set");
|
|
||||||
Debug.Assert(((result & PartialParseResultInternal.Rejected) == PartialParseResultInternal.Rejected) ||
|
|
||||||
((result & PartialParseResultInternal.SpanContextChanged) != PartialParseResultInternal.SpanContextChanged),
|
|
||||||
"Partial Parse result was Accepted AND had SpanContextChanged flag set");
|
|
||||||
Debug.Assert(((result & PartialParseResultInternal.Rejected) == PartialParseResultInternal.Rejected) ||
|
|
||||||
((result & PartialParseResultInternal.AutoCompleteBlock) != PartialParseResultInternal.AutoCompleteBlock),
|
|
||||||
"Partial Parse result was Accepted AND had AutoCompleteBlock flag set");
|
|
||||||
Debug.Assert(((result & PartialParseResultInternal.Accepted) == PartialParseResultInternal.Accepted) ||
|
|
||||||
((result & PartialParseResultInternal.Provisional) != PartialParseResultInternal.Provisional),
|
|
||||||
"Partial Parse result was Rejected AND had Provisional flag set");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class BackgroundParser : IDisposable
|
|
||||||
{
|
|
||||||
private MainThreadState _main;
|
|
||||||
private BackgroundThread _bg;
|
|
||||||
|
|
||||||
public BackgroundParser(RazorEditorParser parser, string filePath)
|
|
||||||
{
|
|
||||||
_main = new MainThreadState(filePath);
|
|
||||||
_bg = new BackgroundThread(_main, parser, filePath);
|
|
||||||
|
|
||||||
_main.ResultsReady += (sender, args) => OnResultsReady(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fired on the main thread.
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<DocumentParseCompleteEventArgs> ResultsReady;
|
|
||||||
|
|
||||||
public bool IsIdle
|
|
||||||
{
|
|
||||||
get { return _main.IsIdle; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
_bg.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cancel()
|
|
||||||
{
|
|
||||||
_main.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void QueueChange(SourceChange change, ITextSnapshot snapshot)
|
|
||||||
{
|
|
||||||
var edit = new Edit(change, snapshot);
|
|
||||||
_main.QueueChange(edit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_main.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDisposable SynchronizeMainThreadState()
|
|
||||||
{
|
|
||||||
return _main.Lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnResultsReady(DocumentParseCompleteEventArgs args)
|
|
||||||
{
|
|
||||||
var handler = ResultsReady;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
handler(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TreesAreDifferent(RazorSyntaxTree leftTree, RazorSyntaxTree rightTree, IEnumerable<Edit> edits, CancellationToken cancelToken)
|
|
||||||
{
|
|
||||||
return TreesAreDifferent(leftTree.Root, rightTree.Root, edits.Select(edit => edit.Change), cancelToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool TreesAreDifferent(Block leftTree, Block rightTree, IEnumerable<SourceChange> changes, CancellationToken cancelToken)
|
|
||||||
{
|
|
||||||
// Apply all the pending changes to the original tree
|
|
||||||
// PERF: If this becomes a bottleneck, we can probably do it the other way around,
|
|
||||||
// i.e. visit the tree and find applicable changes for each node.
|
|
||||||
foreach (var change in changes)
|
|
||||||
{
|
|
||||||
cancelToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
var changeOwner = leftTree.LocateOwner(change);
|
|
||||||
|
|
||||||
// Apply the change to the tree
|
|
||||||
if (changeOwner == null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = changeOwner.EditHandler.ApplyChange(changeOwner, change, force: true);
|
|
||||||
changeOwner.ReplaceWith(result.EditedSpan);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now compare the trees
|
|
||||||
var treesDifferent = !leftTree.EquivalentTo(rightTree);
|
|
||||||
return treesDifferent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class ThreadStateBase
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
private int _id = -1;
|
|
||||||
#endif
|
|
||||||
protected ThreadStateBase()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
|
||||||
protected void SetThreadId(int id)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
_id = id;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
|
||||||
protected void EnsureOnThread()
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
Debug.Assert(_id != -1, "SetThreadId was never called!");
|
|
||||||
Debug.Assert(Thread.CurrentThread.ManagedThreadId == _id, "Called from an unexpected thread!");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
|
||||||
protected void EnsureNotOnThread()
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
Debug.Assert(_id != -1, "SetThreadId was never called!");
|
|
||||||
Debug.Assert(Thread.CurrentThread.ManagedThreadId != _id, "Called from an unexpected thread!");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MainThreadState : ThreadStateBase, IDisposable
|
|
||||||
{
|
|
||||||
private readonly CancellationTokenSource _cancelSource = new CancellationTokenSource();
|
|
||||||
private readonly ManualResetEventSlim _hasParcel = new ManualResetEventSlim(false);
|
|
||||||
private CancellationTokenSource _currentParcelCancelSource;
|
|
||||||
|
|
||||||
private string _fileName;
|
|
||||||
private readonly object _stateLock = new object();
|
|
||||||
private IList<Edit> _changes = new List<Edit>();
|
|
||||||
|
|
||||||
public MainThreadState(string fileName)
|
|
||||||
{
|
|
||||||
_fileName = fileName;
|
|
||||||
|
|
||||||
SetThreadId(Thread.CurrentThread.ManagedThreadId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler<DocumentParseCompleteEventArgs> ResultsReady;
|
|
||||||
|
|
||||||
public CancellationToken CancelToken
|
|
||||||
{
|
|
||||||
get { return _cancelSource.Token; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsIdle
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_stateLock)
|
|
||||||
{
|
|
||||||
return _currentParcelCancelSource == null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cancel()
|
|
||||||
{
|
|
||||||
EnsureOnThread();
|
|
||||||
_cancelSource.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDisposable Lock()
|
|
||||||
{
|
|
||||||
Monitor.Enter(_stateLock);
|
|
||||||
return new DisposableAction(() => Monitor.Exit(_stateLock));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void QueueChange(Edit edit)
|
|
||||||
{
|
|
||||||
EnsureOnThread();
|
|
||||||
lock (_stateLock)
|
|
||||||
{
|
|
||||||
// CurrentParcel token source is not null ==> There's a parse underway
|
|
||||||
if (_currentParcelCancelSource != null)
|
|
||||||
{
|
|
||||||
_currentParcelCancelSource.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
_changes.Add(edit);
|
|
||||||
_hasParcel.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorkParcel GetParcel()
|
|
||||||
{
|
|
||||||
EnsureNotOnThread(); // Only the background thread can get a parcel
|
|
||||||
_hasParcel.Wait(_cancelSource.Token);
|
|
||||||
_hasParcel.Reset();
|
|
||||||
lock (_stateLock)
|
|
||||||
{
|
|
||||||
// Create a cancellation source for this parcel
|
|
||||||
_currentParcelCancelSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var changes = _changes;
|
|
||||||
_changes = new List<Edit>();
|
|
||||||
return new WorkParcel(changes, _currentParcelCancelSource.Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReturnParcel(DocumentParseCompleteEventArgs args)
|
|
||||||
{
|
|
||||||
lock (_stateLock)
|
|
||||||
{
|
|
||||||
// Clear the current parcel cancellation source
|
|
||||||
if (_currentParcelCancelSource != null)
|
|
||||||
{
|
|
||||||
_currentParcelCancelSource.Dispose();
|
|
||||||
_currentParcelCancelSource = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are things waiting to be parsed, just don't fire the event because we're already out of date
|
|
||||||
if (_changes.Any())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var handler = ResultsReady;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
handler(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
if (_currentParcelCancelSource != null)
|
|
||||||
{
|
|
||||||
_currentParcelCancelSource.Dispose();
|
|
||||||
_currentParcelCancelSource = null;
|
|
||||||
}
|
|
||||||
_cancelSource.Dispose();
|
|
||||||
_hasParcel.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BackgroundThread : ThreadStateBase
|
|
||||||
{
|
|
||||||
private MainThreadState _main;
|
|
||||||
private Thread _backgroundThread;
|
|
||||||
private CancellationToken _shutdownToken;
|
|
||||||
private RazorEditorParser _parser;
|
|
||||||
private string _filePath;
|
|
||||||
private RazorSyntaxTree _currentSyntaxTree;
|
|
||||||
private IList<Edit> _previouslyDiscarded = new List<Edit>();
|
|
||||||
|
|
||||||
public BackgroundThread(MainThreadState main, RazorEditorParser parser, string fileName)
|
|
||||||
{
|
|
||||||
// Run on MAIN thread!
|
|
||||||
_main = main;
|
|
||||||
_shutdownToken = _main.CancelToken;
|
|
||||||
_parser = parser;
|
|
||||||
_filePath = fileName;
|
|
||||||
|
|
||||||
_backgroundThread = new Thread(WorkerLoop);
|
|
||||||
SetThreadId(_backgroundThread.ManagedThreadId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// **** ANY THREAD ****
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
_backgroundThread.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// **** BACKGROUND THREAD ****
|
|
||||||
private void WorkerLoop()
|
|
||||||
{
|
|
||||||
var fileNameOnly = Path.GetFileName(_filePath);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
EnsureOnThread();
|
|
||||||
|
|
||||||
while (!_shutdownToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
// Grab the parcel of work to do
|
|
||||||
var parcel = _main.GetParcel();
|
|
||||||
if (parcel.Edits.Any())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DocumentParseCompleteEventArgs args = null;
|
|
||||||
using (var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(_shutdownToken, parcel.CancelToken))
|
|
||||||
{
|
|
||||||
if (!linkedCancel.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
// Collect ALL changes
|
|
||||||
List<Edit> allEdits;
|
|
||||||
|
|
||||||
if (_previouslyDiscarded != null)
|
|
||||||
{
|
|
||||||
allEdits = Enumerable.Concat(_previouslyDiscarded, parcel.Edits).ToList();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
allEdits = parcel.Edits.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
var finalEdit = allEdits.Last();
|
|
||||||
|
|
||||||
var results = ParseChange(finalEdit.Snapshot, linkedCancel.Token);
|
|
||||||
|
|
||||||
if (results != null && !linkedCancel.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
// Clear discarded changes list
|
|
||||||
_previouslyDiscarded = null;
|
|
||||||
|
|
||||||
var treeStructureChanged = _currentSyntaxTree == null || TreesAreDifferent(_currentSyntaxTree, results.GetSyntaxTree(), allEdits, parcel.CancelToken);
|
|
||||||
_currentSyntaxTree = results.GetSyntaxTree();
|
|
||||||
|
|
||||||
// Build Arguments
|
|
||||||
args = new DocumentParseCompleteEventArgs(
|
|
||||||
finalEdit.Change,
|
|
||||||
finalEdit.Snapshot,
|
|
||||||
treeStructureChanged,
|
|
||||||
results);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Parse completed but we were cancelled in the mean time. Add these to the discarded changes set
|
|
||||||
_previouslyDiscarded = allEdits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (args != null)
|
|
||||||
{
|
|
||||||
_main.ReturnParcel(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Thread.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
// Do nothing. Just shut down.
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Clean up main thread resources
|
|
||||||
_main.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private RazorCodeDocument ParseChange(ITextSnapshot snapshot, CancellationToken token)
|
|
||||||
{
|
|
||||||
EnsureOnThread();
|
|
||||||
|
|
||||||
var templateEngine = _parser.TemplateEngine;
|
|
||||||
var sourceDocument = new TextSnapshotSourceDocument(snapshot, _filePath);
|
|
||||||
var imports = templateEngine.GetImports(_filePath);
|
|
||||||
|
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, imports);
|
|
||||||
|
|
||||||
templateEngine.GenerateCode(codeDocument);
|
|
||||||
return codeDocument;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class WorkParcel
|
|
||||||
{
|
|
||||||
public WorkParcel(IList<Edit> changes, CancellationToken cancelToken)
|
|
||||||
{
|
|
||||||
Edits = changes;
|
|
||||||
CancelToken = cancelToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CancellationToken CancelToken { get; }
|
|
||||||
|
|
||||||
public IList<Edit> Edits { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Edit
|
|
||||||
{
|
|
||||||
public Edit(SourceChange change, ITextSnapshot snapshot)
|
|
||||||
{
|
|
||||||
Change = change;
|
|
||||||
Snapshot = snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SourceChange Change { get; }
|
|
||||||
|
|
||||||
public ITextSnapshot Snapshot { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.VisualStudio.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public abstract class RazorSyntaxFactsService
|
|
||||||
{
|
|
||||||
public abstract IReadOnlyList<ClassifiedSpan> GetClassifiedSpans(RazorSyntaxTree syntaxTree);
|
|
||||||
|
|
||||||
public abstract IReadOnlyList<TagHelperSpan> GetTagHelperSpans(RazorSyntaxTree syntaxTree);
|
|
||||||
|
|
||||||
public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line, int indentSize, int tabSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public abstract class RazorTemplateEngineFactoryService
|
|
||||||
{
|
|
||||||
public abstract RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public enum SpanKind
|
|
||||||
{
|
|
||||||
Transition,
|
|
||||||
MetaCode,
|
|
||||||
Comment,
|
|
||||||
Code,
|
|
||||||
Markup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public abstract class TagHelperCompletionService
|
|
||||||
{
|
|
||||||
public abstract AttributeCompletionResult GetAttributeCompletions(AttributeCompletionContext completionContext);
|
|
||||||
|
|
||||||
public abstract ElementCompletionResult GetElementCompletions(ElementCompletionContext completionContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
// NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead
|
|
||||||
// use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
|
||||||
public struct TagHelperSpan
|
|
||||||
{
|
|
||||||
public TagHelperSpan(SourceSpan span, TagHelperBinding binding)
|
|
||||||
{
|
|
||||||
if (binding == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(binding));
|
|
||||||
}
|
|
||||||
|
|
||||||
Span = span;
|
|
||||||
Binding = binding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TagHelperBinding Binding { get; }
|
|
||||||
|
|
||||||
public IEnumerable<TagHelperDescriptor> TagHelpers => Binding.Descriptors;
|
|
||||||
|
|
||||||
public SourceSpan Span { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,7 +9,7 @@ using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
{
|
{
|
||||||
public class RazorDiagnosticJsonConverter : JsonConverter
|
internal class RazorDiagnosticJsonConverter : JsonConverter
|
||||||
{
|
{
|
||||||
public static readonly RazorDiagnosticJsonConverter Instance = new RazorDiagnosticJsonConverter();
|
public static readonly RazorDiagnosticJsonConverter Instance = new RazorDiagnosticJsonConverter();
|
||||||
private const string RazorDiagnosticMessageKey = "Message";
|
private const string RazorDiagnosticMessageKey = "Message";
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
// 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.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
public sealed class RazorLanguageServiceException : Exception
|
|
||||||
{
|
|
||||||
internal RazorLanguageServiceException(string callerClass, string callerMethod, Exception innerException)
|
|
||||||
: base(Resources.FormatUnexpectedException(callerClass, callerMethod), innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>Builds Razor pages for views in a project. For internal use only.</Description>
|
<Description>Builds Razor pages for views in a project. For internal use only.</Description>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
<AssemblyName>dotnet-razorpagegenerator</AssemblyName>
|
<AssemblyName>dotnet-razorpagegenerator</AssemblyName>
|
||||||
<PackageId>RazorPageGenerator</PackageId>
|
<PackageId>RazorPageGenerator</PackageId>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<Project>
|
||||||
|
<Import Project="..\Directory.Build.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<DeveloperBuildTestTfms>netcoreapp2.1</DeveloperBuildTestTfms>
|
||||||
|
<StandardTestTfms>$(DeveloperBuildTestTfms)</StandardTestTfms>
|
||||||
|
<StandardTestTfms Condition=" '$(DeveloperBuild)' != 'true' ">$(StandardTestTfms);netcoreapp2.0</StandardTestTfms>
|
||||||
|
<StandardTestTfms Condition=" '$(DeveloperBuild)' != 'true' AND '$(OS)' == 'Windows_NT' ">net461;$(StandardTestTfms)</StandardTestTfms>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||||
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.0</TargetFrameworks>
|
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||||
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.0</TargetFrameworks>
|
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
|
<TargetFrameworks>$(DeveloperBuildTestTfms)</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(DeveloperBuild)' != 'true' ">$(TargetFrameworks);netcoreapp2.0</TargetFrameworks>
|
||||||
|
<TargetFrameworks Condition=" '$(DeveloperBuild)' != 'true' AND '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net46</TargetFrameworks>
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
|
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||||
{
|
{
|
||||||
CallContext.LogicalSetData("IntegrationTestBase_FileName", new ObjectHandle(value));
|
CallContext.LogicalSetData("IntegrationTestBase_FileName", new ObjectHandle(value));
|
||||||
}
|
}
|
||||||
#elif NETCOREAPP2_0
|
#elif NETCOREAPP2_0 || NETCOREAPP2_1
|
||||||
get { return _fileName.Value; }
|
get { return _fileName.Value; }
|
||||||
set { _fileName.Value = value; }
|
set { _fileName.Value = value; }
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<!-- To generate baselines, run tests with /p:GenerateBaselines=true -->
|
<!-- To generate baselines, run tests with /p:GenerateBaselines=true -->
|
||||||
<DefineConstants Condition="'$(GenerateBaselines)'=='true'">$(DefineConstants);GENERATE_BASELINES</DefineConstants>
|
<DefineConstants Condition="'$(GenerateBaselines)'=='true'">$(DefineConstants);GENERATE_BASELINES</DefineConstants>
|
||||||
<DefineConstants>$(DefineConstants);__RemoveThisBitTo__GENERATE_BASELINES</DefineConstants>
|
<DefineConstants>$(DefineConstants);__RemoveThisBitTo__GENERATE_BASELINES</DefineConstants>
|
||||||
<TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.1;netcoreapp2.0;net46</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
|
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
|
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
|
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
public class DefaultTagHelperFactsServiceTest
|
public class DefaultTagHelperFactsServiceInternalTest
|
||||||
{
|
{
|
||||||
// Purposefully not thoroughly testing DefaultTagHelperFactsService.GetTagHelperBinding because it's a pass through
|
// Purposefully not thoroughly testing DefaultTagHelperFactsService.GetTagHelperBinding because it's a pass through
|
||||||
// into TagHelperDescriptorProvider.GetTagHelperBinding.
|
// into TagHelperDescriptorProvider.GetTagHelperBinding.
|
||||||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
|
var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build(),
|
.Build(),
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
var attributes = new[]
|
var attributes = new[]
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("asp-for", "Name")
|
new KeyValuePair<string, string>("asp-for", "Name")
|
||||||
|
|
@ -108,7 +108,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
documentDescriptors[0].BoundAttributes.Last()
|
documentDescriptors[0].BoundAttributes.Last()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
|
var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -143,7 +143,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
documentDescriptors[0].BoundAttributes.First()
|
documentDescriptors[0].BoundAttributes.First()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
|
var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -164,7 +164,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "!strong", parentTag: null);
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "!strong", parentTag: null);
|
||||||
|
|
@ -184,7 +184,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "p");
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "p");
|
||||||
|
|
@ -217,7 +217,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "a", "div");
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "a", "div");
|
||||||
|
|
@ -244,7 +244,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create("th", documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create("th", documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "thstrong", "div");
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "thstrong", "div");
|
||||||
|
|
@ -277,7 +277,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "div");
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "div");
|
||||||
|
|
@ -297,7 +297,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
|
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
|
||||||
|
|
@ -322,7 +322,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
|
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
|
||||||
|
|
@ -343,7 +343,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, "p");
|
var descriptors = service.GetTagHelpersGivenParent(documentContext, "p");
|
||||||
|
|
@ -376,7 +376,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
var service = new DefaultTagHelperFactsService();
|
var service = new DefaultTagHelperFactsServiceInternal();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, "div");
|
var descriptors = service.GetTagHelpersGivenParent(documentContext, "div");
|
||||||
|
|
@ -5,13 +5,13 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Editor
|
namespace Microsoft.CodeAnalysis.Razor.Editor
|
||||||
{
|
{
|
||||||
public class DefaultEditorSettingsManagerTest
|
public class DefaultEditorSettingsManagerInternalTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InitialSettingsAreDefault()
|
public void InitialSettingsAreDefault()
|
||||||
{
|
{
|
||||||
// Act
|
// Act
|
||||||
var manager = new DefaultEditorSettingsManager();
|
var manager = new DefaultEditorSettingsManagerInternal();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(EditorSettings.Default, manager.Current);
|
Assert.Equal(EditorSettings.Default, manager.Current);
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor
|
||||||
public void Update_TriggersChangedIfEditorSettingsAreDifferent()
|
public void Update_TriggersChangedIfEditorSettingsAreDifferent()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var manager = new DefaultEditorSettingsManager();
|
var manager = new DefaultEditorSettingsManagerInternal();
|
||||||
var called = false;
|
var called = false;
|
||||||
manager.Changed += (caller, args) =>
|
manager.Changed += (caller, args) =>
|
||||||
{
|
{
|
||||||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor
|
||||||
public void Update_DoesNotTriggerChangedIfEditorSettingsAreSame()
|
public void Update_DoesNotTriggerChangedIfEditorSettingsAreSame()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var manager = new DefaultEditorSettingsManager();
|
var manager = new DefaultEditorSettingsManagerInternal();
|
||||||
var called = false;
|
var called = false;
|
||||||
manager.Changed += (caller, args) =>
|
manager.Changed += (caller, args) =>
|
||||||
{
|
{
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
|
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
|
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,20 @@ namespace Microsoft.VisualStudio.Text
|
||||||
return matchingLine;
|
return matchingLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode) => throw new NotImplementedException();
|
public ITextSnapshotLine GetLineFromLineNumber(int lineNumber)
|
||||||
|
{
|
||||||
|
if (lineNumber < 0 || lineNumber >= _lines.Count)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(lineNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _lines[lineNumber];
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode)
|
||||||
|
{
|
||||||
|
return new SnapshotTrackingPoint(position);
|
||||||
|
}
|
||||||
|
|
||||||
public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) => throw new NotImplementedException();
|
public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
|
@ -89,8 +102,6 @@ namespace Microsoft.VisualStudio.Text
|
||||||
|
|
||||||
public ITrackingSpan CreateTrackingSpan(int start, int length, SpanTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) => throw new NotImplementedException();
|
public ITrackingSpan CreateTrackingSpan(int start, int length, SpanTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) => throw new NotImplementedException();
|
||||||
|
|
||||||
public ITextSnapshotLine GetLineFromLineNumber(int lineNumber) => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public int GetLineNumberFromPosition(int position) => throw new NotImplementedException();
|
public int GetLineNumberFromPosition(int position) => throw new NotImplementedException();
|
||||||
|
|
||||||
public string GetText(VisualStudio.Text.Span span) => throw new NotImplementedException();
|
public string GetText(VisualStudio.Text.Span span) => throw new NotImplementedException();
|
||||||
|
|
@ -133,6 +144,30 @@ namespace Microsoft.VisualStudio.Text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SnapshotTrackingPoint : ITrackingPoint
|
||||||
|
{
|
||||||
|
private readonly int _position;
|
||||||
|
|
||||||
|
public SnapshotTrackingPoint(int position)
|
||||||
|
{
|
||||||
|
_position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITextBuffer TextBuffer => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public PointTrackingMode TrackingMode => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public TrackingFidelityMode TrackingFidelity => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public char GetCharacter(ITextSnapshot snapshot) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public SnapshotPoint GetPoint(ITextSnapshot snapshot) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public int GetPosition(ITextSnapshot snapshot) => _position;
|
||||||
|
|
||||||
|
public int GetPosition(ITextVersion version) => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
private class SnapshotLine : ITextSnapshotLine
|
private class SnapshotLine : ITextSnapshotLine
|
||||||
{
|
{
|
||||||
private readonly string _contentWithLineBreak;
|
private readonly string _contentWithLineBreak;
|
||||||
|
|
@ -153,7 +188,9 @@ namespace Microsoft.VisualStudio.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
Start = new SnapshotPoint(owner, start);
|
Start = new SnapshotPoint(owner, start);
|
||||||
|
End = new SnapshotPoint(owner, start + _content.Length);
|
||||||
Snapshot = owner;
|
Snapshot = owner;
|
||||||
|
LineNumber = (owner as StringTextSnapshot)._lines.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ITextSnapshot Snapshot { get; }
|
public ITextSnapshot Snapshot { get; }
|
||||||
|
|
@ -172,14 +209,14 @@ namespace Microsoft.VisualStudio.Text
|
||||||
|
|
||||||
public string GetTextIncludingLineBreak() => _contentWithLineBreak;
|
public string GetTextIncludingLineBreak() => _contentWithLineBreak;
|
||||||
|
|
||||||
public int LineNumber => throw new NotImplementedException();
|
public int LineNumber { get; }
|
||||||
|
|
||||||
|
public SnapshotPoint End { get; }
|
||||||
|
|
||||||
public SnapshotSpan Extent => throw new NotImplementedException();
|
public SnapshotSpan Extent => throw new NotImplementedException();
|
||||||
|
|
||||||
public SnapshotSpan ExtentIncludingLineBreak => throw new NotImplementedException();
|
public SnapshotSpan ExtentIncludingLineBreak => throw new NotImplementedException();
|
||||||
|
|
||||||
public SnapshotPoint End => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public SnapshotPoint EndIncludingLineBreak => throw new NotImplementedException();
|
public SnapshotPoint EndIncludingLineBreak => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Microsoft.VisualStudio.Text;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class DefaultRazorIndentationFactsServiceTest
|
public class DefaultRazorIndentationFactsServiceTest
|
||||||
{
|
{
|
||||||
|
|
@ -15,17 +15,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
public void GetDesiredIndentation_ReturnsNull_IfOwningSpanIsCode()
|
public void GetDesiredIndentation_ReturnsNull_IfOwningSpanIsCode()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = $@"
|
var source = new StringTextSnapshot($@"
|
||||||
@{{
|
@{{
|
||||||
";
|
");
|
||||||
var syntaxTree = GetSyntaxTree(source);
|
var syntaxTree = GetSyntaxTree(source);
|
||||||
var service = new DefaultRazorIndentationFactsService();
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var indentation = service.GetDesiredIndentation(
|
var indentation = service.GetDesiredIndentation(
|
||||||
syntaxTree,
|
syntaxTree,
|
||||||
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
source,
|
||||||
getLineContent: line => GetLineContent(source, line),
|
source.GetLineFromLineNumber(2),
|
||||||
indentSize: 4,
|
indentSize: 4,
|
||||||
tabSize: 1);
|
tabSize: 1);
|
||||||
|
|
||||||
|
|
@ -38,17 +38,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom");
|
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom");
|
||||||
var source = $@"
|
var source = new StringTextSnapshot($@"
|
||||||
@custom
|
@custom
|
||||||
";
|
");
|
||||||
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||||
var service = new DefaultRazorIndentationFactsService();
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var indentation = service.GetDesiredIndentation(
|
var indentation = service.GetDesiredIndentation(
|
||||||
syntaxTree,
|
syntaxTree,
|
||||||
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
source,
|
||||||
getLineContent: line => GetLineContent(source, line),
|
source.GetLineFromLineNumber(2),
|
||||||
indentSize: 4,
|
indentSize: 4,
|
||||||
tabSize: 1);
|
tabSize: 1);
|
||||||
|
|
||||||
|
|
@ -60,17 +60,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForMarkupWithinCodeBlock()
|
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForMarkupWithinCodeBlock()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = $@"@{{
|
var source = new StringTextSnapshot($@"@{{
|
||||||
<div>
|
<div>
|
||||||
";
|
");
|
||||||
var syntaxTree = GetSyntaxTree(source);
|
var syntaxTree = GetSyntaxTree(source);
|
||||||
var service = new DefaultRazorIndentationFactsService();
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var indentation = service.GetDesiredIndentation(
|
var indentation = service.GetDesiredIndentation(
|
||||||
syntaxTree,
|
syntaxTree,
|
||||||
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
source,
|
||||||
getLineContent: line => GetLineContent(source, line),
|
source.GetLineFromLineNumber(2),
|
||||||
indentSize: 4,
|
indentSize: 4,
|
||||||
tabSize: 4);
|
tabSize: 4);
|
||||||
|
|
||||||
|
|
@ -83,18 +83,18 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom");
|
var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom");
|
||||||
var source = $@"@custom
|
var source = new StringTextSnapshot($@"@custom
|
||||||
{{
|
{{
|
||||||
<div>
|
<div>
|
||||||
}}";
|
}}");
|
||||||
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||||
var service = new DefaultRazorIndentationFactsService();
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var indentation = service.GetDesiredIndentation(
|
var indentation = service.GetDesiredIndentation(
|
||||||
syntaxTree,
|
syntaxTree,
|
||||||
previousLineEndIndex: GetLineEndIndexForLine(source, 2),
|
source,
|
||||||
getLineContent: line => GetLineContent(source, line),
|
source.GetLineFromLineNumber(3),
|
||||||
indentSize: 4,
|
indentSize: 4,
|
||||||
tabSize: 4);
|
tabSize: 4);
|
||||||
|
|
||||||
|
|
@ -106,21 +106,21 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForNestedMarkupWithinCodeBlock()
|
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForNestedMarkupWithinCodeBlock()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = $@"
|
var source = new StringTextSnapshot($@"
|
||||||
<div>
|
<div>
|
||||||
@{{
|
@{{
|
||||||
<span>
|
<span>
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
";
|
");
|
||||||
var syntaxTree = GetSyntaxTree(source);
|
var syntaxTree = GetSyntaxTree(source);
|
||||||
var service = new DefaultRazorIndentationFactsService();
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var indentation = service.GetDesiredIndentation(
|
var indentation = service.GetDesiredIndentation(
|
||||||
syntaxTree,
|
syntaxTree,
|
||||||
previousLineEndIndex: GetLineEndIndexForLine(source, 3),
|
source,
|
||||||
getLineContent: line => GetLineContent(source, line),
|
source.GetLineFromLineNumber(4),
|
||||||
indentSize: 4,
|
indentSize: 4,
|
||||||
tabSize: 4);
|
tabSize: 4);
|
||||||
|
|
||||||
|
|
@ -133,20 +133,20 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom");
|
var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom");
|
||||||
var source = $@"@custom
|
var source = new StringTextSnapshot($@"@custom
|
||||||
{{
|
{{
|
||||||
@{{
|
@{{
|
||||||
<div>
|
<div>
|
||||||
}}
|
}}
|
||||||
}}";
|
}}");
|
||||||
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||||
var service = new DefaultRazorIndentationFactsService();
|
var service = new DefaultRazorIndentationFactsService();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var indentation = service.GetDesiredIndentation(
|
var indentation = service.GetDesiredIndentation(
|
||||||
syntaxTree,
|
syntaxTree,
|
||||||
previousLineEndIndex: GetLineEndIndexForLine(source, 3),
|
source,
|
||||||
getLineContent: line => GetLineContent(source, line),
|
source.GetLineFromLineNumber(4),
|
||||||
indentSize: 4,
|
indentSize: 4,
|
||||||
tabSize: 4);
|
tabSize: 4);
|
||||||
|
|
||||||
|
|
@ -154,7 +154,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
Assert.Equal(8, indentation);
|
Assert.Equal(8, indentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RazorSyntaxTree GetSyntaxTree(string source, IEnumerable<DirectiveDescriptor> directives = null)
|
private static RazorSyntaxTree GetSyntaxTree(StringTextSnapshot source, IEnumerable<DirectiveDescriptor> directives = null)
|
||||||
{
|
{
|
||||||
directives = directives ?? Enumerable.Empty<DirectiveDescriptor>();
|
directives = directives ?? Enumerable.Empty<DirectiveDescriptor>();
|
||||||
var engine = RazorEngine.CreateDesignTime(builder =>
|
var engine = RazorEngine.CreateDesignTime(builder =>
|
||||||
|
|
@ -165,45 +165,12 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var sourceDocument = RazorSourceDocument.Create(source, "test.cshtml");
|
var sourceDocument = RazorSourceDocument.Create(source.GetText(), "test.cshtml");
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
|
|
||||||
engine.Process(codeDocument);
|
engine.Process(codeDocument);
|
||||||
|
|
||||||
return codeDocument.GetSyntaxTree();
|
return codeDocument.GetSyntaxTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetLineContent(string source, int lineIndex)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(source))
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lines = source.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
|
||||||
return lines[lineIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetLineEndIndexForLine(string source, int lineIndex)
|
|
||||||
{
|
|
||||||
var absoluteIndex = 0;
|
|
||||||
if (string.IsNullOrEmpty(source))
|
|
||||||
{
|
|
||||||
return absoluteIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lines = source.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
|
||||||
for (var i = 0; i <= lineIndex; i++)
|
|
||||||
{
|
|
||||||
absoluteIndex += lines[i].Length;
|
|
||||||
|
|
||||||
if (i < lineIndex)
|
|
||||||
{
|
|
||||||
absoluteIndex += Environment.NewLine.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return absoluteIndex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,9 +4,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class DefaultTagHelperCompletionServiceTest
|
public class DefaultTagHelperCompletionServiceTest
|
||||||
{
|
{
|
||||||
|
|
@ -994,8 +995,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
|
||||||
private static DefaultTagHelperCompletionService CreateTagHelperCompletionFactsService()
|
private static DefaultTagHelperCompletionService CreateTagHelperCompletionFactsService()
|
||||||
{
|
{
|
||||||
var tagHelperFactService = new DefaultTagHelperFactsService();
|
var tagHelperFactsService = new DefaultTagHelperFactsServiceInternal();
|
||||||
var completionFactService = new DefaultTagHelperCompletionService(tagHelperFactService);
|
var completionFactService = new DefaultTagHelperCompletionService(tagHelperFactsService);
|
||||||
|
|
||||||
return completionFactService;
|
return completionFactService;
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
s.IsSupportedProject(It.IsAny<IVsHierarchy>()) == true &&
|
s.IsSupportedProject(It.IsAny<IVsHierarchy>()) == true &&
|
||||||
s.GetProjectPath(It.IsAny<IVsHierarchy>()) == "C:/Some/Path/TestProject.csproj");
|
s.GetProjectPath(It.IsAny<IVsHierarchy>()) == "C:/Some/Path/TestProject.csproj");
|
||||||
|
|
||||||
private EditorSettingsManager EditorSettingsManager => new DefaultEditorSettingsManager();
|
private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal();
|
||||||
|
|
||||||
private Workspace Workspace => new AdhocWorkspace();
|
private Workspace Workspace => new AdhocWorkspace();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.VisualStudio.Editor.Razor;
|
|
||||||
using Microsoft.VisualStudio.Text;
|
|
||||||
using Microsoft.VisualStudio.Utilities;
|
|
||||||
using Moq;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|
||||||
{
|
|
||||||
public class LegacyTextBufferCodeDocumentProviderTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void TryGetFromBuffer_UsesVisualStudioRazorParserIfAvailable()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedCodeDocument = TestRazorCodeDocument.Create("Hello World");
|
|
||||||
var parser = new DefaultVisualStudioRazorParser(expectedCodeDocument);
|
|
||||||
var properties = new PropertyCollection();
|
|
||||||
properties.AddProperty(typeof(VisualStudioRazorParser), parser);
|
|
||||||
var textBuffer = new Mock<ITextBuffer>();
|
|
||||||
textBuffer.Setup(buffer => buffer.Properties)
|
|
||||||
.Returns(properties);
|
|
||||||
var provider = new LegacyTextBufferCodeDocumentProvider();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.TryGetFromBuffer(textBuffer.Object, out var codeDocument);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(result);
|
|
||||||
Assert.Same(expectedCodeDocument, codeDocument);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TryGetFromBuffer_UsesRazorEditorParserIfAvailable()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedCodeDocument = TestRazorCodeDocument.Create("Hello World");
|
|
||||||
var parser = new RazorEditorParser(expectedCodeDocument);
|
|
||||||
var properties = new PropertyCollection();
|
|
||||||
properties.AddProperty(typeof(RazorEditorParser), parser);
|
|
||||||
var textBuffer = new Mock<ITextBuffer>();
|
|
||||||
textBuffer.Setup(buffer => buffer.Properties)
|
|
||||||
.Returns(properties);
|
|
||||||
var provider = new LegacyTextBufferCodeDocumentProvider();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.TryGetFromBuffer(textBuffer.Object, out var codeDocument);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(result);
|
|
||||||
Assert.Same(expectedCodeDocument, codeDocument);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TryGetFromBuffer_PrefersVisualStudioRazorParserIfRazorEditorParserIsAvailable()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var properties = new PropertyCollection();
|
|
||||||
var expectedCodeDocument = TestRazorCodeDocument.Create("Hello World");
|
|
||||||
var parser = new DefaultVisualStudioRazorParser(expectedCodeDocument);
|
|
||||||
properties.AddProperty(typeof(VisualStudioRazorParser), parser);
|
|
||||||
var unexpectedCodeDocument = TestRazorCodeDocument.Create("Unexpected");
|
|
||||||
var legacyParser = new RazorEditorParser(unexpectedCodeDocument);
|
|
||||||
properties.AddProperty(typeof(RazorEditorParser), legacyParser);
|
|
||||||
var textBuffer = new Mock<ITextBuffer>();
|
|
||||||
textBuffer.Setup(buffer => buffer.Properties)
|
|
||||||
.Returns(properties);
|
|
||||||
var provider = new LegacyTextBufferCodeDocumentProvider();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.TryGetFromBuffer(textBuffer.Object, out var codeDocument);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(result);
|
|
||||||
Assert.Same(expectedCodeDocument, codeDocument);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TryGetFromBuffer_FailsIfNoParserIsAvailable()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var properties = new PropertyCollection();
|
|
||||||
var textBuffer = new Mock<ITextBuffer>();
|
|
||||||
textBuffer.Setup(buffer => buffer.Properties)
|
|
||||||
.Returns(properties);
|
|
||||||
var provider = new LegacyTextBufferCodeDocumentProvider();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.TryGetFromBuffer(textBuffer.Object, out var codeDocument);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.Null(codeDocument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||||
s.IsSupportedProject(It.IsAny<IVsHierarchy>()) == true &&
|
s.IsSupportedProject(It.IsAny<IVsHierarchy>()) == true &&
|
||||||
s.GetProjectPath(It.IsAny<IVsHierarchy>()) == "C:/Some/Path/TestProject.csproj");
|
s.GetProjectPath(It.IsAny<IVsHierarchy>()) == "C:/Some/Path/TestProject.csproj");
|
||||||
|
|
||||||
private EditorSettingsManager EditorSettingsManager => new DefaultEditorSettingsManager();
|
private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal();
|
||||||
|
|
||||||
private Workspace Workspace { get; } = new AdhocWorkspace();
|
private Workspace Workspace { get; } = new AdhocWorkspace();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,964 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
public class DefaultTagHelperCompletionServiceTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_DoesNotReturnCompletionsForAlreadySuppliedAttributes()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("div")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("repeat")))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("visible")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Visible"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("class")
|
|
||||||
.TypeName(typeof(string).FullName)
|
|
||||||
.PropertyName("Class"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["onclick"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
["visible"] = new HashSet<BoundAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
documentDescriptors[0].BoundAttributes.Last()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "onclick" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
attributes: new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
["class"] = "something",
|
|
||||||
["repeat"] = "4"
|
|
||||||
},
|
|
||||||
currentTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_PossibleDescriptorsReturnUnboundRequiredAttributesWithExistingCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("div")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("repeat")))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("*")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("class")))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
["onclick"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
["repeat"] = new HashSet<BoundAttributeDescriptor>()
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "onclick", "class" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_PossibleDescriptorsReturnBoundRequiredAttributesWithExistingCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("div")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("repeat")))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("repeat")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Repeat"))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("visible")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Visible"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("*")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("class")))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("class")
|
|
||||||
.TypeName(typeof(string).FullName)
|
|
||||||
.PropertyName("Class"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["class"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[1].BoundAttributes),
|
|
||||||
["onclick"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
["repeat"] = new HashSet<BoundAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
documentDescriptors[0].BoundAttributes.First()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "onclick" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_AppliedDescriptorsReturnAllBoundAttributesWithExistingCompletionsForSchemaTags()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("repeat")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Repeat"))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("visible")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Visible"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("*")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("class")))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("class")
|
|
||||||
.TypeName(typeof(string).FullName)
|
|
||||||
.PropertyName("Class"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("visible")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Visible"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["onclick"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
["class"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[1].BoundAttributes),
|
|
||||||
["repeat"] = new HashSet<BoundAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
documentDescriptors[0].BoundAttributes.First()
|
|
||||||
},
|
|
||||||
["visible"] = new HashSet<BoundAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
documentDescriptors[0].BoundAttributes.Last(),
|
|
||||||
documentDescriptors[2].BoundAttributes.First(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "class", "onclick" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_AppliedTagOutputHintDescriptorsReturnBoundAttributesWithExistingCompletionsForNonSchemaTags()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("CustomTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("custom"))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("repeat")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Repeat"))
|
|
||||||
.TagOutputHint("div")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
["repeat"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[0].BoundAttributes)
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "class" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "custom");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_AppliedDescriptorsReturnBoundAttributesCompletionsForNonSchemaTags()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("CustomTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("custom"))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("repeat")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Repeat"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["repeat"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[0].BoundAttributes)
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "class" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "custom");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_AppliedDescriptorsReturnBoundAttributesWithExistingCompletionsForSchemaTags()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.BoundAttributeDescriptor(attribute => attribute
|
|
||||||
.Name("repeat")
|
|
||||||
.TypeName(typeof(bool).FullName)
|
|
||||||
.PropertyName("Repeat"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
["repeat"] = new HashSet<BoundAttributeDescriptor>(documentDescriptors[0].BoundAttributes)
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "class" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_NoDescriptorsReturnsExistingCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "class" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
Enumerable.Empty<TagHelperDescriptor>(),
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_NoDescriptorsForUnprefixedTagReturnsExistingCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("div")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("special")))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "class" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "div",
|
|
||||||
tagHelperPrefix: "th:");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetAttributeCompletions_NoDescriptorsForTagReturnsExistingCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("MyTableTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("table")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("special")))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
|
||||||
{
|
|
||||||
["class"] = new HashSet<BoundAttributeDescriptor>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "class" };
|
|
||||||
var completionContext = BuildAttributeCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetAttributeCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_TagOutputHintDoesNotFallThroughToSchemaCheck()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("MyTableTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("my-table"))
|
|
||||||
.TagOutputHint("table")
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("MyTrTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("my-tr"))
|
|
||||||
.TagOutputHint("tr")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["my-table"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
["table"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "table" };
|
|
||||||
var completionContext = BuildElementCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
containingTagName: "body",
|
|
||||||
containingParentTagName: null);
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_CatchAllsOnlyApplyToCompletionsStartingWithPrefix()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("CatchAllTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["th:li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1], documentDescriptors[0] },
|
|
||||||
["li"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "li" };
|
|
||||||
var completionContext = BuildElementCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
containingTagName: "ul",
|
|
||||||
tagHelperPrefix: "th:");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_TagHelperPrefixIsPrependedToTagHelperCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["th:superli"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
["th:li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
|
|
||||||
["li"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "li" };
|
|
||||||
var completionContext = BuildElementCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
containingTagName: "ul",
|
|
||||||
tagHelperPrefix: "th:");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_CatchAllsApplyToOnlyTagHelperCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("CatchAll", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["superli"] = new HashSet<TagHelperDescriptor>() { documentDescriptors[0], documentDescriptors[1] },
|
|
||||||
["li"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "li" };
|
|
||||||
var completionContext = BuildElementCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
containingTagName: "ul");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_CatchAllsApplyToNonTagHelperCompletionsIfStartsWithTagHelperPrefix()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("CatchAll", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["th:superli"] = new HashSet<TagHelperDescriptor>() { documentDescriptors[0], documentDescriptors[1] },
|
|
||||||
["th:li"] = new HashSet<TagHelperDescriptor>() { documentDescriptors[1] },
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "th:li" };
|
|
||||||
var completionContext = BuildElementCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
containingTagName: "ul",
|
|
||||||
tagHelperPrefix: "th:");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_AllowsMultiTargetingTagHelpers()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("BoldTagHelper1", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("b"))
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("bold"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("BoldTagHelper2", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["strong"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
|
||||||
["b"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
["bold"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "strong", "b", "bold" };
|
|
||||||
var completionContext = BuildElementCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
containingTagName: "ul");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_CombinesDescriptorsOnExistingCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("LiTagHelper1", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("LiTagHelper2", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "li" };
|
|
||||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_NewCompletionsForSchemaTagsNotInExistingCompletionsAreIgnored()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
|
||||||
.TagOutputHint("strong")
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
|
|
||||||
["superli"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "li" };
|
|
||||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_OutputHintIsCrossReferencedWithExistingCompletions()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.TagOutputHint("li")
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
|
||||||
.TagOutputHint("strong")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "li" };
|
|
||||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_EnsuresDescriptorsHaveSatisfiedParent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("LiTagHelper1", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("LiTagHelper2", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("li").RequireParentTag("ol"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "li" };
|
|
||||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_AllowedChildrenAreIgnoredWhenAtRoot()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("CatchAll", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.AllowChildTag("b")
|
|
||||||
.AllowChildTag("bold")
|
|
||||||
.AllowChildTag("div")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>());
|
|
||||||
|
|
||||||
var existingCompletions = Enumerable.Empty<string>();
|
|
||||||
var completionContext = BuildElementCompletionContext(
|
|
||||||
documentDescriptors,
|
|
||||||
existingCompletions,
|
|
||||||
containingTagName: null,
|
|
||||||
containingParentTagName: null);
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_DoesNotReturnExistingCompletionsWhenAllowedChildren()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.AllowChildTag("b")
|
|
||||||
.AllowChildTag("bold")
|
|
||||||
.AllowChildTag("div")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] }
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingCompletions = new[] { "p", "em" };
|
|
||||||
var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_NoneTagHelpers()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.AllowChildTag("b")
|
|
||||||
.AllowChildTag("bold")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_SomeTagHelpers()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.AllowChildTag("b")
|
|
||||||
.AllowChildTag("bold")
|
|
||||||
.AllowChildTag("div")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
|
||||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] }
|
|
||||||
});
|
|
||||||
|
|
||||||
var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_AllTagHelpers()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("BoldParentCatchAll", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.AllowChildTag("strong")
|
|
||||||
.AllowChildTag("div")
|
|
||||||
.AllowChildTag("b")
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.AllowChildTag("b")
|
|
||||||
.AllowChildTag("bold")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
|
||||||
{
|
|
||||||
["strong"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
["b"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
["bold"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
|
||||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
|
||||||
});
|
|
||||||
|
|
||||||
var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
|
||||||
var service = CreateTagHelperCompletionFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var completions = service.GetElementCompletions(completionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DefaultTagHelperCompletionService CreateTagHelperCompletionFactsService()
|
|
||||||
{
|
|
||||||
var tagHelperFactService = new DefaultTagHelperFactsService();
|
|
||||||
var completionFactService = new DefaultTagHelperCompletionService(tagHelperFactService);
|
|
||||||
|
|
||||||
return completionFactService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AssertCompletionsAreEquivalent(ElementCompletionResult expected, ElementCompletionResult actual)
|
|
||||||
{
|
|
||||||
Assert.Equal(expected.Completions.Count, actual.Completions.Count);
|
|
||||||
|
|
||||||
foreach (var expectedCompletion in expected.Completions)
|
|
||||||
{
|
|
||||||
var actualValue = actual.Completions[expectedCompletion.Key];
|
|
||||||
Assert.NotNull(actualValue);
|
|
||||||
Assert.Equal(expectedCompletion.Value, actualValue, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AssertCompletionsAreEquivalent(AttributeCompletionResult expected, AttributeCompletionResult actual)
|
|
||||||
{
|
|
||||||
Assert.Equal(expected.Completions.Count, actual.Completions.Count);
|
|
||||||
|
|
||||||
foreach (var expectedCompletion in expected.Completions)
|
|
||||||
{
|
|
||||||
var actualValue = actual.Completions[expectedCompletion.Key];
|
|
||||||
Assert.NotNull(actualValue);
|
|
||||||
Assert.Equal(expectedCompletion.Value, actualValue, BoundAttributeDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ElementCompletionContext BuildElementCompletionContext(
|
|
||||||
IEnumerable<TagHelperDescriptor> descriptors,
|
|
||||||
IEnumerable<string> existingCompletions,
|
|
||||||
string containingTagName,
|
|
||||||
string containingParentTagName = "body",
|
|
||||||
string tagHelperPrefix = "")
|
|
||||||
{
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors);
|
|
||||||
var completionContext = new ElementCompletionContext(
|
|
||||||
documentContext,
|
|
||||||
existingCompletions,
|
|
||||||
containingTagName,
|
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
|
||||||
containingParentTagName: containingParentTagName,
|
|
||||||
inHTMLSchema: (tag) => tag == "strong" || tag == "b" || tag == "bold" || tag == "li" || tag == "div");
|
|
||||||
|
|
||||||
return completionContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AttributeCompletionContext BuildAttributeCompletionContext(
|
|
||||||
IEnumerable<TagHelperDescriptor> descriptors,
|
|
||||||
IEnumerable<string> existingCompletions,
|
|
||||||
string currentTagName,
|
|
||||||
IEnumerable<KeyValuePair<string, string>> attributes = null,
|
|
||||||
string tagHelperPrefix = "")
|
|
||||||
{
|
|
||||||
attributes = attributes ?? Enumerable.Empty<KeyValuePair<string, string>>();
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors);
|
|
||||||
var completionContext = new AttributeCompletionContext(
|
|
||||||
documentContext,
|
|
||||||
existingCompletions,
|
|
||||||
currentTagName,
|
|
||||||
attributes,
|
|
||||||
currentParentTagName: "body",
|
|
||||||
inHTMLSchema: (tag) => tag == "strong" || tag == "b" || tag == "bold" || tag == "li" || tag == "div");
|
|
||||||
|
|
||||||
return completionContext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,388 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
public class DefaultTagHelperFactsServiceTest
|
|
||||||
{
|
|
||||||
// Purposefully not thoroughly testing DefaultTagHelperFactsService.GetTagHelperBinding because it's a pass through
|
|
||||||
// into TagHelperDescriptorProvider.GetTagHelperBinding.
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelperBinding_DoesNotAllowOptOutCharacterPrefix()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Null(binding);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelperBinding_WorksAsExpected()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule =>
|
|
||||||
rule
|
|
||||||
.RequireTagName("a")
|
|
||||||
.RequireAttributeDescriptor(attribute => attribute.Name("asp-for")))
|
|
||||||
.BoundAttributeDescriptor(attribute =>
|
|
||||||
attribute
|
|
||||||
.Name("asp-for")
|
|
||||||
.TypeName(typeof(string).FullName)
|
|
||||||
.PropertyName("AspFor"))
|
|
||||||
.BoundAttributeDescriptor(attribute =>
|
|
||||||
attribute
|
|
||||||
.Name("asp-route")
|
|
||||||
.TypeName(typeof(IDictionary<string, string>).Namespace + "IDictionary<string, string>")
|
|
||||||
.PropertyName("AspRoute")
|
|
||||||
.AsDictionaryAttribute("asp-route-", typeof(string).FullName))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("input"))
|
|
||||||
.BoundAttributeDescriptor(attribute =>
|
|
||||||
attribute
|
|
||||||
.Name("asp-for")
|
|
||||||
.TypeName(typeof(string).FullName)
|
|
||||||
.PropertyName("AspFor"))
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
var attributes = new[]
|
|
||||||
{
|
|
||||||
new KeyValuePair<string, string>("asp-for", "Name")
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var binding = service.GetTagHelperBinding(documentContext, "a", attributes, parentTag: "p");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var descriptor = Assert.Single(binding.Descriptors);
|
|
||||||
Assert.Equal(documentDescriptors[0], descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
var boundRule = Assert.Single(binding.GetBoundRules(descriptor));
|
|
||||||
Assert.Equal(documentDescriptors[0].TagMatchingRules.First(), boundRule, TagMatchingRuleDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetBoundTagHelperAttributes_MatchesPrefixedAttributeName()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("a"))
|
|
||||||
.BoundAttributeDescriptor(attribute =>
|
|
||||||
attribute
|
|
||||||
.Name("asp-for")
|
|
||||||
.TypeName(typeof(string).FullName)
|
|
||||||
.PropertyName("AspFor"))
|
|
||||||
.BoundAttributeDescriptor(attribute =>
|
|
||||||
attribute
|
|
||||||
.Name("asp-route")
|
|
||||||
.TypeName(typeof(IDictionary<string, string>).Namespace + "IDictionary<string, string>")
|
|
||||||
.PropertyName("AspRoute")
|
|
||||||
.AsDictionaryAttribute("asp-route-", typeof(string).FullName))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var expectedAttributeDescriptors = new[]
|
|
||||||
{
|
|
||||||
documentDescriptors[0].BoundAttributes.Last()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetBoundTagHelperAttributes(documentContext, "asp-route-something", binding);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedAttributeDescriptors, descriptors, BoundAttributeDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetBoundTagHelperAttributes_MatchesAttributeName()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("input"))
|
|
||||||
.BoundAttributeDescriptor(attribute =>
|
|
||||||
attribute
|
|
||||||
.Name("asp-for")
|
|
||||||
.TypeName(typeof(string).FullName)
|
|
||||||
.PropertyName("AspFor"))
|
|
||||||
.BoundAttributeDescriptor(attribute =>
|
|
||||||
attribute
|
|
||||||
.Name("asp-extra")
|
|
||||||
.TypeName(typeof(string).FullName)
|
|
||||||
.PropertyName("AspExtra"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var expectedAttributeDescriptors = new[]
|
|
||||||
{
|
|
||||||
documentDescriptors[0].BoundAttributes.First()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetBoundTagHelperAttributes(documentContext, "asp-for", binding);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedAttributeDescriptors, descriptors, BoundAttributeDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenTag_DoesNotAllowOptOutCharacterPrefix()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("*"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "!strong", parentTag: null);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Empty(descriptors);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenTag_RequiresTagName()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "p");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(documentDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnTagName()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(
|
|
||||||
rule => rule
|
|
||||||
.RequireTagName("a")
|
|
||||||
.RequireParentTag("div"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
expectedDescriptors[0],
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(
|
|
||||||
rule => rule
|
|
||||||
.RequireTagName("strong")
|
|
||||||
.RequireParentTag("div"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "a", "div");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnTagHelperPrefix()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
expectedDescriptors[0],
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("thstrong"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create("th", documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "thstrong", "div");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnParent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(
|
|
||||||
rule => rule
|
|
||||||
.RequireTagName("strong")
|
|
||||||
.RequireParentTag("div"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
expectedDescriptors[0],
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(
|
|
||||||
rule => rule
|
|
||||||
.RequireTagName("strong")
|
|
||||||
.RequireParentTag("p"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "div");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenParent_AllowsRootParentTag()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(documentDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenParent_AllowsRootParentTagForParentRestrictedTagHelperDescriptors()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.Build(),
|
|
||||||
TagHelperDescriptorBuilder.Create("PTagHelper", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule
|
|
||||||
.RequireTagName("p")
|
|
||||||
.RequireParentTag("body"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var descriptor = Assert.Single(descriptors);
|
|
||||||
Assert.Equal(documentDescriptors[0], descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenParent_AllowsUnspecifiedParentTagHelpers()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, "p");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(documentDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetTagHelpersGivenParent_RestrictsTagHelpersBasedOnParent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedDescriptors = new[]
|
|
||||||
{
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(
|
|
||||||
rule => rule
|
|
||||||
.RequireTagName("p")
|
|
||||||
.RequireParentTag("div"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentDescriptors = new[]
|
|
||||||
{
|
|
||||||
expectedDescriptors[0],
|
|
||||||
TagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
|
||||||
.TagMatchingRuleDescriptor(
|
|
||||||
rule => rule
|
|
||||||
.RequireTagName("strong")
|
|
||||||
.RequireParentTag("p"))
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
|
||||||
var service = new DefaultTagHelperFactsService();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptors = service.GetTagHelpersGivenParent(documentContext, "div");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
<!-- To generate baselines, run tests with /p:GenerateBaselines=true -->
|
<!-- To generate baselines, run tests with /p:GenerateBaselines=true -->
|
||||||
<DefineConstants Condition="'$(GenerateBaselines)'=='true'">$(DefineConstants);GENERATE_BASELINES</DefineConstants>
|
<DefineConstants Condition="'$(GenerateBaselines)'=='true'">$(DefineConstants);GENERATE_BASELINES</DefineConstants>
|
||||||
<DefineConstants>$(DefineConstants);__RemoveThisBitTo__GENERATE_BASELINES</DefineConstants>
|
<DefineConstants>$(DefineConstants);__RemoveThisBitTo__GENERATE_BASELINES</DefineConstants>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue