From 809dedd40cf971fedb7affe89480bdf480799ae2 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 8 Nov 2017 10:17:50 -0800 Subject: [PATCH 01/11] Preserve DotNetPackageVersionPropsPath when restoring with msbuild.exe --- build/VSIX.targets | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/build/VSIX.targets b/build/VSIX.targets index 621ebbe641..adcf0969de 100644 --- a/build/VSIX.targets +++ b/build/VSIX.targets @@ -4,7 +4,9 @@ $(RestoreDependsOn);RestoreVSIX $(PackageDependsOn);PackageVSIX Microsoft.VisualStudio.RazorExtension + $(BuildDir)$(VSIXName).vsix $(RepositoryRoot)tooling\$(VSIXName)\$(VSIXName).csproj + $(ArtifactsDir)msbuild\ - + $(MSBuildArtifactsDir)vsix-restore.rsp + + + + + + + + + + + + + + @@ -26,13 +53,12 @@ Condition="'$(VisualStudioMSBuildx86Path)' == ''" /> - $(ArtifactsDir)msbuild\ $(MSBuildArtifactsDir)vsix.log - $(MSBuildArtifactsDir)vsix.rsp - $(BuildDir)$(VSIXName).vsix + $(MSBuildArtifactsDir)vsix-build.rsp + - + Date: Thu, 9 Nov 2017 15:18:56 -0800 Subject: [PATCH 02/11] Preparing to update to 2.2 SDK --- build/dependencies.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/dependencies.props b/build/dependencies.props index 5ef67532fd..f3f6353589 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -10,7 +10,7 @@ 2.3.1 2.3.1 2.1.0-preview1-27498 - 2.0.0 + 2.1.0-preview2-25711-01 2.1.0-preview1-27498 2.1.0-preview1-27498 2.0.0 From 82247fbb097d34d310fc7220d4535486986cb4a7 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 14 Nov 2017 08:52:39 -0800 Subject: [PATCH 03/11] Update samples and tests to target netcoreapp2.1 --- korebuild-lock.txt | 4 ++-- src/RazorPageGenerator/RazorPageGenerator.csproj | 2 +- test/Directory.Build.props | 10 ++++++++++ ...crosoft.AspNetCore.Mvc.Razor.Extensions.Test.csproj | 3 +-- ...NetCore.Mvc.Razor.Extensions.Version1_X.Test.csproj | 3 +-- .../Microsoft.AspNetCore.Razor.Language.Test.csproj | 5 +++-- .../Microsoft.AspNetCore.Razor.Runtime.Test.csproj | 3 +-- .../Language/IntegrationTests/IntegrationTestBase.cs | 2 +- .../Microsoft.AspNetCore.Razor.Test.Common.csproj | 2 +- ...oft.AspNetCore.Razor.Test.MvcShim.Version1_X.csproj | 2 +- .../Microsoft.AspNetCore.Razor.Test.MvcShim.csproj | 2 +- .../Microsoft.CodeAnalysis.Razor.Test.csproj | 3 +-- ...Microsoft.CodeAnalysis.Razor.Workspaces.Test.csproj | 3 +-- .../RazorPageGenerator.Test.csproj | 2 +- 14 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 test/Directory.Build.props diff --git a/korebuild-lock.txt b/korebuild-lock.txt index 86352477bb..95f4613014 100644 --- a/korebuild-lock.txt +++ b/korebuild-lock.txt @@ -1,2 +1,2 @@ -version:2.1.0-preview1-15551 -commithash:8fad9553b48533fddbb16a423ea55b9710ea2e63 +version:2.1.0-preview1-15567 +commithash:903e3104807b1bb8cddd28bdef205b1e2dc021d1 diff --git a/src/RazorPageGenerator/RazorPageGenerator.csproj b/src/RazorPageGenerator/RazorPageGenerator.csproj index ef9bda843d..2668c16904 100644 --- a/src/RazorPageGenerator/RazorPageGenerator.csproj +++ b/src/RazorPageGenerator/RazorPageGenerator.csproj @@ -2,7 +2,7 @@ Builds Razor pages for views in a project. For internal use only. - netcoreapp2.0 + netcoreapp2.1 dotnet-razorpagegenerator RazorPageGenerator Exe diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 0000000000..48d90ce756 --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,10 @@ + + + + + netcoreapp2.1 + $(DeveloperBuildTestTfms) + $(StandardTestTfms);netcoreapp2.0 + net461;$(StandardTestTfms) + + diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test.csproj b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test.csproj index 898c995431..8dcca8cc7c 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test.csproj +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test.csproj @@ -1,8 +1,7 @@  - netcoreapp2.0;net461 - netcoreapp2.0 + $(StandardTestTfms) true $(DefaultItemExcludes);TestFiles\** diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test.csproj b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test.csproj index 275be5e7a2..808b0f3eea 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test.csproj +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test.csproj @@ -1,8 +1,7 @@  - netcoreapp2.0;net461 - netcoreapp2.0 + $(StandardTestTfms) true $(DefaultItemExcludes);TestFiles\** diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Microsoft.AspNetCore.Razor.Language.Test.csproj b/test/Microsoft.AspNetCore.Razor.Language.Test/Microsoft.AspNetCore.Razor.Language.Test.csproj index 0a1b946c90..0481f4e948 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Microsoft.AspNetCore.Razor.Language.Test.csproj +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Microsoft.AspNetCore.Razor.Language.Test.csproj @@ -1,8 +1,9 @@  - netcoreapp2.0;net46 - netcoreapp2.0 + $(DeveloperBuildTestTfms) + $(TargetFrameworks);netcoreapp2.0 + $(TargetFrameworks);net46 $(DefaultItemExcludes);TestFiles\**\* diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Microsoft.AspNetCore.Razor.Runtime.Test.csproj b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Microsoft.AspNetCore.Razor.Runtime.Test.csproj index 9c8aa14637..f5623d9d17 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Microsoft.AspNetCore.Razor.Runtime.Test.csproj +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Microsoft.AspNetCore.Razor.Runtime.Test.csproj @@ -1,8 +1,7 @@  - netcoreapp2.0;net461 - netcoreapp2.0 + $(StandardTestTfms) $(DefaultItemExcludes);TestFiles\**\* diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs index f38ddeea52..7b9fc7bd66 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs @@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { CallContext.LogicalSetData("IntegrationTestBase_FileName", new ObjectHandle(value)); } -#elif NETCOREAPP2_0 +#elif NETCOREAPP2_0 || NETCOREAPP2_1 get { return _fileName.Value; } set { _fileName.Value = value; } #endif diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Microsoft.AspNetCore.Razor.Test.Common.csproj b/test/Microsoft.AspNetCore.Razor.Test.Common/Microsoft.AspNetCore.Razor.Test.Common.csproj index 112861c5d1..cf35d8f8d7 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Microsoft.AspNetCore.Razor.Test.Common.csproj +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Microsoft.AspNetCore.Razor.Test.Common.csproj @@ -4,7 +4,7 @@ $(DefineConstants);GENERATE_BASELINES $(DefineConstants);__RemoveThisBitTo__GENERATE_BASELINES - netcoreapp2.0;net46 + netcoreapp2.1;netcoreapp2.0;net46 diff --git a/test/Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X/Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X.csproj b/test/Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X/Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X.csproj index d95c75b444..0fe0b6a3fc 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X/Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X.csproj +++ b/test/Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X/Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X.csproj @@ -1,7 +1,7 @@  - net461;netcoreapp2.0 + $(StandardTestTfms) true diff --git a/test/Microsoft.AspNetCore.Razor.Test.MvcShim/Microsoft.AspNetCore.Razor.Test.MvcShim.csproj b/test/Microsoft.AspNetCore.Razor.Test.MvcShim/Microsoft.AspNetCore.Razor.Test.MvcShim.csproj index d95c75b444..0fe0b6a3fc 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.MvcShim/Microsoft.AspNetCore.Razor.Test.MvcShim.csproj +++ b/test/Microsoft.AspNetCore.Razor.Test.MvcShim/Microsoft.AspNetCore.Razor.Test.MvcShim.csproj @@ -1,7 +1,7 @@  - net461;netcoreapp2.0 + $(StandardTestTfms) true diff --git a/test/Microsoft.CodeAnalysis.Razor.Test/Microsoft.CodeAnalysis.Razor.Test.csproj b/test/Microsoft.CodeAnalysis.Razor.Test/Microsoft.CodeAnalysis.Razor.Test.csproj index 70e48f933c..7b4cde7c14 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Test/Microsoft.CodeAnalysis.Razor.Test.csproj +++ b/test/Microsoft.CodeAnalysis.Razor.Test/Microsoft.CodeAnalysis.Razor.Test.csproj @@ -1,8 +1,7 @@  - netcoreapp2.0;net461 - netcoreapp2.0 + $(StandardTestTfms) $(DefaultItemExcludes);TestFiles\**\* true diff --git a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Microsoft.CodeAnalysis.Razor.Workspaces.Test.csproj b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Microsoft.CodeAnalysis.Razor.Workspaces.Test.csproj index 2c7a2f767c..bb5f05b323 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Microsoft.CodeAnalysis.Razor.Workspaces.Test.csproj +++ b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Microsoft.CodeAnalysis.Razor.Workspaces.Test.csproj @@ -1,8 +1,7 @@  - net461;netcoreapp2.0 - netcoreapp2.0 + $(StandardTestTfms) true diff --git a/test/RazorPageGenerator.Test/RazorPageGenerator.Test.csproj b/test/RazorPageGenerator.Test/RazorPageGenerator.Test.csproj index 4ff3c28d2d..6562721f98 100644 --- a/test/RazorPageGenerator.Test/RazorPageGenerator.Test.csproj +++ b/test/RazorPageGenerator.Test/RazorPageGenerator.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 $(DefineConstants);GENERATE_BASELINES $(DefineConstants);__RemoveThisBitTo__GENERATE_BASELINES From 44a47182b229ef998c9e520eb5d3a4f5d8c7fae1 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Mon, 13 Nov 2017 10:27:25 -0800 Subject: [PATCH 04/11] Add EditorSettingsManager as an exported MEF component. - Internalized the core implementation of `EditorSettingsManagerInternal`. - Added a `VisualStudioWorkspaceAccessor` to enable the `Microsoft.VisualStudio.Editor.Razor` project to have access to the current workspace. Only added the Windows specific implementation of this type since the VS for mac implementation will require infrastructural work to make happen. #1762 --- ...> DefaultEditorSettingsManagerInternal.cs} | 4 +- ...ltEditorSettingsManagerInternalFactory.cs} | 6 +-- ...er.cs => EditorSettingsManagerInternal.cs} | 2 +- .../DefaultEditorSettingsManager.cs | 37 +++++++++++++++++++ .../EditorSettingsManager.cs | 17 +++++++++ .../VisualStudioWorkspaceAccessor.cs | 12 ++++++ .../DefaultVisualStudioWorkspaceAccessor.cs | 22 +++++++++++ .../DefaultVisualStudioDocumentTracker.cs | 4 +- ...faultVisualStudioDocumentTrackerFactory.cs | 4 +- ...faultEditorSettingsManagerInternalTest.cs} | 8 ++-- .../DefaultVisualStudioDocumentTrackerTest.cs | 2 +- .../RazorTextViewConnectionListenerTest.cs | 2 +- 12 files changed, 104 insertions(+), 16 deletions(-) rename src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/{DefaultEditorSettingsManager.cs => DefaultEditorSettingsManagerInternal.cs} (90%) rename src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/{DefaultEditorSettingsManagerFactory.cs => DefaultEditorSettingsManagerInternalFactory.cs} (71%) rename src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/{EditorSettingsManager.cs => EditorSettingsManagerInternal.cs} (86%) create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/DefaultEditorSettingsManager.cs create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/EditorSettingsManager.cs create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/VisualStudioWorkspaceAccessor.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs rename test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Editor/{DefaultEditorSettingsManagerTest.cs => DefaultEditorSettingsManagerInternalTest.cs} (84%) diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManager.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternal.cs similarity index 90% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManager.cs rename to src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternal.cs index ad87e77c7d..092da5f62c 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManager.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternal.cs @@ -5,14 +5,14 @@ using System; namespace Microsoft.CodeAnalysis.Razor.Editor { - internal class DefaultEditorSettingsManager : EditorSettingsManager + internal class DefaultEditorSettingsManagerInternal : EditorSettingsManagerInternal { public override event EventHandler Changed; private readonly object SettingsAccessorLock = new object(); private EditorSettings _settings; - public DefaultEditorSettingsManager() + public DefaultEditorSettingsManagerInternal() { _settings = EditorSettings.Default; } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerFactory.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternalFactory.cs similarity index 71% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerFactory.cs rename to src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternalFactory.cs index 85f6af0536..84f49a95c8 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerFactory.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternalFactory.cs @@ -9,8 +9,8 @@ using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.Razor.Editor { [Shared] - [ExportLanguageServiceFactory(typeof(EditorSettingsManager), RazorLanguage.Name)] - internal class DefaultEditorSettingsManagerFactory : ILanguageServiceFactory + [ExportLanguageServiceFactory(typeof(EditorSettingsManagerInternal), RazorLanguage.Name)] + internal class DefaultEditorSettingsManagerInternalFactory : ILanguageServiceFactory { public ILanguageService CreateLanguageService(HostLanguageServices languageServices) { @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor throw new ArgumentNullException(nameof(languageServices)); } - return new DefaultEditorSettingsManager(); + return new DefaultEditorSettingsManagerInternal(); } } } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManager.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManagerInternal.cs similarity index 86% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManager.cs rename to src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManagerInternal.cs index 41ad1f3985..9dd5a4f5aa 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManager.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManagerInternal.cs @@ -6,7 +6,7 @@ using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Razor.Editor { - public abstract class EditorSettingsManager : ILanguageService + internal abstract class EditorSettingsManagerInternal : ILanguageService { public abstract event EventHandler Changed; diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultEditorSettingsManager.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultEditorSettingsManager.cs new file mode 100644 index 0000000000..389aff0b77 --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultEditorSettingsManager.cs @@ -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(); + } + + public override event EventHandler Changed + { + add => _editorSettingsManager.Changed += value; + remove => _editorSettingsManager.Changed -= value; + } + + public override EditorSettings Current => _editorSettingsManager.Current; + + public override void Update(EditorSettings updateSettings) + { + _editorSettingsManager.Update(updateSettings); + } + } +} diff --git a/src/Microsoft.VisualStudio.Editor.Razor/EditorSettingsManager.cs b/src/Microsoft.VisualStudio.Editor.Razor/EditorSettingsManager.cs new file mode 100644 index 0000000000..c3e0399bde --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/EditorSettingsManager.cs @@ -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 Changed; + + public abstract EditorSettings Current { get; } + + public abstract void Update(EditorSettings updateSettings); + } +} diff --git a/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioWorkspaceAccessor.cs b/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioWorkspaceAccessor.cs new file mode 100644 index 0000000000..2e84e16ec8 --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioWorkspaceAccessor.cs @@ -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; } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs new file mode 100644 index 0000000000..4749c44f5b --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs @@ -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; } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs index c5808dcf3d..e68c049d8b 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor { private readonly string _filePath; private readonly ProjectSnapshotManager _projectManager; - private readonly EditorSettingsManager _editorSettingsManager; + private readonly EditorSettingsManagerInternal _editorSettingsManager; private readonly TextBufferProjectService _projectService; private readonly ITextBuffer _textBuffer; private readonly List _textViews; @@ -33,7 +33,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor string filePath, ProjectSnapshotManager projectManager, TextBufferProjectService projectService, - EditorSettingsManager editorSettingsManager, + EditorSettingsManagerInternal editorSettingsManager, Workspace workspace, ITextBuffer textBuffer) { diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs index eaacbc06df..53c2c074a2 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs @@ -22,7 +22,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor private readonly Workspace _workspace; private readonly ForegroundDispatcher _foregroundDispatcher; private readonly ProjectSnapshotManager _projectManager; - private readonly EditorSettingsManager _editorSettingsManager; + private readonly EditorSettingsManagerInternal _editorSettingsManager; [ImportingConstructor] public DefaultVisualStudioDocumentTrackerFactory( @@ -52,7 +52,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor _foregroundDispatcher = workspace.Services.GetRequiredService(); var razorLanguageServices = workspace.Services.GetLanguageServices(RazorLanguage.Name); _projectManager = razorLanguageServices.GetRequiredService(); - _editorSettingsManager = razorLanguageServices.GetRequiredService(); + _editorSettingsManager = razorLanguageServices.GetRequiredService(); } public override VisualStudioDocumentTracker Create(ITextBuffer textBuffer) diff --git a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Editor/DefaultEditorSettingsManagerTest.cs b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Editor/DefaultEditorSettingsManagerInternalTest.cs similarity index 84% rename from test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Editor/DefaultEditorSettingsManagerTest.cs rename to test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Editor/DefaultEditorSettingsManagerInternalTest.cs index cd1c99ac90..d0a85604eb 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Editor/DefaultEditorSettingsManagerTest.cs +++ b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Editor/DefaultEditorSettingsManagerInternalTest.cs @@ -5,13 +5,13 @@ using Xunit; namespace Microsoft.CodeAnalysis.Razor.Editor { - public class DefaultEditorSettingsManagerTest + public class DefaultEditorSettingsManagerInternalTest { [Fact] public void InitialSettingsAreDefault() { // Act - var manager = new DefaultEditorSettingsManager(); + var manager = new DefaultEditorSettingsManagerInternal(); // Assert Assert.Equal(EditorSettings.Default, manager.Current); @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor public void Update_TriggersChangedIfEditorSettingsAreDifferent() { // Arrange - var manager = new DefaultEditorSettingsManager(); + var manager = new DefaultEditorSettingsManagerInternal(); var called = false; manager.Changed += (caller, args) => { @@ -41,7 +41,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor public void Update_DoesNotTriggerChangedIfEditorSettingsAreSame() { // Arrange - var manager = new DefaultEditorSettingsManager(); + var manager = new DefaultEditorSettingsManagerInternal(); var called = false; manager.Changed += (caller, args) => { diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerTest.cs index 7df72b1190..9a6e4552fe 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerTest.cs +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerTest.cs @@ -30,7 +30,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor s.IsSupportedProject(It.IsAny()) == true && s.GetProjectPath(It.IsAny()) == "C:/Some/Path/TestProject.csproj"); - private EditorSettingsManager EditorSettingsManager => new DefaultEditorSettingsManager(); + private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal(); private Workspace Workspace => new AdhocWorkspace(); diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/RazorTextViewConnectionListenerTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/RazorTextViewConnectionListenerTest.cs index 9a96cc03eb..f543e741f2 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/RazorTextViewConnectionListenerTest.cs +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/RazorTextViewConnectionListenerTest.cs @@ -26,7 +26,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor s.IsSupportedProject(It.IsAny()) == true && s.GetProjectPath(It.IsAny()) == "C:/Some/Path/TestProject.csproj"); - private EditorSettingsManager EditorSettingsManager => new DefaultEditorSettingsManager(); + private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal(); private Workspace Workspace { get; } = new AdhocWorkspace(); From de23788019fc83400be7f60335eed6032c60162c Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Mon, 13 Nov 2017 12:19:28 -0800 Subject: [PATCH 05/11] Move indentation service to VS.Editor.Razor and export it. - Removed indentation service extensions and made `GetDesiredIndentation` understand `ITextSnapshot`s and `ITextLineSnapshot`. - Updated our test `StringTextSnapshot` implementation to have the required features our indentation tests require. - Updated indentation service tests to use new API. #1762 --- ...aultRazorIndentationFactsServiceFactory.cs | 19 ---- .../DefaultRazorIndentationFactsService.cs | 30 +++++-- .../RazorIndentationFactsService.cs | 6 +- .../RazorIndentationFactsServiceExtensions.cs | 30 ------- .../StringTextSnapshot.cs | 49 ++++++++-- ...DefaultRazorIndentationFactsServiceTest.cs | 89 ++++++------------- 6 files changed, 96 insertions(+), 127 deletions(-) delete mode 100644 src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorIndentationFactsServiceFactory.cs rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/DefaultRazorIndentationFactsService.cs (87%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/RazorIndentationFactsService.cs (68%) delete mode 100644 src/Microsoft.VisualStudio.Editor.Razor/RazorIndentationFactsServiceExtensions.cs rename test/{Microsoft.CodeAnalysis.Razor.Workspaces.Test => Microsoft.VisualStudio.Editor.Razor.Test}/DefaultRazorIndentationFactsServiceTest.cs (66%) diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorIndentationFactsServiceFactory.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorIndentationFactsServiceFactory.cs deleted file mode 100644 index a7418928fd..0000000000 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorIndentationFactsServiceFactory.cs +++ /dev/null @@ -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(); - } - } -} diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorIndentationFactsService.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorIndentationFactsService.cs similarity index 87% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorIndentationFactsService.cs rename to src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorIndentationFactsService.cs index c8dcf8be28..76f704eb99 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorIndentationFactsService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorIndentationFactsService.cs @@ -3,28 +3,38 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; 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.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { + [System.Composition.Shared] + [Export(typeof(RazorIndentationFactsService))] internal class DefaultRazorIndentationFactsService : RazorIndentationFactsService { - public override int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, int previousLineEndIndex, Func getLineContent, int indentSize, int tabSize) + public override int? GetDesiredIndentation( + RazorSyntaxTree syntaxTree, + ITextSnapshot syntaxTreeSnapshot, + ITextSnapshotLine line, + int indentSize, + int tabSize) { if (syntaxTree == null) { 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) @@ -37,6 +47,10 @@ namespace Microsoft.CodeAnalysis.Razor 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 owningSpan = LocateOwner(syntaxTree.Root, simulatedChange); 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 // the underlying SyntaxTree text buffer. Because of this, if we want to provide accurate indentations we need to // operate on the current line representation as indicated by the provider. - var line = getLineContent(currentSpan.Start.LineIndex); - desiredIndentation = GetIndentLevelOfLine(line, tabSize) + indentSize; + var lineText = line.Snapshot.GetLineFromLineNumber(currentSpan.Start.LineIndex).GetText(); + desiredIndentation = GetIndentLevelOfLine(lineText, tabSize) + indentSize; } } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorIndentationFactsService.cs b/src/Microsoft.VisualStudio.Editor.Razor/RazorIndentationFactsService.cs similarity index 68% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorIndentationFactsService.cs rename to src/Microsoft.VisualStudio.Editor.Razor/RazorIndentationFactsService.cs index 045c6d8ca3..01f7e6f126 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorIndentationFactsService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/RazorIndentationFactsService.cs @@ -1,14 +1,14 @@ // 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.CodeAnalysis.Host; +using Microsoft.VisualStudio.Text; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public abstract class RazorIndentationFactsService : ILanguageService { - public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, int previousLineEnd, Func lineProvider, int indentSize, int tabSize); + public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line, int indentSize, int tabSize); } } diff --git a/src/Microsoft.VisualStudio.Editor.Razor/RazorIndentationFactsServiceExtensions.cs b/src/Microsoft.VisualStudio.Editor.Razor/RazorIndentationFactsServiceExtensions.cs deleted file mode 100644 index 607fce125b..0000000000 --- a/src/Microsoft.VisualStudio.Editor.Razor/RazorIndentationFactsServiceExtensions.cs +++ /dev/null @@ -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 getLineContentDelegate = (lineIndex) => line.Snapshot.GetLineFromLineNumber(lineIndex).GetText(); - - return service.GetDesiredIndentation(syntaxTree, previousLineEnd, getLineContentDelegate, indentSize, tabSize); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test.Common/StringTextSnapshot.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test.Common/StringTextSnapshot.cs index 16295be713..39ef523739 100644 --- a/test/Microsoft.VisualStudio.Editor.Razor.Test.Common/StringTextSnapshot.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test.Common/StringTextSnapshot.cs @@ -77,7 +77,20 @@ namespace Microsoft.VisualStudio.Text 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(); @@ -89,8 +102,6 @@ namespace Microsoft.VisualStudio.Text 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 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 readonly string _contentWithLineBreak; @@ -153,7 +188,9 @@ namespace Microsoft.VisualStudio.Text } Start = new SnapshotPoint(owner, start); + End = new SnapshotPoint(owner, start + _content.Length); Snapshot = owner; + LineNumber = (owner as StringTextSnapshot)._lines.Count; } public ITextSnapshot Snapshot { get; } @@ -172,14 +209,14 @@ namespace Microsoft.VisualStudio.Text 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 ExtentIncludingLineBreak => throw new NotImplementedException(); - public SnapshotPoint End => throw new NotImplementedException(); - public SnapshotPoint EndIncludingLineBreak => throw new NotImplementedException(); } } diff --git a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultRazorIndentationFactsServiceTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorIndentationFactsServiceTest.cs similarity index 66% rename from test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultRazorIndentationFactsServiceTest.cs rename to test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorIndentationFactsServiceTest.cs index a30aecee7d..3c4f2912c6 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultRazorIndentationFactsServiceTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorIndentationFactsServiceTest.cs @@ -1,13 +1,13 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Razor.Language; +using Microsoft.VisualStudio.Text; using Xunit; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public class DefaultRazorIndentationFactsServiceTest { @@ -15,17 +15,17 @@ namespace Microsoft.CodeAnalysis.Razor public void GetDesiredIndentation_ReturnsNull_IfOwningSpanIsCode() { // Arrange - var source = $@" + var source = new StringTextSnapshot($@" @{{ -"; +"); var syntaxTree = GetSyntaxTree(source); var service = new DefaultRazorIndentationFactsService(); // Act var indentation = service.GetDesiredIndentation( syntaxTree, - previousLineEndIndex: GetLineEndIndexForLine(source, 1), - getLineContent: line => GetLineContent(source, line), + source, + source.GetLineFromLineNumber(2), indentSize: 4, tabSize: 1); @@ -38,17 +38,17 @@ namespace Microsoft.CodeAnalysis.Razor { // Arrange var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom"); - var source = $@" + var source = new StringTextSnapshot($@" @custom -"; +"); var syntaxTree = GetSyntaxTree(source, new[] { customDirective }); var service = new DefaultRazorIndentationFactsService(); // Act var indentation = service.GetDesiredIndentation( syntaxTree, - previousLineEndIndex: GetLineEndIndexForLine(source, 1), - getLineContent: line => GetLineContent(source, line), + source, + source.GetLineFromLineNumber(2), indentSize: 4, tabSize: 1); @@ -60,17 +60,17 @@ namespace Microsoft.CodeAnalysis.Razor public void GetDesiredIndentation_ReturnsCorrectIndentation_ForMarkupWithinCodeBlock() { // Arrange - var source = $@"@{{ + var source = new StringTextSnapshot($@"@{{
-"; +"); var syntaxTree = GetSyntaxTree(source); var service = new DefaultRazorIndentationFactsService(); // Act var indentation = service.GetDesiredIndentation( syntaxTree, - previousLineEndIndex: GetLineEndIndexForLine(source, 1), - getLineContent: line => GetLineContent(source, line), + source, + source.GetLineFromLineNumber(2), indentSize: 4, tabSize: 4); @@ -83,18 +83,18 @@ namespace Microsoft.CodeAnalysis.Razor { // Arrange var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom"); - var source = $@"@custom + var source = new StringTextSnapshot($@"@custom {{
-}}"; +}}"); var syntaxTree = GetSyntaxTree(source, new[] { customDirective }); var service = new DefaultRazorIndentationFactsService(); // Act var indentation = service.GetDesiredIndentation( syntaxTree, - previousLineEndIndex: GetLineEndIndexForLine(source, 2), - getLineContent: line => GetLineContent(source, line), + source, + source.GetLineFromLineNumber(3), indentSize: 4, tabSize: 4); @@ -106,21 +106,21 @@ namespace Microsoft.CodeAnalysis.Razor public void GetDesiredIndentation_ReturnsCorrectIndentation_ForNestedMarkupWithinCodeBlock() { // Arrange - var source = $@" + var source = new StringTextSnapshot($@"
@{{ }}
-"; +"); var syntaxTree = GetSyntaxTree(source); var service = new DefaultRazorIndentationFactsService(); // Act var indentation = service.GetDesiredIndentation( syntaxTree, - previousLineEndIndex: GetLineEndIndexForLine(source, 3), - getLineContent: line => GetLineContent(source, line), + source, + source.GetLineFromLineNumber(4), indentSize: 4, tabSize: 4); @@ -133,20 +133,20 @@ namespace Microsoft.CodeAnalysis.Razor { // Arrange var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom"); - var source = $@"@custom + var source = new StringTextSnapshot($@"@custom {{ @{{
}} -}}"; +}}"); var syntaxTree = GetSyntaxTree(source, new[] { customDirective }); var service = new DefaultRazorIndentationFactsService(); // Act var indentation = service.GetDesiredIndentation( syntaxTree, - previousLineEndIndex: GetLineEndIndexForLine(source, 3), - getLineContent: line => GetLineContent(source, line), + source, + source.GetLineFromLineNumber(4), indentSize: 4, tabSize: 4); @@ -154,7 +154,7 @@ namespace Microsoft.CodeAnalysis.Razor Assert.Equal(8, indentation); } - private static RazorSyntaxTree GetSyntaxTree(string source, IEnumerable directives = null) + private static RazorSyntaxTree GetSyntaxTree(StringTextSnapshot source, IEnumerable directives = null) { directives = directives ?? Enumerable.Empty(); 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); engine.Process(codeDocument); 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; - } } } From 44e58740290f8622f8f83ecf7c3ff5c7728b64b6 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Mon, 13 Nov 2017 12:42:47 -0800 Subject: [PATCH 06/11] Move RazorSyntaxFactsService to VS.Editor.Razor and export it. - Moved service poco types: `AcceptedCharacters`, `BlockKind`, `ClassifiedSpan`, `SpanKind` and `TagHelperSpan` into VS.Editor.Razor. #1762 --- .../DefaultRazorSyntaxFactsServiceFactory.cs | 17 ----------------- .../AcceptedCharacters.cs | 2 +- .../BlockKind.cs | 2 +- .../ClassifiedSpan.cs | 2 +- .../DefaultRazorSyntaxFactsService.cs | 5 ++++- .../RazorSyntaxFactsService.cs | 3 ++- .../SpanKind.cs | 2 +- .../TagHelperSpan.cs | 2 +- 8 files changed, 11 insertions(+), 24 deletions(-) delete mode 100644 src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorSyntaxFactsServiceFactory.cs rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/AcceptedCharacters.cs (91%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/BlockKind.cs (90%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/ClassifiedSpan.cs (94%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/DefaultRazorSyntaxFactsService.cs (96%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/RazorSyntaxFactsService.cs (87%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/SpanKind.cs (86%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/TagHelperSpan.cs (94%) diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorSyntaxFactsServiceFactory.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorSyntaxFactsServiceFactory.cs deleted file mode 100644 index af38ae47e7..0000000000 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorSyntaxFactsServiceFactory.cs +++ /dev/null @@ -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(RazorSyntaxFactsService), RazorLanguage.Name, ServiceLayer.Default)] - internal class DefaultRazorSyntaxFactsServiceFactory : ILanguageServiceFactory - { - public ILanguageService CreateLanguageService(HostLanguageServices languageServices) - { - return new DefaultRazorSyntaxFactsService(); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/AcceptedCharacters.cs b/src/Microsoft.VisualStudio.Editor.Razor/AcceptedCharacters.cs similarity index 91% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/AcceptedCharacters.cs rename to src/Microsoft.VisualStudio.Editor.Razor/AcceptedCharacters.cs index 108513d9bd..1d9ba282e9 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/AcceptedCharacters.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/AcceptedCharacters.cs @@ -3,7 +3,7 @@ using System; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { [Flags] public enum AcceptedCharacters diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/BlockKind.cs b/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs similarity index 90% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/BlockKind.cs rename to src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs index abb703173a..48ad2e4fb4 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/BlockKind.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public enum BlockKind { diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ClassifiedSpan.cs b/src/Microsoft.VisualStudio.Editor.Razor/ClassifiedSpan.cs similarity index 94% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/ClassifiedSpan.cs rename to src/Microsoft.VisualStudio.Editor.Razor/ClassifiedSpan.cs index 01684066e3..dc066c8fce 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ClassifiedSpan.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/ClassifiedSpan.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Razor.Language; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public struct ClassifiedSpan { diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorSyntaxFactsService.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorSyntaxFactsService.cs similarity index 96% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorSyntaxFactsService.cs rename to src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorSyntaxFactsService.cs index 96590bb165..b6cbfe50af 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultRazorSyntaxFactsService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorSyntaxFactsService.cs @@ -3,13 +3,16 @@ 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 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 { public override IReadOnlyList GetClassifiedSpans(RazorSyntaxTree syntaxTree) diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorSyntaxFactsService.cs b/src/Microsoft.VisualStudio.Editor.Razor/RazorSyntaxFactsService.cs similarity index 87% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorSyntaxFactsService.cs rename to src/Microsoft.VisualStudio.Editor.Razor/RazorSyntaxFactsService.cs index d63a34cda7..fc47c81991 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorSyntaxFactsService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/RazorSyntaxFactsService.cs @@ -4,8 +4,9 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Razor; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public abstract class RazorSyntaxFactsService : ILanguageService { diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/SpanKind.cs b/src/Microsoft.VisualStudio.Editor.Razor/SpanKind.cs similarity index 86% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/SpanKind.cs rename to src/Microsoft.VisualStudio.Editor.Razor/SpanKind.cs index c76a471f34..8e66d13408 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/SpanKind.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/SpanKind.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public enum SpanKind { diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperSpan.cs b/src/Microsoft.VisualStudio.Editor.Razor/TagHelperSpan.cs similarity index 94% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperSpan.cs rename to src/Microsoft.VisualStudio.Editor.Razor/TagHelperSpan.cs index 69654e3f06..15a88dee7a 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperSpan.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/TagHelperSpan.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public struct TagHelperSpan { From 44b4bf6bc607a3748feb6b1b8a91c42b5e539579 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Mon, 13 Nov 2017 15:15:47 -0800 Subject: [PATCH 07/11] Cleanup legacy and unintentionally public bits. - Made `RazorTemplateEngineFactoryService`, `TagHelperResolver`, `RazorDiagnosticJsonConverter` and `RazorLanguageServiceException` internal. - Deleted all items in the Legacy folder in VisualStudio.LanguageServices.Razor and `RazorLanguageServiceException`. #1762 --- .../RazorTemplateEngineFactoryService.cs | 2 +- .../TagHelperResolver.cs | 2 +- .../DefaultRazorEngineDirectiveResolver.cs | 7 +- .../DefaultRazorEngineDocumentGenerator.cs | 7 +- .../DefaultTagHelperResolver.cs | 7 +- .../Legacy/AcceptedCharacters.cs | 28 - .../Legacy/AttributeCompletionContext.cs | 69 - .../Legacy/AttributeCompletionResult.cs | 45 - .../Legacy/BlockKind.cs | 28 - .../Legacy/ClassifiedSpan.cs | 33 - .../Legacy/DefaultRazorSyntaxFactsService.cs | 352 ----- .../DefaultTagHelperCompletionService.cs | 285 ---- .../Legacy/DefaultTagHelperFactsService.cs | 168 -- .../Legacy/DocumentParseCompleteEventArgs.cs | 47 - .../Legacy/ElementCompletionContext.cs | 59 - .../Legacy/ElementCompletionResult.cs | 45 - .../Legacy/ITagHelperResolver.cs | 18 - .../Legacy/LegacyTagHelperResolver.cs | 24 - .../LegacyTemplateEngineFactoryService.cs | 62 - .../LegacyTextBufferCodeDocumentProvider.cs | 40 - .../Legacy/PartialParseResult.cs | 21 - .../Legacy/RazorEditorParser.cs | 623 -------- .../Legacy/RazorSyntaxFactsService.cs | 22 - .../RazorTemplateEngineFactoryService.cs | 17 - .../Legacy/SpanKind.cs | 18 - .../Legacy/TagHelperCompletionService.cs | 16 - .../Legacy/TagHelperFactsService.cs | 23 - .../Legacy/TagHelperSpan.cs | 33 - .../RazorDiagnosticJsonConverter.cs | 2 +- .../RazorLanguageServiceException.cs | 15 - ...egacyTextBufferCodeDocumentProviderTest.cs | 99 -- .../DefaultTagHelperCompletionServiceTest.cs | 964 ------------ .../DefaultTagHelperFactsServiceTest.cs | 388 ----- .../Legacy/RazorEditorParserTest.cs | 1345 ----------------- 34 files changed, 15 insertions(+), 4899 deletions(-) delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AcceptedCharacters.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AttributeCompletionContext.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AttributeCompletionResult.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/BlockKind.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ClassifiedSpan.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultRazorSyntaxFactsService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultTagHelperCompletionService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultTagHelperFactsService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DocumentParseCompleteEventArgs.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ElementCompletionContext.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ElementCompletionResult.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ITagHelperResolver.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTemplateEngineFactoryService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTextBufferCodeDocumentProvider.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/PartialParseResult.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorEditorParser.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorSyntaxFactsService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorTemplateEngineFactoryService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/SpanKind.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperCompletionService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperFactsService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperSpan.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/RazorLanguageServiceException.cs delete mode 100644 test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/LegacyTextBufferCodeDocumentProviderTest.cs delete mode 100644 test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/DefaultTagHelperCompletionServiceTest.cs delete mode 100644 test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/DefaultTagHelperFactsServiceTest.cs delete mode 100644 test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/RazorEditorParserTest.cs diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorTemplateEngineFactoryService.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorTemplateEngineFactoryService.cs index 7908a9f116..26ff5b803a 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorTemplateEngineFactoryService.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorTemplateEngineFactoryService.cs @@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Razor { - public abstract class RazorTemplateEngineFactoryService : ILanguageService + internal abstract class RazorTemplateEngineFactoryService : ILanguageService { public abstract RazorTemplateEngine Create(string projectPath, Action configure); } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperResolver.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperResolver.cs index d246743f59..a16f46400b 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperResolver.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperResolver.cs @@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Razor { - public abstract class TagHelperResolver : ILanguageService + internal abstract class TagHelperResolver : ILanguageService { public abstract TagHelperResolutionResult GetTagHelpers(Compilation compilation); diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorEngineDirectiveResolver.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorEngineDirectiveResolver.cs index f0ce0e7e3f..9c1eadae88 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorEngineDirectiveResolver.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorEngineDirectiveResolver.cs @@ -29,9 +29,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor } catch (Exception exception) { - throw new RazorLanguageServiceException( - typeof(DefaultRazorEngineDirectiveResolver).FullName, - nameof(GetRazorEngineDirectivesAsync), + throw new InvalidOperationException( + Resources.FormatUnexpectedException( + typeof(DefaultRazorEngineDirectiveResolver).FullName, + nameof(GetRazorEngineDirectivesAsync)), exception); } } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorEngineDocumentGenerator.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorEngineDocumentGenerator.cs index 09b3f66ce8..b7c0cce061 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorEngineDocumentGenerator.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultRazorEngineDocumentGenerator.cs @@ -27,9 +27,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor } catch (Exception exception) { - throw new RazorLanguageServiceException( - typeof(DefaultRazorEngineDocumentGenerator).FullName, - nameof(GenerateDocumentAsync), + throw new InvalidOperationException( + Resources.FormatUnexpectedException( + typeof(DefaultRazorEngineDocumentGenerator).FullName, + nameof(GenerateDocumentAsync)), exception); } } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperResolver.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperResolver.cs index 5ed80730d7..31713fb3b2 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperResolver.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperResolver.cs @@ -74,9 +74,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor { _errorReporter.ReportError(exception, project); - throw new RazorLanguageServiceException( - typeof(DefaultTagHelperResolver).FullName, - nameof(GetTagHelpersAsync), + throw new InvalidOperationException( + Resources.FormatUnexpectedException( + typeof(DefaultTagHelperResolver).FullName, + nameof(GetTagHelpersAsync)), exception); } } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AcceptedCharacters.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AcceptedCharacters.cs deleted file mode 100644 index 034dda9298..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AcceptedCharacters.cs +++ /dev/null @@ -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 - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AttributeCompletionContext.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AttributeCompletionContext.cs deleted file mode 100644 index f859c1d5c1..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AttributeCompletionContext.cs +++ /dev/null @@ -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 existingCompletions, - string currentTagName, - IEnumerable> attributes, - string currentParentTagName, - Func 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 ExistingCompletions { get; } - - public string CurrentTagName { get; } - - public IEnumerable> Attributes { get; } - - public string CurrentParentTagName { get; } - - public Func InHTMLSchema { get; } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AttributeCompletionResult.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AttributeCompletionResult.cs deleted file mode 100644 index 066d438054..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/AttributeCompletionResult.cs +++ /dev/null @@ -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> Completions { get; } - - internal static AttributeCompletionResult Create(Dictionary> completions) - { - var readonlyCompletions = completions.ToDictionary( - key => key.Key, - value => (IEnumerable)value.Value, - completions.Comparer); - var result = new DefaultAttributeCompletionResult(readonlyCompletions); - - return result; - } - - private class DefaultAttributeCompletionResult : AttributeCompletionResult - { - private readonly IReadOnlyDictionary> _completions; - - public DefaultAttributeCompletionResult(IReadOnlyDictionary> completions) - { - _completions = completions; - } - - public override IReadOnlyDictionary> Completions => _completions; - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/BlockKind.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/BlockKind.cs deleted file mode 100644 index 5591b31943..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/BlockKind.cs +++ /dev/null @@ -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 - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ClassifiedSpan.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ClassifiedSpan.cs deleted file mode 100644 index a254d862dc..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ClassifiedSpan.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultRazorSyntaxFactsService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultRazorSyntaxFactsService.cs deleted file mode 100644 index 944942048b..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultRazorSyntaxFactsService.cs +++ /dev/null @@ -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 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 Flatten(RazorSyntaxTree syntaxTree) - { - var result = new List(); - AppendFlattenedSpans(syntaxTree.Root, result); - return result; - - void AppendFlattenedSpans(SyntaxTreeNode node, List 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 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 GetTagHelperSpans(RazorSyntaxTree syntaxTree) - { - if (syntaxTree == null) - { - throw new ArgumentNullException(nameof(syntaxTree)); - } - - var results = new List(); - - List toProcess = new List(); - List blockChildren = new List(); - toProcess.Add(syntaxTree.Root); - - for (var i = 0; i < toProcess.Count; i++) - { - var blockNode = toProcess[i]; - TagHelperBlock tagHelperNode = blockNode as TagHelperBlock; - if (tagHelperNode != null) - { - results.Add(new TagHelperSpan( - new SourceSpan( - tagHelperNode.Start.FilePath ?? syntaxTree.Source.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 children = new List(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; - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultTagHelperCompletionService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultTagHelperCompletionService.cs deleted file mode 100644 index e81e01ccce..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultTagHelperCompletionService.cs +++ /dev/null @@ -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 _emptyHashSet = new HashSet(); - - [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(), - 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(); - 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(); - 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>(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(), - StringComparer.OrdinalIgnoreCase); - - var catchAllDescriptors = new HashSet(); - 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: | - // 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(); - elementCompletions[tagName] = existingRuleDescriptors; - } - - existingRuleDescriptors.Add(possibleDescriptor); - } - } - - private void AddAllowedChildrenCompletions( - ElementCompletionContext completionContext, - Dictionary> 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(); - elementCompletions[prefixedName] = existingRuleDescriptors; - } - - existingRuleDescriptors.UnionWith(descriptors); - } - } - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultTagHelperFactsService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultTagHelperFactsService.cs deleted file mode 100644 index 8e0b34b431..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DefaultTagHelperFactsService.cs +++ /dev/null @@ -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> 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 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(); - 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 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(); - 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 GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag) - { - if (documentContext == null) - { - throw new ArgumentNullException(nameof(documentContext)); - } - - var matchingDescriptors = new List(); - 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; - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DocumentParseCompleteEventArgs.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DocumentParseCompleteEventArgs.cs deleted file mode 100644 index c8dc5065c8..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/DocumentParseCompleteEventArgs.cs +++ /dev/null @@ -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 -{ - /// - /// Arguments for the event in . - /// - public sealed class DocumentParseCompleteEventArgs : EventArgs - { - public DocumentParseCompleteEventArgs( - SourceChange change, - ITextSnapshot buffer, - bool treeStructureChanged, - RazorCodeDocument codeDocument) - { - SourceChange = change; - Buffer = buffer; - TreeStructureChanged = treeStructureChanged; - CodeDocument = codeDocument; - } - - /// - /// The which triggered the re-parse. - /// - public SourceChange SourceChange { get; } - - /// - /// The text snapshot used in the re-parse. - /// - public ITextSnapshot Buffer { get; } - - /// - /// Indicates if the tree structure has actually changed since the previous re-parse. - /// - public bool TreeStructureChanged { get; } - - /// - /// The result of the parsing and code generation. - /// - public RazorCodeDocument CodeDocument { get; } - } -} \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ElementCompletionContext.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ElementCompletionContext.cs deleted file mode 100644 index c38c8485c7..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ElementCompletionContext.cs +++ /dev/null @@ -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 existingCompletions, - string containingTagName, - IEnumerable> attributes, - string containingParentTagName, - Func 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 ExistingCompletions { get; } - - public string ContainingTagName { get; } - - public IEnumerable> Attributes { get; } - - public string ContainingParentTagName { get; } - - public Func InHTMLSchema { get; } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ElementCompletionResult.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ElementCompletionResult.cs deleted file mode 100644 index 88dc0c6a56..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ElementCompletionResult.cs +++ /dev/null @@ -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> Completions { get; } - - internal static ElementCompletionResult Create(Dictionary> completions) - { - var readonlyCompletions = completions.ToDictionary( - key => key.Key, - value => (IEnumerable)value.Value, - completions.Comparer); - var result = new DefaultElementCompletionResult(readonlyCompletions); - - return result; - } - - private class DefaultElementCompletionResult : ElementCompletionResult - { - private readonly IReadOnlyDictionary> _completions; - - public DefaultElementCompletionResult(IReadOnlyDictionary> completions) - { - _completions = completions; - } - - public override IReadOnlyDictionary> Completions => _completions; - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ITagHelperResolver.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ITagHelperResolver.cs deleted file mode 100644 index 80c2502714..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ITagHelperResolver.cs +++ /dev/null @@ -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 System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Razor; - -namespace Microsoft.VisualStudio.LanguageServices.Razor -{ - // ---------------------------------------------------------------------------------------------------- - // NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead - // use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces - // ---------------------------------------------------------------------------------------------------- - public interface ITagHelperResolver - { - Task GetTagHelpersAsync(Project project); - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs deleted file mode 100644 index a646026ba1..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs +++ /dev/null @@ -1,24 +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.ComponentModel.Composition; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Razor; - -namespace Microsoft.VisualStudio.LanguageServices.Razor -{ - // ---------------------------------------------------------------------------------------------------- - // NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead - // use TagHelperResolver. - // ---------------------------------------------------------------------------------------------------- - [Export(typeof(ITagHelperResolver))] - internal class LegacyTagHelperResolver : DefaultTagHelperResolver, ITagHelperResolver - { - [ImportingConstructor] - public LegacyTagHelperResolver( - [Import(typeof(VisualStudioWorkspace))] Workspace workspace) - : base(workspace.Services.GetRequiredService(), workspace) - { - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTemplateEngineFactoryService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTemplateEngineFactoryService.cs deleted file mode 100644 index fcae83b4ec..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTemplateEngineFactoryService.cs +++ /dev/null @@ -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(); - } - - // 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 configure) - { - if (projectPath == null) - { - throw new ArgumentNullException(nameof(projectPath)); - } - - return _inner.Create(projectPath, configure); - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTextBufferCodeDocumentProvider.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTextBufferCodeDocumentProvider.cs deleted file mode 100644 index 963dfc9fe8..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTextBufferCodeDocumentProvider.cs +++ /dev/null @@ -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; - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/PartialParseResult.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/PartialParseResult.cs deleted file mode 100644 index 2571342f69..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/PartialParseResult.cs +++ /dev/null @@ -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 - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorEditorParser.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorEditorParser.cs deleted file mode 100644 index de7fbc9977..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorEditorParser.cs +++ /dev/null @@ -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(); - } - - /// - /// Event fired when a full reparse of the document completes. - /// - public event EventHandler 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; - } - - /// - /// Disposes of this parser. Should be called when the editor window is closed and the document is unloaded. - /// - 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 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); - } - - /// - /// Fired on the main thread. - /// - public event EventHandler 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 edits, CancellationToken cancelToken) - { - return TreesAreDifferent(leftTree.Root, rightTree.Root, edits.Select(edit => edit.Change), cancelToken); - } - - internal static bool TreesAreDifferent(Block leftTree, Block rightTree, IEnumerable 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 _changes = new List(); - - public MainThreadState(string fileName) - { - _fileName = fileName; - - SetThreadId(Thread.CurrentThread.ManagedThreadId); - } - - public event EventHandler 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(); - 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 _previouslyDiscarded = new List(); - - 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 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 changes, CancellationToken cancelToken) - { - Edits = changes; - CancelToken = cancelToken; - } - - public CancellationToken CancelToken { get; } - - public IList Edits { get; } - } - - private class Edit - { - public Edit(SourceChange change, ITextSnapshot snapshot) - { - Change = change; - Snapshot = snapshot; - } - - public SourceChange Change { get; } - - public ITextSnapshot Snapshot { get; set; } - } - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorSyntaxFactsService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorSyntaxFactsService.cs deleted file mode 100644 index 6b187680e9..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorSyntaxFactsService.cs +++ /dev/null @@ -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 GetClassifiedSpans(RazorSyntaxTree syntaxTree); - - public abstract IReadOnlyList GetTagHelperSpans(RazorSyntaxTree syntaxTree); - - public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line, int indentSize, int tabSize); - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorTemplateEngineFactoryService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorTemplateEngineFactoryService.cs deleted file mode 100644 index 784437df22..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/RazorTemplateEngineFactoryService.cs +++ /dev/null @@ -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 configure); - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/SpanKind.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/SpanKind.cs deleted file mode 100644 index d557849f6d..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/SpanKind.cs +++ /dev/null @@ -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 - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperCompletionService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperCompletionService.cs deleted file mode 100644 index 78f3e4b821..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperCompletionService.cs +++ /dev/null @@ -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); - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperFactsService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperFactsService.cs deleted file mode 100644 index 5fca65c218..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperFactsService.cs +++ /dev/null @@ -1,23 +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; - -namespace Microsoft.VisualStudio.LanguageServices.Razor -{ - // ---------------------------------------------------------------------------------------------------- - // NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead - // use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces - // ---------------------------------------------------------------------------------------------------- - public abstract class TagHelperFactsService - { - public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable> attributes, string parentTag); - - public abstract IEnumerable GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding); - - public abstract IReadOnlyList GetTagHelpersGivenTag(TagHelperDocumentContext documentContext, string tagName, string parentTag); - - public abstract IReadOnlyList GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag); - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperSpan.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperSpan.cs deleted file mode 100644 index 337234f3d0..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/TagHelperSpan.cs +++ /dev/null @@ -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 TagHelpers => Binding.Descriptors; - - public SourceSpan Span { get; } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorDiagnosticJsonConverter.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorDiagnosticJsonConverter.cs index 96db50dc92..d9d4d966e1 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorDiagnosticJsonConverter.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorDiagnosticJsonConverter.cs @@ -9,7 +9,7 @@ using Newtonsoft.Json.Linq; namespace Microsoft.VisualStudio.LanguageServices.Razor { - public class RazorDiagnosticJsonConverter : JsonConverter + internal class RazorDiagnosticJsonConverter : JsonConverter { public static readonly RazorDiagnosticJsonConverter Instance = new RazorDiagnosticJsonConverter(); private const string RazorDiagnosticMessageKey = "Message"; diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorLanguageServiceException.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorLanguageServiceException.cs deleted file mode 100644 index 80c5d88a61..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorLanguageServiceException.cs +++ /dev/null @@ -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) - { - } - } -} diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/LegacyTextBufferCodeDocumentProviderTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/LegacyTextBufferCodeDocumentProviderTest.cs deleted file mode 100644 index 4127851928..0000000000 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/LegacyTextBufferCodeDocumentProviderTest.cs +++ /dev/null @@ -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(); - 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(); - 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(); - 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(); - 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); - } - } -} diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/DefaultTagHelperCompletionServiceTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/DefaultTagHelperCompletionServiceTest.cs deleted file mode 100644 index 5d6ad363bc..0000000000 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/DefaultTagHelperCompletionServiceTest.cs +++ /dev/null @@ -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>() - { - ["onclick"] = new HashSet(), - ["visible"] = new HashSet() - { - documentDescriptors[0].BoundAttributes.Last() - } - }); - - var existingCompletions = new[] { "onclick" }; - var completionContext = BuildAttributeCompletionContext( - documentDescriptors, - existingCompletions, - attributes: new Dictionary() - { - ["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>() - { - ["class"] = new HashSet(), - ["onclick"] = new HashSet(), - ["repeat"] = new HashSet() - }); - - 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>() - { - ["class"] = new HashSet(documentDescriptors[1].BoundAttributes), - ["onclick"] = new HashSet(), - ["repeat"] = new HashSet() - { - 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>() - { - ["onclick"] = new HashSet(), - ["class"] = new HashSet(documentDescriptors[1].BoundAttributes), - ["repeat"] = new HashSet() - { - documentDescriptors[0].BoundAttributes.First() - }, - ["visible"] = new HashSet() - { - 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>() - { - ["class"] = new HashSet(), - ["repeat"] = new HashSet(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>() - { - ["repeat"] = new HashSet(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>() - { - ["class"] = new HashSet(), - ["repeat"] = new HashSet(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>() - { - ["class"] = new HashSet(), - }); - - var existingCompletions = new[] { "class" }; - var completionContext = BuildAttributeCompletionContext( - Enumerable.Empty(), - 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>() - { - ["class"] = new HashSet(), - }); - - 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>() - { - ["class"] = new HashSet(), - }); - - 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>() - { - ["my-table"] = new HashSet { documentDescriptors[0] }, - ["table"] = new HashSet(), - }); - - 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>() - { - ["th:li"] = new HashSet { documentDescriptors[1], documentDescriptors[0] }, - ["li"] = new HashSet(), - }); - - 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>() - { - ["th:superli"] = new HashSet { documentDescriptors[0] }, - ["th:li"] = new HashSet { documentDescriptors[1] }, - ["li"] = new HashSet(), - }); - - 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>() - { - ["superli"] = new HashSet() { documentDescriptors[0], documentDescriptors[1] }, - ["li"] = new HashSet(), - }); - - 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>() - { - ["th:superli"] = new HashSet() { documentDescriptors[0], documentDescriptors[1] }, - ["th:li"] = new HashSet() { 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>() - { - ["strong"] = new HashSet { documentDescriptors[0], documentDescriptors[1] }, - ["b"] = new HashSet { documentDescriptors[0] }, - ["bold"] = new HashSet { 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>() - { - ["li"] = new HashSet { 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>() - { - ["li"] = new HashSet { documentDescriptors[1] }, - ["superli"] = new HashSet { 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>() - { - ["div"] = new HashSet { documentDescriptors[0] }, - ["li"] = new HashSet { 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>() - { - ["li"] = new HashSet { 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>()); - - var existingCompletions = Enumerable.Empty(); - 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>() - { - ["b"] = new HashSet(), - ["bold"] = new HashSet(), - ["div"] = new HashSet { 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>() - { - ["b"] = new HashSet(), - ["bold"] = new HashSet(), - }); - - var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty(), 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>() - { - ["b"] = new HashSet(), - ["bold"] = new HashSet(), - ["div"] = new HashSet { documentDescriptors[0] } - }); - - var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty(), 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>() - { - ["strong"] = new HashSet { documentDescriptors[0] }, - ["b"] = new HashSet { documentDescriptors[0] }, - ["bold"] = new HashSet { documentDescriptors[0] }, - ["div"] = new HashSet { documentDescriptors[0], documentDescriptors[1] }, - }); - - var completionContext = BuildElementCompletionContext(documentDescriptors, Enumerable.Empty(), 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 descriptors, - IEnumerable existingCompletions, - string containingTagName, - string containingParentTagName = "body", - string tagHelperPrefix = "") - { - var documentContext = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors); - var completionContext = new ElementCompletionContext( - documentContext, - existingCompletions, - containingTagName, - attributes: Enumerable.Empty>(), - containingParentTagName: containingParentTagName, - inHTMLSchema: (tag) => tag == "strong" || tag == "b" || tag == "bold" || tag == "li" || tag == "div"); - - return completionContext; - } - - private static AttributeCompletionContext BuildAttributeCompletionContext( - IEnumerable descriptors, - IEnumerable existingCompletions, - string currentTagName, - IEnumerable> attributes = null, - string tagHelperPrefix = "") - { - attributes = attributes ?? Enumerable.Empty>(); - 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; - } - } -} \ No newline at end of file diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/DefaultTagHelperFactsServiceTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/DefaultTagHelperFactsServiceTest.cs deleted file mode 100644 index f62597b32f..0000000000 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/DefaultTagHelperFactsServiceTest.cs +++ /dev/null @@ -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>(), 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).Namespace + "IDictionary") - .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("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).Namespace + "IDictionary") - .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>(), 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>(), 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); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/RazorEditorParserTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/RazorEditorParserTest.cs deleted file mode 100644 index 0f6e3e32e9..0000000000 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Legacy/RazorEditorParserTest.cs +++ /dev/null @@ -1,1345 +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.Threading; -using Microsoft.AspNetCore.Mvc.Razor.Extensions; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.Language.Legacy; -using Microsoft.VisualStudio.Text; -using Xunit; - -namespace Microsoft.VisualStudio.LanguageServices.Razor -{ - public class RazorEditorParserTest - { - private static readonly TestFile SimpleCSHTMLDocument = TestFile.Create("TestFiles/DesignTime/Simple.cshtml", typeof(RazorEditorParserTest)); - private static readonly TestFile SimpleCSHTMLDocumentGenerated = TestFile.Create("TestFiles/DesignTime/Simple.txt", typeof(RazorEditorParserTest)); - private const string TestLinePragmaFileName = "C:\\This\\Path\\Is\\Just\\For\\Line\\Pragmas.cshtml"; - - public static TheoryData TagHelperPartialParseRejectData - { - get - { - return new TheoryData - { - CreateInsertionChange("

", 2, " "), - CreateInsertionChange("

", 6, " "), - CreateInsertionChange("

", 12, " "), - CreateInsertionChange("

", 12, "ibute"), - CreateInsertionChange("

", 2, " before"), - }; - } - } - - [Theory] - [MemberData(nameof(TagHelperPartialParseRejectData))] - public void TagHelperTagBodiesRejectPartialChanges(object editObject) - { - // Arrange - var edit = (TestEdit)editObject; - var builder = TagHelperDescriptorBuilder.Create("PTagHelper", "TestAssembly"); - builder.SetTypeName("PTagHelper"); - builder.TagMatchingRule(rule => rule.TagName = "p"); - var descriptors = new[] - { - builder.Build() - }; - - var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path"), @"C:\This\Is\A\Test\Path"); - - using (var manager = new TestParserManager(parser)) - { - manager.InitializeWithDocument(edit.OldSnapshot); - - // Act - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(PartialParseResult.Rejected, result); - Assert.Equal(2, manager.ParseCount); - } - } - - public static TheoryData TagHelperAttributeAcceptData - { - get - { - var factory = new SpanFactory(); - - // change, (Block)expectedDocument, partialParseResult - return new TheoryData - { - { - CreateInsertionChange("

", 22, "."), - PartialParseResult.Accepted | PartialParseResult.Provisional - }, - { - CreateInsertionChange("

", 21, "."), - PartialParseResult.Accepted - }, - { - CreateInsertionChange("

", 25, "."), - PartialParseResult.Accepted - }, - { - CreateInsertionChange("

", 34, "."), - PartialParseResult.Accepted | PartialParseResult.Provisional - }, - { - CreateInsertionChange("

", 29, "."), - PartialParseResult.Accepted | PartialParseResult.Provisional - }, - }; - } - } - - [Theory] - [MemberData(nameof(TagHelperAttributeAcceptData))] - public void TagHelperAttributesAreLocatedAndAcceptChangesCorrectly(object editObject, PartialParseResult partialParseResult) - { - // Arrange - var edit = (TestEdit)editObject; - var builder = TagHelperDescriptorBuilder.Create("PTagHelper", "Test"); - builder.SetTypeName("PTagHelper"); - builder.TagMatchingRule(rule => rule.TagName = "p"); - builder.BindAttribute(attribute => - { - attribute.Name = "obj-attr"; - attribute.TypeName = typeof(object).FullName; - attribute.SetPropertyName("ObjectAttribute"); - }); - builder.BindAttribute(attribute => - { - attribute.Name = "str-attr"; - attribute.TypeName = typeof(string).FullName; - attribute.SetPropertyName("StringAttribute"); - }); - var descriptors = new[] { builder.Build() }; - - var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path", descriptors), @"C:\This\Is\A\Test\Path"); - - using (var manager = new TestParserManager(parser)) - { - manager.InitializeWithDocument(edit.OldSnapshot); - - // Act - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(partialParseResult, result); - Assert.Equal(1, manager.ParseCount); - } - } - - [Fact] - public void ConstructorRequiresNonNullPhysicalPath() - { - Assert.Throws("filePath", () => new RazorEditorParser(CreateTemplateEngine(), null)); - } - - [Fact] - public void ConstructorRequiresNonEmptyPhysicalPath() - { - Assert.Throws("filePath", () => new RazorEditorParser(CreateTemplateEngine(), string.Empty)); - } - - [Theory] - [InlineData(" ")] - [InlineData("\r\n")] - [InlineData("abcdefg")] - [InlineData("\f\r\n abcd \t")] - public void TreesAreDifferentReturnsFalseForAddedContent(string content) - { - // Arrange - var factory = new SpanFactory(); - var blockFactory = new BlockFactory(factory); - var original = new MarkupBlock( - blockFactory.MarkupTagBlock("

"), - blockFactory.TagHelperBlock( - tagName: "div", - tagMode: TagMode.StartTagAndEndTag, - start: new SourceLocation(3, 0, 3), - startTag: blockFactory.MarkupTagBlock("

"), - children: new SyntaxTreeNode[] - { - factory.Markup($"{Environment.NewLine}{Environment.NewLine}") - }, - endTag: blockFactory.MarkupTagBlock("
")), - blockFactory.MarkupTagBlock("

")); - - factory.Reset(); - - var modified = new MarkupBlock( - blockFactory.MarkupTagBlock("

"), - blockFactory.TagHelperBlock( - tagName: "div", - tagMode: TagMode.StartTagAndEndTag, - start: new SourceLocation(3, 0, 3), - startTag: blockFactory.MarkupTagBlock("

"), - children: new SyntaxTreeNode[] - { - factory.Markup($"{Environment.NewLine}{content}{Environment.NewLine}") - }, - endTag: blockFactory.MarkupTagBlock("
")), - blockFactory.MarkupTagBlock("

")); - original.LinkNodes(); - modified.LinkNodes(); - - // Act - var treesAreDifferent = RazorEditorParser.BackgroundParser.TreesAreDifferent( - original, - modified, - new[] - { - new SourceChange( - absoluteIndex: 8 + Environment.NewLine.Length, - length: 0, - newText: content) - }, - CancellationToken.None); - - // Assert - Assert.False(treesAreDifferent); - } - - [Fact] - public void TreesAreDifferentReturnsTrueIfTreeStructureIsDifferent() - { - var factory = new SpanFactory(); - var original = new MarkupBlock( - factory.Markup("

"), - new ExpressionBlock( - factory.CodeTransition()), - factory.Markup("

")); - var modified = new MarkupBlock( - factory.Markup("

"), - new ExpressionBlock( - factory.CodeTransition("@"), - factory.Code("f") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)), - factory.Markup("

")); - Assert.True(RazorEditorParser.BackgroundParser.TreesAreDifferent( - original, - modified, - new[] - { - new SourceChange(absoluteIndex: 4, length: 0, newText: "f") - }, - CancellationToken.None)); - } - - [Fact] - public void TreesAreDifferentReturnsFalseIfTreeStructureIsSame() - { - var factory = new SpanFactory(); - var original = new MarkupBlock( - factory.Markup("

"), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("f") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)), - factory.Markup("

")); - factory.Reset(); - var modified = new MarkupBlock( - factory.Markup("

"), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("foo") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)), - factory.Markup("

")); - original.LinkNodes(); - modified.LinkNodes(); - Assert.False(RazorEditorParser.BackgroundParser.TreesAreDifferent( - original, - modified, - new[] - { - new SourceChange(absoluteIndex: 5, length: 0, newText: "oo") - }, - CancellationToken.None)); - } - - [Fact] - public void CheckForStructureChangesStartsFullReparseIfChangeOverlapsMultipleSpans() - { - // Arrange - using (var parser = new RazorEditorParser(CreateTemplateEngine(), TestLinePragmaFileName)) - { - var original = new StringTextSnapshot("Foo @bar Baz"); - var changed = new StringTextSnapshot("Foo @bap Daz"); - var change = new SourceChange(7, 3, "p D"); - - var parseComplete = new ManualResetEventSlim(); - var parseCount = 0; - parser.DocumentParseComplete += (sender, args) => - { - Interlocked.Increment(ref parseCount); - parseComplete.Set(); - }; - - Assert.Equal(PartialParseResult.Rejected, parser.CheckForStructureChanges(change, original)); - DoWithTimeoutIfNotDebugging(parseComplete.Wait); // Wait for the parse to finish - parseComplete.Reset(); - - // Act - var result = parser.CheckForStructureChanges(change, original); - - // Assert - Assert.Equal(PartialParseResult.Rejected, result); - DoWithTimeoutIfNotDebugging(parseComplete.Wait); - Assert.Equal(2, parseCount); - } - } - - [Fact] - public void AwaitPeriodInsertionAcceptedProvisionally() - { - // Arrange - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @await Html. baz"); - var old = new StringTextSnapshot("foo @await Html baz"); - - // Act and Assert - RunPartialParseTest(new TestEdit(15, 0, old, 1, changed, "."), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("await Html.").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.WhiteSpace | AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz")), additionalFlags: PartialParseResult.Provisional); - } - - [Fact] - public void ImplicitExpressionAcceptsInnerInsertionsInStatementBlock() - { - // Arrange - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateTime..Now" + Environment.NewLine - + "}"); - var old = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateTime.Now" + Environment.NewLine - + "}"); - - // Act and Assert - RunPartialParseTest(new TestEdit(17, 0, old, 1, changed, "."), - new MarkupBlock( - factory.EmptyHtml(), - new StatementBlock( - factory.CodeTransition(), - factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None), - factory.Code(Environment.NewLine + " ") - .AsStatement() - .AutoCompleteWith(autoCompleteString: null), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime..Now") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Code(Environment.NewLine).AsStatement(), - factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), - factory.EmptyHtml())); - } - - [Fact] - public void ImplicitExpressionAcceptsInnerInsertions() - { - // Arrange - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @DateTime..Now baz"); - var old = new StringTextSnapshot("foo @DateTime.Now baz"); - - // Act and Assert - RunPartialParseTest(new TestEdit(13, 0, old, 1, changed, "."), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime..Now").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz")), additionalFlags: PartialParseResult.Provisional); - } - - [Fact] - public void ImplicitExpressionAcceptsWholeIdentifierReplacement() - { - // Arrange - var factory = new SpanFactory(); - var old = new StringTextSnapshot("foo @date baz"); - var changed = new StringTextSnapshot("foo @DateTime baz"); - - // Act and Assert - RunPartialParseTest(new TestEdit(5, 4, old, 8, changed, "DateTime"), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - } - - [Fact] - public void ImplicitExpressionRejectsWholeIdentifierReplacementToKeyword() - { - // Arrange - var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path"), @"C:\This\Is\A\Test\Path"); - - using (var manager = new TestParserManager(parser)) - { - var old = new StringTextSnapshot("foo @date baz"); - var changed = new StringTextSnapshot("foo @if baz"); - var edit = new TestEdit(5, 4, old, 2, changed, "if"); - manager.InitializeWithDocument(old); - - // Act - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(PartialParseResult.Rejected, result); - Assert.Equal(2, manager.ParseCount); - } - } - - [Fact] - public void ImplicitExpressionRejectsWholeIdentifierReplacementToDirective() - { - // Arrange - var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path"), @"C:\This\Is\A\Test\Path"); - - using (var manager = new TestParserManager(parser)) - { - var old = new StringTextSnapshot("foo @date baz"); - var changed = new StringTextSnapshot("foo @inherits baz"); - var SourceChange = new TestEdit(5, 4, old, 8, changed, "inherits"); - manager.InitializeWithDocument(old); - - // Act - var result = manager.CheckForStructureChangesAndWait(SourceChange); - - // Assert - Assert.Equal(PartialParseResult.Rejected | PartialParseResult.SpanContextChanged, result); - Assert.Equal(2, manager.ParseCount); - } - } - - [Fact] - public void ImplicitExpressionAcceptsPrefixIdentifierReplacements_SingleSymbol() - { - // Arrange - var factory = new SpanFactory(); - var old = new StringTextSnapshot("foo @dTime baz"); - var changed = new StringTextSnapshot("foo @DateTime baz"); - - // Act and Assert - RunPartialParseTest(new TestEdit(5, 1, old, 4, changed, "Date"), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - } - - [Fact] - public void ImplicitExpressionAcceptsPrefixIdentifierReplacements_MultipleSymbols() - { - // Arrange - var factory = new SpanFactory(); - var old = new StringTextSnapshot("foo @dTime.Now baz"); - var changed = new StringTextSnapshot("foo @DateTime.Now baz"); - - // Act and Assert - RunPartialParseTest(new TestEdit(5, 1, old, 4, changed, "Date"), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - } - - [Fact] - public void ImplicitExpressionAcceptsSuffixIdentifierReplacements_SingleSymbol() - { - // Arrange - var factory = new SpanFactory(); - var old = new StringTextSnapshot("foo @Datet baz"); - var changed = new StringTextSnapshot("foo @DateTime baz"); - - // Act and Assert - RunPartialParseTest(new TestEdit(9, 1, old, 4, changed, "Time"), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - } - - [Fact] - public void ImplicitExpressionAcceptsSuffixIdentifierReplacements_MultipleSymbols() - { - // Arrange - var factory = new SpanFactory(); - var old = new StringTextSnapshot("foo @DateTime.n baz"); - var changed = new StringTextSnapshot("foo @DateTime.Now baz"); - - // Act and Assert - RunPartialParseTest(new TestEdit(14, 1, old, 3, changed, "Now"), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - } - - [Fact] - public void ImplicitExpressionAcceptsSurroundedIdentifierReplacements() - { - // Arrange - var factory = new SpanFactory(); - var old = new StringTextSnapshot("foo @DateTime.n.ToString() baz"); - var changed = new StringTextSnapshot("foo @DateTime.Now.ToString() baz"); - - // Act and Assert - RunPartialParseTest(new TestEdit(14, 1, old, 3, changed, "Now"), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("DateTime.Now.ToString()").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - } - - [Fact] - public void ImplicitExpressionAcceptsDotlessCommitInsertionsInStatementBlockAfterIdentifiers() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateTime." + Environment.NewLine - + "}"); - var old = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateTime" + Environment.NewLine - + "}"); - - var edit = new TestEdit(15 + Environment.NewLine.Length, 0, old, 1, changed, "."); - using (var manager = CreateParserManager()) - { - Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => - { - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(expectedResult, result); - Assert.Equal(1, manager.ParseCount); - ParserTestBase.EvaluateParseTree(manager.Parser.CurrentSyntaxTree.Root, new MarkupBlock( - factory.EmptyHtml(), - new StatementBlock( - factory.CodeTransition(), - factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None), - factory.Code(Environment.NewLine + " ") - .AsStatement() - .AutoCompleteWith(autoCompleteString: null), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code(expectedCode) - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Code(Environment.NewLine).AsStatement(), - factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), - factory.EmptyHtml())); - }; - - manager.InitializeWithDocument(edit.OldSnapshot); - - // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted, "DateTime."); - - old = changed; - changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateTime.." + Environment.NewLine - + "}"); - edit = new TestEdit(16 + Environment.NewLine.Length, 0, old, 1, changed, "."); - - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted, "DateTime.."); - - old = changed; - changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateTime.Now." + Environment.NewLine - + "}"); - edit = new TestEdit(16 + Environment.NewLine.Length, 0, old, 3, changed, "Now"); - - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted, "DateTime.Now."); - } - } - - [Fact] - public void ImplicitExpressionAcceptsDotlessCommitInsertionsInStatementBlock() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateT." + Environment.NewLine - + "}"); - var old = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateT" + Environment.NewLine - + "}"); - - var edit = new TestEdit(12 + Environment.NewLine.Length, 0, old, 1, changed, "."); - using (var manager = CreateParserManager()) - { - Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => - { - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(expectedResult, result); - Assert.Equal(1, manager.ParseCount); - ParserTestBase.EvaluateParseTree(manager.Parser.CurrentSyntaxTree.Root, new MarkupBlock( - factory.EmptyHtml(), - new StatementBlock( - factory.CodeTransition(), - factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None), - factory.Code(Environment.NewLine + " ") - .AsStatement() - .AutoCompleteWith(autoCompleteString: null), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code(expectedCode) - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Code(Environment.NewLine).AsStatement(), - factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), - factory.EmptyHtml())); - }; - - manager.InitializeWithDocument(edit.OldSnapshot); - - // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted, "DateT."); - - old = changed; - changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @DateTime." + Environment.NewLine - + "}"); - edit = new TestEdit(12 + Environment.NewLine.Length, 0, old, 3, changed, "ime"); - - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted, "DateTime."); - } - } - - [Fact] - public void ImplicitExpressionProvisionallyAcceptsDotlessCommitInsertions() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @DateT. baz"); - var old = new StringTextSnapshot("foo @DateT baz"); - var edit = new TestEdit(10, 0, old, 1, changed, "."); - using (var manager = CreateParserManager()) - { - Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => - { - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(expectedResult, result); - Assert.Equal(1, manager.ParseCount); - - ParserTestBase.EvaluateParseTree(manager.Parser.CurrentSyntaxTree.Root, new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code(expectedCode).AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - }; - - manager.InitializeWithDocument(edit.OldSnapshot); - - // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateT."); - - old = changed; - changed = new StringTextSnapshot("foo @DateTime. baz"); - edit = new TestEdit(10, 0, old, 3, changed, "ime"); - - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime."); - } - } - - [Fact] - public void ImplicitExpressionProvisionallyAcceptsDotlessCommitInsertionsAfterIdentifiers() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @DateTime. baz"); - var old = new StringTextSnapshot("foo @DateTime baz"); - var edit = new TestEdit(13, 0, old, 1, changed, "."); - using (var manager = CreateParserManager()) - { - Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => - { - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(expectedResult, result); - Assert.Equal(1, manager.ParseCount); - - ParserTestBase.EvaluateParseTree(manager.Parser.CurrentSyntaxTree.Root, new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code(expectedCode).AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - }; - - manager.InitializeWithDocument(edit.OldSnapshot); - - // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime."); - - old = changed; - changed = new StringTextSnapshot("foo @DateTime.. baz"); - edit = new TestEdit(14, 0, old, 1, changed, "."); - - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime.."); - - old = changed; - changed = new StringTextSnapshot("foo @DateTime.Now. baz"); - edit = new TestEdit(14, 0, old, 3, changed, "Now"); - - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime.Now."); - } - } - - [Fact] - public void ImplicitExpressionProvisionallyAcceptsCaseInsensitiveDotlessCommitInsertions_NewRoslynIntegration() - { - var factory = new SpanFactory(); - var old = new StringTextSnapshot("foo @date baz"); - var changed = new StringTextSnapshot("foo @date. baz"); - var edit = new TestEdit(9, 0, old, 1, changed, "."); - using (var manager = CreateParserManager()) - { - Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => - { - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(expectedResult, result); - Assert.Equal(1, manager.ParseCount); - - ParserTestBase.EvaluateParseTree(manager.Parser.CurrentSyntaxTree.Root, new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code(expectedCode).AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - }; - - manager.InitializeWithDocument(edit.OldSnapshot); - - // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. - - // @date => @date. - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted | PartialParseResult.Provisional, "date."); - - old = changed; - changed = new StringTextSnapshot("foo @date baz"); - edit = new TestEdit(9, 1, old, 0, changed, ""); - - // @date. => @date - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted, "date"); - - old = changed; - changed = new StringTextSnapshot("foo @DateTime baz"); - edit = new TestEdit(5, 4, old, 8, changed, "DateTime"); - - // @date => @DateTime - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted, "DateTime"); - - old = changed; - changed = new StringTextSnapshot("foo @DateTime. baz"); - edit = new TestEdit(13, 0, old, 1, changed, "."); - - // @DateTime => @DateTime. - applyAndVerifyPartialChange(edit, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime."); - } - } - - [Fact] - public void ImplicitExpressionProvisionallyAcceptsDeleteOfIdentifierPartsIfDotRemains() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @User. baz"); - var old = new StringTextSnapshot("foo @User.Name baz"); - RunPartialParseTest(new TestEdit(10, 4, old, 0, changed, string.Empty), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("User.").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz")), - additionalFlags: PartialParseResult.Provisional); - } - - [Fact] - public void ImplicitExpressionAcceptsDeleteOfIdentifierPartsIfSomeOfIdentifierRemains() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @Us baz"); - var old = new StringTextSnapshot("foo @User baz"); - RunPartialParseTest(new TestEdit(7, 2, old, 0, changed, string.Empty), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("Us").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - } - - [Fact] - public void ImplicitExpressionProvisionallyAcceptsMultipleInsertionIfItCausesIdentifierExpansionAndTrailingDot() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @User. baz"); - var old = new StringTextSnapshot("foo @U baz"); - RunPartialParseTest(new TestEdit(6, 0, old, 4, changed, "ser."), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("User.").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz")), - additionalFlags: PartialParseResult.Provisional); - } - - [Fact] - public void ImplicitExpressionAcceptsMultipleInsertionIfItOnlyCausesIdentifierExpansion() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @barbiz baz"); - var old = new StringTextSnapshot("foo @bar baz"); - RunPartialParseTest(new TestEdit(8, 0, old, 3, changed, "biz"), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("barbiz").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" baz"))); - } - - [Fact] - public void ImplicitExpressionAcceptsIdentifierExpansionAtEndOfNonWhitespaceCharacters() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @food" + Environment.NewLine - + "}"); - var old = new StringTextSnapshot("@{" + Environment.NewLine - + " @foo" + Environment.NewLine - + "}"); - RunPartialParseTest(new TestEdit(10 + Environment.NewLine.Length, 0, old, 1, changed, "d"), - new MarkupBlock( - factory.EmptyHtml(), - new StatementBlock( - factory.CodeTransition(), - factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None), - factory.Code(Environment.NewLine + " ") - .AsStatement() - .AutoCompleteWith(autoCompleteString: null), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("food") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Code(Environment.NewLine).AsStatement(), - factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), - factory.EmptyHtml())); - } - - [Fact] - public void ImplicitExpressionAcceptsIdentifierAfterDotAtEndOfNonWhitespaceCharacters() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @foo.d" + Environment.NewLine - + "}"); - var old = new StringTextSnapshot("@{" + Environment.NewLine - + " @foo." + Environment.NewLine - + "}"); - RunPartialParseTest(new TestEdit(11 + Environment.NewLine.Length, 0, old, 1, changed, "d"), - new MarkupBlock( - factory.EmptyHtml(), - new StatementBlock( - factory.CodeTransition(), - factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None), - factory.Code(Environment.NewLine + " ") - .AsStatement() - .AutoCompleteWith(autoCompleteString: null), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("foo.d") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Code(Environment.NewLine).AsStatement(), - factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), - factory.EmptyHtml())); - } - - [Fact] - public void ImplicitExpressionAcceptsDotAtEndOfNonWhitespaceCharacters() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("@{" + Environment.NewLine - + " @foo." + Environment.NewLine - + "}"); - var old = new StringTextSnapshot("@{" + Environment.NewLine - + " @foo" + Environment.NewLine - + "}"); - RunPartialParseTest(new TestEdit(10 + Environment.NewLine.Length, 0, old, 1, changed, "."), - new MarkupBlock( - factory.EmptyHtml(), - new StatementBlock( - factory.CodeTransition(), - factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None), - factory.Code(Environment.NewLine + " ") - .AsStatement() - .AutoCompleteWith(autoCompleteString: null), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code(@"foo.") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Code(Environment.NewLine).AsStatement(), - factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), - factory.EmptyHtml())); - } - - [Fact] - public void ImplicitExpressionRejectsChangeWhichWouldHaveBeenAcceptedIfLastChangeWasProvisionallyAcceptedOnDifferentSpan() - { - var factory = new SpanFactory(); - - // Arrange - var dotTyped = new TestEdit(8, 0, new StringTextSnapshot("foo @foo @bar"), 1, new StringTextSnapshot("foo @foo. @bar"), "."); - var charTyped = new TestEdit(14, 0, new StringTextSnapshot("foo @foo. @bar"), 1, new StringTextSnapshot("foo @foo. @barb"), "b"); - using (var manager = CreateParserManager()) - { - manager.InitializeWithDocument(dotTyped.OldSnapshot); - - // Apply the dot change - Assert.Equal(PartialParseResult.Provisional | PartialParseResult.Accepted, manager.CheckForStructureChangesAndWait(dotTyped)); - - // Act (apply the identifier start char change) - var result = manager.CheckForStructureChangesAndWait(charTyped); - - // Assert - Assert.Equal(PartialParseResult.Rejected, result); - Assert.False(manager.Parser.LastResultProvisional, "LastResultProvisional flag should have been cleared but it was not"); - ParserTestBase.EvaluateParseTree(manager.Parser.CurrentSyntaxTree.Root, - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("foo") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(". "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("barb") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.EmptyHtml())); - } - } - - [Fact] - public void ImplicitExpressionAcceptsIdentifierTypedAfterDotIfLastChangeWasProvisionalAcceptanceOfDot() - { - var factory = new SpanFactory(); - - // Arrange - var dotTyped = new TestEdit(8, 0, new StringTextSnapshot("foo @foo bar"), 1, new StringTextSnapshot("foo @foo. bar"), "."); - var charTyped = new TestEdit(9, 0, new StringTextSnapshot("foo @foo. bar"), 1, new StringTextSnapshot("foo @foo.b bar"), "b"); - using (var manager = CreateParserManager()) - { - manager.InitializeWithDocument(dotTyped.OldSnapshot); - - // Apply the dot change - Assert.Equal(PartialParseResult.Provisional | PartialParseResult.Accepted, manager.CheckForStructureChangesAndWait(dotTyped)); - - // Act (apply the identifier start char change) - var result = manager.CheckForStructureChangesAndWait(charTyped); - - // Assert - Assert.Equal(PartialParseResult.Accepted, result); - Assert.False(manager.Parser.LastResultProvisional, "LastResultProvisional flag should have been cleared but it was not"); - ParserTestBase.EvaluateParseTree(manager.Parser.CurrentSyntaxTree.Root, - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("foo.b") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" bar"))); - } - } - - [Fact] - public void ImplicitExpressionProvisionallyAcceptsDotAfterIdentifierInMarkup() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @foo. bar"); - var old = new StringTextSnapshot("foo @foo bar"); - RunPartialParseTest(new TestEdit(8, 0, old, 1, changed, "."), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("foo.") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" bar")), - additionalFlags: PartialParseResult.Provisional); - } - - [Fact] - public void ImplicitExpressionAcceptsAdditionalIdentifierCharactersIfEndOfSpanIsIdentifier() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("foo @foob bar"); - var old = new StringTextSnapshot("foo @foo bar"); - RunPartialParseTest(new TestEdit(8, 0, old, 1, changed, "b"), - new MarkupBlock( - factory.Markup("foo "), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("foob") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" bar"))); - } - - [Fact] - public void ImplicitExpressionAcceptsAdditionalIdentifierStartCharactersIfEndOfSpanIsDot() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("@{@foo.b}"); - var old = new StringTextSnapshot("@{@foo.}"); - RunPartialParseTest(new TestEdit(7, 0, old, 1, changed, "b"), - new MarkupBlock( - factory.EmptyHtml(), - new StatementBlock( - factory.CodeTransition(), - factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None), - factory.EmptyCSharp() - .AsStatement() - .AutoCompleteWith(autoCompleteString: null), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("foo.b") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.EmptyCSharp().AsStatement(), - factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), - factory.EmptyHtml())); - } - - [Fact] - public void ImplicitExpressionAcceptsDotIfTrailingDotsAreAllowed() - { - var factory = new SpanFactory(); - var changed = new StringTextSnapshot("@{@foo.}"); - var old = new StringTextSnapshot("@{@foo}"); - RunPartialParseTest(new TestEdit(6, 0, old, 1, changed, "."), - new MarkupBlock( - factory.EmptyHtml(), - new StatementBlock( - factory.CodeTransition(), - factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None), - factory.EmptyCSharp() - .AsStatement() - .AutoCompleteWith(autoCompleteString: null), - new ExpressionBlock( - factory.CodeTransition(), - factory.Code("foo.") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.EmptyCSharp().AsStatement(), - factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), - factory.EmptyHtml())); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfIfKeywordTyped() - { - RunTypeKeywordTest("if"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfDoKeywordTyped() - { - RunTypeKeywordTest("do"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfTryKeywordTyped() - { - RunTypeKeywordTest("try"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfForKeywordTyped() - { - RunTypeKeywordTest("for"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfForEachKeywordTyped() - { - RunTypeKeywordTest("foreach"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfWhileKeywordTyped() - { - RunTypeKeywordTest("while"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfSwitchKeywordTyped() - { - RunTypeKeywordTest("switch"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfLockKeywordTyped() - { - RunTypeKeywordTest("lock"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfUsingKeywordTyped() - { - RunTypeKeywordTest("using"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfSectionKeywordTyped() - { - RunTypeKeywordTest("section"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfInheritsKeywordTyped() - { - RunTypeKeywordTest("inherits"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfFunctionsKeywordTyped() - { - RunTypeKeywordTest("functions"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfNamespaceKeywordTyped() - { - RunTypeKeywordTest("namespace"); - } - - [Fact] - public void ImplicitExpressionCorrectlyTriggersReparseIfClassKeywordTyped() - { - RunTypeKeywordTest("class"); - } - - private static TestEdit CreateInsertionChange(string initialText, int insertionLocation, string insertionText) - { - var changedText = initialText.Insert(insertionLocation, insertionText); - var sourceChange = new SourceChange(insertionLocation, 0, insertionText); - var oldSnapshot = new StringTextSnapshot(initialText); - var changedSnapshot = new StringTextSnapshot(changedText); - return new TestEdit - { - Change = sourceChange, - OldSnapshot = oldSnapshot, - NewSnapshot = changedSnapshot, - }; - } - - private static void RunFullReparseTest(TestEdit edit, PartialParseResult additionalFlags = (PartialParseResult)0) - { - // Arrange - using (var manager = CreateParserManager()) - { - manager.InitializeWithDocument(edit.OldSnapshot); - - // Act - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(PartialParseResult.Rejected | additionalFlags, result); - Assert.Equal(2, manager.ParseCount); - } - } - - private static void RunPartialParseTest(TestEdit edit, Block newTreeRoot, PartialParseResult additionalFlags = (PartialParseResult)0) - { - // Arrange - using (var manager = CreateParserManager()) - { - manager.InitializeWithDocument(edit.OldSnapshot); - - // Act - var result = manager.CheckForStructureChangesAndWait(edit); - - // Assert - Assert.Equal(PartialParseResult.Accepted | additionalFlags, result); - Assert.Equal(1, manager.ParseCount); - ParserTestBase.EvaluateParseTree(manager.Parser.CurrentSyntaxTree.Root, newTreeRoot); - } - } - - private static TestParserManager CreateParserManager() - { - var parser = new RazorEditorParser(CreateTemplateEngine(), TestLinePragmaFileName); - return new TestParserManager(parser); - } - - private static RazorTemplateEngine CreateTemplateEngine( - string path = TestLinePragmaFileName, - IEnumerable tagHelpers = null) - { - var engine = RazorEngine.CreateDesignTime(builder => - { - RazorExtensions.Register(builder); - - if (tagHelpers != null) - { - builder.AddTagHelpers(tagHelpers); - } - }); - - // GetImports on RazorTemplateEngine will at least check that the item exists, so we need to pretend - // that it does. - var items = new List(); - items.Add(new TestRazorProjectItem(path)); - - var project = new TestRazorProject(items); - - var templateEngine = new RazorTemplateEngine(engine, project); - templateEngine.Options.DefaultImports = RazorSourceDocument.Create("@addTagHelper *, Test", "_TestImports.cshtml"); - return templateEngine; - } - - private static void RunTypeKeywordTest(string keyword) - { - var before = "@" + keyword.Substring(0, keyword.Length - 1); - var after = "@" + keyword; - var changed = new StringTextSnapshot(after); - var old = new StringTextSnapshot(before); - var change = new SourceChange(keyword.Length, 0, keyword[keyword.Length - 1].ToString()); - var edit = new TestEdit - { - Change = change, - NewSnapshot = changed, - OldSnapshot = old - }; - RunFullReparseTest(edit, additionalFlags: PartialParseResult.SpanContextChanged); - } - - private static void DoWithTimeoutIfNotDebugging(Func withTimeout) - { -#if DEBUG - if (Debugger.IsAttached) - { - withTimeout(Timeout.Infinite); - } - else - { -#endif - Assert.True(withTimeout((int)TimeSpan.FromSeconds(1).TotalMilliseconds), "Timeout expired!"); -#if DEBUG - } -#endif - } - - private class TestParserManager : IDisposable - { - public int ParseCount; - - private readonly ManualResetEventSlim _parserComplete; - - public TestParserManager(RazorEditorParser parser) - { - _parserComplete = new ManualResetEventSlim(); - ParseCount = 0; - Parser = parser; - parser.DocumentParseComplete += (sender, args) => - { - Interlocked.Increment(ref ParseCount); - _parserComplete.Set(); - }; - } - - public RazorEditorParser Parser { get; } - - public void InitializeWithDocument(ITextSnapshot snapshot) - { - var initialChange = new SourceChange(0, 0, string.Empty); - var edit = new TestEdit - { - Change = initialChange, - OldSnapshot = snapshot, - NewSnapshot = snapshot - }; - CheckForStructureChangesAndWait(edit); - } - - public PartialParseResult CheckForStructureChangesAndWait(TestEdit edit) - { - var result = Parser.CheckForStructureChanges(edit.Change, edit.NewSnapshot); - if (result.HasFlag(PartialParseResult.Rejected)) - { - WaitForParse(); - } - return result; - } - - public void WaitForParse() - { - DoWithTimeoutIfNotDebugging(_parserComplete.Wait); // Wait for the parse to finish - _parserComplete.Reset(); - } - - public void Dispose() - { - Parser.Dispose(); - } - } - - private class TestEdit - { - public TestEdit() - { - } - - public TestEdit(int position, int oldLength, ITextSnapshot oldSnapshot, int newLength, ITextSnapshot newSnapshot, string newText) - { - Change = new SourceChange(position, oldLength, newText); - OldSnapshot = oldSnapshot; - NewSnapshot = newSnapshot; - } - - public SourceChange Change { get; set; } - - public ITextSnapshot OldSnapshot { get; set; } - - public ITextSnapshot NewSnapshot { get; set; } - } - } -} \ No newline at end of file From 520876e55d3bb86d90b3bf22e4c71db07283dd58 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Tue, 14 Nov 2017 16:49:35 -0800 Subject: [PATCH 08/11] Re-add some Legacy ITagHelperResolver infrastructure. - This is required until the TagHelperProjectSystem is completed. --- .../Legacy/ITagHelperResolver.cs | 18 ++++++++++++++ .../Legacy/LegacyTagHelperResolver.cs | 24 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ITagHelperResolver.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ITagHelperResolver.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ITagHelperResolver.cs new file mode 100644 index 0000000000..a0ae0eb27e --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/ITagHelperResolver.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor; + +namespace Microsoft.VisualStudio.LanguageServices.Razor +{ + // ---------------------------------------------------------------------------------------------------- + // NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead + // use the Microsoft.CodeAnalysis.Razor variant from Microsoft.CodeAnalysis.Razor.Workspaces + // ---------------------------------------------------------------------------------------------------- + public interface ITagHelperResolver + { + Task GetTagHelpersAsync(Project project); + } +} \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs new file mode 100644 index 0000000000..ed6cc73313 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs @@ -0,0 +1,24 @@ +// 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.CodeAnalysis.Razor; + +namespace Microsoft.VisualStudio.LanguageServices.Razor +{ + // ---------------------------------------------------------------------------------------------------- + // NOTE: This is only here for VisualStudio binary compatibility. This type should not be used; instead + // use TagHelperResolver. + // ---------------------------------------------------------------------------------------------------- + [Export(typeof(ITagHelperResolver))] + internal class LegacyTagHelperResolver : DefaultTagHelperResolver, ITagHelperResolver + { + [ImportingConstructor] + public LegacyTagHelperResolver( + [Import(typeof(VisualStudioWorkspace))] Workspace workspace) + : base(workspace.Services.GetRequiredService(), workspace) + { + } + } +} \ No newline at end of file From 458111ad03c8b46a8a1ebab472f3b23f845415c3 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Tue, 14 Nov 2017 11:57:37 -0800 Subject: [PATCH 09/11] Move TagHelperFactsService and export it. - Created a `TagHelperFactsServiceInternal` and export its public counterpart in `Microsoft.VisualStudio.Editor.Razor`. - Updated tests to react to new naming. #1762 --- .../DefaultTagHelperCompletionService.cs | 4 +- ...efaultTagHelperCompletionServiceFactory.cs | 2 +- ...> DefaultTagHelperFactsServiceInternal.cs} | 2 +- ...ltTagHelperFactsServiceInternalFactory.cs} | 6 +- .../TagHelperFactsServiceInternal.cs | 20 +++++++ .../DefaultTagHelperFactsService.cs | 56 +++++++++++++++++++ .../TagHelperFactsService.cs | 5 +- .../DefaultTagHelperCompletionServiceTest.cs | 4 +- ...faultTagHelperFactsServiceInternalTest.cs} | 28 +++++----- 9 files changed, 101 insertions(+), 26 deletions(-) rename src/Microsoft.CodeAnalysis.Razor.Workspaces/{DefaultTagHelperFactsService.cs => DefaultTagHelperFactsServiceInternal.cs} (98%) rename src/Microsoft.CodeAnalysis.Razor.Workspaces/{DefaultTagHelperFactsServiceFactory.cs => DefaultTagHelperFactsServiceInternalFactory.cs} (60%) create mode 100644 src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsServiceInternal.cs create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperFactsService.cs rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/TagHelperFactsService.cs (87%) rename test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/{DefaultTagHelperFactsServiceTest.cs => DefaultTagHelperFactsServiceInternalTest.cs} (94%) diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs index 92daeecb12..65a99fa85b 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs @@ -11,10 +11,10 @@ namespace Microsoft.CodeAnalysis.Razor { internal class DefaultTagHelperCompletionService : TagHelperCompletionService { - private readonly TagHelperFactsService _tagHelperFactsService; + private readonly TagHelperFactsServiceInternal _tagHelperFactsService; private static readonly HashSet _emptyHashSet = new HashSet(); - public DefaultTagHelperCompletionService(TagHelperFactsService tagHelperFactsService) + public DefaultTagHelperCompletionService(TagHelperFactsServiceInternal tagHelperFactsService) { _tagHelperFactsService = tagHelperFactsService; } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionServiceFactory.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionServiceFactory.cs index 3dd4ea4e4c..aaa4f7dc2b 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionServiceFactory.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionServiceFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Razor { public ILanguageService CreateLanguageService(HostLanguageServices languageServices) { - var tagHelperFactsService = languageServices.GetRequiredService(); + var tagHelperFactsService = languageServices.GetRequiredService(); return new DefaultTagHelperCompletionService(tagHelperFactsService); } } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsService.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternal.cs similarity index 98% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsService.cs rename to src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternal.cs index 3b0943b055..99cf4f6d12 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsService.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternal.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Razor.Language; namespace Microsoft.CodeAnalysis.Razor { - internal class DefaultTagHelperFactsService : TagHelperFactsService + internal class DefaultTagHelperFactsServiceInternal : TagHelperFactsServiceInternal { public override TagHelperBinding GetTagHelperBinding( TagHelperDocumentContext documentContext, diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceFactory.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternalFactory.cs similarity index 60% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceFactory.cs rename to src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternalFactory.cs index adfbeb2dc5..4e89fcdff1 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceFactory.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternalFactory.cs @@ -6,12 +6,12 @@ using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.Razor { - [ExportLanguageServiceFactory(typeof(TagHelperFactsService), RazorLanguage.Name, ServiceLayer.Default)] - internal class DefaultTagHelperFactsServiceFactory : ILanguageServiceFactory + [ExportLanguageServiceFactory(typeof(TagHelperFactsServiceInternal), RazorLanguage.Name, ServiceLayer.Default)] + internal class DefaultTagHelperFactsServiceInternalFactory : ILanguageServiceFactory { public ILanguageService CreateLanguageService(HostLanguageServices languageServices) { - return new DefaultTagHelperFactsService(); + return new DefaultTagHelperFactsServiceInternal(); } } } \ No newline at end of file diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsServiceInternal.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsServiceInternal.cs new file mode 100644 index 0000000000..78d26cfdbb --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsServiceInternal.cs @@ -0,0 +1,20 @@ +// 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.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.Razor +{ + internal abstract class TagHelperFactsServiceInternal : ILanguageService + { + public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable> attributes, string parentTag, bool parentIsTagHelper); + + public abstract IEnumerable GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding); + + public abstract IReadOnlyList GetTagHelpersGivenTag(TagHelperDocumentContext documentContext, string tagName, string parentTag); + + public abstract IReadOnlyList GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag); + } +} diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperFactsService.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperFactsService.cs new file mode 100644 index 0000000000..2769d59657 --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperFactsService.cs @@ -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(); + } + + public override TagHelperBinding GetTagHelperBinding( + TagHelperDocumentContext documentContext, + string tagName, + IEnumerable> attributes, + string parentTag, + bool parentIsTagHelper) + { + return _tagHelperFactsService.GetTagHelperBinding(documentContext, tagName, attributes, parentTag, parentIsTagHelper); + } + + public override IEnumerable GetBoundTagHelperAttributes( + TagHelperDocumentContext documentContext, + string attributeName, + TagHelperBinding binding) + { + return _tagHelperFactsService.GetBoundTagHelperAttributes(documentContext, attributeName, binding); + } + + public override IReadOnlyList GetTagHelpersGivenTag( + TagHelperDocumentContext documentContext, + string tagName, + string parentTag) + { + return _tagHelperFactsService.GetTagHelpersGivenTag(documentContext, tagName, parentTag); + } + + public override IReadOnlyList GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag) + { + return _tagHelperFactsService.GetTagHelpersGivenParent(documentContext, parentTag); + } + } +} diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsService.cs b/src/Microsoft.VisualStudio.Editor.Razor/TagHelperFactsService.cs similarity index 87% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsService.cs rename to src/Microsoft.VisualStudio.Editor.Razor/TagHelperFactsService.cs index 7022e2e36e..e7fd77279d 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/TagHelperFactsService.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis.Host; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { - public abstract class TagHelperFactsService : ILanguageService + public abstract class TagHelperFactsService { public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable> attributes, string parentTag, bool parentIsTagHelper); diff --git a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs index 9e4a114a7f..c37f51a79e 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs +++ b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs @@ -994,8 +994,8 @@ namespace Microsoft.CodeAnalysis.Razor private static DefaultTagHelperCompletionService CreateTagHelperCompletionFactsService() { - var tagHelperFactService = new DefaultTagHelperFactsService(); - var completionFactService = new DefaultTagHelperCompletionService(tagHelperFactService); + var tagHelperFactsService = new DefaultTagHelperFactsServiceInternal(); + var completionFactService = new DefaultTagHelperCompletionService(tagHelperFactsService); return completionFactService; } diff --git a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceTest.cs b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceInternalTest.cs similarity index 94% rename from test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceTest.cs rename to test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceInternalTest.cs index fe6222d517..5001398a36 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceTest.cs +++ b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceInternalTest.cs @@ -8,7 +8,7 @@ using Xunit; namespace Microsoft.CodeAnalysis.Razor { - public class DefaultTagHelperFactsServiceTest + public class DefaultTagHelperFactsServiceInternalTest { // Purposefully not thoroughly testing DefaultTagHelperFactsService.GetTagHelperBinding because it's a pass through // into TagHelperDescriptorProvider.GetTagHelperBinding. @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty>(), parentTag: null, parentIsTagHelper: false); @@ -66,7 +66,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build(), }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); var attributes = new[] { new KeyValuePair("asp-for", "Name") @@ -108,7 +108,7 @@ namespace Microsoft.CodeAnalysis.Razor documentDescriptors[0].BoundAttributes.Last() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty>(), parentTag: null, parentIsTagHelper: false); // Act @@ -143,7 +143,7 @@ namespace Microsoft.CodeAnalysis.Razor documentDescriptors[0].BoundAttributes.First() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty>(), parentTag: null, parentIsTagHelper: false); // Act @@ -164,7 +164,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenTag(documentContext, "!strong", parentTag: null); @@ -184,7 +184,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "p"); @@ -217,7 +217,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenTag(documentContext, "a", "div"); @@ -244,7 +244,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create("th", documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenTag(documentContext, "thstrong", "div"); @@ -277,7 +277,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "div"); @@ -297,7 +297,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */); @@ -322,7 +322,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */); @@ -343,7 +343,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenParent(documentContext, "p"); @@ -376,7 +376,7 @@ namespace Microsoft.CodeAnalysis.Razor .Build() }; var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); - var service = new DefaultTagHelperFactsService(); + var service = new DefaultTagHelperFactsServiceInternal(); // Act var descriptors = service.GetTagHelpersGivenParent(documentContext, "div"); From 6cf78ceb1bbdfd0a8d573925f09fd540f5f146c1 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Tue, 14 Nov 2017 16:17:19 -0800 Subject: [PATCH 10/11] Move TagHelperCompletionService to VS.Editor.Razor and export it. - Moved service poco types: `AttributeCompletionContext`, `AttributeCompletionResult`, `ElementCompletionContext` and `ElementCompletionResult`. - Exported DefaultTagHelperCompletionService. #1762 --- ...DefaultTagHelperCompletionServiceFactory.cs | 18 ------------------ .../AttributeCompletionContext.cs | 2 +- .../AttributeCompletionResult.cs | 2 +- .../DefaultTagHelperCompletionService.cs | 16 ++++++++++++++-- .../ElementCompletionContext.cs | 2 +- .../ElementCompletionResult.cs | 2 +- .../TagHelperCompletionService.cs | 6 ++---- .../DefaultTagHelperCompletionServiceTest.cs | 3 ++- 8 files changed, 22 insertions(+), 29 deletions(-) delete mode 100644 src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionServiceFactory.cs rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/AttributeCompletionContext.cs (98%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/AttributeCompletionResult.cs (97%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/DefaultTagHelperCompletionService.cs (94%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/ElementCompletionContext.cs (97%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/ElementCompletionResult.cs (96%) rename src/{Microsoft.CodeAnalysis.Razor.Workspaces => Microsoft.VisualStudio.Editor.Razor}/TagHelperCompletionService.cs (74%) rename test/{Microsoft.CodeAnalysis.Razor.Workspaces.Test => Microsoft.VisualStudio.Editor.Razor.Test}/DefaultTagHelperCompletionServiceTest.cs (99%) diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionServiceFactory.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionServiceFactory.cs deleted file mode 100644 index aaa4f7dc2b..0000000000 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionServiceFactory.cs +++ /dev/null @@ -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(); - return new DefaultTagHelperCompletionService(tagHelperFactsService); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionContext.cs b/src/Microsoft.VisualStudio.Editor.Razor/AttributeCompletionContext.cs similarity index 98% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionContext.cs rename to src/Microsoft.VisualStudio.Editor.Razor/AttributeCompletionContext.cs index bad51cc347..08b4ffe032 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionContext.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/AttributeCompletionContext.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public class AttributeCompletionContext { diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionResult.cs b/src/Microsoft.VisualStudio.Editor.Razor/AttributeCompletionResult.cs similarity index 97% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionResult.cs rename to src/Microsoft.VisualStudio.Editor.Razor/AttributeCompletionResult.cs index 1142b9ce6c..c6a11d67ed 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionResult.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/AttributeCompletionResult.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Razor.Language; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public abstract class AttributeCompletionResult { diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperCompletionService.cs similarity index 94% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs rename to src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperCompletionService.cs index 65a99fa85b..0bb688e1b3 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperCompletionService.cs @@ -3,18 +3,30 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; 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 { private readonly TagHelperFactsServiceInternal _tagHelperFactsService; private static readonly HashSet _emptyHashSet = new HashSet(); - public DefaultTagHelperCompletionService(TagHelperFactsServiceInternal tagHelperFactsService) + [ImportingConstructor] + public DefaultTagHelperCompletionService(VisualStudioWorkspaceAccessor workspaceAccessor) + { + var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name); + _tagHelperFactsService = razorLanguageServices.GetRequiredService(); + } + + // Internal for testing + internal DefaultTagHelperCompletionService(TagHelperFactsServiceInternal tagHelperFactsService) { _tagHelperFactsService = tagHelperFactsService; } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ElementCompletionContext.cs b/src/Microsoft.VisualStudio.Editor.Razor/ElementCompletionContext.cs similarity index 97% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/ElementCompletionContext.cs rename to src/Microsoft.VisualStudio.Editor.Razor/ElementCompletionContext.cs index 614d5ac7f9..6d2c3965f5 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ElementCompletionContext.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/ElementCompletionContext.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public sealed class ElementCompletionContext { diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ElementCompletionResult.cs b/src/Microsoft.VisualStudio.Editor.Razor/ElementCompletionResult.cs similarity index 96% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/ElementCompletionResult.cs rename to src/Microsoft.VisualStudio.Editor.Razor/ElementCompletionResult.cs index 9bd1121380..3f5a9ad2d7 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ElementCompletionResult.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/ElementCompletionResult.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Razor.Language; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public abstract class ElementCompletionResult { diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperCompletionService.cs b/src/Microsoft.VisualStudio.Editor.Razor/TagHelperCompletionService.cs similarity index 74% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperCompletionService.cs rename to src/Microsoft.VisualStudio.Editor.Razor/TagHelperCompletionService.cs index 0e75536f40..61eb751796 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperCompletionService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/TagHelperCompletionService.cs @@ -1,11 +1,9 @@ // 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; - -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { - public abstract class TagHelperCompletionService : ILanguageService + public abstract class TagHelperCompletionService { public abstract AttributeCompletionResult GetAttributeCompletions(AttributeCompletionContext completionContext); diff --git a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperCompletionServiceTest.cs similarity index 99% rename from test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs rename to test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperCompletionServiceTest.cs index c37f51a79e..1422e44815 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperCompletionServiceTest.cs @@ -4,9 +4,10 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Razor; using Xunit; -namespace Microsoft.CodeAnalysis.Razor +namespace Microsoft.VisualStudio.Editor.Razor { public class DefaultTagHelperCompletionServiceTest { From 9a2e3ceab64f04f933b3a4ab2886d7293f508eb3 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Wed, 15 Nov 2017 15:52:02 -0800 Subject: [PATCH 11/11] Added public api changes for TagHelper project system --- .../ProjectSystem/ProjectChangeKind.cs | 1 + .../ContextChangeEventArgs.cs | 17 +++++++++++++ .../ContextChangeKind.cs | 12 ++++++++++ .../DefaultVisualStudioRazorParser.cs | 3 +-- .../VisualStudioDocumentTracker.cs | 5 +++- .../DefaultVisualStudioDocumentTracker.cs | 24 +++++++++++++------ 6 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/ContextChangeEventArgs.cs create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/ContextChangeKind.cs diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectChangeKind.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectChangeKind.cs index 000ebd1953..c2ff3feacf 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectChangeKind.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectChangeKind.cs @@ -8,5 +8,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem Added, Removed, Changed, + TagHelpersChanged, } } diff --git a/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeEventArgs.cs b/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeEventArgs.cs new file mode 100644 index 0000000000..ad28406a15 --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeEventArgs.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeKind.cs b/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeKind.cs new file mode 100644 index 0000000000..867fa9857f --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeKind.cs @@ -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, + } +} \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs index 4962d978ee..99a7163ba8 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -133,7 +132,7 @@ namespace Microsoft.VisualStudio.Editor.Razor } // Internal for testing - internal void DocumentTracker_ContextChanged(object sender, EventArgs args) + internal void DocumentTracker_ContextChanged(object sender, ContextChangeEventArgs args) { _dispatcher.AssertForegroundThread(); diff --git a/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioDocumentTracker.cs b/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioDocumentTracker.cs index 9c9159fafb..efd1016bd1 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioDocumentTracker.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioDocumentTracker.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor.Editor; using Microsoft.CodeAnalysis.Razor.ProjectSystem; @@ -13,12 +14,14 @@ namespace Microsoft.VisualStudio.Editor.Razor { public abstract class VisualStudioDocumentTracker { - public abstract event EventHandler ContextChanged; + public abstract event EventHandler ContextChanged; internal abstract ProjectExtensibilityConfiguration Configuration { get; } public abstract EditorSettings EditorSettings { get; } + public abstract IReadOnlyList TagHelpers { get; } + public abstract bool IsSupportedProject { get; } public abstract string FilePath { get; } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs index e68c049d8b..1cc76169aa 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Editor; @@ -27,7 +28,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor private ProjectSnapshot _project; private string _projectPath; - public override event EventHandler ContextChanged; + public override event EventHandler ContextChanged; public DefaultVisualStudioDocumentTracker( string filePath, @@ -81,6 +82,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor public override EditorSettings EditorSettings => _editorSettingsManager.Current; + public override IReadOnlyList TagHelpers => Array.Empty(); + public override bool IsSupportedProject => _isSupportedProject; public override Project Project => _workspace.CurrentSolution.GetProject(_project.UnderlyingProject.Id); @@ -176,7 +179,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor _projectManager.Changed += ProjectManager_Changed; _editorSettingsManager.Changed += EditorSettingsManager_Changed; - OnContextChanged(_project); + OnContextChanged(_project, ContextChangeKind.ProjectChanged); } private void Unsubscribe() @@ -187,17 +190,17 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor // Detached from project. _isSupportedProject = false; _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; var handler = ContextChanged; 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 && 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 void EditorSettingsManager_Changed(object sender, EditorSettingsChangedEventArgs args) { - OnContextChanged(_project); + OnContextChanged(_project, ContextChangeKind.EditorSettingsChanged); } } }