diff --git a/src/Razor/Directory.Build.props b/src/Razor/Directory.Build.props
index 8260b6f326..220748df5f 100644
--- a/src/Razor/Directory.Build.props
+++ b/src/Razor/Directory.Build.props
@@ -14,9 +14,8 @@
$(MSBuildThisFileDirectory)
$(MSBuildThisFileDirectory)build\Key.snk
true
- true
true
- Microsoft
+ 7.3
diff --git a/src/Razor/Directory.Build.targets b/src/Razor/Directory.Build.targets
index 53b3f6e1da..78626b773e 100644
--- a/src/Razor/Directory.Build.targets
+++ b/src/Razor/Directory.Build.targets
@@ -1,7 +1,10 @@
-
+
$(MicrosoftNETCoreApp20PackageVersion)
$(MicrosoftNETCoreApp21PackageVersion)
+ $(MicrosoftNETCoreApp22PackageVersion)
$(NETStandardLibrary20PackageVersion)
+
+ 99.9
diff --git a/src/Razor/NuGetPackageVerifier.json b/src/Razor/NuGetPackageVerifier.json
index aeabac8d86..b62352e8b9 100644
--- a/src/Razor/NuGetPackageVerifier.json
+++ b/src/Razor/NuGetPackageVerifier.json
@@ -6,6 +6,7 @@
"packages": {
"Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources": {},
"RazorPageGenerator": {},
+ "Microsoft.CodeAnalysis.Razor.Workspaces": {},
"Microsoft.CodeAnalysis.Remote.Razor": {},
"Microsoft.VisualStudio.Editor.Razor": {},
"Microsoft.VisualStudio.LanguageServices.Razor": {},
diff --git a/src/Razor/Razor.sln b/src/Razor/Razor.sln
index 6ec8cff6c0..5bb3137be2 100644
--- a/src/Razor/Razor.sln
+++ b/src/Razor/Razor.sln
@@ -81,8 +81,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Design.Test", "test\Microsoft.AspNetCore.Razor.Design.Test\Microsoft.AspNetCore.Razor.Design.Test.csproj", "{1D90F276-E1CA-4FDF-A173-EB889E7D3150}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Tasks", "src\Microsoft.AspNetCore.Razor.Tasks\Microsoft.AspNetCore.Razor.Tasks.csproj", "{043B9497-C0BA-4770-9210-4456D2F81CE0}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test", "test\Microsoft.AspNetCore.Razor.Test\Microsoft.AspNetCore.Razor.Test.csproj", "{323553F0-14AB-4FBD-9CF0-1CC0BE8056F8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Performance", "benchmarks\Microsoft.AspNetCore.Razor.Performance\Microsoft.AspNetCore.Razor.Performance.csproj", "{6205467F-E381-4C42-AEEC-763BD62B3D5E}"
@@ -345,14 +343,6 @@ Global
{1D90F276-E1CA-4FDF-A173-EB889E7D3150}.Release|Any CPU.Build.0 = Release|Any CPU
{1D90F276-E1CA-4FDF-A173-EB889E7D3150}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{1D90F276-E1CA-4FDF-A173-EB889E7D3150}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
- {043B9497-C0BA-4770-9210-4456D2F81CE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {043B9497-C0BA-4770-9210-4456D2F81CE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {043B9497-C0BA-4770-9210-4456D2F81CE0}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
- {043B9497-C0BA-4770-9210-4456D2F81CE0}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
- {043B9497-C0BA-4770-9210-4456D2F81CE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {043B9497-C0BA-4770-9210-4456D2F81CE0}.Release|Any CPU.Build.0 = Release|Any CPU
- {043B9497-C0BA-4770-9210-4456D2F81CE0}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
- {043B9497-C0BA-4770-9210-4456D2F81CE0}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{323553F0-14AB-4FBD-9CF0-1CC0BE8056F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{323553F0-14AB-4FBD-9CF0-1CC0BE8056F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{323553F0-14AB-4FBD-9CF0-1CC0BE8056F8}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
@@ -391,8 +381,8 @@ Global
{7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}.Release|Any CPU.Build.0 = Release|Any CPU
- {7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}.ReleaseNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
- {7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}.ReleaseNoVSIX|Any CPU.Build.0 = Debug|Any CPU
+ {7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
+ {7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{6EA56B2B-89EC-4C38-A384-97D203375B06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6EA56B2B-89EC-4C38-A384-97D203375B06}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EA56B2B-89EC-4C38-A384-97D203375B06}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
@@ -444,7 +434,6 @@ Global
{B8A3E4CA-D54A-441F-A3BF-E00F060CA042} = {92463391-81BE-462B-AC3C-78C6C760741F}
{5257B25D-330A-4DCF-ACED-B4709CFBF916} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{1D90F276-E1CA-4FDF-A173-EB889E7D3150} = {92463391-81BE-462B-AC3C-78C6C760741F}
- {043B9497-C0BA-4770-9210-4456D2F81CE0} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{323553F0-14AB-4FBD-9CF0-1CC0BE8056F8} = {92463391-81BE-462B-AC3C-78C6C760741F}
{6205467F-E381-4C42-AEEC-763BD62B3D5E} = {C2C98051-0F39-47F2-80B6-E72B29159F2C}
{933101DA-C4CC-401A-AA01-2784E1025B7F} = {92463391-81BE-462B-AC3C-78C6C760741F}
diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/Microsoft.AspNetCore.Razor.Performance.csproj b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/Microsoft.AspNetCore.Razor.Performance.csproj
index 60fc7c6b47..ab0ec8c6fc 100644
--- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/Microsoft.AspNetCore.Razor.Performance.csproj
+++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/Microsoft.AspNetCore.Razor.Performance.csproj
@@ -17,6 +17,18 @@
Serialization\%(FileName)%(Extension)
+
+ TestServices\%(FileName)%(Extension)
+
+
+ TestServices\%(FileName)%(Extension)
+
+
+ TestServices\%(FileName)%(Extension)
+
+
+ TestServices\%(FileName)%(Extension)
+
diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/BackgroundCodeGenerationBenchmark.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/BackgroundCodeGenerationBenchmark.cs
new file mode 100644
index 0000000000..40b903844b
--- /dev/null
+++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/BackgroundCodeGenerationBenchmark.cs
@@ -0,0 +1,53 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Attributes;
+using Microsoft.CodeAnalysis.Razor.ProjectSystem;
+
+namespace Microsoft.AspNetCore.Razor.Performance
+{
+ public class BackgroundCodeGenerationBenchmark : ProjectSnapshotManagerBenchmarkBase
+ {
+ [IterationSetup]
+ public void Setup()
+ {
+ SnapshotManager = CreateProjectSnapshotManager();
+ SnapshotManager.HostProjectAdded(HostProject);
+ SnapshotManager.Changed += SnapshotManager_Changed;
+ }
+
+ [IterationCleanup]
+ public void Cleanup()
+ {
+ SnapshotManager.Changed -= SnapshotManager_Changed;
+
+ Tasks.Clear();
+ }
+
+ private List Tasks { get; } = new List();
+
+ private DefaultProjectSnapshotManager SnapshotManager { get; set; }
+
+ [Benchmark(Description = "Generates the code for 100 files", OperationsPerInvoke = 100)]
+ public async Task BackgroundCodeGeneration_Generate100Files()
+ {
+ for (var i = 0; i < Documents.Length; i++)
+ {
+ SnapshotManager.DocumentAdded(HostProject, Documents[i], TextLoaders[i % 4]);
+ }
+
+ await Task.WhenAll(Tasks);
+ }
+
+ private void SnapshotManager_Changed(object sender, ProjectChangeEventArgs e)
+ {
+ // The real work happens here.
+ var project = SnapshotManager.GetLoadedProject(e.ProjectFilePath);
+ var document = project.GetDocument(e.DocumentFilePath);
+
+ Tasks.Add(document.GetGeneratedOutputAsync());
+ }
+ }
+}
diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/ProjectLoadBenchmark.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/ProjectLoadBenchmark.cs
new file mode 100644
index 0000000000..58baba8faf
--- /dev/null
+++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/ProjectLoadBenchmark.cs
@@ -0,0 +1,30 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using BenchmarkDotNet.Attributes;
+using Microsoft.CodeAnalysis.Razor.ProjectSystem;
+
+namespace Microsoft.AspNetCore.Razor.Performance
+{
+ public class ProjectLoadBenchmark : ProjectSnapshotManagerBenchmarkBase
+ {
+ [IterationSetup]
+ public void Setup()
+ {
+ SnapshotManager = CreateProjectSnapshotManager();
+ }
+
+ private DefaultProjectSnapshotManager SnapshotManager { get; set; }
+
+ [Benchmark(Description = "Initializes a project and 100 files", OperationsPerInvoke = 100)]
+ public void ProjectLoad_AddProjectAnd100Files()
+ {
+ SnapshotManager.HostProjectAdded(HostProject);
+
+ for (var i= 0; i < Documents.Length; i++)
+ {
+ SnapshotManager.DocumentAdded(HostProject, Documents[i], TextLoaders[i % 4]);
+ }
+ }
+ }
+}
diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/ProjectSnapshotManagerBenchmarkBase.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/ProjectSnapshotManagerBenchmarkBase.cs
new file mode 100644
index 0000000000..70590a6a35
--- /dev/null
+++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/ProjectSystem/ProjectSnapshotManagerBenchmarkBase.cs
@@ -0,0 +1,154 @@
+// 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.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Razor.Extensions;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Razor;
+using Microsoft.CodeAnalysis.Razor.ProjectSystem;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
+using Newtonsoft.Json;
+
+namespace Microsoft.AspNetCore.Razor.Performance
+{
+ public class ProjectSnapshotManagerBenchmarkBase
+ {
+ public ProjectSnapshotManagerBenchmarkBase()
+ {
+ var current = new DirectoryInfo(AppContext.BaseDirectory);
+ while (current != null && !File.Exists(Path.Combine(current.FullName, "Razor.sln")))
+ {
+ current = current.Parent;
+ }
+
+ var root = current;
+ var projectRoot = Path.Combine(root.FullName, "test", "testapps", "LargeProject");
+
+ HostProject = new HostProject(Path.Combine(projectRoot, "LargeProject.csproj"), FallbackRazorConfiguration.MVC_2_1);
+
+ TextLoaders = new TextLoader[4];
+ for (var i = 0; i < 4; i++)
+ {
+ var filePath = Path.Combine(projectRoot, "Views", "Home", $"View00{i % 4}.cshtml");
+ var text = SourceText.From(filePath, encoding: null);
+ TextLoaders[i] = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create()));
+ }
+
+ Documents = new HostDocument[100];
+ for (var i = 0; i < Documents.Length; i++)
+ {
+ var filePath = Path.Combine(projectRoot, "Views", "Home", $"View00{i % 4}.cshtml");
+ Documents[i] = new HostDocument(filePath, $"/Views/Home/View00{i}.cshtml");
+ }
+
+ var tagHelpers = Path.Combine(root.FullName, "benchmarks", "Microsoft.AspNetCore.Razor.Performance", "taghelpers.json");
+ TagHelperResolver = new StaticTagHelperResolver(ReadTagHelpers(tagHelpers));
+ }
+
+ internal HostProject HostProject { get; }
+
+ internal HostDocument[] Documents { get; }
+
+ internal TextLoader[] TextLoaders { get; }
+
+ internal TagHelperResolver TagHelperResolver { get; }
+
+ internal DefaultProjectSnapshotManager CreateProjectSnapshotManager()
+ {
+ var services = TestServices.Create(
+ new IWorkspaceService[]
+ {
+ new StaticProjectSnapshotProjectEngineFactory(),
+ },
+ new ILanguageService[]
+ {
+ TagHelperResolver,
+ });
+
+ return new DefaultProjectSnapshotManager(
+ new TestForegroundDispatcher(),
+ new TestErrorReporter(),
+ Array.Empty(),
+ new AdhocWorkspace(services));
+ }
+
+ private static IReadOnlyList ReadTagHelpers(string filePath)
+ {
+ var serializer = new JsonSerializer();
+ serializer.Converters.Add(new RazorDiagnosticJsonConverter());
+ serializer.Converters.Add(new TagHelperDescriptorJsonConverter());
+
+ using (var reader = new JsonTextReader(File.OpenText(filePath)))
+ {
+ return serializer.Deserialize>(reader);
+ }
+ }
+
+ private class TestForegroundDispatcher : ForegroundDispatcher
+ {
+ public override bool IsForegroundThread => true;
+
+ public override TaskScheduler ForegroundScheduler => TaskScheduler.Default;
+
+ public override TaskScheduler BackgroundScheduler => TaskScheduler.Default;
+ }
+
+ private class TestErrorReporter : ErrorReporter
+ {
+ public override void ReportError(Exception exception)
+ {
+ }
+
+ public override void ReportError(Exception exception, ProjectSnapshot project)
+ {
+ }
+
+ public override void ReportError(Exception exception, Project workspaceProject)
+ {
+ }
+ }
+
+ private class StaticTagHelperResolver : TagHelperResolver
+ {
+ private readonly IReadOnlyList _tagHelpers;
+
+ public StaticTagHelperResolver(IReadOnlyList tagHelpers)
+ {
+ this._tagHelpers = tagHelpers;
+ }
+
+ public override Task GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(new TagHelperResolutionResult(_tagHelpers, Array.Empty()));
+ }
+ }
+
+ private class StaticProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
+ {
+ public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action configure)
+ {
+ return RazorProjectEngine.Create(project.Configuration, fileSystem, b =>
+ {
+ RazorExtensions.Register(b);
+ });
+ }
+
+ public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project)
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+}
diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/SyntaxTreeGenerationBenckmark.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/SyntaxTreeGenerationBenckmark.cs
new file mode 100644
index 0000000000..d1ec9a0a55
--- /dev/null
+++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Performance/SyntaxTreeGenerationBenckmark.cs
@@ -0,0 +1,77 @@
+// 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.IO;
+using System.Linq;
+using BenchmarkDotNet.Attributes;
+using Microsoft.AspNetCore.Mvc.Razor.Extensions;
+using Microsoft.AspNetCore.Razor.Language;
+
+namespace Microsoft.AspNetCore.Razor.Performance
+{
+ public class SyntaxTreeGenerationBenchmark
+ {
+ public SyntaxTreeGenerationBenchmark()
+ {
+ var current = new DirectoryInfo(AppContext.BaseDirectory);
+ while (current != null && !File.Exists(Path.Combine(current.FullName, "MSN.cshtml")))
+ {
+ current = current.Parent;
+ }
+
+ var root = current;
+ var fileSystem = RazorProjectFileSystem.Create(root.FullName);
+
+ ProjectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, b => RazorExtensions.Register(b)); ;
+
+ var projectItem = fileSystem.GetItem(Path.Combine(root.FullName, "MSN.cshtml"));
+ MSN = RazorSourceDocument.ReadFrom(projectItem);
+
+ var directiveFeature = ProjectEngine.EngineFeatures.OfType().FirstOrDefault();
+ Directives = directiveFeature?.Directives.ToArray() ?? Array.Empty();
+ }
+
+ public RazorProjectEngine ProjectEngine { get; }
+
+ public RazorSourceDocument MSN { get; }
+
+ public DirectiveDescriptor[] Directives { get; }
+
+ [Benchmark(Description = "Razor Design Time Syntax Tree Generation of MSN.com")]
+ public void SyntaxTreeGeneration_DesignTime_LargeStaticFile()
+ {
+ var options = RazorParserOptions.CreateDesignTime(o =>
+ {
+ foreach (var directive in Directives)
+ {
+ o.Directives.Add(directive);
+ }
+ });
+ var syntaxTree = RazorSyntaxTree.Parse(MSN, options);
+
+ if (syntaxTree.Diagnostics.Count != 0)
+ {
+ throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, syntaxTree.Diagnostics));
+ }
+ }
+
+ [Benchmark(Description = "Razor Runtime Syntax Tree Generation of MSN.com")]
+ public void SyntaxTreeGeneration_Runtime_LargeStaticFile()
+ {
+ var options = RazorParserOptions.Create(o =>
+ {
+ foreach (var directive in Directives)
+ {
+ o.Directives.Add(directive);
+ }
+ });
+ var syntaxTree = RazorSyntaxTree.Parse(MSN, options);
+
+ if (syntaxTree.Diagnostics.Count != 0)
+ {
+ throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, syntaxTree.Diagnostics));
+ }
+ }
+ }
+}
diff --git a/src/Razor/build/MPack.targets b/src/Razor/build/MPack.targets
index 54ace6edf4..ce81b91622 100644
--- a/src/Razor/build/MPack.targets
+++ b/src/Razor/build/MPack.targets
@@ -30,7 +30,9 @@
$(MPackArtifactCategory)
-
+
+
+
diff --git a/src/Razor/build/VSIX.targets b/src/Razor/build/VSIX.targets
index febfc39d48..eda19a3bef 100644
--- a/src/Razor/build/VSIX.targets
+++ b/src/Razor/build/VSIX.targets
@@ -42,7 +42,8 @@
$(VSIXName)
-
+
+
@@ -63,7 +64,6 @@
@@ -96,7 +96,6 @@
diff --git a/src/Razor/build/dependencies.props b/src/Razor/build/dependencies.props
index e128924edb..b76ea87d30 100644
--- a/src/Razor/build/dependencies.props
+++ b/src/Razor/build/dependencies.props
@@ -2,19 +2,26 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
-
-
-
+
0.10.13
- 2.1.3-rtm-15802
+ 2.2.0-preview2-20181108.4
+ 2.2.0-rtm-181106-13
+ 2.2.0-rtm-35661
+ 2.2.0-rtm-181106-13
15.6.82
15.6.82
15.6.82
2.8.0
2.8.0
+ 2.2.0-rtm-181106-13
+ 2.2.0-rtm-181106-13
2.1.0
- 2.0.0
- 2.1.2
+ 2.2.0-rtm-181106-13
+ 2.2.0-rtm-181106-13
+ 2.2.0-rtm-35661
+ 2.0.9
+ 2.1.3
+ 2.2.0-rtm-27105-02
15.6.1
15.0.26606
15.6.161-preview
@@ -33,7 +40,7 @@
15.6.161-preview
1.3.8
1.0.1
- 4.7.49
+ 4.10.0
2.0.3
11.0.2
1.1.92
@@ -41,32 +48,21 @@
4.3.0
4.5.0
9.0.1
- 2.8.0
- 2.8.0
- 2.8.0
- 2.8.0
- 2.8.0
- 2.8.0-beta2-62721-09
- 2.8.0
- 2.8.0
- 2.8.0
- 2.8.0-beta2-62721-09
- 0.8.0
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 2.9.0-beta4-62911-02
+ 0.10.0
2.3.1
- 2.4.0-beta.1.build3945
+ 2.4.0
+
-
-
-
-
- 2.1.1
- 2.1.1
- 2.1.0
- 2.1.1
- 2.1.1
- 2.1.1
- 2.1.1
-
-
\ No newline at end of file
+
diff --git a/src/Razor/build/repo.props b/src/Razor/build/repo.props
index ec177662e4..def34f0b6f 100644
--- a/src/Razor/build/repo.props
+++ b/src/Razor/build/repo.props
@@ -21,12 +21,13 @@
Internal.AspNetCore.Universe.Lineup
- 2.1.0-rc1-*
+ 2.2.0-*
https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json
+
diff --git a/src/Razor/build/repo.targets b/src/Razor/build/repo.targets
index 2e234b2b72..550069ec47 100644
--- a/src/Razor/build/repo.targets
+++ b/src/Razor/build/repo.targets
@@ -1,6 +1,7 @@
+
@@ -21,7 +22,11 @@
Outputs="$(MSBuildLocationFileOutput)">
- MSBuildLocation=$(VisualStudioMSBuildx86Path)
+
+ MSBuildLocation=$(VisualStudioMSBuildx86Path);
+ MicrosoftNETCoreAppPackageVersion=$(MicrosoftNETCoreApp22PackageVersion);
+ NETStandardLibraryPackageVersion=$(NETStandardLibrary20PackageVersion)
+
";
@class.Modifiers.Clear();
@class.Modifiers.Add("public");
@@ -37,5 +50,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
method.Modifiers.Add("override");
method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}";
}
+
+ private static string BytesToString(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException(nameof(bytes));
+ }
+
+ var result = new StringBuilder(bytes.Length);
+ for (var i = 0; i < bytes.Length; i++)
+ {
+ // The x2 format means lowercase hex, where each byte is a 2-character string.
+ result.Append(bytes[i].ToString("x2"));
+ }
+
+ return result.ToString();
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperDescriptorProvider.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperDescriptorProvider.cs
index af79139c69..318994ec0e 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperDescriptorProvider.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperDescriptorProvider.cs
@@ -27,8 +27,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
return;
}
+ var vcAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.ViewComponentAttribute);
+ var nonVCAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.NonViewComponentAttribute);
+ if (vcAttribute == null || vcAttribute.TypeKind == TypeKind.Error)
+ {
+ // Could not find attributes we care about in the compilation. Nothing to do.
+ return;
+ }
+
var types = new List();
- var visitor = ViewComponentTypeVisitor.Create(compilation, types);
+ var visitor = new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, types);
// We always visit the global namespace.
visitor.Visit(compilation.Assembly.GlobalNamespace);
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperIntermediateNode.cs
index bfe937aaa8..b1399ffbea 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperIntermediateNode.cs
@@ -47,5 +47,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
extension.WriteViewComponentTagHelper(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(ClassName);
+
+ formatter.WriteProperty(nameof(ClassName), ClassName);
+ formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTypeVisitor.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTypeVisitor.cs
index be987cbdd5..db0912087a 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTypeVisitor.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTypeVisitor.cs
@@ -16,13 +16,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
private readonly INamedTypeSymbol _nonViewComponentAttribute;
private readonly List _results;
- public static ViewComponentTypeVisitor Create(Compilation compilation, List results)
- {
- var vcAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.ViewComponentAttribute);
- var nonVCAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.NonViewComponentAttribute);
- return new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, results);
- }
-
public ViewComponentTypeVisitor(
INamedTypeSymbol viewComponentAttribute,
INamedTypeSymbol nonViewComponentAttribute,
@@ -31,12 +24,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
_viewComponentAttribute = viewComponentAttribute;
_nonViewComponentAttribute = nonViewComponentAttribute;
_results = results;
-
- Enabled = _viewComponentAttribute != null;
}
- public bool Enabled { get; set; }
-
public override void VisitNamedType(INamedTypeSymbol symbol)
{
if (IsViewComponent(symbol))
@@ -65,11 +54,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
internal bool IsViewComponent(INamedTypeSymbol symbol)
{
- if (!Enabled)
- {
- return false;
- }
-
if (symbol.DeclaredAccessibility != Accessibility.Public ||
symbol.IsAbstract ||
symbol.IsGenericType ||
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/InjectIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/InjectIntermediateNode.cs
index e1dfe9cf15..572d8bb1dc 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/InjectIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/InjectIntermediateNode.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
@@ -47,5 +46,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
extension.WriteInjectProperty(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(MemberName);
+
+ formatter.WriteProperty(nameof(MemberName), MemberName);
+ formatter.WriteProperty(nameof(TypeName), TypeName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/InstrumentationPass.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/InstrumentationPass.cs
index 1ae0a72210..d80c2b9b68 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/InstrumentationPass.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/InstrumentationPass.cs
@@ -33,8 +33,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
private static void AddInstrumentation(InstrumentationItem item)
{
- var beginContextMethodName = "BeginContext"; /* ORIGINAL: BeginContextMethodName */
- var endContextMethodName = "EndContext"; /* ORIGINAL: EndContextMethodName */
+ var beginContextMethodName = "BeginContext"; // ORIGINAL: BeginContextMethodName
+ var endContextMethodName = "EndContext"; // ORIGINAL: EndContextMethodName
var beginNode = new CSharpCodeIntermediateNode();
beginNode.Children.Add(new IntermediateToken()
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcViewDocumentClassifierPass.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcViewDocumentClassifierPass.cs
index 2378f23591..57e9ad23a2 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcViewDocumentClassifierPass.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcViewDocumentClassifierPass.cs
@@ -1,6 +1,8 @@
// 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.Text;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
@@ -25,7 +27,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
@namespace.Content = "AspNetCore";
var filePath = codeDocument.Source.RelativePath ?? codeDocument.Source.FilePath;
- @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath);
+ if (string.IsNullOrEmpty(filePath))
+ {
+ // It's possible for a Razor document to not have a file path.
+ // Eg. When we try to generate code for an in memory document like default imports.
+ var checksum = BytesToString(codeDocument.Source.GetChecksum());
+ @class.ClassName = $"AspNetCore_{checksum}";
+ }
+ else
+ {
+ @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath);
+ }
+
@class.BaseType = "global::Microsoft.AspNetCore.Mvc.Razor.RazorPage";
@class.Modifiers.Clear();
@class.Modifiers.Add("public");
@@ -37,5 +50,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
method.Modifiers.Add("override");
method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}";
}
+
+ private static string BytesToString(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException(nameof(bytes));
+ }
+
+ var result = new StringBuilder(bytes.Length);
+ for (var i = 0; i < bytes.Length; i++)
+ {
+ // The x2 format means lowercase hex, where each byte is a 2-character string.
+ result.Append(bytes[i].ToString("x2"));
+ }
+
+ return result.ToString();
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs
index daf8e0e606..9af7873874 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs
@@ -1,7 +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 System;
using System.Diagnostics;
+using System.Text;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
@@ -52,7 +54,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
@class.BaseType = "global::Microsoft.AspNetCore.Mvc.RazorPages.Page";
var filePath = codeDocument.Source.RelativePath ?? codeDocument.Source.FilePath;
- @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath);
+ if (string.IsNullOrEmpty(filePath))
+ {
+ // It's possible for a Razor document to not have a file path.
+ // Eg. When we try to generate code for an in memory document like default imports.
+ var checksum = BytesToString(codeDocument.Source.GetChecksum());
+ @class.ClassName = $"AspNetCore_{checksum}";
+ }
+ else
+ {
+ @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath);
+ }
@class.Modifiers.Clear();
@class.Modifiers.Add("public");
@@ -131,5 +143,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
options.ParseLeadingDirectives = true;
}
}
+
+ private static string BytesToString(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException(nameof(bytes));
+ }
+
+ var result = new StringBuilder(bytes.Length);
+ for (var i = 0; i < bytes.Length; i++)
+ {
+ // The x2 format means lowercase hex, where each byte is a 2-character string.
+ result.Append(bytes[i].ToString("x2"));
+ }
+
+ return result.ToString();
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorFactory.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorFactory.cs
index cb64a4611a..a5181fd789 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorFactory.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorFactory.cs
@@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
var descriptorBuilder = TagHelperDescriptorBuilder.Create(ViewComponentTagHelperConventions.Kind, typeName, assemblyName);
descriptorBuilder.SetTypeName(typeName);
descriptorBuilder.DisplayName = displayName;
-
+
if (TryFindInvokeMethod(type, out var method, out var diagnostic))
{
var methodParameters = method.Parameters;
@@ -84,21 +84,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
private bool TryFindInvokeMethod(INamedTypeSymbol type, out IMethodSymbol method, out RazorDiagnostic diagnostic)
{
- var methods = type.GetMembers()
- .OfType()
- .Where(m =>
- m.DeclaredAccessibility == Accessibility.Public &&
- (string.Equals(m.Name, ViewComponentTypes.AsyncMethodName, StringComparison.Ordinal) ||
- string.Equals(m.Name, ViewComponentTypes.SyncMethodName, StringComparison.Ordinal)))
- .ToArray();
+ var methods = GetInvokeMethods(type);
- if (methods.Length == 0)
+ if (methods.Count == 0)
{
- diagnostic = RazorExtensionsDiagnosticFactory.CreateViewComponent_CannotFindMethod(type.ToDisplayString(FullNameTypeDisplayFormat));
+ diagnostic = RazorExtensionsDiagnosticFactory.CreateViewComponent_CannotFindMethod(type.ToDisplayString(FullNameTypeDisplayFormat));
method = null;
return false;
}
- else if (methods.Length > 1)
+ else if (methods.Count > 1)
{
diagnostic = RazorExtensionsDiagnosticFactory.CreateViewComponent_AmbiguousMethods(type.ToDisplayString(FullNameTypeDisplayFormat));
method = null;
@@ -153,6 +147,27 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
return true;
}
+ private static IReadOnlyList GetInvokeMethods(INamedTypeSymbol type)
+ {
+ var methods = new List();
+ while (type != null)
+ {
+ var currentTypeMethods = type.GetMembers()
+ .OfType()
+ .Where(m =>
+ m.DeclaredAccessibility == Accessibility.Public &&
+ !m.IsStatic &&
+ (string.Equals(m.Name, ViewComponentTypes.AsyncMethodName, StringComparison.Ordinal) ||
+ string.Equals(m.Name, ViewComponentTypes.SyncMethodName, StringComparison.Ordinal)));
+
+ methods.AddRange(currentTypeMethods);
+
+ type = type.BaseType;
+ }
+
+ return methods;
+ }
+
private void AddRequiredAttributes(ImmutableArray methodParameters, TagMatchingRuleDescriptorBuilder builder)
{
foreach (var parameter in methodParameters)
@@ -164,7 +179,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
builder.Attribute(attributeBuilder =>
{
var lowerKebabName = HtmlConventions.ToHtmlCase(parameter.Name);
- attributeBuilder.Name =lowerKebabName;
+ attributeBuilder.Name = lowerKebabName;
});
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorProvider.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorProvider.cs
index a5c05dc585..d0d9805f6b 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorProvider.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorProvider.cs
@@ -27,8 +27,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
return;
}
+ var vcAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.ViewComponentAttribute);
+ var nonVCAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.NonViewComponentAttribute);
+ if (vcAttribute == null || vcAttribute.TypeKind == TypeKind.Error)
+ {
+ // Could not find attributes we care about in the compilation. Nothing to do.
+ return;
+ }
+
var types = new List();
- var visitor = ViewComponentTypeVisitor.Create(compilation, types);
+ var visitor = new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, types);
// We always visit the global namespace.
visitor.Visit(compilation.Assembly.GlobalNamespace);
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperIntermediateNode.cs
index 09b124970e..9283b2c092 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperIntermediateNode.cs
@@ -47,5 +47,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
extension.WriteViewComponentTagHelper(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(ClassName);
+
+ formatter.WriteProperty(nameof(ClassName), ClassName);
+ formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTypeVisitor.cs b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTypeVisitor.cs
index 2fd215d99d..9e1f936060 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTypeVisitor.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTypeVisitor.cs
@@ -13,13 +13,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
private readonly INamedTypeSymbol _nonViewComponentAttribute;
private readonly List _results;
- public static ViewComponentTypeVisitor Create(Compilation compilation, List results)
- {
- var vcAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.ViewComponentAttribute);
- var nonVCAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.NonViewComponentAttribute);
- return new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, results);
- }
-
public ViewComponentTypeVisitor(
INamedTypeSymbol viewComponentAttribute,
INamedTypeSymbol nonViewComponentAttribute,
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Design/Microsoft.AspNetCore.Razor.Design.csproj b/src/Razor/src/Microsoft.AspNetCore.Razor.Design/Microsoft.AspNetCore.Razor.Design.csproj
index 13ab584df8..083a24e847 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Design/Microsoft.AspNetCore.Razor.Design.csproj
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Design/Microsoft.AspNetCore.Razor.Design.csproj
@@ -1,103 +1,56 @@
-
-
-
-
-
+
Razor is a markup syntax for adding server-side logic to web pages. This package contains MSBuild support for Razor.
- netstandard2.0
+ netcoreapp2.0
false
+ false
+ false
$(MSBuildProjectName).nuspec
-
-
-
-
-
-
-
-
-
-
-
-
-
- _BuildDependencyProjects;$(GenerateNuspecDependsOn)
- _BuildDependencyProjects;$(BuildDependsOn)
+ ..\Microsoft.AspNetCore.Razor.Tools\Microsoft.AspNetCore.Razor.Tools.csproj
-
-
+
- First, build the project, then copy it to the ouput directory, then add it as packable content.
- -->
-
+
+
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_RazorTool Include="$(OutputPath)tools\**\*" />
-
-
-
-
-
-
+
true
+
+ unknown
+
id=$(PackageId);
version=$(PackageVersion);
@@ -111,15 +64,21 @@
repositoryCommit=$(RepositoryCommit);
copyright=$(Copyright);
-
- TaskAssemblyNet46=@(TaskAssemblyNet46);
- TaskSymbolNet46=@(TaskSymbolNet46);
- TaskAssemblyNetStandard=@(TaskAssemblyNetStandard);
- TaskSymbolNetStandard=@(TaskSymbolNetStandard);
-
- ToolAssembly=$(OutputPath)tools\**\*;
+ ToolFiles=$(OutputPath)tools\**\*;
+
+
+
+
+
+
+ <_RazorTool Include="$(OutputPath)tools\**\*" />
+
+
+
+
+
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Design/Microsoft.AspNetCore.Razor.Design.nuspec b/src/Razor/src/Microsoft.AspNetCore.Razor.Design/Microsoft.AspNetCore.Razor.Design.nuspec
index 687b5cf1d8..47d16cd554 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Design/Microsoft.AspNetCore.Razor.Design.nuspec
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Design/Microsoft.AspNetCore.Razor.Design.nuspec
@@ -26,6 +26,6 @@
-
+
-
\ No newline at end of file
+
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets b/src/Razor/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets
index c390749c45..c81d741e1f 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets
@@ -4,6 +4,16 @@
This target is explicitly imported by Razor SDK.
-->
+
+
+
+
<_RazorMSBuildRoot Condition="'$(_RazorMSBuildRoot)'==''">$(MSBuildThisFileDirectory)..\..\
- <_RazorTaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' ">netstandard2.0
- <_RazorTaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' ">net46
-
- <_RazorTaskAssembly Condition="'$(_RazorTaskAssembly)'==''">$(_RazorMSBuildRoot)\tasks\$(_RazorTaskFolder)\Microsoft.AspNetCore.Razor.Tasks.dll
-
<_RazorToolAssembly Condition="'$(_RazorToolAssembly)'==''">$(_RazorMSBuildRoot)tools\rzc.dll
@@ -47,6 +42,4 @@
-
-
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/AllowedChildTagDescriptorComparer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/AllowedChildTagDescriptorComparer.cs
index d73e3ec71e..37f0a55417 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/AllowedChildTagDescriptorComparer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/AllowedChildTagDescriptorComparer.cs
@@ -54,20 +54,18 @@ namespace Microsoft.AspNetCore.Razor.Language
return false;
}
- return descriptorX != null &&
+ return
string.Equals(descriptorX.Name, descriptorY.Name, _stringComparison) &&
- string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
- Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics);
+ string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal);
}
///
public virtual int GetHashCode(AllowedChildTagDescriptor descriptor)
{
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.Name, _stringComparer);
- hashCodeCombiner.Add(descriptor.DisplayName, StringComparer.Ordinal);
+ var hash = HashCodeCombiner.Start();
+ hash.Add(descriptor.Name, _stringComparer);
- return hashCodeCombiner.CombinedHash;
+ return hash.CombinedHash;
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorComparer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorComparer.cs
index 21b610e45b..fbf68929eb 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorComparer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorComparer.cs
@@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Razor.Language
return false;
}
- return descriptorX != null &&
+ return
string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal) &&
descriptorX.IsIndexerStringProperty == descriptorY.IsIndexerStringProperty &&
descriptorX.IsEnum == descriptorY.IsEnum &&
@@ -61,7 +61,6 @@ namespace Microsoft.AspNetCore.Razor.Language
string.Equals(descriptorX.IndexerTypeName, descriptorY.IndexerTypeName, StringComparison.Ordinal) &&
string.Equals(descriptorX.Documentation, descriptorY.Documentation, StringComparison.Ordinal) &&
string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
- Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics) &&
Enumerable.SequenceEqual(
descriptorX.Metadata.OrderBy(propertyX => propertyX.Key, StringComparer.Ordinal),
descriptorY.Metadata.OrderBy(propertyY => propertyY.Key, StringComparer.Ordinal));
@@ -74,19 +73,11 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(descriptor));
}
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.Kind);
- hashCodeCombiner.Add(descriptor.IsIndexerStringProperty);
- hashCodeCombiner.Add(descriptor.IsEnum);
- hashCodeCombiner.Add(descriptor.HasIndexer);
- hashCodeCombiner.Add(descriptor.Name, _stringComparer);
- hashCodeCombiner.Add(descriptor.IndexerNamePrefix, _stringComparer);
- hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.IndexerTypeName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.Documentation, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.DisplayName, StringComparer.Ordinal);
+ var hash = HashCodeCombiner.Start();
+ hash.Add(descriptor.Kind);
+ hash.Add(descriptor.Name, _stringComparer);
- return hashCodeCombiner.CombinedHash;
+ return hash.CombinedHash;
}
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/DesignTimeNodeWriter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/DesignTimeNodeWriter.cs
index 46837d0e23..d937c6f626 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/DesignTimeNodeWriter.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/DesignTimeNodeWriter.cs
@@ -88,32 +88,13 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
public override void WriteCSharpCode(CodeRenderingContext context, CSharpCodeIntermediateNode node)
{
- var isWhitespaceStatement = true;
- for (var i = 0; i < node.Children.Count; i++)
- {
- var token = node.Children[i] as IntermediateToken;
- if (token == null || !string.IsNullOrWhiteSpace(token.Content))
- {
- isWhitespaceStatement = false;
- break;
- }
- }
-
IDisposable linePragmaScope = null;
if (node.Source != null)
{
- if (!isWhitespaceStatement)
- {
- linePragmaScope = context.CodeWriter.BuildLinePragma(node.Source.Value);
- }
+ linePragmaScope = context.CodeWriter.BuildLinePragma(node.Source.Value);
context.CodeWriter.WritePadding(0, node.Source.Value, context);
}
- else if (isWhitespaceStatement)
- {
- // Don't write whitespace if there is no line mapping for it.
- return;
- }
for (var i = 0; i < node.Children.Count; i++)
{
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/RuntimeNodeWriter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/RuntimeNodeWriter.cs
index 9f08216548..f913a53fc4 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/RuntimeNodeWriter.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/RuntimeNodeWriter.cs
@@ -325,28 +325,56 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
var content = builder.ToString();
- var charactersConsumed = 0;
+ WriteHtmlLiteral(context, MaxStringLiteralLength, content);
+ }
- // Render the string in pieces to avoid Roslyn OOM exceptions at compile time: https://github.com/aspnet/External/issues/54
- while (charactersConsumed < content.Length)
+ // Internal for testing
+ internal void WriteHtmlLiteral(CodeRenderingContext context, int maxStringLiteralLength, string literal)
+ {
+ if (literal.Length <= maxStringLiteralLength)
{
- string textToRender;
- if (content.Length <= MaxStringLiteralLength)
+ WriteLiteral(literal);
+ return;
+ }
+
+ // String is too large, render the string in pieces to avoid Roslyn OOM exceptions at compile time: https://github.com/aspnet/External/issues/54
+ var charactersConsumed = 0;
+ do
+ {
+ var charactersRemaining = literal.Length - charactersConsumed;
+ var charactersToSubstring = Math.Min(maxStringLiteralLength, charactersRemaining);
+ var lastCharBeforeSplitIndex = charactersConsumed + charactersToSubstring - 1;
+ var lastCharBeforeSplit = literal[lastCharBeforeSplitIndex];
+
+ if (char.IsHighSurrogate(lastCharBeforeSplit))
{
- textToRender = content;
- }
- else
- {
- var charactersToSubstring = Math.Min(MaxStringLiteralLength, content.Length - charactersConsumed);
- textToRender = content.Substring(charactersConsumed, charactersToSubstring);
+ if (charactersRemaining > 1)
+ {
+ // Take one less character this iteration. We're attempting to split inbetween a surrogate pair.
+ // This can happen when something like an emoji sits on the barrier between splits; if we were to
+ // split the emoji we'd end up with invalid bytes in our output.
+ charactersToSubstring--;
+ }
+ else
+ {
+ // The user has an invalid file with a partial surrogate a the splitting point.
+ // We'll let the invalid character flow but we'll explode later on.
+ }
}
- context.CodeWriter
- .WriteStartMethodInvocation(WriteHtmlContentMethod)
- .WriteStringLiteral(textToRender)
- .WriteEndMethodInvocation();
+ var textToRender = literal.Substring(charactersConsumed, charactersToSubstring);
+
+ WriteLiteral(textToRender);
charactersConsumed += textToRender.Length;
+ } while (charactersConsumed < literal.Length);
+
+ void WriteLiteral(string content)
+ {
+ context.CodeWriter
+ .WriteStartMethodInvocation(WriteHtmlContentMethod)
+ .WriteStringLiteral(content)
+ .WriteEndMethodInvocation();
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorIntermediateNodeLoweringPhase.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorIntermediateNodeLoweringPhase.cs
index 5e3ae8a258..a711567547 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorIntermediateNodeLoweringPhase.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorIntermediateNodeLoweringPhase.cs
@@ -34,13 +34,13 @@ namespace Microsoft.AspNetCore.Razor.Language
document.Options = codeDocument.GetCodeGenerationOptions() ?? _optionsFeature.GetOptions();
- var namespaces = new Dictionary(StringComparer.Ordinal);
+ IReadOnlyList importedUsings = Array.Empty();
// The import documents should be inserted logically before the main document.
var imports = codeDocument.GetImportSyntaxTrees();
if (imports != null)
{
- var importsVisitor = new ImportsVisitor(document, builder, namespaces, syntaxTree.Options.FeatureFlags);
+ var importsVisitor = new ImportsVisitor(document, builder, syntaxTree.Options.FeatureFlags);
for (var j = 0; j < imports.Count; j++)
{
@@ -49,26 +49,40 @@ namespace Microsoft.AspNetCore.Razor.Language
importsVisitor.FilePath = import.Source.FilePath;
importsVisitor.VisitBlock(import.Root);
}
+
+ importedUsings = importsVisitor.Usings;
}
var tagHelperPrefix = tagHelperContext?.Prefix;
- var visitor = new MainSourceVisitor(document, builder, namespaces, tagHelperPrefix, syntaxTree.Options.FeatureFlags)
+ var visitor = new MainSourceVisitor(document, builder, tagHelperPrefix, syntaxTree.Options.FeatureFlags)
{
FilePath = syntaxTree.Source.FilePath,
};
visitor.VisitBlock(syntaxTree.Root);
+ // 1. Prioritize non-imported usings over imported ones.
+ // 2. Don't import usings that already exist in primary document.
+ // 3. Allow duplicate usings in primary document (C# warning).
+ var usingReferences = new List(visitor.Usings);
+ for (var j = importedUsings.Count - 1; j >= 0; j--)
+ {
+ if (!usingReferences.Contains(importedUsings[j]))
+ {
+ usingReferences.Insert(0, importedUsings[j]);
+ }
+ }
+
// In each lowering piece above, namespaces were tracked. We render them here to ensure every
// lowering action has a chance to add a source location to a namespace. Ultimately, closest wins.
var i = 0;
- foreach (var @namespace in namespaces)
+ foreach (var reference in usingReferences)
{
var @using = new UsingDirectiveIntermediateNode()
{
- Content = @namespace.Key,
- Source = @namespace.Value,
+ Content = reference.Namespace,
+ Source = reference.Source,
};
builder.Insert(i++, @using);
@@ -147,21 +161,51 @@ namespace Microsoft.AspNetCore.Razor.Language
}
}
+ private struct UsingReference : IEquatable
+ {
+ public UsingReference(string @namespace, SourceSpan? source)
+ {
+ Namespace = @namespace;
+ Source = source;
+ }
+ public string Namespace { get; }
+
+ public SourceSpan? Source { get; }
+
+ public override bool Equals(object other)
+ {
+ if (other is UsingReference reference)
+ {
+ return Equals(reference);
+ }
+
+ return false;
+ }
+ public bool Equals(UsingReference other)
+ {
+ return string.Equals(Namespace, other.Namespace, StringComparison.Ordinal);
+ }
+
+ public override int GetHashCode() => Namespace.GetHashCode();
+ }
+
private class LoweringVisitor : ParserVisitor
{
protected readonly IntermediateNodeBuilder _builder;
protected readonly DocumentIntermediateNode _document;
- protected readonly Dictionary _namespaces;
+ protected readonly List _usings;
protected readonly RazorParserFeatureFlags _featureFlags;
- public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary namespaces, RazorParserFeatureFlags featureFlags)
+ public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, RazorParserFeatureFlags featureFlags)
{
_document = document;
_builder = builder;
- _namespaces = namespaces;
+ _usings = new List();
_featureFlags = featureFlags;
}
+ public IReadOnlyList Usings => _usings;
+
public string FilePath { get; set; }
public override void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
@@ -212,7 +256,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{
var namespaceImport = chunkGenerator.Namespace.Trim();
var namespaceSpan = BuildSourceSpanFromNode(span);
- _namespaces[namespaceImport] = namespaceSpan;
+ _usings.Add(new UsingReference(namespaceImport, namespaceSpan));
}
public override void VisitAddTagHelperSpan(AddTagHelperChunkGenerator chunkGenerator, Span span)
@@ -353,8 +397,8 @@ namespace Microsoft.AspNetCore.Razor.Language
{
private readonly string _tagHelperPrefix;
- public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary namespaces, string tagHelperPrefix, RazorParserFeatureFlags featureFlags)
- : base(document, builder, namespaces, featureFlags)
+ public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, string tagHelperPrefix, RazorParserFeatureFlags featureFlags)
+ : base(document, builder, featureFlags)
{
_tagHelperPrefix = tagHelperPrefix;
}
@@ -549,14 +593,14 @@ namespace Microsoft.AspNetCore.Razor.Language
public override void VisitMarkupSpan(MarkupChunkGenerator chunkGenerator, Span span)
{
- if (span.Symbols.Count == 1)
+ if (span.Tokens.Count == 1)
{
- var symbol = span.Symbols[0] as HtmlSymbol;
- if (symbol != null &&
- symbol.Type == HtmlSymbolType.Unknown &&
- symbol.Content.Length == 0)
+ var token = span.Tokens[0] as HtmlToken;
+ if (token != null &&
+ token.Type == HtmlTokenType.Unknown &&
+ token.Content.Length == 0)
{
- // We don't want to create IR nodes for marker symbols.
+ // We don't want to create IR nodes for marker tokens.
return;
}
}
@@ -731,8 +775,8 @@ namespace Microsoft.AspNetCore.Razor.Language
private class ImportsVisitor : LoweringVisitor
{
- public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary namespaces, RazorParserFeatureFlags featureFlags)
- : base(document, new ImportBuilder(builder), namespaces, featureFlags)
+ public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, RazorParserFeatureFlags featureFlags)
+ : base(document, new ImportBuilder(builder), featureFlags)
{
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorProjectEngine.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorProjectEngine.cs
index e5993ce1a3..a560a0030a 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorProjectEngine.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorProjectEngine.cs
@@ -68,10 +68,23 @@ namespace Microsoft.AspNetCore.Razor.Language
var importItems = importFeature.GetImports(projectItem);
var importSourceDocuments = GetImportSourceDocuments(importItems);
+ return CreateCodeDocumentCore(sourceDocument, importSourceDocuments, tagHelpers: null);
+ }
+
+ internal override RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument sourceDocument, IReadOnlyList importSourceDocuments, IReadOnlyList tagHelpers)
+ {
+ if (sourceDocument == null)
+ {
+ throw new ArgumentNullException(nameof(sourceDocument));
+ }
+
var parserOptions = GetRequiredFeature().Create(ConfigureParserOptions);
var codeGenerationOptions = GetRequiredFeature().Create(ConfigureCodeGenerationOptions);
- return RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
+ var codeDocument = RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
+ codeDocument.SetTagHelpers(tagHelpers);
+
+ return codeDocument;
}
protected override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem)
@@ -87,10 +100,23 @@ namespace Microsoft.AspNetCore.Razor.Language
var importItems = importFeature.GetImports(projectItem);
var importSourceDocuments = GetImportSourceDocuments(importItems, suppressExceptions: true);
+ return CreateCodeDocumentDesignTimeCore(sourceDocument, importSourceDocuments, tagHelpers: null);
+ }
+
+ internal override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument sourceDocument, IReadOnlyList importSourceDocuments, IReadOnlyList tagHelpers)
+ {
+ if (sourceDocument == null)
+ {
+ throw new ArgumentNullException(nameof(sourceDocument));
+ }
+
var parserOptions = GetRequiredFeature().Create(ConfigureDesignTimeParserOptions);
var codeGenerationOptions = GetRequiredFeature().Create(ConfigureDesignTimeCodeGenerationOptions);
- return RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
+ var codeDocument = RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
+ codeDocument.SetTagHelpers(tagHelpers);
+
+ return codeDocument;
}
protected override void ProcessCore(RazorCodeDocument codeDocument)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorTagHelperBinderPhase.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorTagHelperBinderPhase.cs
index 7d9dfb7c6e..aa0668a539 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorTagHelperBinderPhase.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorTagHelperBinderPhase.cs
@@ -15,18 +15,23 @@ namespace Microsoft.AspNetCore.Razor.Language
var syntaxTree = codeDocument.GetSyntaxTree();
ThrowForMissingDocumentDependency(syntaxTree);
- var feature = Engine.Features.OfType().FirstOrDefault();
- if (feature == null)
+ var descriptors = codeDocument.GetTagHelpers();
+ if (descriptors == null)
{
- // No feature, nothing to do.
- return;
+ var feature = Engine.Features.OfType().FirstOrDefault();
+ if (feature == null)
+ {
+ // No feature, nothing to do.
+ return;
+ }
+
+ descriptors = feature.GetDescriptors();
}
// We need to find directives in all of the *imports* as well as in the main razor file
//
// The imports come logically before the main razor file and are in the order they
// should be processed.
- var descriptors = feature.GetDescriptors();
var visitor = new DirectiveVisitor(descriptors);
var imports = codeDocument.GetImportSyntaxTrees();
if (imports != null)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DirectiveTokenEditHandler.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DirectiveTokenEditHandler.cs
index 6abaa03f5c..308c274518 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DirectiveTokenEditHandler.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/DirectiveTokenEditHandler.cs
@@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{
internal class DirectiveTokenEditHandler : SpanEditHandler
{
- public DirectiveTokenEditHandler(Func> tokenizer) : base(tokenizer)
+ public DirectiveTokenEditHandler(Func> tokenizer) : base(tokenizer)
{
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperCreateIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperCreateIntermediateNode.cs
index 92aa2e3038..0ef75f1b7a 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperCreateIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperCreateIntermediateNode.cs
@@ -48,5 +48,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteTagHelperCreate(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(TypeName);
+
+ formatter.WriteProperty(nameof(FieldName), FieldName);
+ formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
+ formatter.WriteProperty(nameof(TypeName), TypeName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperHtmlAttributeIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperHtmlAttributeIntermediateNode.cs
index 4122f66ef2..0290be8323 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperHtmlAttributeIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperHtmlAttributeIntermediateNode.cs
@@ -72,5 +72,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteTagHelperHtmlAttribute(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(AttributeName);
+
+ formatter.WriteProperty(nameof(AttributeName), AttributeName);
+ formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperPropertyIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperPropertyIntermediateNode.cs
index 083d84f32e..c0362f0d60 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperPropertyIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperPropertyIntermediateNode.cs
@@ -85,5 +85,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteTagHelperProperty(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(AttributeName);
+
+ formatter.WriteProperty(nameof(AttributeName), AttributeName);
+ formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
+ formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName);
+ formatter.WriteProperty(nameof(FieldName), FieldName);
+ formatter.WriteProperty(nameof(IsIndexerNameMatch), IsIndexerNameMatch.ToString());
+ formatter.WriteProperty(nameof(PropertyName), PropertyName);
+ formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperTargetExtension.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperTargetExtension.cs
index ad85ebf807..884ef07df5 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperTargetExtension.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DefaultTagHelperTargetExtension.cs
@@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
var uniqueId = (string)context.Items[CodeRenderingContext.SuppressUniqueIds];
if (uniqueId == null)
{
- uniqueId = Guid.NewGuid().ToString("N");
+ uniqueId = GetDeterministicId(context);
}
context.CodeWriter.WriteStringLiteral(node.TagName)
@@ -637,6 +637,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
return builder.ToString();
}
+ // Internal for testing
+ internal static string GetDeterministicId(CodeRenderingContext context)
+ {
+ // Use the file checksum along with the absolute position in the generated code to create a unique id for each tag helper call site.
+ var checksum = Checksum.BytesToString(context.SourceDocument.GetChecksum());
+ var uniqueId = checksum + context.CodeWriter.Location.AbsoluteIndex;
+
+ return uniqueId;
+ }
+
private static string GetPropertyAccessor(DefaultTagHelperPropertyIntermediateNode node)
{
var propertyAccessor = $"{node.FieldName}.{node.PropertyName}";
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DesignTimeDirectiveIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DesignTimeDirectiveIntermediateNode.cs
index ef1e971584..957e73a17d 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DesignTimeDirectiveIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DesignTimeDirectiveIntermediateNode.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Text;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DesignTimeDirectiveTargetExtension.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DesignTimeDirectiveTargetExtension.cs
index bc73343044..316c255bfe 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DesignTimeDirectiveTargetExtension.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/DesignTimeDirectiveTargetExtension.cs
@@ -59,8 +59,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
if (string.IsNullOrEmpty(node.Content))
{
- // This is most likely a marker symbol.
- WriteMarkerSymbol(context, node);
+ // This is most likely a marker token.
+ WriteMarkerToken(context, node);
break;
}
@@ -80,8 +80,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
if (string.IsNullOrEmpty(node.Content))
{
- // This is most likely a marker symbol.
- WriteMarkerSymbol(context, node);
+ // This is most likely a marker token.
+ WriteMarkerToken(context, node);
break;
}
@@ -102,8 +102,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
if (string.IsNullOrEmpty(node.Content))
{
- // This is most likely a marker symbol.
- WriteMarkerSymbol(context, node);
+ // This is most likely a marker token.
+ WriteMarkerToken(context, node);
break;
}
@@ -155,9 +155,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
context.CodeWriter.WriteLine("))();");
}
- private void WriteMarkerSymbol(CodeRenderingContext context, DirectiveTokenIntermediateNode node)
+ private void WriteMarkerToken(CodeRenderingContext context, DirectiveTokenIntermediateNode node)
{
- // We want to map marker symbols to a location in the generated document
+ // We want to map marker tokens to a location in the generated document
// that will provide CSharp intellisense.
context.AddSourceMappingFor(node);
context.CodeWriter.Write(" ");
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperHtmlAttributeIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperHtmlAttributeIntermediateNode.cs
index 11501c1a26..70a9c61fa6 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperHtmlAttributeIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperHtmlAttributeIntermediateNode.cs
@@ -44,5 +44,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteTagHelperHtmlAttribute(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(VariableName);
+
+ formatter.WriteProperty(nameof(VariableName), VariableName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperHtmlAttributeValueIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperHtmlAttributeValueIntermediateNode.cs
index 03bb175094..5faed5344b 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperHtmlAttributeValueIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperHtmlAttributeValueIntermediateNode.cs
@@ -74,5 +74,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteTagHelperHtmlAttributeValue(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(AttributeName);
+
+ formatter.WriteProperty(nameof(AttributeName), AttributeName);
+ formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
+ formatter.WriteProperty(nameof(Value), Value);
+ formatter.WriteProperty(nameof(VariableName), VariableName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperPropertyIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperPropertyIntermediateNode.cs
index d062328cb3..7db273a1d6 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperPropertyIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperPropertyIntermediateNode.cs
@@ -79,5 +79,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteTagHelperProperty(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(AttributeName);
+
+ formatter.WriteProperty(nameof(AttributeName), AttributeName);
+ formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
+ formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName);
+ formatter.WriteProperty(nameof(FieldName), FieldName);
+ formatter.WriteProperty(nameof(IsIndexerNameMatch), IsIndexerNameMatch.ToString());
+ formatter.WriteProperty(nameof(PropertyName), PropertyName);
+ formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
+ formatter.WriteProperty(nameof(VariableName), VariableName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperPropertyValueIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperPropertyValueIntermediateNode.cs
index d769905ff4..6516a22133 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperPropertyValueIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/PreallocatedTagHelperPropertyValueIntermediateNode.cs
@@ -50,5 +50,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteTagHelperPropertyValue(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(AttributeName);
+
+ formatter.WriteProperty(nameof(AttributeName), AttributeName);
+ formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
+ formatter.WriteProperty(nameof(Value), Value);
+ formatter.WriteProperty(nameof(VariableName), VariableName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/RazorCompiledItemAttributeIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/RazorCompiledItemAttributeIntermediateNode.cs
index 792fb4ecc1..ee82fca81a 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/RazorCompiledItemAttributeIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/RazorCompiledItemAttributeIntermediateNode.cs
@@ -48,5 +48,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteRazorCompiledItemAttribute(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteProperty(nameof(Identifier), Identifier);
+ formatter.WriteProperty(nameof(Kind), Kind);
+ formatter.WriteProperty(nameof(TypeName), TypeName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/RazorCompiledItemMetadataAttributeIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/RazorCompiledItemMetadataAttributeIntermediateNode.cs
index 0c05523028..52f7b92012 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/RazorCompiledItemMetadataAttributeIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/RazorCompiledItemMetadataAttributeIntermediateNode.cs
@@ -55,5 +55,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteRazorCompiledItemMetadataAttribute(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteProperty(nameof(Key), Key);
+ formatter.WriteProperty(nameof(Value), Value);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/SectionIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/SectionIntermediateNode.cs
index 7076e3df06..d3d4c10557 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/SectionIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Extensions/SectionIntermediateNode.cs
@@ -44,5 +44,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
extension.WriteSection(context, this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(SectionName);
+
+ formatter.WriteProperty(nameof(SectionName), SectionName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpCodeAttributeValueIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpCodeAttributeValueIntermediateNode.cs
index 23eff1efda..4a9ce5d1a9 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpCodeAttributeValueIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpCodeAttributeValueIntermediateNode.cs
@@ -20,5 +20,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitCSharpCodeAttributeValue(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteChildren(Children);
+
+ formatter.WriteProperty(nameof(Prefix), Prefix);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpCodeIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpCodeIntermediateNode.cs
index f15ce8b067..f888a561f8 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpCodeIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpCodeIntermediateNode.cs
@@ -18,5 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitCSharpCode(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteChildren(Children);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpExpressionAttributeValueIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpExpressionAttributeValueIntermediateNode.cs
index 6cc7a837e4..c058b67acc 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpExpressionAttributeValueIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpExpressionAttributeValueIntermediateNode.cs
@@ -20,5 +20,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitCSharpExpressionAttributeValue(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteChildren(Children);
+
+ formatter.WriteProperty(nameof(Prefix), Prefix);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpExpressionIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpExpressionIntermediateNode.cs
index 42b25a8df3..c4c9e02962 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpExpressionIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/CSharpExpressionIntermediateNode.cs
@@ -18,5 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitCSharpExpression(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteChildren(Children);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/ClassDeclarationIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/ClassDeclarationIntermediateNode.cs
index cf923eb429..ea178228c3 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/ClassDeclarationIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/ClassDeclarationIntermediateNode.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
@@ -29,5 +30,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitClassDeclaration(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(ClassName);
+
+ formatter.WriteProperty(nameof(BaseType), BaseType);
+ formatter.WriteProperty(nameof(ClassName), ClassName);
+ formatter.WriteProperty(nameof(Interfaces), string.Join(", ", Interfaces));
+ formatter.WriteProperty(nameof(Modifiers), string.Join(", ", Modifiers));
+ formatter.WriteProperty(nameof(TypeParameters), string.Join(", ", TypeParameters.Select(t => t.ParameterName)));
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DebuggerDisplayFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DebuggerDisplayFormatter.cs
new file mode 100644
index 0000000000..705148b8ab
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DebuggerDisplayFormatter.cs
@@ -0,0 +1,21 @@
+// 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.IO;
+
+namespace Microsoft.AspNetCore.Razor.Language.Intermediate
+{
+ internal class DebuggerDisplayFormatter : IntermediateNodeFormatterBase
+ {
+ public DebuggerDisplayFormatter()
+ {
+ Writer = new StringWriter();
+ ContentMode = FormatterContentMode.PreferContent;
+ }
+
+ public override string ToString()
+ {
+ return Writer.ToString();
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DirectiveIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DirectiveIntermediateNode.cs
index abb3a933de..6f954a89f8 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DirectiveIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DirectiveIntermediateNode.cs
@@ -20,5 +20,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
visitor.VisitDirective(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(DirectiveName);
+
+ formatter.WriteProperty(nameof(Directive), Directive?.DisplayName);
+ formatter.WriteProperty(nameof(DirectiveName), DirectiveName);
+ }
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DirectiveTokenIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DirectiveTokenIntermediateNode.cs
index 7db3769a08..a01ed25b80 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DirectiveTokenIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DirectiveTokenIntermediateNode.cs
@@ -1,6 +1,8 @@
// 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.Text;
+
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public sealed class DirectiveTokenIntermediateNode : IntermediateNode
@@ -15,5 +17,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
visitor.VisitDirectiveToken(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(Content);
+
+ formatter.WriteProperty(nameof(Content), Content);
+ }
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DocumentIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DocumentIntermediateNode.cs
index af071a7488..9570092e46 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DocumentIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/DocumentIntermediateNode.cs
@@ -25,5 +25,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitDocument(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(DocumentKind);
+
+ formatter.WriteProperty(nameof(DocumentKind), DocumentKind);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/FieldDeclarationIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/FieldDeclarationIntermediateNode.cs
index 6719f0768e..0d6b731390 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/FieldDeclarationIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/FieldDeclarationIntermediateNode.cs
@@ -25,5 +25,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitFieldDeclaration(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(FieldName);
+
+ formatter.WriteProperty(nameof(FieldName), FieldName);
+ formatter.WriteProperty(nameof(FieldType), FieldType);
+ formatter.WriteProperty(nameof(Modifiers), string.Join(" ", Modifiers));
+ }
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlAttributeIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlAttributeIntermediateNode.cs
index 46f772fce9..861b14b227 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlAttributeIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlAttributeIntermediateNode.cs
@@ -24,5 +24,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitHtmlAttribute(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(AttributeName);
+
+ formatter.WriteProperty(nameof(AttributeName), AttributeName);
+ formatter.WriteProperty(nameof(Prefix), Prefix);
+ formatter.WriteProperty(nameof(Suffix), Suffix);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlAttributeValueIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlAttributeValueIntermediateNode.cs
index d22eaa78aa..6fdc5247a3 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlAttributeValueIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlAttributeValueIntermediateNode.cs
@@ -20,5 +20,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitHtmlAttributeValue(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteChildren(Children);
+
+ formatter.WriteProperty(nameof(Prefix), Prefix);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlContentIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlContentIntermediateNode.cs
index d41b8a6a47..3a213fe460 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlContentIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/HtmlContentIntermediateNode.cs
@@ -18,5 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitHtml(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteChildren(Children);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNode.cs
index f8970d23f6..02c2976c55 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNode.cs
@@ -1,8 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System.Diagnostics;
+
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
+ [DebuggerDisplay("{DebuggerToString(),nq}")]
public abstract class IntermediateNode
{
private ItemCollection _annotations;
@@ -39,7 +42,30 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
public bool HasDiagnostics => _diagnostics != null && _diagnostics.Count > 0;
public SourceSpan? Source { get; set; }
-
+
public abstract void Accept(IntermediateNodeVisitor visitor);
+
+ [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
+ private string Tree
+ {
+ get
+ {
+ var formatter = new DebuggerDisplayFormatter();
+ formatter.FormatTree(this);
+ return formatter.ToString();
+ }
+ }
+
+ private string DebuggerToString()
+ {
+ var formatter = new DebuggerDisplayFormatter();
+ formatter.FormatNode(this);
+ return formatter.ToString();
+ }
+
+
+ public virtual void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNodeFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNodeFormatter.cs
new file mode 100644
index 0000000000..c453dbe6e9
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNodeFormatter.cs
@@ -0,0 +1,16 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+
+namespace Microsoft.AspNetCore.Razor.Language.Intermediate
+{
+ public abstract class IntermediateNodeFormatter
+ {
+ public abstract void WriteChildren(IntermediateNodeCollection children);
+
+ public abstract void WriteContent(string content);
+
+ public abstract void WriteProperty(string key, string value);
+ }
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNodeFormatterBase.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNodeFormatterBase.cs
new file mode 100644
index 0000000000..54846acaaf
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateNodeFormatterBase.cs
@@ -0,0 +1,180 @@
+// 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.IO;
+using System.Linq;
+
+namespace Microsoft.AspNetCore.Razor.Language.Intermediate
+{
+ internal class IntermediateNodeFormatterBase : IntermediateNodeFormatter
+ {
+ private string _content;
+ private Dictionary _properties = new Dictionary(StringComparer.Ordinal);
+
+ protected FormatterContentMode ContentMode { get; set; }
+
+ protected bool IncludeSource { get; set; }
+
+ protected TextWriter Writer { get; set; }
+
+ public override void WriteChildren(IntermediateNodeCollection children)
+ {
+ if (children == null)
+ {
+ throw new ArgumentNullException(nameof(children));
+ }
+
+ Writer.Write(" ");
+ Writer.Write("\"");
+ for (var i = 0; i < children.Count; i++)
+ {
+ var child = children[i] as IntermediateToken;
+ if (child != null)
+ {
+ Writer.Write(EscapeNewlines(child.Content));
+ }
+ }
+ Writer.Write("\"");
+ }
+
+ public override void WriteContent(string content)
+ {
+ if (content == null)
+ {
+ return;
+ }
+
+ _content = EscapeNewlines(content);
+ }
+
+ public override void WriteProperty(string key, string value)
+ {
+ if (key == null)
+ {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ if (value == null)
+ {
+ return;
+ }
+
+ _properties.Add(key, EscapeNewlines(value));
+ }
+
+ public void FormatNode(IntermediateNode node)
+ {
+ if (node == null)
+ {
+ throw new ArgumentNullException(nameof(node));
+ }
+
+ BeginNode(node);
+ node.FormatNode(this);
+ EndNode(node);
+ }
+
+ public void FormatTree(IntermediateNode node)
+ {
+ if (node == null)
+ {
+ throw new ArgumentNullException(nameof(node));
+ }
+
+ var visitor = new FormatterVisitor(this);
+ visitor.Visit(node);
+ }
+
+ private void BeginNode(IntermediateNode node)
+ {
+ Writer.Write(GetShortName(node));
+
+ if (IncludeSource)
+ {
+ Writer.Write(" ");
+ Writer.Write(node.Source?.ToString() ?? "(n/a)");
+ }
+ }
+
+ private void EndNode(IntermediateNode node)
+ {
+ if (_content != null && (_properties.Count == 0 || ContentMode == FormatterContentMode.PreferContent))
+ {
+ Writer.Write(" ");
+ Writer.Write("\"");
+ Writer.Write(EscapeNewlines(_content));
+ Writer.Write("\"");
+ }
+
+ if (_properties.Count > 0 && (_content == null || ContentMode == FormatterContentMode.PreferProperties))
+ {
+ Writer.Write(" ");
+ Writer.Write("{ ");
+ Writer.Write(string.Join(", ", _properties.Select(kvp => $"{kvp.Key}: \"{kvp.Value}\"")));
+ Writer.Write(" }");
+ }
+
+ _content = null;
+ _properties.Clear();
+ }
+
+ private string GetShortName(IntermediateNode node)
+ {
+ var typeName = node.GetType().Name;
+ return
+ typeName.EndsWith(nameof(IntermediateNode), StringComparison.Ordinal) ?
+ typeName.Substring(0, typeName.Length - nameof(IntermediateNode).Length) :
+ typeName;
+ }
+
+ private string EscapeNewlines(string content)
+ {
+ return content.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t");
+ }
+
+ // Depending on the usage of the formatter we might prefer thoroughness (properties)
+ // or brevity (content). Generally if a node has a single string that provides value
+ // it has content.
+ //
+ // Some nodes have neither: TagHelperBody
+ // Some nodes have content: HtmlContent
+ // Some nodes have properties: Document
+ // Some nodes have both: TagHelperProperty
+ protected enum FormatterContentMode
+ {
+ PreferContent,
+ PreferProperties,
+ }
+
+ protected class FormatterVisitor : IntermediateNodeWalker
+ {
+ private const int IndentSize = 2;
+
+ private readonly IntermediateNodeFormatterBase _formatter;
+ private int _indent = 0;
+
+ public FormatterVisitor(IntermediateNodeFormatterBase formatter)
+ {
+ _formatter = formatter;
+ }
+
+ public override void VisitDefault(IntermediateNode node)
+ {
+ // Indent
+ for (var i = 0; i < _indent; i++)
+ {
+ _formatter.Writer.Write(' ');
+ }
+ _formatter.FormatNode(node);
+ _formatter.Writer.WriteLine();
+
+ // Process children
+ _indent += IndentSize;
+ base.VisitDefault(node);
+ _indent -= IndentSize;
+ }
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateToken.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateToken.cs
index d2da5fae18..d60643d24a 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateToken.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/IntermediateToken.cs
@@ -26,7 +26,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitToken(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(Content);
+
+ formatter.WriteProperty(nameof(Content), Content);
+ }
}
-}
-
-
+}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/MalformedDirectiveIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/MalformedDirectiveIntermediateNode.cs
index 22d23927ca..d37a2316a5 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/MalformedDirectiveIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/MalformedDirectiveIntermediateNode.cs
@@ -20,5 +20,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
visitor.VisitMalformedDirective(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(DirectiveName);
+
+ formatter.WriteProperty(nameof(Directive), Directive?.DisplayName);
+ formatter.WriteProperty(nameof(DirectiveName), DirectiveName);
+ }
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/MethodDeclarationIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/MethodDeclarationIntermediateNode.cs
index e1a039c5e0..ad15636824 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/MethodDeclarationIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/MethodDeclarationIntermediateNode.cs
@@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using System.Text;
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
@@ -28,5 +30,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitMethodDeclaration(this);
}
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(MethodName);
+
+ formatter.WriteProperty(nameof(MethodName), MethodName);
+ formatter.WriteProperty(nameof(Modifiers), string.Join(", ", Modifiers));
+ formatter.WriteProperty(nameof(Parameters), string.Join(", ", Parameters.Select(FormatMethodParameter)));
+ formatter.WriteProperty(nameof(ReturnType), ReturnType);
+ }
+
+ private static string FormatMethodParameter(MethodParameter parameter)
+ {
+ var builder = new StringBuilder();
+ for (var i = 0; i t.DisplayName)));
+ formatter.WriteProperty(nameof(TagMode), TagMode.ToString());
+ formatter.WriteProperty(nameof(TagName), TagName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/TagHelperPropertyIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/TagHelperPropertyIntermediateNode.cs
index 073571b185..1a755fe6f9 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/TagHelperPropertyIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/TagHelperPropertyIntermediateNode.cs
@@ -28,5 +28,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitTagHelperProperty(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(AttributeName);
+
+ formatter.WriteProperty(nameof(AttributeName), AttributeName);
+ formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
+ formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName);
+ formatter.WriteProperty(nameof(IsIndexerNameMatch), IsIndexerNameMatch.ToString());
+ formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/UsingDirectiveIntermediateNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/UsingDirectiveIntermediateNode.cs
index 4199c95e97..67c64f79e7 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/UsingDirectiveIntermediateNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Intermediate/UsingDirectiveIntermediateNode.cs
@@ -20,5 +20,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitUsingDirective(this);
}
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ formatter.WriteContent(Content);
+
+ formatter.WriteProperty(nameof(Content), Content);
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/AddTagHelperChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/AddTagHelperChunkGenerator.cs
index 8c9d98633b..f446be12c0 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/AddTagHelperChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/AddTagHelperChunkGenerator.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
@@ -63,5 +64,28 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return combiner.CombinedHash;
}
+
+ public override string ToString()
+ {
+ var builder = new StringBuilder("AddTagHelper:{");
+ builder.Append(LookupText);
+ builder.Append(";");
+ builder.Append(DirectiveText);
+ builder.Append(";");
+ builder.Append(TypePattern);
+ builder.Append(";");
+ builder.Append(AssemblyName);
+ builder.Append("}");
+
+ if (Diagnostics.Count > 0)
+ {
+ builder.Append(" [");
+ var ids = string.Join(", ", Diagnostics.Select(diagnostic => $"{diagnostic.Id}{diagnostic.Span}"));
+ builder.Append(ids);
+ builder.Append("]");
+ }
+
+ return builder.ToString();
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/AutoCompleteEditHandler.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/AutoCompleteEditHandler.cs
index bd73bbc1b2..3aecb29a52 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/AutoCompleteEditHandler.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/AutoCompleteEditHandler.cs
@@ -11,18 +11,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
private static readonly int TypeHashCode = typeof(AutoCompleteEditHandler).GetHashCode();
- public AutoCompleteEditHandler(Func> tokenizer)
+ public AutoCompleteEditHandler(Func> tokenizer)
: base(tokenizer)
{
}
- public AutoCompleteEditHandler(Func> tokenizer, bool autoCompleteAtEndOfSpan)
+ public AutoCompleteEditHandler(Func> tokenizer, bool autoCompleteAtEndOfSpan)
: this(tokenizer)
{
AutoCompleteAtEndOfSpan = autoCompleteAtEndOfSpan;
}
- public AutoCompleteEditHandler(Func> tokenizer, AcceptedCharactersInternal accepted)
+ public AutoCompleteEditHandler(Func> tokenizer, AcceptedCharactersInternal accepted)
: base(tokenizer, accepted)
{
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs
index 13d8352ba6..a75c983fec 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs
@@ -8,14 +8,14 @@ using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal class CSharpCodeParser : TokenizerBackedParser
+ internal class CSharpCodeParser : TokenizerBackedParser
{
private static HashSet InvalidNonWhitespaceNameCharacters = new HashSet(new[]
{
'@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*'
});
- private static readonly Func IsValidStatementSpacingSymbol =
+ private static readonly Func IsValidStatementSpacingToken =
IsSpacingToken(includeNewLines: true, includeComments: true);
internal static readonly DirectiveDescriptor AddTagHelperDirectiveDescriptor = DirectiveDescriptor.CreateDirective(
@@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public bool IsNested { get; set; }
- protected override bool SymbolTypeEquals(CSharpSymbolType x, CSharpSymbolType y) => x == y;
+ protected override bool TokenTypeEquals(CSharpTokenType x, CSharpTokenType y) => x == y;
protected void MapDirectives(Action handler, params string[] directives)
{
@@ -161,16 +161,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
[Conditional("DEBUG")]
internal void Assert(CSharpKeyword expectedKeyword)
{
- Debug.Assert(CurrentSymbol.Type == CSharpSymbolType.Keyword &&
- CurrentSymbol.Keyword.HasValue &&
- CurrentSymbol.Keyword.Value == expectedKeyword);
+ Debug.Assert(CurrentToken.Type == CSharpTokenType.Keyword &&
+ CurrentToken.Keyword.HasValue &&
+ CurrentToken.Keyword.Value == expectedKeyword);
}
protected internal bool At(CSharpKeyword keyword)
{
- return At(CSharpSymbolType.Keyword) &&
- CurrentSymbol.Keyword.HasValue &&
- CurrentSymbol.Keyword.Value == keyword;
+ return At(CSharpTokenType.Keyword) &&
+ CurrentToken.Keyword.HasValue &&
+ CurrentToken.Keyword.Value == keyword;
}
protected internal bool AcceptIf(CSharpKeyword keyword)
@@ -183,11 +183,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
- protected static Func IsSpacingToken(bool includeNewLines, bool includeComments)
+ protected static Func IsSpacingToken(bool includeNewLines, bool includeComments)
{
- return sym => sym.Type == CSharpSymbolType.WhiteSpace ||
- (includeNewLines && sym.Type == CSharpSymbolType.NewLine) ||
- (includeComments && sym.Type == CSharpSymbolType.Comment);
+ return token => token.Type == CSharpTokenType.WhiteSpace ||
+ (includeNewLines && token.Type == CSharpTokenType.NewLine) ||
+ (includeComments && token.Type == CSharpTokenType.Comment);
}
public override void ParseBlock()
@@ -208,27 +208,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
- var current = CurrentSymbol;
- if (At(CSharpSymbolType.StringLiteral) &&
- CurrentSymbol.Content.Length > 0 &&
- CurrentSymbol.Content[0] == SyntaxConstants.TransitionCharacter)
+ var current = CurrentToken;
+ if (At(CSharpTokenType.StringLiteral) &&
+ CurrentToken.Content.Length > 0 &&
+ CurrentToken.Content[0] == SyntaxConstants.TransitionCharacter)
{
- var split = Language.SplitSymbol(CurrentSymbol, 1, CSharpSymbolType.Transition);
+ var split = Language.SplitToken(CurrentToken, 1, CSharpTokenType.Transition);
current = split.Item1;
// Back up to the end of the transition
Context.Source.Position -= split.Item2.Content.Length;
NextToken();
}
- else if (At(CSharpSymbolType.Transition))
+ else if (At(CSharpTokenType.Transition))
{
NextToken();
}
// Accept "@" if we see it, but if we don't, that's OK. We assume we were started for a good reason
- if (current.Type == CSharpSymbolType.Transition)
+ if (current.Type == CSharpTokenType.Transition)
{
- if (Span.Symbols.Count > 0)
+ if (Span.Tokens.Count > 0)
{
Output(SpanKindInternal.Code);
}
@@ -251,9 +251,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
span.ChunkGenerator = new StatementChunkGenerator();
}
- private void AtTransition(CSharpSymbol current)
+ private void AtTransition(CSharpToken current)
{
- Debug.Assert(current.Type == CSharpSymbolType.Transition);
+ Debug.Assert(current.Type == CSharpTokenType.Transition);
Accept(current);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
Span.ChunkGenerator = SpanChunkGenerator.Null;
@@ -273,16 +273,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// What type of block is this?
if (!EndOfFile)
{
- if (CurrentSymbol.Type == CSharpSymbolType.LeftParenthesis)
+ if (CurrentToken.Type == CSharpTokenType.LeftParenthesis)
{
Context.Builder.CurrentBlock.Type = BlockKindInternal.Expression;
Context.Builder.CurrentBlock.ChunkGenerator = new ExpressionChunkGenerator();
ExplicitExpression();
return;
}
- else if (CurrentSymbol.Type == CSharpSymbolType.Identifier)
+ else if (CurrentToken.Type == CSharpTokenType.Identifier)
{
- if (TryGetDirectiveHandler(CurrentSymbol.Content, out var handler))
+ if (TryGetDirectiveHandler(CurrentToken.Content, out var handler))
{
Span.ChunkGenerator = SpanChunkGenerator.Null;
handler();
@@ -291,13 +291,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else
{
if (string.Equals(
- CurrentSymbol.Content,
+ CurrentToken.Content,
SyntaxConstants.CSharp.HelperKeyword,
StringComparison.Ordinal))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_HelperDirectiveNotAvailable(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length)));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length)));
}
Context.Builder.CurrentBlock.Type = BlockKindInternal.Expression;
@@ -306,9 +306,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
}
- else if (CurrentSymbol.Type == CSharpSymbolType.Keyword)
+ else if (CurrentToken.Type == CSharpTokenType.Keyword)
{
- if (TryGetDirectiveHandler(CurrentSymbol.Content, out var handler))
+ if (TryGetDirectiveHandler(CurrentToken.Content, out var handler))
{
Span.ChunkGenerator = SpanChunkGenerator.Null;
handler();
@@ -320,7 +320,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
}
- else if (CurrentSymbol.Type == CSharpSymbolType.LeftBrace)
+ else if (CurrentToken.Type == CSharpTokenType.LeftBrace)
{
VerbatimBlock();
return;
@@ -330,7 +330,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Invalid character
Context.Builder.CurrentBlock.Type = BlockKindInternal.Expression;
Context.Builder.CurrentBlock.ChunkGenerator = new ExpressionChunkGenerator();
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
Span.ChunkGenerator = new ExpressionChunkGenerator();
Span.EditHandler = new ImplicitExpressionEditHandler(
Language.TokenizeString,
@@ -339,11 +339,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
AcceptedCharacters = AcceptedCharactersInternal.NonWhiteSpace
};
- if (At(CSharpSymbolType.WhiteSpace) || At(CSharpSymbolType.NewLine))
+ if (At(CSharpTokenType.WhiteSpace) || At(CSharpTokenType.NewLine))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnexpectedWhiteSpaceAtStartOfCodeBlock(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length)));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length)));
}
else if (EndOfFile)
{
@@ -355,8 +355,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnexpectedCharacterAtStartOfCodeBlock(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length),
- CurrentSymbol.Content));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length),
+ CurrentToken.Content));
}
}
finally
@@ -369,7 +369,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void VerbatimBlock()
{
- Assert(CSharpSymbolType.LeftBrace);
+ Assert(CSharpTokenType.LeftBrace);
var block = new Block(Resources.BlockName_Code, CurrentStart);
AcceptAndMoveNext();
@@ -384,14 +384,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
CodeBlock(false, block);
Span.ChunkGenerator = new StatementChunkGenerator();
- AddMarkerSymbolIfNecessary();
- if (!At(CSharpSymbolType.RightBrace))
+ AddMarkerTokenIfNecessary();
+ if (!At(CSharpTokenType.RightBrace))
{
editHandler.AutoCompleteString = "}";
}
Output(SpanKindInternal.Code);
- if (Optional(CSharpSymbolType.RightBrace))
+ if (Optional(CSharpTokenType.RightBrace))
{
// Set up the "}" span
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
@@ -401,8 +401,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (!IsNested)
{
EnsureCurrent();
- if (At(CSharpSymbolType.NewLine) ||
- (At(CSharpSymbolType.WhiteSpace) && NextIs(CSharpSymbolType.NewLine)))
+ if (At(CSharpTokenType.NewLine) ||
+ (At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.NewLine)))
{
Context.NullGenerateWhitespaceAndNewLine = true;
}
@@ -456,13 +456,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
if (!EndOfFile)
{
- if (CurrentSymbol.Type == CSharpSymbolType.LeftParenthesis ||
- CurrentSymbol.Type == CSharpSymbolType.LeftBracket)
+ if (CurrentToken.Type == CSharpTokenType.LeftParenthesis ||
+ CurrentToken.Type == CSharpTokenType.LeftBracket)
{
// If we end within "(", whitespace is fine
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
- CSharpSymbolType right;
+ CSharpTokenType right;
bool success;
using (PushSpanConfig((span, prev) =>
@@ -471,13 +471,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
}))
{
- right = Language.FlipBracket(CurrentSymbol.Type);
+ right = Language.FlipBracket(CurrentToken.Type);
success = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates);
}
if (!success)
{
- AcceptUntil(CSharpSymbolType.LessThan);
+ AcceptUntil(CSharpTokenType.LessThan);
}
if (At(right))
{
@@ -488,22 +488,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
return MethodCallOrArrayIndex(acceptedCharacters);
}
- if (At(CSharpSymbolType.QuestionMark))
+ if (At(CSharpTokenType.QuestionMark))
{
var next = Lookahead(count: 1);
if (next != null)
{
- if (next.Type == CSharpSymbolType.Dot)
+ if (next.Type == CSharpTokenType.Dot)
{
// Accept null conditional dot operator (?.).
AcceptAndMoveNext();
AcceptAndMoveNext();
// If the next piece after the ?. is a keyword or identifier then we want to continue.
- return At(CSharpSymbolType.Identifier) || At(CSharpSymbolType.Keyword);
+ return At(CSharpTokenType.Identifier) || At(CSharpTokenType.Keyword);
}
- else if (next.Type == CSharpSymbolType.LeftBracket)
+ else if (next.Type == CSharpTokenType.LeftBracket)
{
// We're at the ? for a null conditional bracket operator (?[).
AcceptAndMoveNext();
@@ -513,12 +513,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
}
- else if (At(CSharpSymbolType.Dot))
+ else if (At(CSharpTokenType.Dot))
{
- var dot = CurrentSymbol;
+ var dot = CurrentToken;
if (NextToken())
{
- if (At(CSharpSymbolType.Identifier) || At(CSharpSymbolType.Keyword))
+ if (At(CSharpTokenType.Identifier) || At(CSharpTokenType.Keyword))
{
// Accept the dot and return to the start
Accept(dot);
@@ -526,7 +526,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else
{
- // Put the symbol back
+ // Put the token back
PutCurrentBack();
}
}
@@ -540,7 +540,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Accept(dot);
}
}
- else if (!At(CSharpSymbolType.WhiteSpace) && !At(CSharpSymbolType.NewLine))
+ else if (!At(CSharpTokenType.WhiteSpace) && !At(CSharpTokenType.NewLine))
{
PutCurrentBack();
}
@@ -564,7 +564,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
if (insertMarkerIfNecessary && Context.Builder.LastAcceptedCharacters != AcceptedCharactersInternal.Any)
{
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
}
EnsureCurrent();
@@ -587,8 +587,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void CaptureWhitespaceAtEndOfCodeOnlyLine()
{
- var whitespace = ReadWhile(sym => sym.Type == CSharpSymbolType.WhiteSpace);
- if (At(CSharpSymbolType.NewLine))
+ var whitespace = ReadWhile(token => token.Type == CSharpTokenType.WhiteSpace);
+ if (At(CSharpTokenType.NewLine))
{
Accept(whitespace);
AcceptAndMoveNext();
@@ -610,7 +610,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void ExplicitExpression()
{
var block = new Block(Resources.BlockName_ExplicitExpression, CurrentStart);
- Assert(CSharpSymbolType.LeftParenthesis);
+ Assert(CSharpTokenType.LeftParenthesis);
AcceptAndMoveNext();
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
Span.ChunkGenerator = SpanChunkGenerator.Null;
@@ -621,28 +621,28 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
BalancingModes.BacktrackOnFailure |
BalancingModes.NoErrorOnFailure |
BalancingModes.AllowCommentsAndTemplates,
- CSharpSymbolType.LeftParenthesis,
- CSharpSymbolType.RightParenthesis,
+ CSharpTokenType.LeftParenthesis,
+ CSharpTokenType.RightParenthesis,
block.Start);
if (!success)
{
- AcceptUntil(CSharpSymbolType.LessThan);
+ AcceptUntil(CSharpTokenType.LessThan);
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF(
new SourceSpan(block.Start, contentLength: 1 /* ( */), block.Name, ")", "("));
}
- // If necessary, put an empty-content marker symbol here
- if (Span.Symbols.Count == 0)
+ // If necessary, put an empty-content marker token here
+ if (Span.Tokens.Count == 0)
{
- Accept(new CSharpSymbol(string.Empty, CSharpSymbolType.Unknown));
+ Accept(new CSharpToken(string.Empty, CSharpTokenType.Unknown));
}
// Output the content span and then capture the ")"
Output(SpanKindInternal.Code);
}
- Optional(CSharpSymbolType.RightParenthesis);
+ Optional(CSharpTokenType.RightParenthesis);
if (!EndOfFile)
{
PutCurrentBack();
@@ -701,19 +701,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// No embedded transitions in C#, so ignore that param
return allowTemplatesAndComments
- && ((Language.IsTransition(CurrentSymbol)
- && NextIs(CSharpSymbolType.LessThan, CSharpSymbolType.Colon, CSharpSymbolType.DoubleColon))
- || Language.IsCommentStart(CurrentSymbol));
+ && ((Language.IsTransition(CurrentToken)
+ && NextIs(CSharpTokenType.LessThan, CSharpTokenType.Colon, CSharpTokenType.DoubleColon))
+ || Language.IsCommentStart(CurrentToken));
}
protected override void HandleEmbeddedTransition()
{
- if (Language.IsTransition(CurrentSymbol))
+ if (Language.IsTransition(CurrentToken))
{
PutCurrentBack();
Template();
}
- else if (Language.IsCommentStart(CurrentSymbol))
+ else if (Language.IsCommentStart(CurrentToken))
{
RazorComment();
}
@@ -761,7 +761,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_ReservedWord(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length), CurrentSymbol.Content));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length), CurrentToken.Content));
AcceptAndMoveNext();
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
@@ -783,12 +783,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void CaseStatement(bool topLevel)
{
- Assert(CSharpSymbolType.Keyword);
- Debug.Assert(CurrentSymbol.Keyword != null &&
- (CurrentSymbol.Keyword.Value == CSharpKeyword.Case ||
- CurrentSymbol.Keyword.Value == CSharpKeyword.Default));
- AcceptUntil(CSharpSymbolType.Colon);
- Optional(CSharpSymbolType.Colon);
+ Assert(CSharpTokenType.Keyword);
+ Debug.Assert(CurrentToken.Keyword != null &&
+ (CurrentToken.Keyword.Value == CSharpKeyword.Case ||
+ CurrentToken.Keyword.Value == CSharpKeyword.Default));
+ AcceptUntil(CSharpTokenType.Colon);
+ Optional(CSharpTokenType.Colon);
}
private void DoStatement(bool topLevel)
@@ -813,7 +813,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Assert(CSharpKeyword.While);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
- if (AcceptCondition() && Optional(CSharpSymbolType.Semicolon))
+ if (AcceptCondition() && Optional(CSharpTokenType.Semicolon))
{
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
}
@@ -828,16 +828,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void UsingKeyword(bool topLevel)
{
Assert(CSharpKeyword.Using);
- var block = new Block(CurrentSymbol, CurrentStart);
+ var block = new Block(CurrentToken, CurrentStart);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
- if (At(CSharpSymbolType.LeftParenthesis))
+ if (At(CSharpTokenType.LeftParenthesis))
{
// using ( ==> Using Statement
UsingStatement(block);
}
- else if (At(CSharpSymbolType.Identifier) || At(CSharpKeyword.Static))
+ else if (At(CSharpTokenType.Identifier) || At(CSharpKeyword.Static))
{
// using Identifier ==> Using Declaration
if (!topLevel)
@@ -865,16 +865,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Context.Builder.CurrentBlock.Type = BlockKindInternal.Directive;
var start = CurrentStart;
- if (At(CSharpSymbolType.Identifier))
+ if (At(CSharpTokenType.Identifier))
{
// non-static using
NamespaceOrTypeName();
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
- if (At(CSharpSymbolType.Assign))
+ if (At(CSharpTokenType.Assign))
{
// Alias
Accept(whitespace);
- Assert(CSharpSymbolType.Assign);
+ Assert(CSharpTokenType.Assign);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
@@ -898,13 +898,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.AnyExceptNewline;
Span.ChunkGenerator = new AddImportChunkGenerator(new LocationTagged(
- string.Concat(Span.Symbols.Skip(1).Select(s => s.Content)),
+ string.Concat(Span.Tokens.Skip(1).Select(s => s.Content)),
start));
// Optional ";"
if (EnsureCurrent())
{
- Optional(CSharpSymbolType.Semicolon);
+ Optional(CSharpTokenType.Semicolon);
}
}
@@ -920,15 +920,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var tokens = ReadWhile(token =>
{
var type = token.Type;
- if ((expectingDot && type == CSharpSymbolType.Dot) ||
- (!expectingDot && type == CSharpSymbolType.Identifier))
+ if ((expectingDot && type == CSharpTokenType.Dot) ||
+ (!expectingDot && type == CSharpTokenType.Identifier))
{
expectingDot = !expectingDot;
return true;
}
- if (type != CSharpSymbolType.WhiteSpace &&
- type != CSharpSymbolType.NewLine)
+ if (type != CSharpTokenType.WhiteSpace &&
+ type != CSharpTokenType.NewLine)
{
expectingDot = false;
currentIdentifierLength += token.Content.Length;
@@ -966,69 +966,69 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected bool NamespaceOrTypeName()
{
- if (Optional(CSharpSymbolType.LeftParenthesis))
+ if (Optional(CSharpTokenType.LeftParenthesis))
{
- while (!Optional(CSharpSymbolType.RightParenthesis) && !EndOfFile)
+ while (!Optional(CSharpTokenType.RightParenthesis) && !EndOfFile)
{
- Optional(CSharpSymbolType.WhiteSpace);
+ Optional(CSharpTokenType.WhiteSpace);
if (!NamespaceOrTypeName())
{
return false;
}
- Optional(CSharpSymbolType.WhiteSpace);
- Optional(CSharpSymbolType.Identifier);
- Optional(CSharpSymbolType.WhiteSpace);
- Optional(CSharpSymbolType.Comma);
+ Optional(CSharpTokenType.WhiteSpace);
+ Optional(CSharpTokenType.Identifier);
+ Optional(CSharpTokenType.WhiteSpace);
+ Optional(CSharpTokenType.Comma);
}
- if (At(CSharpSymbolType.WhiteSpace) && NextIs(CSharpSymbolType.QuestionMark))
+ if (At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.QuestionMark))
{
// Only accept the whitespace if we are going to consume the next token.
AcceptAndMoveNext();
}
- Optional(CSharpSymbolType.QuestionMark); // Nullable
+ Optional(CSharpTokenType.QuestionMark); // Nullable
return true;
}
- else if (Optional(CSharpSymbolType.Identifier) || Optional(CSharpSymbolType.Keyword))
+ else if (Optional(CSharpTokenType.Identifier) || Optional(CSharpTokenType.Keyword))
{
- if (Optional(CSharpSymbolType.DoubleColon))
+ if (Optional(CSharpTokenType.DoubleColon))
{
- if (!Optional(CSharpSymbolType.Identifier))
+ if (!Optional(CSharpTokenType.Identifier))
{
- Optional(CSharpSymbolType.Keyword);
+ Optional(CSharpTokenType.Keyword);
}
}
- if (At(CSharpSymbolType.LessThan))
+ if (At(CSharpTokenType.LessThan))
{
TypeArgumentList();
}
- if (Optional(CSharpSymbolType.Dot))
+ if (Optional(CSharpTokenType.Dot))
{
NamespaceOrTypeName();
}
- if (At(CSharpSymbolType.WhiteSpace) && NextIs(CSharpSymbolType.QuestionMark))
+ if (At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.QuestionMark))
{
// Only accept the whitespace if we are going to consume the next token.
AcceptAndMoveNext();
}
- Optional(CSharpSymbolType.QuestionMark); // Nullable
+ Optional(CSharpTokenType.QuestionMark); // Nullable
- if (At(CSharpSymbolType.WhiteSpace) && NextIs(CSharpSymbolType.LeftBracket))
+ if (At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.LeftBracket))
{
// Only accept the whitespace if we are going to consume the next token.
AcceptAndMoveNext();
}
- while (At(CSharpSymbolType.LeftBracket))
+ while (At(CSharpTokenType.LeftBracket))
{
Balance(BalancingModes.None);
- Optional(CSharpSymbolType.RightBracket);
+ Optional(CSharpTokenType.RightBracket);
}
return true;
}
@@ -1040,14 +1040,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void TypeArgumentList()
{
- Assert(CSharpSymbolType.LessThan);
+ Assert(CSharpTokenType.LessThan);
Balance(BalancingModes.None);
- Optional(CSharpSymbolType.GreaterThan);
+ Optional(CSharpTokenType.GreaterThan);
}
private void UsingStatement(Block block)
{
- Assert(CSharpSymbolType.LeftParenthesis);
+ Assert(CSharpTokenType.LeftParenthesis);
// Parse condition
if (AcceptCondition())
@@ -1136,7 +1136,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return;
}
- var block = new Block(CurrentSymbol, CurrentStart);
+ var block = new Block(CurrentToken, CurrentStart);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
@@ -1159,13 +1159,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (!EndOfFile)
{
// Check for "{" to make sure we're at a block
- if (!At(CSharpSymbolType.LeftBrace))
+ if (!At(CSharpTokenType.LeftBrace))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_SingleLineControlFlowStatementsNotAllowed(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length),
- Language.GetSample(CSharpSymbolType.LeftBrace),
- CurrentSymbol.Content));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length),
+ Language.GetSample(CSharpTokenType.LeftBrace),
+ CurrentToken.Content));
}
// Parse the statement and then we're done
@@ -1175,8 +1175,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void UnconditionalBlock()
{
- Assert(CSharpSymbolType.Keyword);
- var block = new Block(CurrentSymbol, CurrentStart);
+ Assert(CSharpTokenType.Keyword);
+ var block = new Block(CurrentToken, CurrentStart);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
ExpectCodeBlock(block);
@@ -1186,22 +1186,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Assert(CSharpKeyword.Catch);
- var block = new Block(CurrentSymbol, CurrentStart);
+ var block = new Block(CurrentToken, CurrentStart);
// Accept "catch"
AcceptAndMoveNext();
- AcceptWhile(IsValidStatementSpacingSymbol);
+ AcceptWhile(IsValidStatementSpacingToken);
// Parse the catch condition if present. If not present, let the C# compiler complain.
if (AcceptCondition())
{
- AcceptWhile(IsValidStatementSpacingSymbol);
+ AcceptWhile(IsValidStatementSpacingToken);
if (At(CSharpKeyword.When))
{
// Accept "when".
AcceptAndMoveNext();
- AcceptWhile(IsValidStatementSpacingSymbol);
+ AcceptWhile(IsValidStatementSpacingToken);
// Parse the filter condition if present. If not present, let the C# compiler complain.
if (!AcceptCondition())
@@ -1210,7 +1210,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
- AcceptWhile(IsValidStatementSpacingSymbol);
+ AcceptWhile(IsValidStatementSpacingToken);
}
ExpectCodeBlock(block);
@@ -1219,8 +1219,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void ConditionalBlock(bool topLevel)
{
- Assert(CSharpSymbolType.Keyword);
- var block = new Block(CurrentSymbol, CurrentStart);
+ Assert(CSharpTokenType.Keyword);
+ var block = new Block(CurrentToken, CurrentStart);
ConditionalBlock(block);
if (topLevel)
{
@@ -1243,16 +1243,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private bool AcceptCondition()
{
- if (At(CSharpSymbolType.LeftParenthesis))
+ if (At(CSharpTokenType.LeftParenthesis))
{
var complete = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates);
if (!complete)
{
- AcceptUntil(CSharpSymbolType.NewLine);
+ AcceptUntil(CSharpTokenType.NewLine);
}
else
{
- Optional(CSharpSymbolType.RightParenthesis);
+ Optional(CSharpTokenType.RightParenthesis);
}
return complete;
}
@@ -1280,16 +1280,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
- var type = CurrentSymbol.Type;
+ var type = CurrentToken.Type;
var loc = CurrentStart;
// Both cases @: and @:: are triggered as markup, second colon in second case will be triggered as a plain text
- var isSingleLineMarkup = type == CSharpSymbolType.Transition &&
- (NextIs(CSharpSymbolType.Colon, CSharpSymbolType.DoubleColon));
+ var isSingleLineMarkup = type == CSharpTokenType.Transition &&
+ (NextIs(CSharpTokenType.Colon, CSharpTokenType.DoubleColon));
var isMarkup = isSingleLineMarkup ||
- type == CSharpSymbolType.LessThan ||
- (type == CSharpSymbolType.Transition && NextIs(CSharpSymbolType.LessThan));
+ type == CSharpTokenType.LessThan ||
+ (type == CSharpTokenType.Transition && NextIs(CSharpTokenType.LessThan));
if (Context.DesignTimeMode || !isMarkup)
{
@@ -1301,14 +1301,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else
{
- var nextSymbol = Lookahead(1);
+ var nextToken = Lookahead(1);
// MARKUP owns whitespace EXCEPT in DesignTimeMode.
PutCurrentBack();
// Put back the whitespace unless it precedes a '' tag.
- if (nextSymbol != null &&
- !string.Equals(nextSymbol.Content, SyntaxConstants.TextTagName, StringComparison.Ordinal))
+ if (nextToken != null &&
+ !string.Equals(nextToken.Content, SyntaxConstants.TextTagName, StringComparison.Ordinal))
{
PutBack(lastWhitespace);
}
@@ -1321,7 +1321,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (isMarkup)
{
- if (type == CSharpSymbolType.Transition && !isSingleLineMarkup)
+ if (type == CSharpTokenType.Transition && !isSingleLineMarkup)
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_AtInCodeMustBeFollowedByColonParenOrIdentifierStart(
@@ -1330,8 +1330,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Markup block
Output(SpanKindInternal.Code);
- if (Context.DesignTimeMode && CurrentSymbol != null &&
- (CurrentSymbol.Type == CSharpSymbolType.LessThan || CurrentSymbol.Type == CSharpSymbolType.Transition))
+ if (Context.DesignTimeMode && CurrentToken != null &&
+ (CurrentToken.Type == CSharpTokenType.LessThan || CurrentToken.Type == CSharpTokenType.Transition))
{
PutCurrentBack();
}
@@ -1344,33 +1344,33 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- private void HandleStatement(Block block, CSharpSymbolType type)
+ private void HandleStatement(Block block, CSharpTokenType type)
{
switch (type)
{
- case CSharpSymbolType.RazorCommentTransition:
+ case CSharpTokenType.RazorCommentTransition:
Output(SpanKindInternal.Code);
RazorComment();
Statement(block);
break;
- case CSharpSymbolType.LeftBrace:
+ case CSharpTokenType.LeftBrace:
// Verbatim Block
block = block ?? new Block(Resources.BlockName_Code, CurrentStart);
AcceptAndMoveNext();
CodeBlock(block);
break;
- case CSharpSymbolType.Keyword:
+ case CSharpTokenType.Keyword:
// Keyword block
HandleKeyword(false, StandardStatement);
break;
- case CSharpSymbolType.Transition:
+ case CSharpTokenType.Transition:
// Embedded Expression block
EmbeddedExpression();
break;
- case CSharpSymbolType.RightBrace:
+ case CSharpTokenType.RightBrace:
// Possible end of Code Block, just run the continuation
break;
- case CSharpSymbolType.Comment:
+ case CSharpTokenType.Comment:
AcceptAndMoveNext();
break;
default:
@@ -1383,11 +1383,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void EmbeddedExpression()
{
// First, verify the type of the block
- Assert(CSharpSymbolType.Transition);
- var transition = CurrentSymbol;
+ Assert(CSharpTokenType.Transition);
+ var transition = CurrentToken;
NextToken();
- if (At(CSharpSymbolType.Transition))
+ if (At(CSharpTokenType.Transition))
{
// Escaped "@"
Output(SpanKindInternal.Code);
@@ -1397,14 +1397,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.Code);
- Assert(CSharpSymbolType.Transition);
+ Assert(CSharpTokenType.Transition);
AcceptAndMoveNext();
StandardStatement();
}
else
{
// Throw errors as necessary, but continue parsing
- if (At(CSharpSymbolType.LeftBrace))
+ if (At(CSharpTokenType.LeftBrace))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnexpectedNestedCodeBlock(
@@ -1416,7 +1416,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
PutBack(transition);
// Before exiting, add a marker span if necessary
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
NestedBlock();
}
@@ -1427,49 +1427,49 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
while (!EndOfFile)
{
var bookmark = CurrentStart.AbsoluteIndex;
- var read = ReadWhile(sym =>
- sym.Type != CSharpSymbolType.Semicolon &&
- sym.Type != CSharpSymbolType.RazorCommentTransition &&
- sym.Type != CSharpSymbolType.Transition &&
- sym.Type != CSharpSymbolType.LeftBrace &&
- sym.Type != CSharpSymbolType.LeftParenthesis &&
- sym.Type != CSharpSymbolType.LeftBracket &&
- sym.Type != CSharpSymbolType.RightBrace);
+ var read = ReadWhile(token =>
+ token.Type != CSharpTokenType.Semicolon &&
+ token.Type != CSharpTokenType.RazorCommentTransition &&
+ token.Type != CSharpTokenType.Transition &&
+ token.Type != CSharpTokenType.LeftBrace &&
+ token.Type != CSharpTokenType.LeftParenthesis &&
+ token.Type != CSharpTokenType.LeftBracket &&
+ token.Type != CSharpTokenType.RightBrace);
- if (At(CSharpSymbolType.LeftBrace) ||
- At(CSharpSymbolType.LeftParenthesis) ||
- At(CSharpSymbolType.LeftBracket))
+ if (At(CSharpTokenType.LeftBrace) ||
+ At(CSharpTokenType.LeftParenthesis) ||
+ At(CSharpTokenType.LeftBracket))
{
Accept(read);
if (Balance(BalancingModes.AllowCommentsAndTemplates | BalancingModes.BacktrackOnFailure))
{
- Optional(CSharpSymbolType.RightBrace);
+ Optional(CSharpTokenType.RightBrace);
}
else
{
// Recovery
- AcceptUntil(CSharpSymbolType.LessThan, CSharpSymbolType.RightBrace);
+ AcceptUntil(CSharpTokenType.LessThan, CSharpTokenType.RightBrace);
return;
}
}
- else if (At(CSharpSymbolType.Transition) && (NextIs(CSharpSymbolType.LessThan, CSharpSymbolType.Colon)))
+ else if (At(CSharpTokenType.Transition) && (NextIs(CSharpTokenType.LessThan, CSharpTokenType.Colon)))
{
Accept(read);
Output(SpanKindInternal.Code);
Template();
}
- else if (At(CSharpSymbolType.RazorCommentTransition))
+ else if (At(CSharpTokenType.RazorCommentTransition))
{
Accept(read);
RazorComment();
}
- else if (At(CSharpSymbolType.Semicolon))
+ else if (At(CSharpTokenType.Semicolon))
{
Accept(read);
AcceptAndMoveNext();
return;
}
- else if (At(CSharpSymbolType.RightBrace))
+ else if (At(CSharpTokenType.RightBrace))
{
Accept(read);
return;
@@ -1478,7 +1478,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.Source.Position = bookmark;
NextToken();
- AcceptUntil(CSharpSymbolType.LessThan, CSharpSymbolType.LeftBrace, CSharpSymbolType.RightBrace);
+ AcceptUntil(CSharpTokenType.LessThan, CSharpTokenType.LeftBrace, CSharpTokenType.RightBrace);
return;
}
}
@@ -1492,7 +1492,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void CodeBlock(bool acceptTerminatingBrace, Block block)
{
EnsureCurrent();
- while (!EndOfFile && !At(CSharpSymbolType.RightBrace))
+ while (!EndOfFile && !At(CSharpTokenType.RightBrace))
{
// Parse a statement, then return here
Statement();
@@ -1507,7 +1507,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else if (acceptTerminatingBrace)
{
- Assert(CSharpSymbolType.RightBrace);
+ Assert(CSharpTokenType.RightBrace);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
AcceptAndMoveNext();
}
@@ -1515,8 +1515,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void HandleKeyword(bool topLevel, Action fallback)
{
- Debug.Assert(CurrentSymbol.Type == CSharpSymbolType.Keyword && CurrentSymbol.Keyword != null);
- if (_keywordParsers.TryGetValue(CurrentSymbol.Keyword.Value, out var handler))
+ Debug.Assert(CurrentToken.Type == CSharpTokenType.Keyword && CurrentToken.Keyword != null);
+ if (_keywordParsers.TryGetValue(CurrentToken.Keyword.Value, out var handler))
{
handler(topLevel);
}
@@ -1526,12 +1526,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- private IEnumerable SkipToNextImportantToken()
+ private IEnumerable SkipToNextImportantToken()
{
while (!EndOfFile)
{
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
- if (At(CSharpSymbolType.RazorCommentTransition))
+ if (At(CSharpTokenType.RazorCommentTransition))
{
Accept(whitespace);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
@@ -1542,13 +1542,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return whitespace;
}
}
- return Enumerable.Empty();
+ return Enumerable.Empty();
}
// Common code for Parsers, but FxCop REALLY doesn't like it in the base class.. moving it here for now.
protected override void OutputSpanBeforeRazorComment()
{
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
Output(SpanKindInternal.Code);
}
@@ -1610,7 +1610,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (!char.IsWhiteSpace(@char))
{
- var currentDirective = CurrentSymbol.Content;
+ var currentDirective = CurrentToken.Content;
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_DirectiveMustAppearAtStartOfLine(
new SourceSpan(CurrentStart, currentDirective.Length), currentDirective));
@@ -1644,13 +1644,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
for (var i = 0; i < descriptor.Tokens.Count; i++)
{
- if (!At(CSharpSymbolType.WhiteSpace) &&
- !At(CSharpSymbolType.NewLine) &&
+ if (!At(CSharpTokenType.WhiteSpace) &&
+ !At(CSharpTokenType.NewLine) &&
!EndOfFile)
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_DirectiveTokensMustBeSeparatedByWhitespace(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length), descriptor.Directive));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length), descriptor.Directive));
return;
}
@@ -1664,10 +1664,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.Code, AcceptedCharactersInternal.WhiteSpace);
- if (EndOfFile || At(CSharpSymbolType.NewLine))
+ if (EndOfFile || At(CSharpTokenType.NewLine))
{
- // Add a marker symbol to provide CSharp intellisense when we start typing the directive token.
- AddMarkerSymbolIfNecessary();
+ // Add a marker token to provide CSharp intellisense when we start typing the directive token.
+ AddMarkerTokenIfNecessary();
Span.ChunkGenerator = new DirectiveTokenChunkGenerator(tokenDescriptor);
Span.EditHandler = new DirectiveTokenEditHandler(Language.TokenizeString);
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhiteSpace);
@@ -1679,7 +1679,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.WhiteSpace);
}
- if (tokenDescriptor.Optional && (EndOfFile || At(CSharpSymbolType.NewLine)))
+ if (tokenDescriptor.Optional && (EndOfFile || At(CSharpTokenType.NewLine)))
{
break;
}
@@ -1700,7 +1700,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_DirectiveExpectsTypeName(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length), descriptor.Directive));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length), descriptor.Directive));
return;
}
@@ -1718,7 +1718,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
break;
case DirectiveTokenKind.Member:
- if (At(CSharpSymbolType.Identifier))
+ if (At(CSharpTokenType.Identifier))
{
AcceptAndMoveNext();
}
@@ -1726,13 +1726,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_DirectiveExpectsIdentifier(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length), descriptor.Directive));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length), descriptor.Directive));
return;
}
break;
case DirectiveTokenKind.String:
- if (At(CSharpSymbolType.StringLiteral) && CurrentSymbol.Errors.Count == 0)
+ if (At(CSharpTokenType.StringLiteral) && CurrentToken.Errors.Count == 0)
{
AcceptAndMoveNext();
}
@@ -1740,7 +1740,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_DirectiveExpectsQuotedStringLiteral(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length), descriptor.Directive));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length), descriptor.Directive));
return;
}
break;
@@ -1759,13 +1759,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
case DirectiveKind.SingleLine:
Output(SpanKindInternal.None, AcceptedCharactersInternal.WhiteSpace);
- Optional(CSharpSymbolType.Semicolon);
+ Optional(CSharpTokenType.Semicolon);
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.MetaCode, AcceptedCharactersInternal.WhiteSpace);
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
- if (At(CSharpSymbolType.NewLine))
+ if (At(CSharpTokenType.NewLine))
{
AcceptAndMoveNext();
}
@@ -1773,7 +1773,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnexpectedDirectiveLiteral(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length),
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length),
descriptor.Directive,
Resources.ErrorComponent_Newline));
}
@@ -1816,9 +1816,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
{
NextToken();
- Balance(BalancingModes.NoErrorOnFailure, CSharpSymbolType.LeftBrace, CSharpSymbolType.RightBrace, startingBraceLocation);
+ Balance(BalancingModes.NoErrorOnFailure, CSharpTokenType.LeftBrace, CSharpTokenType.RightBrace, startingBraceLocation);
Span.ChunkGenerator = new StatementChunkGenerator();
+ var existingEditHandler = Span.EditHandler;
+ Span.EditHandler = new CodeBlockEditHandler(Language.TokenizeString);
+
+ AddMarkerTokenIfNecessary();
+
Output(SpanKindInternal.Code);
+
+ Span.EditHandler = existingEditHandler;
});
break;
}
@@ -1861,25 +1868,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
RazorDiagnosticFactory.CreateParsing_UnexpectedEOFAfterDirective(
new SourceSpan(CurrentStart, contentLength: 1 /* { */), descriptor.Directive, "{"));
}
- else if (!At(CSharpSymbolType.LeftBrace))
+ else if (!At(CSharpTokenType.LeftBrace))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnexpectedDirectiveLiteral(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length), descriptor.Directive, "{"));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length), descriptor.Directive, "{"));
}
else
{
var editHandler = new AutoCompleteEditHandler(Language.TokenizeString, autoCompleteAtEndOfSpan: true);
Span.EditHandler = editHandler;
var startingBraceLocation = CurrentStart;
- Accept(CurrentSymbol);
+ Accept(CurrentToken);
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.MetaCode, AcceptedCharactersInternal.None);
parseChildren(startingBraceLocation);
Span.ChunkGenerator = SpanChunkGenerator.Null;
- if (!Optional(CSharpSymbolType.RightBrace))
+ if (!Optional(CSharpTokenType.RightBrace))
{
editHandler.AutoCompleteString = "}";
Context.ErrorSink.OnError(
@@ -1975,9 +1982,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Ex: @addTagHelper "*, Microsoft.AspNetCore.CoolLibrary"
// ^ ^
// Start End
- if (Span.Symbols.Count == 1 && (Span.Symbols[0] as CSharpSymbol)?.Type == CSharpSymbolType.StringLiteral)
+ if (Span.Tokens.Count == 1 && (Span.Tokens[0] as CSharpToken)?.Type == CSharpTokenType.StringLiteral)
{
- offset += Span.Symbols[0].Content.IndexOf(directiveText, StringComparison.Ordinal);
+ offset += Span.Tokens[0].Content.IndexOf(directiveText, StringComparison.Ordinal);
// This is safe because inside one of these directives all of the text needs to be on the
// same line.
@@ -2074,8 +2081,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
[Conditional("DEBUG")]
protected void AssertDirective(string directive)
{
- Debug.Assert(CurrentSymbol.Type == CSharpSymbolType.Identifier || CurrentSymbol.Type == CSharpSymbolType.Keyword);
- Debug.Assert(string.Equals(CurrentSymbol.Content, directive, StringComparison.Ordinal));
+ Debug.Assert(CurrentToken.Type == CSharpTokenType.Identifier || CurrentToken.Type == CSharpTokenType.Keyword);
+ Debug.Assert(string.Equals(CurrentToken.Content, directive, StringComparison.Ordinal));
}
private void TagHelperDirective(string keyword, Func, ISpanChunkGenerator> chunkGeneratorFactory)
@@ -2101,18 +2108,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var keywordLength = Span.End.AbsoluteIndex - Span.Start.AbsoluteIndex;
- var foundWhitespace = At(CSharpSymbolType.WhiteSpace);
+ var foundWhitespace = At(CSharpTokenType.WhiteSpace);
// If we found whitespace then any content placed within the whitespace MAY cause a destructive change
// to the document. We can't accept it.
var acceptedCharacters = foundWhitespace ? AcceptedCharactersInternal.None : AcceptedCharactersInternal.AnyExceptNewline;
Output(SpanKindInternal.MetaCode, acceptedCharacters);
- AcceptWhile(CSharpSymbolType.WhiteSpace);
+ AcceptWhile(CSharpTokenType.WhiteSpace);
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.Markup, acceptedCharacters);
- if (EndOfFile || At(CSharpSymbolType.NewLine))
+ if (EndOfFile || At(CSharpTokenType.NewLine))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_DirectiveMustHaveValue(
@@ -2127,10 +2134,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Parse to the end of the line. Essentially accepts anything until end of line, comments, invalid code
// etc.
- AcceptUntil(CSharpSymbolType.NewLine);
+ AcceptUntil(CSharpTokenType.NewLine);
// Pull out the value and remove whitespaces and optional quotes
- var rawValue = string.Concat(Span.Symbols.Select(s => s.Content)).Trim();
+ var rawValue = string.Concat(Span.Tokens.Select(s => s.Content)).Trim();
var startsWithQuote = rawValue.StartsWith("\"", StringComparison.Ordinal);
var endsWithQuote = rawValue.EndsWith("\"", StringComparison.Ordinal);
@@ -2163,21 +2170,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Start = start;
}
- public Block(CSharpSymbol symbol, SourceLocation start)
- : this(GetName(symbol), start)
+ public Block(CSharpToken token, SourceLocation start)
+ : this(GetName(token), start)
{
}
public string Name { get; set; }
public SourceLocation Start { get; set; }
- private static string GetName(CSharpSymbol sym)
+ private static string GetName(CSharpToken token)
{
- if (sym.Type == CSharpSymbolType.Keyword)
+ if (token.Type == CSharpTokenType.Keyword)
{
- return CSharpLanguageCharacteristics.GetKeyword(sym.Keyword.Value);
+ return CSharpLanguageCharacteristics.GetKeyword(token.Keyword.Value);
}
- return sym.Content;
+ return token.Content;
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpLanguageCharacteristics.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpLanguageCharacteristics.cs
index 44ce3a89ce..70d4bfeda1 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpLanguageCharacteristics.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpLanguageCharacteristics.cs
@@ -6,61 +6,61 @@ using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal class CSharpLanguageCharacteristics : LanguageCharacteristics
+ internal class CSharpLanguageCharacteristics : LanguageCharacteristics
{
private static readonly CSharpLanguageCharacteristics _instance = new CSharpLanguageCharacteristics();
- private static Dictionary _symbolSamples = new Dictionary()
+ private static Dictionary _tokenSamples = new Dictionary()
{
- { CSharpSymbolType.Arrow, "->" },
- { CSharpSymbolType.Minus, "-" },
- { CSharpSymbolType.Decrement, "--" },
- { CSharpSymbolType.MinusAssign, "-=" },
- { CSharpSymbolType.NotEqual, "!=" },
- { CSharpSymbolType.Not, "!" },
- { CSharpSymbolType.Modulo, "%" },
- { CSharpSymbolType.ModuloAssign, "%=" },
- { CSharpSymbolType.AndAssign, "&=" },
- { CSharpSymbolType.And, "&" },
- { CSharpSymbolType.DoubleAnd, "&&" },
- { CSharpSymbolType.LeftParenthesis, "(" },
- { CSharpSymbolType.RightParenthesis, ")" },
- { CSharpSymbolType.Star, "*" },
- { CSharpSymbolType.MultiplyAssign, "*=" },
- { CSharpSymbolType.Comma, "," },
- { CSharpSymbolType.Dot, "." },
- { CSharpSymbolType.Slash, "/" },
- { CSharpSymbolType.DivideAssign, "/=" },
- { CSharpSymbolType.DoubleColon, "::" },
- { CSharpSymbolType.Colon, ":" },
- { CSharpSymbolType.Semicolon, ";" },
- { CSharpSymbolType.QuestionMark, "?" },
- { CSharpSymbolType.NullCoalesce, "??" },
- { CSharpSymbolType.RightBracket, "]" },
- { CSharpSymbolType.LeftBracket, "[" },
- { CSharpSymbolType.XorAssign, "^=" },
- { CSharpSymbolType.Xor, "^" },
- { CSharpSymbolType.LeftBrace, "{" },
- { CSharpSymbolType.OrAssign, "|=" },
- { CSharpSymbolType.DoubleOr, "||" },
- { CSharpSymbolType.Or, "|" },
- { CSharpSymbolType.RightBrace, "}" },
- { CSharpSymbolType.Tilde, "~" },
- { CSharpSymbolType.Plus, "+" },
- { CSharpSymbolType.PlusAssign, "+=" },
- { CSharpSymbolType.Increment, "++" },
- { CSharpSymbolType.LessThan, "<" },
- { CSharpSymbolType.LessThanEqual, "<=" },
- { CSharpSymbolType.LeftShift, "<<" },
- { CSharpSymbolType.LeftShiftAssign, "<<=" },
- { CSharpSymbolType.Assign, "=" },
- { CSharpSymbolType.Equals, "==" },
- { CSharpSymbolType.GreaterThan, ">" },
- { CSharpSymbolType.GreaterThanEqual, ">=" },
- { CSharpSymbolType.RightShift, ">>" },
- { CSharpSymbolType.RightShiftAssign, ">>=" },
- { CSharpSymbolType.Hash, "#" },
- { CSharpSymbolType.Transition, "@" },
+ { CSharpTokenType.Arrow, "->" },
+ { CSharpTokenType.Minus, "-" },
+ { CSharpTokenType.Decrement, "--" },
+ { CSharpTokenType.MinusAssign, "-=" },
+ { CSharpTokenType.NotEqual, "!=" },
+ { CSharpTokenType.Not, "!" },
+ { CSharpTokenType.Modulo, "%" },
+ { CSharpTokenType.ModuloAssign, "%=" },
+ { CSharpTokenType.AndAssign, "&=" },
+ { CSharpTokenType.And, "&" },
+ { CSharpTokenType.DoubleAnd, "&&" },
+ { CSharpTokenType.LeftParenthesis, "(" },
+ { CSharpTokenType.RightParenthesis, ")" },
+ { CSharpTokenType.Star, "*" },
+ { CSharpTokenType.MultiplyAssign, "*=" },
+ { CSharpTokenType.Comma, "," },
+ { CSharpTokenType.Dot, "." },
+ { CSharpTokenType.Slash, "/" },
+ { CSharpTokenType.DivideAssign, "/=" },
+ { CSharpTokenType.DoubleColon, "::" },
+ { CSharpTokenType.Colon, ":" },
+ { CSharpTokenType.Semicolon, ";" },
+ { CSharpTokenType.QuestionMark, "?" },
+ { CSharpTokenType.NullCoalesce, "??" },
+ { CSharpTokenType.RightBracket, "]" },
+ { CSharpTokenType.LeftBracket, "[" },
+ { CSharpTokenType.XorAssign, "^=" },
+ { CSharpTokenType.Xor, "^" },
+ { CSharpTokenType.LeftBrace, "{" },
+ { CSharpTokenType.OrAssign, "|=" },
+ { CSharpTokenType.DoubleOr, "||" },
+ { CSharpTokenType.Or, "|" },
+ { CSharpTokenType.RightBrace, "}" },
+ { CSharpTokenType.Tilde, "~" },
+ { CSharpTokenType.Plus, "+" },
+ { CSharpTokenType.PlusAssign, "+=" },
+ { CSharpTokenType.Increment, "++" },
+ { CSharpTokenType.LessThan, "<" },
+ { CSharpTokenType.LessThanEqual, "<=" },
+ { CSharpTokenType.LeftShift, "<<" },
+ { CSharpTokenType.LeftShiftAssign, "<<=" },
+ { CSharpTokenType.Assign, "=" },
+ { CSharpTokenType.Equals, "==" },
+ { CSharpTokenType.GreaterThan, ">" },
+ { CSharpTokenType.GreaterThanEqual, ">=" },
+ { CSharpTokenType.RightShift, ">>" },
+ { CSharpTokenType.RightShiftAssign, ">>=" },
+ { CSharpTokenType.Hash, "#" },
+ { CSharpTokenType.Transition, "@" },
};
protected CSharpLanguageCharacteristics()
@@ -74,96 +74,96 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return new CSharpTokenizer(source);
}
- protected override CSharpSymbol CreateSymbol(string content, CSharpSymbolType type, IReadOnlyList errors)
+ protected override CSharpToken CreateToken(string content, CSharpTokenType type, IReadOnlyList errors)
{
- return new CSharpSymbol(content, type, errors);
+ return new CSharpToken(content, type, errors);
}
- public override string GetSample(CSharpSymbolType type)
+ public override string GetSample(CSharpTokenType type)
{
string sample;
- if (!_symbolSamples.TryGetValue(type, out sample))
+ if (!_tokenSamples.TryGetValue(type, out sample))
{
switch (type)
{
- case CSharpSymbolType.Identifier:
- return Resources.CSharpSymbol_Identifier;
- case CSharpSymbolType.Keyword:
- return Resources.CSharpSymbol_Keyword;
- case CSharpSymbolType.IntegerLiteral:
- return Resources.CSharpSymbol_IntegerLiteral;
- case CSharpSymbolType.NewLine:
- return Resources.CSharpSymbol_Newline;
- case CSharpSymbolType.WhiteSpace:
- return Resources.CSharpSymbol_Whitespace;
- case CSharpSymbolType.Comment:
- return Resources.CSharpSymbol_Comment;
- case CSharpSymbolType.RealLiteral:
- return Resources.CSharpSymbol_RealLiteral;
- case CSharpSymbolType.CharacterLiteral:
- return Resources.CSharpSymbol_CharacterLiteral;
- case CSharpSymbolType.StringLiteral:
- return Resources.CSharpSymbol_StringLiteral;
+ case CSharpTokenType.Identifier:
+ return Resources.CSharpToken_Identifier;
+ case CSharpTokenType.Keyword:
+ return Resources.CSharpToken_Keyword;
+ case CSharpTokenType.IntegerLiteral:
+ return Resources.CSharpToken_IntegerLiteral;
+ case CSharpTokenType.NewLine:
+ return Resources.CSharpToken_Newline;
+ case CSharpTokenType.WhiteSpace:
+ return Resources.CSharpToken_Whitespace;
+ case CSharpTokenType.Comment:
+ return Resources.CSharpToken_Comment;
+ case CSharpTokenType.RealLiteral:
+ return Resources.CSharpToken_RealLiteral;
+ case CSharpTokenType.CharacterLiteral:
+ return Resources.CSharpToken_CharacterLiteral;
+ case CSharpTokenType.StringLiteral:
+ return Resources.CSharpToken_StringLiteral;
default:
- return Resources.Symbol_Unknown;
+ return Resources.Token_Unknown;
}
}
return sample;
}
- public override CSharpSymbol CreateMarkerSymbol()
+ public override CSharpToken CreateMarkerToken()
{
- return new CSharpSymbol(string.Empty, CSharpSymbolType.Unknown);
+ return new CSharpToken(string.Empty, CSharpTokenType.Unknown);
}
- public override CSharpSymbolType GetKnownSymbolType(KnownSymbolType type)
+ public override CSharpTokenType GetKnownTokenType(KnownTokenType type)
{
switch (type)
{
- case KnownSymbolType.Identifier:
- return CSharpSymbolType.Identifier;
- case KnownSymbolType.Keyword:
- return CSharpSymbolType.Keyword;
- case KnownSymbolType.NewLine:
- return CSharpSymbolType.NewLine;
- case KnownSymbolType.WhiteSpace:
- return CSharpSymbolType.WhiteSpace;
- case KnownSymbolType.Transition:
- return CSharpSymbolType.Transition;
- case KnownSymbolType.CommentStart:
- return CSharpSymbolType.RazorCommentTransition;
- case KnownSymbolType.CommentStar:
- return CSharpSymbolType.RazorCommentStar;
- case KnownSymbolType.CommentBody:
- return CSharpSymbolType.RazorComment;
+ case KnownTokenType.Identifier:
+ return CSharpTokenType.Identifier;
+ case KnownTokenType.Keyword:
+ return CSharpTokenType.Keyword;
+ case KnownTokenType.NewLine:
+ return CSharpTokenType.NewLine;
+ case KnownTokenType.WhiteSpace:
+ return CSharpTokenType.WhiteSpace;
+ case KnownTokenType.Transition:
+ return CSharpTokenType.Transition;
+ case KnownTokenType.CommentStart:
+ return CSharpTokenType.RazorCommentTransition;
+ case KnownTokenType.CommentStar:
+ return CSharpTokenType.RazorCommentStar;
+ case KnownTokenType.CommentBody:
+ return CSharpTokenType.RazorComment;
default:
- return CSharpSymbolType.Unknown;
+ return CSharpTokenType.Unknown;
}
}
- public override CSharpSymbolType FlipBracket(CSharpSymbolType bracket)
+ public override CSharpTokenType FlipBracket(CSharpTokenType bracket)
{
switch (bracket)
{
- case CSharpSymbolType.LeftBrace:
- return CSharpSymbolType.RightBrace;
- case CSharpSymbolType.LeftBracket:
- return CSharpSymbolType.RightBracket;
- case CSharpSymbolType.LeftParenthesis:
- return CSharpSymbolType.RightParenthesis;
- case CSharpSymbolType.LessThan:
- return CSharpSymbolType.GreaterThan;
- case CSharpSymbolType.RightBrace:
- return CSharpSymbolType.LeftBrace;
- case CSharpSymbolType.RightBracket:
- return CSharpSymbolType.LeftBracket;
- case CSharpSymbolType.RightParenthesis:
- return CSharpSymbolType.LeftParenthesis;
- case CSharpSymbolType.GreaterThan:
- return CSharpSymbolType.LessThan;
+ case CSharpTokenType.LeftBrace:
+ return CSharpTokenType.RightBrace;
+ case CSharpTokenType.LeftBracket:
+ return CSharpTokenType.RightBracket;
+ case CSharpTokenType.LeftParenthesis:
+ return CSharpTokenType.RightParenthesis;
+ case CSharpTokenType.LessThan:
+ return CSharpTokenType.GreaterThan;
+ case CSharpTokenType.RightBrace:
+ return CSharpTokenType.LeftBrace;
+ case CSharpTokenType.RightBracket:
+ return CSharpTokenType.LeftBracket;
+ case CSharpTokenType.RightParenthesis:
+ return CSharpTokenType.LeftParenthesis;
+ case CSharpTokenType.GreaterThan:
+ return CSharpTokenType.LessThan;
default:
Debug.Fail("FlipBracket must be called with a bracket character");
- return CSharpSymbolType.Unknown;
+ return CSharpTokenType.Unknown;
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpSymbol.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpToken.cs
similarity index 83%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpSymbol.cs
rename to src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpToken.cs
index 21cd94915d..c928f0880e 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpSymbol.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpToken.cs
@@ -6,11 +6,11 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal class CSharpSymbol : SymbolBase
+ internal class CSharpToken : TokenBase
{
- public CSharpSymbol(
+ public CSharpToken(
string content,
- CSharpSymbolType type)
+ CSharpTokenType type)
: base(content, type, RazorDiagnostic.EmptyArray)
{
if (content == null)
@@ -19,9 +19,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- public CSharpSymbol(
+ public CSharpToken(
string content,
- CSharpSymbolType type,
+ CSharpTokenType type,
IReadOnlyList errors)
: base(content, type, errors)
{
@@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public override bool Equals(object obj)
{
- var other = obj as CSharpSymbol;
+ var other = obj as CSharpToken;
return base.Equals(other) &&
other.Keyword == Keyword;
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpSymbolType.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpTokenType.cs
similarity index 97%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpSymbolType.cs
rename to src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpTokenType.cs
index 059d7c48a6..10f3478d6d 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpSymbolType.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpTokenType.cs
@@ -3,7 +3,7 @@
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal enum CSharpSymbolType
+ internal enum CSharpTokenType
{
Unknown,
Identifier,
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpTokenizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpTokenizer.cs
index a1943070f3..f819e98a3a 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpTokenizer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpTokenizer.cs
@@ -8,9 +8,9 @@ using System.Globalization;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal class CSharpTokenizer : Tokenizer
+ internal class CSharpTokenizer : Tokenizer
{
- private Dictionary> _operatorHandlers;
+ private Dictionary> _operatorHandlers;
private static readonly Dictionary _keywords = new Dictionary(StringComparer.Ordinal)
{
@@ -100,31 +100,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
base.CurrentState = StartState;
- _operatorHandlers = new Dictionary>()
+ _operatorHandlers = new Dictionary>()
{
{ '-', MinusOperator },
{ '<', LessThanOperator },
{ '>', GreaterThanOperator },
- { '&', CreateTwoCharOperatorHandler(CSharpSymbolType.And, '=', CSharpSymbolType.AndAssign, '&', CSharpSymbolType.DoubleAnd) },
- { '|', CreateTwoCharOperatorHandler(CSharpSymbolType.Or, '=', CSharpSymbolType.OrAssign, '|', CSharpSymbolType.DoubleOr) },
- { '+', CreateTwoCharOperatorHandler(CSharpSymbolType.Plus, '=', CSharpSymbolType.PlusAssign, '+', CSharpSymbolType.Increment) },
- { '=', CreateTwoCharOperatorHandler(CSharpSymbolType.Assign, '=', CSharpSymbolType.Equals, '>', CSharpSymbolType.GreaterThanEqual) },
- { '!', CreateTwoCharOperatorHandler(CSharpSymbolType.Not, '=', CSharpSymbolType.NotEqual) },
- { '%', CreateTwoCharOperatorHandler(CSharpSymbolType.Modulo, '=', CSharpSymbolType.ModuloAssign) },
- { '*', CreateTwoCharOperatorHandler(CSharpSymbolType.Star, '=', CSharpSymbolType.MultiplyAssign) },
- { ':', CreateTwoCharOperatorHandler(CSharpSymbolType.Colon, ':', CSharpSymbolType.DoubleColon) },
- { '?', CreateTwoCharOperatorHandler(CSharpSymbolType.QuestionMark, '?', CSharpSymbolType.NullCoalesce) },
- { '^', CreateTwoCharOperatorHandler(CSharpSymbolType.Xor, '=', CSharpSymbolType.XorAssign) },
- { '(', () => CSharpSymbolType.LeftParenthesis },
- { ')', () => CSharpSymbolType.RightParenthesis },
- { '{', () => CSharpSymbolType.LeftBrace },
- { '}', () => CSharpSymbolType.RightBrace },
- { '[', () => CSharpSymbolType.LeftBracket },
- { ']', () => CSharpSymbolType.RightBracket },
- { ',', () => CSharpSymbolType.Comma },
- { ';', () => CSharpSymbolType.Semicolon },
- { '~', () => CSharpSymbolType.Tilde },
- { '#', () => CSharpSymbolType.Hash }
+ { '&', CreateTwoCharOperatorHandler(CSharpTokenType.And, '=', CSharpTokenType.AndAssign, '&', CSharpTokenType.DoubleAnd) },
+ { '|', CreateTwoCharOperatorHandler(CSharpTokenType.Or, '=', CSharpTokenType.OrAssign, '|', CSharpTokenType.DoubleOr) },
+ { '+', CreateTwoCharOperatorHandler(CSharpTokenType.Plus, '=', CSharpTokenType.PlusAssign, '+', CSharpTokenType.Increment) },
+ { '=', CreateTwoCharOperatorHandler(CSharpTokenType.Assign, '=', CSharpTokenType.Equals, '>', CSharpTokenType.GreaterThanEqual) },
+ { '!', CreateTwoCharOperatorHandler(CSharpTokenType.Not, '=', CSharpTokenType.NotEqual) },
+ { '%', CreateTwoCharOperatorHandler(CSharpTokenType.Modulo, '=', CSharpTokenType.ModuloAssign) },
+ { '*', CreateTwoCharOperatorHandler(CSharpTokenType.Star, '=', CSharpTokenType.MultiplyAssign) },
+ { ':', CreateTwoCharOperatorHandler(CSharpTokenType.Colon, ':', CSharpTokenType.DoubleColon) },
+ { '?', CreateTwoCharOperatorHandler(CSharpTokenType.QuestionMark, '?', CSharpTokenType.NullCoalesce) },
+ { '^', CreateTwoCharOperatorHandler(CSharpTokenType.Xor, '=', CSharpTokenType.XorAssign) },
+ { '(', () => CSharpTokenType.LeftParenthesis },
+ { ')', () => CSharpTokenType.RightParenthesis },
+ { '{', () => CSharpTokenType.LeftBrace },
+ { '}', () => CSharpTokenType.RightBrace },
+ { '[', () => CSharpTokenType.LeftBracket },
+ { ']', () => CSharpTokenType.RightBracket },
+ { ',', () => CSharpTokenType.Comma },
+ { ';', () => CSharpTokenType.Semicolon },
+ { '~', () => CSharpTokenType.Tilde },
+ { '#', () => CSharpTokenType.Hash }
};
}
@@ -132,11 +132,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private new CSharpTokenizerState? CurrentState => (CSharpTokenizerState?)base.CurrentState;
- public override CSharpSymbolType RazorCommentType => CSharpSymbolType.RazorComment;
+ public override CSharpTokenType RazorCommentType => CSharpTokenType.RazorComment;
- public override CSharpSymbolType RazorCommentTransitionType => CSharpSymbolType.RazorCommentTransition;
+ public override CSharpTokenType RazorCommentTransitionType => CSharpTokenType.RazorCommentTransition;
- public override CSharpSymbolType RazorCommentStarType => CSharpSymbolType.RazorCommentStar;
+ public override CSharpTokenType RazorCommentStarType => CSharpTokenType.RazorCommentStar;
protected override StateResult Dispatch()
{
@@ -160,8 +160,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return RazorCommentBody();
case CSharpTokenizerState.StarAfterRazorCommentBody:
return StarAfterRazorCommentBody();
- case CSharpTokenizerState.AtSymbolAfterRazorCommentBody:
- return AtSymbolAfterRazorCommentBody();
+ case CSharpTokenizerState.AtTokenAfterRazorCommentBody:
+ return AtTokenAfterRazorCommentBody();
default:
Debug.Fail("Invalid TokenizerState");
return default(StateResult);
@@ -169,15 +169,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Optimize memory allocation by returning constants for the most frequent cases
- protected override string GetSymbolContent(CSharpSymbolType type)
+ protected override string GetTokenContent(CSharpTokenType type)
{
- var symbolLength = Buffer.Length;
+ var tokenLength = Buffer.Length;
- if (symbolLength == 1)
+ if (tokenLength == 1)
{
switch (type)
{
- case CSharpSymbolType.IntegerLiteral:
+ case CSharpTokenType.IntegerLiteral:
switch (Buffer[0])
{
case '0':
@@ -202,13 +202,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return "9";
}
break;
- case CSharpSymbolType.NewLine:
+ case CSharpTokenType.NewLine:
if (Buffer[0] == '\n')
{
return "\n";
}
break;
- case CSharpSymbolType.WhiteSpace:
+ case CSharpTokenType.WhiteSpace:
if (Buffer[0] == ' ')
{
return " ";
@@ -218,134 +218,134 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return "\t";
}
break;
- case CSharpSymbolType.Minus:
+ case CSharpTokenType.Minus:
return "-";
- case CSharpSymbolType.Not:
+ case CSharpTokenType.Not:
return "!";
- case CSharpSymbolType.Modulo:
+ case CSharpTokenType.Modulo:
return "%";
- case CSharpSymbolType.And:
+ case CSharpTokenType.And:
return "&";
- case CSharpSymbolType.LeftParenthesis:
+ case CSharpTokenType.LeftParenthesis:
return "(";
- case CSharpSymbolType.RightParenthesis:
+ case CSharpTokenType.RightParenthesis:
return ")";
- case CSharpSymbolType.Star:
+ case CSharpTokenType.Star:
return "*";
- case CSharpSymbolType.Comma:
+ case CSharpTokenType.Comma:
return ",";
- case CSharpSymbolType.Dot:
+ case CSharpTokenType.Dot:
return ".";
- case CSharpSymbolType.Slash:
+ case CSharpTokenType.Slash:
return "/";
- case CSharpSymbolType.Colon:
+ case CSharpTokenType.Colon:
return ":";
- case CSharpSymbolType.Semicolon:
+ case CSharpTokenType.Semicolon:
return ";";
- case CSharpSymbolType.QuestionMark:
+ case CSharpTokenType.QuestionMark:
return "?";
- case CSharpSymbolType.RightBracket:
+ case CSharpTokenType.RightBracket:
return "]";
- case CSharpSymbolType.LeftBracket:
+ case CSharpTokenType.LeftBracket:
return "[";
- case CSharpSymbolType.Xor:
+ case CSharpTokenType.Xor:
return "^";
- case CSharpSymbolType.LeftBrace:
+ case CSharpTokenType.LeftBrace:
return "{";
- case CSharpSymbolType.Or:
+ case CSharpTokenType.Or:
return "|";
- case CSharpSymbolType.RightBrace:
+ case CSharpTokenType.RightBrace:
return "}";
- case CSharpSymbolType.Tilde:
+ case CSharpTokenType.Tilde:
return "~";
- case CSharpSymbolType.Plus:
+ case CSharpTokenType.Plus:
return "+";
- case CSharpSymbolType.LessThan:
+ case CSharpTokenType.LessThan:
return "<";
- case CSharpSymbolType.Assign:
+ case CSharpTokenType.Assign:
return "=";
- case CSharpSymbolType.GreaterThan:
+ case CSharpTokenType.GreaterThan:
return ">";
- case CSharpSymbolType.Hash:
+ case CSharpTokenType.Hash:
return "#";
- case CSharpSymbolType.Transition:
+ case CSharpTokenType.Transition:
return "@";
}
}
- else if (symbolLength == 2)
+ else if (tokenLength == 2)
{
switch (type)
{
- case CSharpSymbolType.NewLine:
+ case CSharpTokenType.NewLine:
return "\r\n";
- case CSharpSymbolType.Arrow:
+ case CSharpTokenType.Arrow:
return "->";
- case CSharpSymbolType.Decrement:
+ case CSharpTokenType.Decrement:
return "--";
- case CSharpSymbolType.MinusAssign:
+ case CSharpTokenType.MinusAssign:
return "-=";
- case CSharpSymbolType.NotEqual:
+ case CSharpTokenType.NotEqual:
return "!=";
- case CSharpSymbolType.ModuloAssign:
+ case CSharpTokenType.ModuloAssign:
return "%=";
- case CSharpSymbolType.AndAssign:
+ case CSharpTokenType.AndAssign:
return "&=";
- case CSharpSymbolType.DoubleAnd:
+ case CSharpTokenType.DoubleAnd:
return "&&";
- case CSharpSymbolType.MultiplyAssign:
+ case CSharpTokenType.MultiplyAssign:
return "*=";
- case CSharpSymbolType.DivideAssign:
+ case CSharpTokenType.DivideAssign:
return "/=";
- case CSharpSymbolType.DoubleColon:
+ case CSharpTokenType.DoubleColon:
return "::";
- case CSharpSymbolType.NullCoalesce:
+ case CSharpTokenType.NullCoalesce:
return "??";
- case CSharpSymbolType.XorAssign:
+ case CSharpTokenType.XorAssign:
return "^=";
- case CSharpSymbolType.OrAssign:
+ case CSharpTokenType.OrAssign:
return "|=";
- case CSharpSymbolType.DoubleOr:
+ case CSharpTokenType.DoubleOr:
return "||";
- case CSharpSymbolType.PlusAssign:
+ case CSharpTokenType.PlusAssign:
return "+=";
- case CSharpSymbolType.Increment:
+ case CSharpTokenType.Increment:
return "++";
- case CSharpSymbolType.LessThanEqual:
+ case CSharpTokenType.LessThanEqual:
return "<=";
- case CSharpSymbolType.LeftShift:
+ case CSharpTokenType.LeftShift:
return "<<";
- case CSharpSymbolType.Equals:
+ case CSharpTokenType.Equals:
return "==";
- case CSharpSymbolType.GreaterThanEqual:
+ case CSharpTokenType.GreaterThanEqual:
if (Buffer[0] == '=')
{
return "=>";
}
return ">=";
- case CSharpSymbolType.RightShift:
+ case CSharpTokenType.RightShift:
return ">>";
}
}
- else if (symbolLength == 3)
+ else if (tokenLength == 3)
{
switch (type)
{
- case CSharpSymbolType.LeftShiftAssign:
+ case CSharpTokenType.LeftShiftAssign:
return "<<=";
- case CSharpSymbolType.RightShiftAssign:
+ case CSharpTokenType.RightShiftAssign:
return ">>=";
}
}
- return base.GetSymbolContent(type);
+ return base.GetTokenContent(type);
}
- protected override CSharpSymbol CreateSymbol(string content, CSharpSymbolType type, IReadOnlyList errors)
+ protected override CSharpToken CreateToken(string content, CSharpTokenType type, IReadOnlyList errors)
{
- return new CSharpSymbol(content, type, errors);
+ return new CSharpToken(content, type, errors);
}
private StateResult Data()
@@ -359,13 +359,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeCurrent();
}
- return Stay(EndSymbol(CSharpSymbolType.NewLine));
+ return Stay(EndToken(CSharpTokenType.NewLine));
}
else if (ParserHelpers.IsWhitespace(CurrentCharacter))
{
// CSharp Spec §2.3.3
TakeUntil(c => !ParserHelpers.IsWhitespace(c));
- return Stay(EndSymbol(CSharpSymbolType.WhiteSpace));
+ return Stay(EndToken(CSharpTokenType.WhiteSpace));
}
else if (IsIdentifierStart(CurrentCharacter))
{
@@ -378,7 +378,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
switch (CurrentCharacter)
{
case '@':
- return AtSymbol();
+ return AtToken();
case '\'':
TakeCurrent();
return Transition(CSharpTokenizerState.QuotedCharacterLiteral);
@@ -390,7 +390,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return RealLiteral();
}
- return Stay(Single(CSharpSymbolType.Dot));
+ return Stay(Single(CSharpTokenType.Dot));
case '/':
TakeCurrent();
if (CurrentCharacter == '/')
@@ -406,18 +406,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else if (CurrentCharacter == '=')
{
TakeCurrent();
- return Stay(EndSymbol(CSharpSymbolType.DivideAssign));
+ return Stay(EndToken(CSharpTokenType.DivideAssign));
}
else
{
- return Stay(EndSymbol(CSharpSymbolType.Slash));
+ return Stay(EndToken(CSharpTokenType.Slash));
}
default:
- return Stay(EndSymbol(Operator()));
+ return Stay(EndToken(Operator()));
}
}
- private StateResult AtSymbol()
+ private StateResult AtToken()
{
TakeCurrent();
if (CurrentCharacter == '"')
@@ -429,78 +429,78 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return Transition(
CSharpTokenizerState.AfterRazorCommentTransition,
- EndSymbol(CSharpSymbolType.RazorCommentTransition));
+ EndToken(CSharpTokenType.RazorCommentTransition));
}
else if (CurrentCharacter == '@')
{
// Could be escaped comment transition
return Transition(
CSharpTokenizerState.EscapedRazorCommentTransition,
- EndSymbol(CSharpSymbolType.Transition));
+ EndToken(CSharpTokenType.Transition));
}
- return Stay(EndSymbol(CSharpSymbolType.Transition));
+ return Stay(EndToken(CSharpTokenType.Transition));
}
private StateResult EscapedRazorCommentTransition()
{
TakeCurrent();
- return Transition(CSharpTokenizerState.Data, EndSymbol(CSharpSymbolType.Transition));
+ return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Transition));
}
- private CSharpSymbolType Operator()
+ private CSharpTokenType Operator()
{
var first = CurrentCharacter;
TakeCurrent();
- Func handler;
+ Func handler;
if (_operatorHandlers.TryGetValue(first, out handler))
{
return handler();
}
- return CSharpSymbolType.Unknown;
+ return CSharpTokenType.Unknown;
}
- private CSharpSymbolType LessThanOperator()
+ private CSharpTokenType LessThanOperator()
{
if (CurrentCharacter == '=')
{
TakeCurrent();
- return CSharpSymbolType.LessThanEqual;
+ return CSharpTokenType.LessThanEqual;
}
- return CSharpSymbolType.LessThan;
+ return CSharpTokenType.LessThan;
}
- private CSharpSymbolType GreaterThanOperator()
+ private CSharpTokenType GreaterThanOperator()
{
if (CurrentCharacter == '=')
{
TakeCurrent();
- return CSharpSymbolType.GreaterThanEqual;
+ return CSharpTokenType.GreaterThanEqual;
}
- return CSharpSymbolType.GreaterThan;
+ return CSharpTokenType.GreaterThan;
}
- private CSharpSymbolType MinusOperator()
+ private CSharpTokenType MinusOperator()
{
if (CurrentCharacter == '>')
{
TakeCurrent();
- return CSharpSymbolType.Arrow;
+ return CSharpTokenType.Arrow;
}
else if (CurrentCharacter == '-')
{
TakeCurrent();
- return CSharpSymbolType.Decrement;
+ return CSharpTokenType.Decrement;
}
else if (CurrentCharacter == '=')
{
TakeCurrent();
- return CSharpSymbolType.MinusAssign;
+ return CSharpTokenType.MinusAssign;
}
- return CSharpSymbolType.Minus;
+ return CSharpTokenType.Minus;
}
- private Func CreateTwoCharOperatorHandler(CSharpSymbolType typeIfOnlyFirst, char second, CSharpSymbolType typeIfBoth)
+ private Func CreateTwoCharOperatorHandler(CSharpTokenType typeIfOnlyFirst, char second, CSharpTokenType typeIfBoth)
{
return () =>
{
@@ -513,7 +513,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
};
}
- private Func CreateTwoCharOperatorHandler(CSharpSymbolType typeIfOnlyFirst, char option1, CSharpSymbolType typeIfOption1, char option2, CSharpSymbolType typeIfOption2)
+ private Func CreateTwoCharOperatorHandler(CSharpTokenType typeIfOnlyFirst, char option1, CSharpTokenType typeIfOption1, char option2, CSharpTokenType typeIfOption2)
{
return () =>
{
@@ -550,14 +550,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
RazorDiagnosticFactory.CreateParsing_UnterminatedStringLiteral(
new SourceSpan(CurrentStart, contentLength: 1 /* end of file */)));
}
- return Transition(CSharpTokenizerState.Data, EndSymbol(CSharpSymbolType.StringLiteral));
+ return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.StringLiteral));
}
- private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', CSharpSymbolType.CharacterLiteral);
+ private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', CSharpTokenType.CharacterLiteral);
- private StateResult QuotedStringLiteral() => QuotedLiteral('\"', CSharpSymbolType.StringLiteral);
+ private StateResult QuotedStringLiteral() => QuotedLiteral('\"', CSharpTokenType.StringLiteral);
- private StateResult QuotedLiteral(char quote, CSharpSymbolType literalType)
+ private StateResult QuotedLiteral(char quote, CSharpTokenType literalType)
{
TakeUntil(c => c == '\\' || c == quote || ParserHelpers.IsNewLine(c));
if (CurrentCharacter == '\\')
@@ -581,7 +581,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeCurrent(); // No-op if at EOF
}
- return Transition(CSharpTokenizerState.Data, EndSymbol(literalType));
+ return Transition(CSharpTokenizerState.Data, EndToken(literalType));
}
// CSharp Spec §2.3.2
@@ -594,7 +594,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
RazorDiagnosticFactory.CreateParsing_BlockCommentNotTerminated(
new SourceSpan(CurrentStart, contentLength: 1 /* end of file */)));
- return Transition(CSharpTokenizerState.Data, EndSymbol(CSharpSymbolType.Comment));
+ return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Comment));
}
if (CurrentCharacter == '*')
{
@@ -602,7 +602,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (CurrentCharacter == '/')
{
TakeCurrent();
- return Transition(CSharpTokenizerState.Data, EndSymbol(CSharpSymbolType.Comment));
+ return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Comment));
}
}
return Stay();
@@ -612,7 +612,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private StateResult SingleLineComment()
{
TakeUntil(c => ParserHelpers.IsNewLine(c));
- return Stay(EndSymbol(CSharpSymbolType.Comment));
+ return Stay(EndToken(CSharpTokenType.Comment));
}
// CSharp Spec §2.4.4
@@ -632,7 +632,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeUntil(c => !IsHexDigit(c));
TakeIntegerSuffix();
- return Stay(EndSymbol(CSharpSymbolType.IntegerLiteral));
+ return Stay(EndToken(CSharpTokenType.IntegerLiteral));
}
private StateResult DecimalLiteral()
@@ -650,7 +650,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else
{
TakeIntegerSuffix();
- return Stay(EndSymbol(CSharpSymbolType.IntegerLiteral));
+ return Stay(EndToken(CSharpTokenType.IntegerLiteral));
}
}
@@ -669,7 +669,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeCurrent();
}
- return Stay(EndSymbol(CSharpSymbolType.RealLiteral));
+ return Stay(EndToken(CSharpTokenType.RealLiteral));
}
// CSharp Spec §2.4.4.3
@@ -708,27 +708,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Debug.Assert(IsIdentifierStart(CurrentCharacter));
TakeCurrent();
TakeUntil(c => !IsIdentifierPart(c));
- CSharpSymbol symbol = null;
+ CSharpToken token = null;
if (HaveContent)
{
CSharpKeyword keyword;
- var type = CSharpSymbolType.Identifier;
- var symbolContent = Buffer.ToString();
- if (_keywords.TryGetValue(symbolContent, out keyword))
+ var type = CSharpTokenType.Identifier;
+ var tokenContent = Buffer.ToString();
+ if (_keywords.TryGetValue(tokenContent, out keyword))
{
- type = CSharpSymbolType.Keyword;
+ type = CSharpTokenType.Keyword;
}
- symbol = new CSharpSymbol(symbolContent, type)
+ token = new CSharpToken(tokenContent, type)
{
- Keyword = type == CSharpSymbolType.Keyword ? (CSharpKeyword?)keyword : null,
+ Keyword = type == CSharpTokenType.Keyword ? (CSharpKeyword?)keyword : null,
};
Buffer.Clear();
CurrentErrors.Clear();
}
- return Stay(symbol);
+ return Stay(token);
}
private StateResult Transition(CSharpTokenizerState state)
@@ -736,7 +736,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Transition((int)state, result: null);
}
- private StateResult Transition(CSharpTokenizerState state, CSharpSymbol result)
+ private StateResult Transition(CSharpTokenizerState state, CSharpToken result)
{
return Transition((int)state, result);
}
@@ -793,7 +793,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
EscapedRazorCommentTransition = RazorCommentTokenizerState.EscapedRazorCommentTransition,
RazorCommentBody = RazorCommentTokenizerState.RazorCommentBody,
StarAfterRazorCommentBody = RazorCommentTokenizerState.StarAfterRazorCommentBody,
- AtSymbolAfterRazorCommentBody = RazorCommentTokenizerState.AtSymbolAfterRazorCommentBody,
+ AtTokenAfterRazorCommentBody = RazorCommentTokenizerState.AtTokenAfterRazorCommentBody,
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ClassifiedSpanInternal.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ClassifiedSpanInternal.cs
new file mode 100644
index 0000000000..93c4331923
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ClassifiedSpanInternal.cs
@@ -0,0 +1,27 @@
+// 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.AspNetCore.Razor.Language.Legacy
+{
+ internal struct ClassifiedSpanInternal
+ {
+ public ClassifiedSpanInternal(SourceSpan span, SourceSpan blockSpan, SpanKindInternal spanKind, BlockKindInternal blockKind, AcceptedCharactersInternal acceptedCharacters)
+ {
+ Span = span;
+ BlockSpan = blockSpan;
+ SpanKind = spanKind;
+ BlockKind = blockKind;
+ AcceptedCharacters = acceptedCharacters;
+ }
+
+ public AcceptedCharactersInternal AcceptedCharacters { get; }
+
+ public BlockKindInternal BlockKind { get; }
+
+ public SourceSpan BlockSpan { get; }
+
+ public SourceSpan Span { get; }
+
+ public SpanKindInternal SpanKind { get; }
+ }
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CodeBlockEditHandler.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CodeBlockEditHandler.cs
new file mode 100644
index 0000000000..d5ce71c43e
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CodeBlockEditHandler.cs
@@ -0,0 +1,126 @@
+// 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.Globalization;
+
+namespace Microsoft.AspNetCore.Razor.Language.Legacy
+{
+ internal class CodeBlockEditHandler : SpanEditHandler
+ {
+ public CodeBlockEditHandler(Func> tokenizer) : base(tokenizer)
+ {
+ }
+
+ protected override PartialParseResultInternal CanAcceptChange(Span target, SourceChange change)
+ {
+ if (IsAcceptableDeletion(target, change))
+ {
+ return PartialParseResultInternal.Accepted;
+ }
+
+ if (IsAcceptableReplacement(target, change))
+ {
+ return PartialParseResultInternal.Accepted;
+ }
+
+ if (IsAcceptableInsertion(change))
+ {
+ return PartialParseResultInternal.Accepted;
+ }
+
+ return PartialParseResultInternal.Rejected;
+ }
+
+ // Internal for testing
+ internal static bool IsAcceptableReplacement(Span target, SourceChange change)
+ {
+ if (!change.IsReplace)
+ {
+ return false;
+ }
+
+ if (ContainsInvalidContent(change))
+ {
+ return false;
+ }
+
+ if (ModifiesInvalidContent(target, change))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ // Internal for testing
+ internal static bool IsAcceptableDeletion(Span target, SourceChange change)
+ {
+ if (!change.IsDelete)
+ {
+ return false;
+ }
+
+ if (ModifiesInvalidContent(target, change))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ // Internal for testing
+ internal static bool ModifiesInvalidContent(Span target, SourceChange change)
+ {
+ var relativePosition = change.Span.AbsoluteIndex - target.Start.AbsoluteIndex;
+
+ if (target.Content.IndexOfAny(new[] { '{', '}' }, relativePosition, change.Span.Length) >= 0)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Internal for testing
+ internal static bool IsAcceptableInsertion(SourceChange change)
+ {
+ if (!change.IsInsert)
+ {
+ return false;
+ }
+
+ if (ContainsInvalidContent(change))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ // Internal for testing
+ internal static bool ContainsInvalidContent(SourceChange change)
+ {
+ if (change.NewText.IndexOfAny(new[] { '{', '}' }) >= 0)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0};CodeBlock", base.ToString());
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is CodeBlockEditHandler other &&
+ base.Equals(other);
+ }
+
+ public override int GetHashCode() => base.GetHashCode();
+ }
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveCSharpTokenizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveCSharpTokenizer.cs
index d9507e8522..a9befaa302 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveCSharpTokenizer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveCSharpTokenizer.cs
@@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
_visitedFirstTokenStart = true;
}
- else if (result.Result != null && _visitedFirstTokenStart && result.Result.Type == CSharpSymbolType.NewLine)
+ else if (result.Result != null && _visitedFirstTokenStart && result.Result.Type == CSharpTokenType.NewLine)
{
_visitedFirstTokenLineEnd = true;
}
@@ -29,34 +29,34 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return result;
}
- public override CSharpSymbol NextSymbol()
+ public override CSharpToken NextToken()
{
// Post-Condition: Buffer should be empty at the start of Next()
Debug.Assert(Buffer.Length == 0);
- StartSymbol();
+ StartToken();
if (EndOfFile || (_visitedFirstTokenStart && _visitedFirstTokenLineEnd))
{
return null;
}
- var symbol = Turn();
+ var token = Turn();
// Post-Condition: Buffer should be empty at the end of Next()
Debug.Assert(Buffer.Length == 0);
- return symbol;
+ return token;
}
- private bool IsValidTokenType(CSharpSymbolType type)
+ private bool IsValidTokenType(CSharpTokenType type)
{
- return type != CSharpSymbolType.WhiteSpace &&
- type != CSharpSymbolType.NewLine &&
- type != CSharpSymbolType.Comment &&
- type != CSharpSymbolType.RazorComment &&
- type != CSharpSymbolType.RazorCommentStar &&
- type != CSharpSymbolType.RazorCommentTransition &&
- type != CSharpSymbolType.Transition;
+ return type != CSharpTokenType.WhiteSpace &&
+ type != CSharpTokenType.NewLine &&
+ type != CSharpTokenType.Comment &&
+ type != CSharpTokenType.RazorComment &&
+ type != CSharpTokenType.RazorCommentStar &&
+ type != CSharpTokenType.RazorCommentTransition &&
+ type != CSharpTokenType.Transition;
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveChunkGenerator.cs
index c584de9cc8..19a6bad463 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveChunkGenerator.cs
@@ -60,14 +60,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// This is used primarily at test time to show an identifiable representation of the chunk generator.
- var builder = new StringBuilder("Directive {");
+ var builder = new StringBuilder("Directive:{");
builder.Append(Descriptor.Directive);
+ builder.Append(";");
+ builder.Append(Descriptor.Kind);
+ builder.Append(";");
+ builder.Append(Descriptor.Usage);
builder.Append("}");
if (Diagnostics.Count > 0)
{
builder.Append(" [");
- var ids = string.Join(", ", Diagnostics.Select(diagnostic => diagnostic.Id));
+ var ids = string.Join(", ", Diagnostics.Select(diagnostic => $"{diagnostic.Id}{diagnostic.Span}"));
builder.Append(ids);
builder.Append("]");
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveHtmlTokenizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveHtmlTokenizer.cs
index 5e52dd6ade..034977c758 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveHtmlTokenizer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveHtmlTokenizer.cs
@@ -24,33 +24,33 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return result;
}
- public override HtmlSymbol NextSymbol()
+ public override HtmlToken NextToken()
{
// Post-Condition: Buffer should be empty at the start of Next()
Debug.Assert(Buffer.Length == 0);
- StartSymbol();
+ StartToken();
if (EndOfFile || _visitedFirstTokenStart)
{
return null;
}
- var symbol = Turn();
+ var token = Turn();
// Post-Condition: Buffer should be empty at the end of Next()
Debug.Assert(Buffer.Length == 0);
- return symbol;
+ return token;
}
- private bool IsValidTokenType(HtmlSymbolType type)
+ private bool IsValidTokenType(HtmlTokenType type)
{
- return type != HtmlSymbolType.WhiteSpace &&
- type != HtmlSymbolType.NewLine &&
- type != HtmlSymbolType.RazorComment &&
- type != HtmlSymbolType.RazorCommentStar &&
- type != HtmlSymbolType.RazorCommentTransition &&
- type != HtmlSymbolType.Transition;
+ return type != HtmlTokenType.WhiteSpace &&
+ type != HtmlTokenType.NewLine &&
+ type != HtmlTokenType.RazorComment &&
+ type != HtmlTokenType.RazorCommentStar &&
+ type != HtmlTokenType.RazorCommentTransition &&
+ type != HtmlTokenType.Transition;
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveTokenChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveTokenChunkGenerator.cs
index b7b2c9fe84..69327ba6e2 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveTokenChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/DirectiveTokenChunkGenerator.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Text;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
@@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Descriptor = tokenDescriptor;
}
- public DirectiveTokenDescriptor Descriptor { get; set; }
+ public DirectiveTokenDescriptor Descriptor { get; }
public override void Accept(ParserVisitor visitor, Span span)
{
@@ -37,5 +38,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return combiner.CombinedHash;
}
+
+ public override string ToString()
+ {
+ var builder = new StringBuilder("DirectiveToken {");
+ builder.Append(Descriptor.Name);
+ builder.Append(";");
+ builder.Append(Descriptor.Kind);
+ builder.Append(";Opt:");
+ builder.Append(Descriptor.Optional);
+ builder.Append("}");
+
+ return builder.ToString();
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlLanguageCharacteristics.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlLanguageCharacteristics.cs
index cc44a34368..096ae84dc0 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlLanguageCharacteristics.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlLanguageCharacteristics.cs
@@ -6,7 +6,7 @@ using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal class HtmlLanguageCharacteristics : LanguageCharacteristics
+ internal class HtmlLanguageCharacteristics : LanguageCharacteristics
{
private static readonly HtmlLanguageCharacteristics _instance = new HtmlLanguageCharacteristics();
@@ -19,50 +19,50 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
get { return _instance; }
}
- public override string GetSample(HtmlSymbolType type)
+ public override string GetSample(HtmlTokenType type)
{
switch (type)
{
- case HtmlSymbolType.Text:
- return Resources.HtmlSymbol_Text;
- case HtmlSymbolType.WhiteSpace:
- return Resources.HtmlSymbol_WhiteSpace;
- case HtmlSymbolType.NewLine:
- return Resources.HtmlSymbol_NewLine;
- case HtmlSymbolType.OpenAngle:
+ case HtmlTokenType.Text:
+ return Resources.HtmlToken_Text;
+ case HtmlTokenType.WhiteSpace:
+ return Resources.HtmlToken_WhiteSpace;
+ case HtmlTokenType.NewLine:
+ return Resources.HtmlToken_NewLine;
+ case HtmlTokenType.OpenAngle:
return "<";
- case HtmlSymbolType.Bang:
+ case HtmlTokenType.Bang:
return "!";
- case HtmlSymbolType.ForwardSlash:
+ case HtmlTokenType.ForwardSlash:
return "/";
- case HtmlSymbolType.QuestionMark:
+ case HtmlTokenType.QuestionMark:
return "?";
- case HtmlSymbolType.DoubleHyphen:
+ case HtmlTokenType.DoubleHyphen:
return "--";
- case HtmlSymbolType.LeftBracket:
+ case HtmlTokenType.LeftBracket:
return "[";
- case HtmlSymbolType.CloseAngle:
+ case HtmlTokenType.CloseAngle:
return ">";
- case HtmlSymbolType.RightBracket:
+ case HtmlTokenType.RightBracket:
return "]";
- case HtmlSymbolType.Equals:
+ case HtmlTokenType.Equals:
return "=";
- case HtmlSymbolType.DoubleQuote:
+ case HtmlTokenType.DoubleQuote:
return "\"";
- case HtmlSymbolType.SingleQuote:
+ case HtmlTokenType.SingleQuote:
return "'";
- case HtmlSymbolType.Transition:
+ case HtmlTokenType.Transition:
return "@";
- case HtmlSymbolType.Colon:
+ case HtmlTokenType.Colon:
return ":";
- case HtmlSymbolType.RazorComment:
- return Resources.HtmlSymbol_RazorComment;
- case HtmlSymbolType.RazorCommentStar:
+ case HtmlTokenType.RazorComment:
+ return Resources.HtmlToken_RazorComment;
+ case HtmlTokenType.RazorCommentStar:
return "*";
- case HtmlSymbolType.RazorCommentTransition:
+ case HtmlTokenType.RazorCommentTransition:
return "@";
default:
- return Resources.Symbol_Unknown;
+ return Resources.Token_Unknown;
}
}
@@ -71,57 +71,57 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return new HtmlTokenizer(source);
}
- public override HtmlSymbolType FlipBracket(HtmlSymbolType bracket)
+ public override HtmlTokenType FlipBracket(HtmlTokenType bracket)
{
switch (bracket)
{
- case HtmlSymbolType.LeftBracket:
- return HtmlSymbolType.RightBracket;
- case HtmlSymbolType.OpenAngle:
- return HtmlSymbolType.CloseAngle;
- case HtmlSymbolType.RightBracket:
- return HtmlSymbolType.LeftBracket;
- case HtmlSymbolType.CloseAngle:
- return HtmlSymbolType.OpenAngle;
+ case HtmlTokenType.LeftBracket:
+ return HtmlTokenType.RightBracket;
+ case HtmlTokenType.OpenAngle:
+ return HtmlTokenType.CloseAngle;
+ case HtmlTokenType.RightBracket:
+ return HtmlTokenType.LeftBracket;
+ case HtmlTokenType.CloseAngle:
+ return HtmlTokenType.OpenAngle;
default:
Debug.Fail("FlipBracket must be called with a bracket character");
- return HtmlSymbolType.Unknown;
+ return HtmlTokenType.Unknown;
}
}
- public override HtmlSymbol CreateMarkerSymbol()
+ public override HtmlToken CreateMarkerToken()
{
- return new HtmlSymbol(string.Empty, HtmlSymbolType.Unknown);
+ return new HtmlToken(string.Empty, HtmlTokenType.Unknown);
}
- public override HtmlSymbolType GetKnownSymbolType(KnownSymbolType type)
+ public override HtmlTokenType GetKnownTokenType(KnownTokenType type)
{
switch (type)
{
- case KnownSymbolType.CommentStart:
- return HtmlSymbolType.RazorCommentTransition;
- case KnownSymbolType.CommentStar:
- return HtmlSymbolType.RazorCommentStar;
- case KnownSymbolType.CommentBody:
- return HtmlSymbolType.RazorComment;
- case KnownSymbolType.Identifier:
- return HtmlSymbolType.Text;
- case KnownSymbolType.Keyword:
- return HtmlSymbolType.Text;
- case KnownSymbolType.NewLine:
- return HtmlSymbolType.NewLine;
- case KnownSymbolType.Transition:
- return HtmlSymbolType.Transition;
- case KnownSymbolType.WhiteSpace:
- return HtmlSymbolType.WhiteSpace;
+ case KnownTokenType.CommentStart:
+ return HtmlTokenType.RazorCommentTransition;
+ case KnownTokenType.CommentStar:
+ return HtmlTokenType.RazorCommentStar;
+ case KnownTokenType.CommentBody:
+ return HtmlTokenType.RazorComment;
+ case KnownTokenType.Identifier:
+ return HtmlTokenType.Text;
+ case KnownTokenType.Keyword:
+ return HtmlTokenType.Text;
+ case KnownTokenType.NewLine:
+ return HtmlTokenType.NewLine;
+ case KnownTokenType.Transition:
+ return HtmlTokenType.Transition;
+ case KnownTokenType.WhiteSpace:
+ return HtmlTokenType.WhiteSpace;
default:
- return HtmlSymbolType.Unknown;
+ return HtmlTokenType.Unknown;
}
}
- protected override HtmlSymbol CreateSymbol(string content, HtmlSymbolType type, IReadOnlyList errors)
+ protected override HtmlToken CreateToken(string content, HtmlTokenType type, IReadOnlyList errors)
{
- return new HtmlSymbol(content, type, errors);
+ return new HtmlToken(content, type, errors);
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs
index d038f58cc5..2408c552dd 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs
@@ -8,16 +8,16 @@ using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal class HtmlMarkupParser : TokenizerBackedParser
+ internal class HtmlMarkupParser : TokenizerBackedParser
{
private const string ScriptTagName = "script";
- private static readonly HtmlSymbol[] nonAllowedHtmlCommentEnding = new[] { HtmlSymbol.Hyphen, new HtmlSymbol("!", HtmlSymbolType.Bang), new HtmlSymbol("<", HtmlSymbolType.OpenAngle) };
- private static readonly HtmlSymbol[] singleHyphenArray = new[] { HtmlSymbol.Hyphen };
+ private static readonly HtmlToken[] nonAllowedHtmlCommentEnding = new[] { HtmlToken.Hyphen, new HtmlToken("!", HtmlTokenType.Bang), new HtmlToken("<", HtmlTokenType.OpenAngle) };
+ private static readonly HtmlToken[] singleHyphenArray = new[] { HtmlToken.Hyphen };
private static readonly char[] ValidAfterTypeAttributeNameCharacters = { ' ', '\t', '\r', '\n', '\f', '=' };
private SourceLocation _lastTagStart = SourceLocation.Zero;
- private HtmlSymbol _bufferedOpenAngle;
+ private HtmlToken _bufferedOpenAngle;
//From http://dev.w3.org/html5/spec/Overview.html#elements-0
private ISet _voidElements = new HashSet(StringComparer.OrdinalIgnoreCase)
@@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
get { return CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; }
}
- protected override bool SymbolTypeEquals(HtmlSymbolType x, HtmlSymbolType y) => x == y;
+ protected override bool TokenTypeEquals(HtmlTokenType x, HtmlTokenType y) => x == y;
public override void BuildSpan(SpanBuilder span, SourceLocation start, string content)
{
@@ -73,30 +73,30 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Output(SpanKindInternal.Markup);
}
- protected void SkipToAndParseCode(HtmlSymbolType type)
+ protected void SkipToAndParseCode(HtmlTokenType type)
{
- SkipToAndParseCode(sym => sym.Type == type);
+ SkipToAndParseCode(token => token.Type == type);
}
- protected void SkipToAndParseCode(Func condition)
+ protected void SkipToAndParseCode(Func condition)
{
- HtmlSymbol last = null;
+ HtmlToken last = null;
var startOfLine = false;
- while (!EndOfFile && !condition(CurrentSymbol))
+ while (!EndOfFile && !condition(CurrentToken))
{
if (Context.NullGenerateWhitespaceAndNewLine)
{
Context.NullGenerateWhitespaceAndNewLine = false;
Span.ChunkGenerator = SpanChunkGenerator.Null;
- AcceptWhile(symbol => symbol.Type == HtmlSymbolType.WhiteSpace);
- if (At(HtmlSymbolType.NewLine))
+ AcceptWhile(token => token.Type == HtmlTokenType.WhiteSpace);
+ if (At(HtmlTokenType.NewLine))
{
AcceptAndMoveNext();
}
Output(SpanKindInternal.Markup);
}
- else if (At(HtmlSymbolType.NewLine))
+ else if (At(HtmlTokenType.NewLine))
{
if (last != null)
{
@@ -108,11 +108,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
last = null;
AcceptAndMoveNext();
}
- else if (At(HtmlSymbolType.Transition))
+ else if (At(HtmlTokenType.Transition))
{
- var transition = CurrentSymbol;
+ var transition = CurrentToken;
NextToken();
- if (At(HtmlSymbolType.Transition))
+ if (At(HtmlTokenType.Transition))
{
if (last != null)
{
@@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Handle whitespace rewriting
if (last != null)
{
- if (!Context.DesignTimeMode && last.Type == HtmlSymbolType.WhiteSpace && startOfLine)
+ if (!Context.DesignTimeMode && last.Type == HtmlTokenType.WhiteSpace && startOfLine)
{
// Put the whitespace back too
startOfLine = false;
@@ -155,15 +155,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
OtherParserBlock();
}
- else if (At(HtmlSymbolType.RazorCommentTransition))
+ else if (At(HtmlTokenType.RazorCommentTransition))
{
if (last != null)
{
// Don't render the whitespace between the start of the line and the razor comment.
- if (startOfLine && last.Type == HtmlSymbolType.WhiteSpace)
+ if (startOfLine && last.Type == HtmlTokenType.WhiteSpace)
{
- AddMarkerSymbolIfNecessary();
- // Output the symbols that may have been accepted prior to the whitespace.
+ AddMarkerTokenIfNecessary();
+ // Output the tokens that may have been accepted prior to the whitespace.
Output(SpanKindInternal.Markup);
Span.ChunkGenerator = SpanChunkGenerator.Null;
@@ -173,15 +173,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
last = null;
}
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
Output(SpanKindInternal.Markup);
RazorComment();
// Handle the whitespace and newline at the end of a razor comment.
if (startOfLine &&
- (At(HtmlSymbolType.NewLine) ||
- (At(HtmlSymbolType.WhiteSpace) && NextIs(HtmlSymbolType.NewLine))))
+ (At(HtmlTokenType.NewLine) ||
+ (At(HtmlTokenType.WhiteSpace) && NextIs(HtmlTokenType.NewLine))))
{
AcceptWhile(IsSpacingToken(includeNewLines: false));
AcceptAndMoveNext();
@@ -192,7 +192,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else
{
// As long as we see whitespace, we're still at the "start" of the line
- startOfLine &= At(HtmlSymbolType.WhiteSpace);
+ startOfLine &= At(HtmlTokenType.WhiteSpace);
// If there's a last token, accept it
if (last != null)
@@ -202,7 +202,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Advance
- last = CurrentSymbol;
+ last = CurrentToken;
NextToken();
}
}
@@ -213,14 +213,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- protected static Func IsSpacingToken(bool includeNewLines)
+ protected static Func IsSpacingToken(bool includeNewLines)
{
- return sym => sym.Type == HtmlSymbolType.WhiteSpace || (includeNewLines && sym.Type == HtmlSymbolType.NewLine);
+ return token => token.Type == HtmlTokenType.WhiteSpace || (includeNewLines && token.Type == HtmlTokenType.NewLine);
}
private void OtherParserBlock()
{
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
Output(SpanKindInternal.Markup);
using (PushSpanConfig())
@@ -238,12 +238,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var potentialBang = Lookahead(lookahead);
if (potentialBang != null &&
- potentialBang.Type == HtmlSymbolType.Bang)
+ potentialBang.Type == HtmlTokenType.Bang)
{
var afterBang = Lookahead(lookahead + 1);
return afterBang != null &&
- afterBang.Type == HtmlSymbolType.Text &&
+ afterBang.Type == HtmlTokenType.Text &&
!string.Equals(afterBang.Content, "DOCTYPE", StringComparison.OrdinalIgnoreCase);
}
@@ -257,7 +257,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Output(SpanKindInternal.Markup);
// Accept the parser escape character '!'.
- Assert(HtmlSymbolType.Bang);
+ Assert(HtmlTokenType.Bang);
AcceptAndMoveNext();
// Setup the metacode span that we will be outputing.
@@ -286,23 +286,23 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AcceptWhile(IsSpacingToken(includeNewLines: true));
- if (CurrentSymbol.Type == HtmlSymbolType.OpenAngle)
+ if (CurrentToken.Type == HtmlTokenType.OpenAngle)
{
// "<" => Implicit Tag Block
- TagBlock(new Stack>());
+ TagBlock(new Stack>());
}
- else if (CurrentSymbol.Type == HtmlSymbolType.Transition)
+ else if (CurrentToken.Type == HtmlTokenType.Transition)
{
// "@" => Explicit Tag/Single Line Block OR Template
Output(SpanKindInternal.Markup);
// Definitely have a transition span
- Assert(HtmlSymbolType.Transition);
+ Assert(HtmlTokenType.Transition);
AcceptAndMoveNext();
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.Transition);
- if (At(HtmlSymbolType.Transition))
+ if (At(HtmlTokenType.Transition))
{
Span.ChunkGenerator = SpanChunkGenerator.Null;
AcceptAndMoveNext();
@@ -314,7 +314,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_MarkupBlockMustStartWithTag(
- new SourceSpan(CurrentStart, CurrentSymbol.Content.Length)));
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length)));
}
Output(SpanKindInternal.Markup);
}
@@ -330,10 +330,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void AfterTransition()
{
// "@:" => Explicit Single Line Block
- if (CurrentSymbol.Type == HtmlSymbolType.Text && CurrentSymbol.Content.Length > 0 && CurrentSymbol.Content[0] == ':')
+ if (CurrentToken.Type == HtmlTokenType.Text && CurrentToken.Content.Length > 0 && CurrentToken.Content[0] == ':')
{
// Split the token
- Tuple split = Language.SplitSymbol(CurrentSymbol, 1, HtmlSymbolType.Colon);
+ Tuple split = Language.SplitToken(CurrentToken, 1, HtmlTokenType.Colon);
// The first part (left) is added to this span and we return a MetaCode span
Accept(split.Item1);
@@ -346,9 +346,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
NextToken();
SingleLineMarkup();
}
- else if (CurrentSymbol.Type == HtmlSymbolType.OpenAngle)
+ else if (CurrentToken.Type == HtmlTokenType.OpenAngle)
{
- TagBlock(new Stack>());
+ TagBlock(new Stack>());
}
}
@@ -359,8 +359,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var old = Context.WhiteSpaceIsSignificantToAncestorBlock;
Context.WhiteSpaceIsSignificantToAncestorBlock = true;
Span.EditHandler = new SpanEditHandler(Language.TokenizeString);
- SkipToAndParseCode(HtmlSymbolType.NewLine);
- if (!EndOfFile && CurrentSymbol.Type == HtmlSymbolType.NewLine)
+ SkipToAndParseCode(HtmlTokenType.NewLine);
+ if (!EndOfFile && CurrentToken.Type == HtmlTokenType.NewLine)
{
AcceptAndMoveNext();
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
@@ -370,13 +370,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Output(SpanKindInternal.Markup);
}
- private void TagBlock(Stack> tags)
+ private void TagBlock(Stack> tags)
{
// Skip Whitespace and Text
var complete = false;
do
{
- SkipToAndParseCode(HtmlSymbolType.OpenAngle);
+ SkipToAndParseCode(HtmlTokenType.OpenAngle);
// Output everything prior to the OpenAngle into a markup span
Output(SpanKindInternal.Markup);
@@ -401,8 +401,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
_bufferedOpenAngle = null;
_lastTagStart = CurrentStart;
- Assert(HtmlSymbolType.OpenAngle);
- _bufferedOpenAngle = CurrentSymbol;
+ Assert(HtmlTokenType.OpenAngle);
+ _bufferedOpenAngle = CurrentToken;
var tagStart = CurrentStart;
if (!NextToken())
{
@@ -440,18 +440,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
private bool AfterTagStart(SourceLocation tagStart,
- Stack> tags,
+ Stack> tags,
bool atSpecialTag,
IDisposable tagBlockWrapper)
{
if (!EndOfFile)
{
- switch (CurrentSymbol.Type)
+ switch (CurrentToken.Type)
{
- case HtmlSymbolType.ForwardSlash:
+ case HtmlTokenType.ForwardSlash:
// End Tag
return EndTag(tagStart, tags, tagBlockWrapper);
- case HtmlSymbolType.Bang:
+ case HtmlTokenType.Bang:
// Comment, CDATA, DOCTYPE, or a parser-escaped HTML tag.
if (atSpecialTag)
{
@@ -462,7 +462,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
goto default;
}
- case HtmlSymbolType.QuestionMark:
+ case HtmlTokenType.QuestionMark:
// XML PI
Accept(_bufferedOpenAngle);
return XmlPI();
@@ -483,15 +483,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private bool XmlPI()
{
// Accept "?"
- Assert(HtmlSymbolType.QuestionMark);
+ Assert(HtmlTokenType.QuestionMark);
AcceptAndMoveNext();
- return AcceptUntilAll(HtmlSymbolType.QuestionMark, HtmlSymbolType.CloseAngle);
+ return AcceptUntilAll(HtmlTokenType.QuestionMark, HtmlTokenType.CloseAngle);
}
private bool BangTag()
{
// Accept "!"
- Assert(HtmlSymbolType.Bang);
+ Assert(HtmlTokenType.Bang);
if (AcceptAndMoveNext())
{
@@ -499,17 +499,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
using (Context.Builder.StartBlock(BlockKindInternal.HtmlComment))
{
- // Accept the double-hyphen symbol at the beginning of the comment block.
+ // Accept the double-hyphen token at the beginning of the comment block.
AcceptAndMoveNext();
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.None);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.WhiteSpace;
while (!EndOfFile)
{
- SkipToAndParseCode(HtmlSymbolType.DoubleHyphen);
+ SkipToAndParseCode(HtmlTokenType.DoubleHyphen);
var lastDoubleHyphen = AcceptAllButLastDoubleHyphens();
- if (At(HtmlSymbolType.CloseAngle))
+ if (At(HtmlTokenType.CloseAngle))
{
// Output the content in the comment block as a separate markup
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.WhiteSpace);
@@ -527,7 +527,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
}
- else if (CurrentSymbol.Type == HtmlSymbolType.LeftBracket)
+ else if (CurrentToken.Type == HtmlTokenType.LeftBracket)
{
if (AcceptAndMoveNext())
{
@@ -537,19 +537,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else
{
AcceptAndMoveNext();
- return AcceptUntilAll(HtmlSymbolType.CloseAngle);
+ return AcceptUntilAll(HtmlTokenType.CloseAngle);
}
}
return false;
}
- protected HtmlSymbol AcceptAllButLastDoubleHyphens()
+ protected HtmlToken AcceptAllButLastDoubleHyphens()
{
- var lastDoubleHyphen = CurrentSymbol;
+ var lastDoubleHyphen = CurrentToken;
AcceptWhile(s =>
{
- if (NextIs(HtmlSymbolType.DoubleHyphen))
+ if (NextIs(HtmlTokenType.DoubleHyphen))
{
lastDoubleHyphen = s;
return true;
@@ -560,10 +560,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
NextToken();
- if (At(HtmlSymbolType.Text) && IsHyphen(CurrentSymbol))
+ if (At(HtmlTokenType.Text) && IsHyphen(CurrentToken))
{
- // Doing this here to maintain the order of symbols
- if (!NextIs(HtmlSymbolType.CloseAngle))
+ // Doing this here to maintain the order of tokens
+ if (!NextIs(HtmlTokenType.CloseAngle))
{
Accept(lastDoubleHyphen);
lastDoubleHyphen = null;
@@ -575,53 +575,50 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return lastDoubleHyphen;
}
- internal static bool IsHyphen(HtmlSymbol symbol)
+ internal static bool IsHyphen(HtmlToken token)
{
- return symbol.Equals(HtmlSymbol.Hyphen);
+ return token.Equals(HtmlToken.Hyphen);
}
protected bool IsHtmlCommentAhead()
{
- /*
- * From HTML5 Specification, available at http://www.w3.org/TR/html52/syntax.html#comments
- *
- * Comments must have the following format:
- * 1. The string "" // As we will be treating this as a comment ending, there is no need to handle this case at all.
- * 2.2.3 "--!>"
- * 2.3 nor end with the string ""
- *
- * */
+ // From HTML5 Specification, available at http://www.w3.org/TR/html52/syntax.html#comments
+
+ // Comments must have the following format:
+ // 1. The string "" As we will be treating this as a comment ending, there is no need to handle this case at all.
+ // 2.2.3 "--!>"
+ // 2.3 nor end with the string ""
- if (CurrentSymbol.Type != HtmlSymbolType.DoubleHyphen)
+ if (CurrentToken.Type != HtmlTokenType.DoubleHyphen)
{
return false;
}
// Check condition 2.1
- if (NextIs(HtmlSymbolType.CloseAngle) || NextIs(next => IsHyphen(next) && NextIs(HtmlSymbolType.CloseAngle)))
+ if (NextIs(HtmlTokenType.CloseAngle) || NextIs(next => IsHyphen(next) && NextIs(HtmlTokenType.CloseAngle)))
{
return false;
}
// Check condition 2.2
var isValidComment = false;
- LookaheadUntil((symbol, prevSymbols) =>
+ LookaheadUntil((token, prevTokens) =>
{
- if (symbol.Type == HtmlSymbolType.DoubleHyphen)
+ if (token.Type == HtmlTokenType.DoubleHyphen)
{
- if (NextIs(HtmlSymbolType.CloseAngle))
+ if (NextIs(HtmlTokenType.CloseAngle))
{
// Check condition 2.3: We're at the end of a comment. Check to make sure the text ending is allowed.
- isValidComment = !IsCommentContentEndingInvalid(prevSymbols);
+ isValidComment = !IsCommentContentEndingInvalid(prevTokens);
return true;
}
- else if (NextIs(ns => IsHyphen(ns) && NextIs(HtmlSymbolType.CloseAngle)))
+ else if (NextIs(ns => IsHyphen(ns) && NextIs(HtmlTokenType.CloseAngle)))
{
// Check condition 2.3: we're at the end of a comment, which has an extra dash.
// Need to treat the dash as part of the content and check the ending.
@@ -630,17 +627,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
isValidComment = true;
return true;
}
- else if (NextIs(ns => ns.Type == HtmlSymbolType.Bang && NextIs(HtmlSymbolType.CloseAngle)))
+ else if (NextIs(ns => ns.Type == HtmlTokenType.Bang && NextIs(HtmlTokenType.CloseAngle)))
{
// This is condition 2.2.3
isValidComment = false;
return true;
}
}
- else if (symbol.Type == HtmlSymbolType.OpenAngle)
+ else if (token.Type == HtmlTokenType.OpenAngle)
{
// Checking condition 2.2.1
- if (NextIs(ns => ns.Type == HtmlSymbolType.Bang && NextIs(HtmlSymbolType.DoubleHyphen)))
+ if (NextIs(ns => ns.Type == HtmlTokenType.Bang && NextIs(HtmlTokenType.DoubleHyphen)))
{
isValidComment = false;
return true;
@@ -654,9 +651,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
///
- /// Verifies, that the sequence doesn't end with the "<!-" HtmlSymbols. Note, the first symbol is an opening bracket symbol
+ /// Verifies, that the sequence doesn't end with the "<!-" HtmlTokens. Note, the first token is an opening bracket token
///
- internal static bool IsCommentContentEndingInvalid(IEnumerable sequence)
+ internal static bool IsCommentContentEndingInvalid(IEnumerable sequence)
{
var reversedSequence = sequence.Reverse();
var index = 0;
@@ -678,13 +675,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private bool CData()
{
- if (CurrentSymbol.Type == HtmlSymbolType.Text && string.Equals(CurrentSymbol.Content, "cdata", StringComparison.OrdinalIgnoreCase))
+ if (CurrentToken.Type == HtmlTokenType.Text && string.Equals(CurrentToken.Content, "cdata", StringComparison.OrdinalIgnoreCase))
{
if (AcceptAndMoveNext())
{
- if (CurrentSymbol.Type == HtmlSymbolType.LeftBracket)
+ if (CurrentToken.Type == HtmlTokenType.LeftBracket)
{
- return AcceptUntilAll(HtmlSymbolType.RightBracket, HtmlSymbolType.RightBracket, HtmlSymbolType.CloseAngle);
+ return AcceptUntilAll(HtmlTokenType.RightBracket, HtmlTokenType.RightBracket, HtmlTokenType.CloseAngle);
}
}
}
@@ -693,12 +690,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
private bool EndTag(SourceLocation tagStart,
- Stack> tags,
+ Stack> tags,
IDisposable tagBlockWrapper)
{
// Accept "/" and move next
- Assert(HtmlSymbolType.ForwardSlash);
- var forwardSlash = CurrentSymbol;
+ Assert(HtmlTokenType.ForwardSlash);
+ var forwardSlash = CurrentToken;
if (!NextToken())
{
Accept(_bufferedOpenAngle);
@@ -708,22 +705,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else
{
var tagName = string.Empty;
- HtmlSymbol bangSymbol = null;
+ HtmlToken bangToken = null;
- if (At(HtmlSymbolType.Bang))
+ if (At(HtmlTokenType.Bang))
{
- bangSymbol = CurrentSymbol;
+ bangToken = CurrentToken;
- var nextSymbol = Lookahead(count: 1);
+ var nextToken = Lookahead(count: 1);
- if (nextSymbol != null && nextSymbol.Type == HtmlSymbolType.Text)
+ if (nextToken != null && nextToken.Type == HtmlTokenType.Text)
{
- tagName = "!" + nextSymbol.Content;
+ tagName = "!" + nextToken.Content;
}
}
- else if (At(HtmlSymbolType.Text))
+ else if (At(HtmlTokenType.Text))
{
- tagName = CurrentSymbol.Content;
+ tagName = CurrentToken.Content;
}
var matched = RemoveTag(tags, tagName, tagStart);
@@ -741,32 +738,32 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
OptionalBangEscape();
- AcceptUntil(HtmlSymbolType.CloseAngle);
+ AcceptUntil(HtmlTokenType.CloseAngle);
// Accept the ">"
- return Optional(HtmlSymbolType.CloseAngle);
+ return Optional(HtmlTokenType.CloseAngle);
}
}
private void RecoverTextTag()
{
// We don't want to skip-to and parse because there shouldn't be anything in the body of text tags.
- AcceptUntil(HtmlSymbolType.CloseAngle, HtmlSymbolType.NewLine);
+ AcceptUntil(HtmlTokenType.CloseAngle, HtmlTokenType.NewLine);
// Include the close angle in the text tag block if it's there, otherwise just move on
- Optional(HtmlSymbolType.CloseAngle);
+ Optional(HtmlTokenType.CloseAngle);
}
- private bool EndTextTag(HtmlSymbol solidus, IDisposable tagBlockWrapper)
+ private bool EndTextTag(HtmlToken solidus, IDisposable tagBlockWrapper)
{
Accept(_bufferedOpenAngle);
Accept(solidus);
var textLocation = CurrentStart;
- Assert(HtmlSymbolType.Text);
+ Assert(HtmlTokenType.Text);
AcceptAndMoveNext();
- var seenCloseAngle = Optional(HtmlSymbolType.CloseAngle);
+ var seenCloseAngle = Optional(HtmlTokenType.CloseAngle);
if (!seenCloseAngle)
{
@@ -794,32 +791,32 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
get
{
- if (At(HtmlSymbolType.OpenAngle))
+ if (At(HtmlTokenType.OpenAngle))
{
- if (NextIs(HtmlSymbolType.Bang))
+ if (NextIs(HtmlTokenType.Bang))
{
return !IsBangEscape(lookahead: 1);
}
- return NextIs(HtmlSymbolType.QuestionMark);
+ return NextIs(HtmlTokenType.QuestionMark);
}
return false;
}
}
- private bool IsTagRecoveryStopPoint(HtmlSymbol sym)
+ private bool IsTagRecoveryStopPoint(HtmlToken token)
{
- return sym.Type == HtmlSymbolType.CloseAngle ||
- sym.Type == HtmlSymbolType.ForwardSlash ||
- sym.Type == HtmlSymbolType.OpenAngle ||
- sym.Type == HtmlSymbolType.SingleQuote ||
- sym.Type == HtmlSymbolType.DoubleQuote;
+ return token.Type == HtmlTokenType.CloseAngle ||
+ token.Type == HtmlTokenType.ForwardSlash ||
+ token.Type == HtmlTokenType.OpenAngle ||
+ token.Type == HtmlTokenType.SingleQuote ||
+ token.Type == HtmlTokenType.DoubleQuote;
}
private void TagContent()
{
- if (!At(HtmlSymbolType.WhiteSpace) && !At(HtmlSymbolType.NewLine))
+ if (!At(HtmlTokenType.WhiteSpace) && !At(HtmlTokenType.NewLine))
{
// We should be right after the tag name, so if there's no whitespace or new line, something is wrong
RecoverToEndOfTag();
@@ -836,9 +833,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private bool IsEndOfTag()
{
- if (At(HtmlSymbolType.ForwardSlash))
+ if (At(HtmlTokenType.ForwardSlash))
{
- if (NextIs(HtmlSymbolType.CloseAngle))
+ if (NextIs(HtmlTokenType.CloseAngle))
{
return true;
}
@@ -847,16 +844,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AcceptAndMoveNext();
}
}
- return At(HtmlSymbolType.CloseAngle) || At(HtmlSymbolType.OpenAngle);
+ return At(HtmlTokenType.CloseAngle) || At(HtmlTokenType.OpenAngle);
}
private void BeforeAttribute()
{
// http://dev.w3.org/html5/spec/tokenization.html#before-attribute-name-state
// Capture whitespace
- var whitespace = ReadWhile(sym => sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine);
+ var whitespace = ReadWhile(token => token.Type == HtmlTokenType.WhiteSpace || token.Type == HtmlTokenType.NewLine);
- if (At(HtmlSymbolType.Transition))
+ if (At(HtmlTokenType.Transition))
{
// Transition outside of attribute value => Switch to recovery mode
Accept(whitespace);
@@ -866,21 +863,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// http://dev.w3.org/html5/spec/tokenization.html#attribute-name-state
// Read the 'name' (i.e. read until the '=' or whitespace/newline)
- var name = Enumerable.Empty();
- var whitespaceAfterAttributeName = Enumerable.Empty();
- if (IsValidAttributeNameSymbol(CurrentSymbol))
+ var name = Enumerable.Empty();
+ var whitespaceAfterAttributeName = Enumerable.Empty();
+ if (IsValidAttributeNameToken(CurrentToken))
{
- name = ReadWhile(sym =>
- sym.Type != HtmlSymbolType.WhiteSpace &&
- sym.Type != HtmlSymbolType.NewLine &&
- sym.Type != HtmlSymbolType.Equals &&
- sym.Type != HtmlSymbolType.CloseAngle &&
- sym.Type != HtmlSymbolType.OpenAngle &&
- (sym.Type != HtmlSymbolType.ForwardSlash || !NextIs(HtmlSymbolType.CloseAngle)));
+ name = ReadWhile(token =>
+ token.Type != HtmlTokenType.WhiteSpace &&
+ token.Type != HtmlTokenType.NewLine &&
+ token.Type != HtmlTokenType.Equals &&
+ token.Type != HtmlTokenType.CloseAngle &&
+ token.Type != HtmlTokenType.OpenAngle &&
+ (token.Type != HtmlTokenType.ForwardSlash || !NextIs(HtmlTokenType.CloseAngle)));
// capture whitespace after attribute name (if any)
whitespaceAfterAttributeName = ReadWhile(
- sym => sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine);
+ token => token.Type == HtmlTokenType.WhiteSpace || token.Type == HtmlTokenType.NewLine);
}
else
{
@@ -890,7 +887,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
- if (!At(HtmlSymbolType.Equals))
+ if (!At(HtmlTokenType.Equals))
{
// Minimized attribute
@@ -925,32 +922,32 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
private void AttributePrefix(
- IEnumerable whitespace,
- IEnumerable nameSymbols,
- IEnumerable whitespaceAfterAttributeName)
+ IEnumerable whitespace,
+ IEnumerable nameTokens,
+ IEnumerable whitespaceAfterAttributeName)
{
// First, determine if this is a 'data-' attribute (since those can't use conditional attributes)
- var name = string.Concat(nameSymbols.Select(s => s.Content));
+ var name = string.Concat(nameTokens.Select(s => s.Content));
var attributeCanBeConditional =
Context.FeatureFlags.EXPERIMENTAL_AllowConditionalDataDashAttributes ||
!name.StartsWith("data-", StringComparison.OrdinalIgnoreCase);
// Accept the whitespace and name
Accept(whitespace);
- Accept(nameSymbols);
+ Accept(nameTokens);
// Since this is not a minimized attribute, the whitespace after attribute name belongs to this attribute.
Accept(whitespaceAfterAttributeName);
- Assert(HtmlSymbolType.Equals); // We should be at "="
+ Assert(HtmlTokenType.Equals); // We should be at "="
AcceptAndMoveNext();
- var whitespaceAfterEquals = ReadWhile(sym => sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine);
- var quote = HtmlSymbolType.Unknown;
- if (At(HtmlSymbolType.SingleQuote) || At(HtmlSymbolType.DoubleQuote))
+ var whitespaceAfterEquals = ReadWhile(token => token.Type == HtmlTokenType.WhiteSpace || token.Type == HtmlTokenType.NewLine);
+ var quote = HtmlTokenType.Unknown;
+ if (At(HtmlTokenType.SingleQuote) || At(HtmlTokenType.DoubleQuote))
{
// Found a quote, the whitespace belongs to this attribute.
Accept(whitespaceAfterEquals);
- quote = CurrentSymbol.Type;
+ quote = CurrentToken.Type;
AcceptAndMoveNext();
}
else if (whitespaceAfterEquals.Any())
@@ -961,7 +958,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// We now have the prefix: (i.e. ' foo="')
- var prefix = new LocationTagged(string.Concat(Span.Symbols.Select(s => s.Content)), Span.Start);
+ var prefix = new LocationTagged(string.Concat(Span.Tokens.Select(s => s.Content)), Span.Start);
if (attributeCanBeConditional)
{
@@ -970,10 +967,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Read the attribute value only if the value is quoted
// or if there is no whitespace between '=' and the unquoted value.
- if (quote != HtmlSymbolType.Unknown || !whitespaceAfterEquals.Any())
+ if (quote != HtmlTokenType.Unknown || !whitespaceAfterEquals.Any())
{
// Read the attribute value.
- while (!EndOfFile && !IsEndOfAttributeValue(quote, CurrentSymbol))
+ while (!EndOfFile && !IsEndOfAttributeValue(quote, CurrentToken))
{
AttributeValue(quote);
}
@@ -981,13 +978,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Capture the suffix
var suffix = new LocationTagged(string.Empty, CurrentStart);
- if (quote != HtmlSymbolType.Unknown && At(quote))
+ if (quote != HtmlTokenType.Unknown && At(quote))
{
- suffix = new LocationTagged(CurrentSymbol.Content, CurrentStart);
+ suffix = new LocationTagged(CurrentToken.Content, CurrentStart);
AcceptAndMoveNext();
}
- if (Span.Symbols.Count > 0)
+ if (Span.Tokens.Count > 0)
{
// Again, block chunk generator will render the suffix
Span.ChunkGenerator = SpanChunkGenerator.Null;
@@ -1003,18 +1000,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Output the attribute name, the equals and optional quote. Ex: foo="
Output(SpanKindInternal.Markup);
- if (quote == HtmlSymbolType.Unknown && whitespaceAfterEquals.Any())
+ if (quote == HtmlTokenType.Unknown && whitespaceAfterEquals.Any())
{
return;
}
// Not a "conditional" attribute, so just read the value
- SkipToAndParseCode(sym => IsEndOfAttributeValue(quote, sym));
+ SkipToAndParseCode(token => IsEndOfAttributeValue(quote, token));
// Output the attribute value (will include everything in-between the attribute's quotes).
Output(SpanKindInternal.Markup);
- if (quote != HtmlSymbolType.Unknown)
+ if (quote != HtmlTokenType.Unknown)
{
Optional(quote);
}
@@ -1022,14 +1019,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- private void AttributeValue(HtmlSymbolType quote)
+ private void AttributeValue(HtmlTokenType quote)
{
var prefixStart = CurrentStart;
- var prefix = ReadWhile(sym => sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine);
+ var prefix = ReadWhile(token => token.Type == HtmlTokenType.WhiteSpace || token.Type == HtmlTokenType.NewLine);
- if (At(HtmlSymbolType.Transition))
+ if (At(HtmlTokenType.Transition))
{
- if (NextIs(HtmlSymbolType.Transition))
+ if (NextIs(HtmlTokenType.Transition))
{
// Wrapping this in a block so that the ConditionalAttributeCollapser doesn't rewrite it.
using (Context.Builder.StartBlock(BlockKindInternal.Markup))
@@ -1039,7 +1036,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Render a single "@" in place of "@@".
Span.ChunkGenerator = new LiteralAttributeChunkGenerator(
new LocationTagged(string.Concat(prefix.Select(s => s.Content)), prefixStart),
- new LocationTagged(CurrentSymbol.Content, CurrentStart));
+ new LocationTagged(CurrentToken.Content, CurrentStart));
AcceptAndMoveNext();
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.None);
@@ -1074,17 +1071,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Accept(prefix);
// Literal value
- // 'quote' should be "Unknown" if not quoted and symbols coming from the tokenizer should never have
+ // 'quote' should be "Unknown" if not quoted and tokens coming from the tokenizer should never have
// "Unknown" type.
var valueStart = CurrentStart;
- var value = ReadWhile(sym =>
+ var value = ReadWhile(token =>
// These three conditions find separators which break the attribute value into portions
- sym.Type != HtmlSymbolType.WhiteSpace &&
- sym.Type != HtmlSymbolType.NewLine &&
- sym.Type != HtmlSymbolType.Transition &&
+ token.Type != HtmlTokenType.WhiteSpace &&
+ token.Type != HtmlTokenType.NewLine &&
+ token.Type != HtmlTokenType.Transition &&
// This condition checks for the end of the attribute value (it repeats some of the checks above
// but for now that's ok)
- !IsEndOfAttributeValue(quote, sym));
+ !IsEndOfAttributeValue(quote, token));
Accept(value);
Span.ChunkGenerator = new LiteralAttributeChunkGenerator(
new LocationTagged(string.Concat(prefix.Select(s => s.Content)), prefixStart),
@@ -1093,27 +1090,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Output(SpanKindInternal.Markup);
}
- private bool IsEndOfAttributeValue(HtmlSymbolType quote, HtmlSymbol sym)
+ private bool IsEndOfAttributeValue(HtmlTokenType quote, HtmlToken token)
{
- return EndOfFile || sym == null ||
- (quote != HtmlSymbolType.Unknown
- ? sym.Type == quote // If quoted, just wait for the quote
- : IsUnquotedEndOfAttributeValue(sym));
+ return EndOfFile || token == null ||
+ (quote != HtmlTokenType.Unknown
+ ? token.Type == quote // If quoted, just wait for the quote
+ : IsUnquotedEndOfAttributeValue(token));
}
- private bool IsUnquotedEndOfAttributeValue(HtmlSymbol sym)
+ private bool IsUnquotedEndOfAttributeValue(HtmlToken token)
{
// If unquoted, we have a larger set of terminating characters:
// http://dev.w3.org/html5/spec/tokenization.html#attribute-value-unquoted-state
// Also we need to detect "/" and ">"
- return sym.Type == HtmlSymbolType.DoubleQuote ||
- sym.Type == HtmlSymbolType.SingleQuote ||
- sym.Type == HtmlSymbolType.OpenAngle ||
- sym.Type == HtmlSymbolType.Equals ||
- (sym.Type == HtmlSymbolType.ForwardSlash && NextIs(HtmlSymbolType.CloseAngle)) ||
- sym.Type == HtmlSymbolType.CloseAngle ||
- sym.Type == HtmlSymbolType.WhiteSpace ||
- sym.Type == HtmlSymbolType.NewLine;
+ return token.Type == HtmlTokenType.DoubleQuote ||
+ token.Type == HtmlTokenType.SingleQuote ||
+ token.Type == HtmlTokenType.OpenAngle ||
+ token.Type == HtmlTokenType.Equals ||
+ (token.Type == HtmlTokenType.ForwardSlash && NextIs(HtmlTokenType.CloseAngle)) ||
+ token.Type == HtmlTokenType.CloseAngle ||
+ token.Type == HtmlTokenType.WhiteSpace ||
+ token.Type == HtmlTokenType.NewLine;
}
private void RecoverToEndOfTag()
@@ -1125,17 +1122,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (!EndOfFile)
{
EnsureCurrent();
- switch (CurrentSymbol.Type)
+ switch (CurrentToken.Type)
{
- case HtmlSymbolType.SingleQuote:
- case HtmlSymbolType.DoubleQuote:
+ case HtmlTokenType.SingleQuote:
+ case HtmlTokenType.DoubleQuote:
ParseQuoted();
break;
- case HtmlSymbolType.OpenAngle:
+ case HtmlTokenType.OpenAngle:
// Another "<" means this tag is invalid.
- case HtmlSymbolType.ForwardSlash:
+ case HtmlTokenType.ForwardSlash:
// Empty tag
- case HtmlSymbolType.CloseAngle:
+ case HtmlTokenType.CloseAngle:
// End of tag
return;
default:
@@ -1148,12 +1145,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void ParseQuoted()
{
- var type = CurrentSymbol.Type;
+ var type = CurrentToken.Type;
AcceptAndMoveNext();
ParseQuoted(type);
}
- private void ParseQuoted(HtmlSymbolType type)
+ private void ParseQuoted(HtmlTokenType type)
{
SkipToAndParseCode(type);
if (!EndOfFile)
@@ -1163,38 +1160,38 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- private bool StartTag(Stack> tags, IDisposable tagBlockWrapper)
+ private bool StartTag(Stack> tags, IDisposable tagBlockWrapper)
{
- HtmlSymbol bangSymbol = null;
- HtmlSymbol potentialTagNameSymbol;
+ HtmlToken bangToken = null;
+ HtmlToken potentialTagNameToken;
- if (At(HtmlSymbolType.Bang))
+ if (At(HtmlTokenType.Bang))
{
- bangSymbol = CurrentSymbol;
+ bangToken = CurrentToken;
- potentialTagNameSymbol = Lookahead(count: 1);
+ potentialTagNameToken = Lookahead(count: 1);
}
else
{
- potentialTagNameSymbol = CurrentSymbol;
+ potentialTagNameToken = CurrentToken;
}
- HtmlSymbol tagName;
+ HtmlToken tagName;
- if (potentialTagNameSymbol == null || potentialTagNameSymbol.Type != HtmlSymbolType.Text)
+ if (potentialTagNameToken == null || potentialTagNameToken.Type != HtmlTokenType.Text)
{
- tagName = new HtmlSymbol(string.Empty, HtmlSymbolType.Unknown);
+ tagName = new HtmlToken(string.Empty, HtmlTokenType.Unknown);
}
- else if (bangSymbol != null)
+ else if (bangToken != null)
{
- tagName = new HtmlSymbol("!" + potentialTagNameSymbol.Content, HtmlSymbolType.Text);
+ tagName = new HtmlToken("!" + potentialTagNameToken.Content, HtmlTokenType.Text);
}
else
{
- tagName = potentialTagNameSymbol;
+ tagName = potentialTagNameToken;
}
- Tuple tag = Tuple.Create(tagName, _lastTagStart);
+ Tuple tag = Tuple.Create(tagName, _lastTagStart);
if (tags.Count == 0 &&
// Note tagName may contain a '!' escape character. This ensures doesn't match here.
@@ -1206,23 +1203,23 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Accept(_bufferedOpenAngle);
var textLocation = CurrentStart;
- Assert(HtmlSymbolType.Text);
+ Assert(HtmlTokenType.Text);
AcceptAndMoveNext();
var bookmark = CurrentStart.AbsoluteIndex;
- IEnumerable tokens = ReadWhile(IsSpacingToken(includeNewLines: true));
- var empty = At(HtmlSymbolType.ForwardSlash);
+ IEnumerable tokens = ReadWhile(IsSpacingToken(includeNewLines: true));
+ var empty = At(HtmlTokenType.ForwardSlash);
if (empty)
{
Accept(tokens);
- Assert(HtmlSymbolType.ForwardSlash);
+ Assert(HtmlTokenType.ForwardSlash);
AcceptAndMoveNext();
bookmark = CurrentStart.AbsoluteIndex;
tokens = ReadWhile(IsSpacingToken(includeNewLines: true));
}
- if (!Optional(HtmlSymbolType.CloseAngle))
+ if (!Optional(HtmlTokenType.CloseAngle))
{
Context.Source.Position = bookmark;
NextToken();
@@ -1250,24 +1247,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Accept(_bufferedOpenAngle);
OptionalBangEscape();
- Optional(HtmlSymbolType.Text);
+ Optional(HtmlTokenType.Text);
return RestOfTag(tag, tags, tagBlockWrapper);
}
- private bool RestOfTag(Tuple tag,
- Stack> tags,
+ private bool RestOfTag(Tuple tag,
+ Stack> tags,
IDisposable tagBlockWrapper)
{
TagContent();
// We are now at a possible end of the tag
// Found '<', so we just abort this tag.
- if (At(HtmlSymbolType.OpenAngle))
+ if (At(HtmlTokenType.OpenAngle))
{
return false;
}
- var isEmpty = At(HtmlSymbolType.ForwardSlash);
+ var isEmpty = At(HtmlTokenType.ForwardSlash);
// Found a solidus, so don't accept it but DON'T push the tag to the stack
if (isEmpty)
{
@@ -1275,7 +1272,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Check for the '>' to determine if the tag is finished
- var seenClose = Optional(HtmlSymbolType.CloseAngle);
+ var seenClose = Optional(HtmlTokenType.CloseAngle);
if (!seenClose)
{
Context.ErrorSink.OnError(
@@ -1301,17 +1298,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var bookmark = CurrentStart.AbsoluteIndex;
// Skip whitespace
- IEnumerable whiteSpace = ReadWhile(IsSpacingToken(includeNewLines: true));
+ IEnumerable whiteSpace = ReadWhile(IsSpacingToken(includeNewLines: true));
// Open Angle
- if (At(HtmlSymbolType.OpenAngle) && NextIs(HtmlSymbolType.ForwardSlash))
+ if (At(HtmlTokenType.OpenAngle) && NextIs(HtmlTokenType.ForwardSlash))
{
- var openAngle = CurrentSymbol;
+ var openAngle = CurrentToken;
NextToken();
- Assert(HtmlSymbolType.ForwardSlash);
- var solidus = CurrentSymbol;
+ Assert(HtmlTokenType.ForwardSlash);
+ var solidus = CurrentToken;
NextToken();
- if (At(HtmlSymbolType.Text) && string.Equals(CurrentSymbol.Content, tagName, StringComparison.OrdinalIgnoreCase))
+ if (At(HtmlTokenType.Text) && string.Equals(CurrentToken.Content, tagName, StringComparison.OrdinalIgnoreCase))
{
// Accept up to here
Accept(whiteSpace);
@@ -1324,9 +1321,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AcceptAndMoveNext();
// Accept to '>', '<' or EOF
- AcceptUntil(HtmlSymbolType.CloseAngle, HtmlSymbolType.OpenAngle);
+ AcceptUntil(HtmlTokenType.CloseAngle, HtmlTokenType.OpenAngle);
// Accept the '>' if we saw it. And if we do see it, we're complete
- var complete = Optional(HtmlSymbolType.CloseAngle);
+ var complete = Optional(HtmlTokenType.CloseAngle);
if (complete)
{
@@ -1376,18 +1373,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
while (!seenEndScript && !EndOfFile)
{
- SkipToAndParseCode(HtmlSymbolType.OpenAngle);
+ SkipToAndParseCode(HtmlTokenType.OpenAngle);
var tagStart = CurrentStart;
- if (NextIs(HtmlSymbolType.ForwardSlash))
+ if (NextIs(HtmlTokenType.ForwardSlash))
{
- var openAngle = CurrentSymbol;
+ var openAngle = CurrentToken;
NextToken(); // Skip over '<', current is '/'
- var solidus = CurrentSymbol;
+ var solidus = CurrentToken;
NextToken(); // Skip over '/', current should be text
- if (At(HtmlSymbolType.Text) &&
- string.Equals(CurrentSymbol.Content, ScriptTagName, StringComparison.OrdinalIgnoreCase))
+ if (At(HtmlTokenType.Text) &&
+ string.Equals(CurrentToken.Content, ScriptTagName, StringComparison.OrdinalIgnoreCase))
{
seenEndScript = true;
}
@@ -1398,7 +1395,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
PutBack(solidus); // Put back '/'
PutBack(openAngle); // Put back '<'
- // We just looked ahead, this NextToken will set CurrentSymbol to an open angle bracket.
+ // We just looked ahead, this NextToken will set CurrentToken to an open angle bracket.
NextToken();
}
@@ -1412,8 +1409,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AcceptAndMoveNext(); // '<'
AcceptAndMoveNext(); // '/'
- SkipToAndParseCode(HtmlSymbolType.CloseAngle);
- if (!Optional(HtmlSymbolType.CloseAngle))
+ SkipToAndParseCode(HtmlTokenType.CloseAngle);
+ if (!Optional(HtmlTokenType.CloseAngle))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnfinishedTag(
@@ -1444,7 +1441,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
tagBlockWrapper.Dispose();
}
- private bool AcceptUntilAll(params HtmlSymbolType[] endSequence)
+ private bool AcceptUntilAll(params HtmlTokenType[] endSequence)
{
while (!EndOfFile)
{
@@ -1459,9 +1456,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
- private bool RemoveTag(Stack> tags, string tagName, SourceLocation tagStart)
+ private bool RemoveTag(Stack> tags, string tagName, SourceLocation tagStart)
{
- Tuple currentTag = null;
+ Tuple currentTag = null;
while (tags.Count > 0)
{
currentTag = tags.Pop();
@@ -1489,7 +1486,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
- private void EndTagBlock(Stack> tags, bool complete)
+ private void EndTagBlock(Stack> tags, bool complete)
{
if (tags.Count > 0)
{
@@ -1517,46 +1514,46 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (Context.Builder.LastSpan.Kind == SpanKindInternal.Transition)
{
- var symbols = ReadWhile(
- f => (f.Type == HtmlSymbolType.WhiteSpace) || (f.Type == HtmlSymbolType.NewLine));
+ var tokens = ReadWhile(
+ f => (f.Type == HtmlTokenType.WhiteSpace) || (f.Type == HtmlTokenType.NewLine));
- // Make sure the current symbol is not markup, which can be html start tag or @:
- if (!(At(HtmlSymbolType.OpenAngle) ||
- (At(HtmlSymbolType.Transition) && Lookahead(count: 1).Content.StartsWith(":"))))
+ // Make sure the current token is not markup, which can be html start tag or @:
+ if (!(At(HtmlTokenType.OpenAngle) ||
+ (At(HtmlTokenType.Transition) && Lookahead(count: 1).Content.StartsWith(":"))))
{
// Don't accept whitespace as markup if the end text tag is followed by csharp.
shouldAcceptWhitespaceAndNewLine = false;
}
PutCurrentBack();
- PutBack(symbols);
+ PutBack(tokens);
EnsureCurrent();
}
if (shouldAcceptWhitespaceAndNewLine)
{
// Accept whitespace and a single newline if present
- AcceptWhile(HtmlSymbolType.WhiteSpace);
- Optional(HtmlSymbolType.NewLine);
+ AcceptWhile(HtmlTokenType.WhiteSpace);
+ Optional(HtmlTokenType.NewLine);
}
}
else if (Span.EditHandler.AcceptedCharacters == AcceptedCharactersInternal.Any)
{
- AcceptWhile(HtmlSymbolType.WhiteSpace);
- Optional(HtmlSymbolType.NewLine);
+ AcceptWhile(HtmlTokenType.WhiteSpace);
+ Optional(HtmlTokenType.NewLine);
}
PutCurrentBack();
if (!complete)
{
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
}
Output(SpanKindInternal.Markup);
}
- internal static bool IsValidAttributeNameSymbol(HtmlSymbol symbol)
+ internal static bool IsValidAttributeNameToken(HtmlToken token)
{
- if (symbol == null)
+ if (token == null)
{
return false;
}
@@ -1565,16 +1562,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// However, it's not all of it. For instance we don't special case control characters or allow OpenAngle.
// It also doesn't try to exclude Razor specific features such as the @ transition. This is based on the
// expectation that the parser handles such scenarios prior to falling through to name resolution.
- var symbolType = symbol.Type;
- return symbolType != HtmlSymbolType.WhiteSpace &&
- symbolType != HtmlSymbolType.NewLine &&
- symbolType != HtmlSymbolType.CloseAngle &&
- symbolType != HtmlSymbolType.OpenAngle &&
- symbolType != HtmlSymbolType.ForwardSlash &&
- symbolType != HtmlSymbolType.DoubleQuote &&
- symbolType != HtmlSymbolType.SingleQuote &&
- symbolType != HtmlSymbolType.Equals &&
- symbolType != HtmlSymbolType.Unknown;
+ var tokenType = token.Type;
+ return tokenType != HtmlTokenType.WhiteSpace &&
+ tokenType != HtmlTokenType.NewLine &&
+ tokenType != HtmlTokenType.CloseAngle &&
+ tokenType != HtmlTokenType.OpenAngle &&
+ tokenType != HtmlTokenType.ForwardSlash &&
+ tokenType != HtmlTokenType.DoubleQuote &&
+ tokenType != HtmlTokenType.SingleQuote &&
+ tokenType != HtmlTokenType.Equals &&
+ tokenType != HtmlTokenType.Unknown;
}
public void ParseDocument()
@@ -1593,10 +1590,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
NextToken();
while (!EndOfFile)
{
- SkipToAndParseCode(HtmlSymbolType.OpenAngle);
+ SkipToAndParseCode(HtmlTokenType.OpenAngle);
ScanTagInDocumentContext();
}
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
Output(SpanKindInternal.Markup);
}
}
@@ -1608,14 +1605,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
///
private void ScanTagInDocumentContext()
{
- if (At(HtmlSymbolType.OpenAngle))
+ if (At(HtmlTokenType.OpenAngle))
{
- if (NextIs(HtmlSymbolType.Bang))
+ if (NextIs(HtmlTokenType.Bang))
{
// Checking to see if we meet the conditions of a special '!' tag: ';" as an HTML tag.
@@ -1670,13 +1667,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// Parsing an end tag
// This section can accept things like: '
' or '' etc.
- Optional(HtmlSymbolType.ForwardSlash);
+ Optional(HtmlTokenType.ForwardSlash);
// Whitespace here is invalid (according to the spec)
OptionalBangEscape();
- Optional(HtmlSymbolType.Text);
- Optional(HtmlSymbolType.WhiteSpace);
- Optional(HtmlSymbolType.CloseAngle);
+ Optional(HtmlTokenType.Text);
+ Optional(HtmlTokenType.WhiteSpace);
+ Optional(HtmlTokenType.CloseAngle);
}
Output(SpanKindInternal.Markup);
@@ -1758,7 +1755,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
NestingSection(nestingSequences);
}
- AddMarkerSymbolIfNecessary();
+ AddMarkerTokenIfNecessary();
Output(SpanKindInternal.Markup);
}
}
@@ -1768,7 +1765,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
do
{
- SkipToAndParseCode(sym => sym.Type == HtmlSymbolType.OpenAngle || AtEnd(nestingSequenceComponents));
+ SkipToAndParseCode(token => token.Type == HtmlTokenType.OpenAngle || AtEnd(nestingSequenceComponents));
ScanTagInDocumentContext();
if (!EndOfFile && AtEnd(nestingSequenceComponents))
{
@@ -1785,13 +1782,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var nesting = 1;
while (nesting > 0 && !EndOfFile)
{
- SkipToAndParseCode(sym =>
- sym.Type == HtmlSymbolType.Text ||
- sym.Type == HtmlSymbolType.OpenAngle);
- if (At(HtmlSymbolType.Text))
+ SkipToAndParseCode(token =>
+ token.Type == HtmlTokenType.Text ||
+ token.Type == HtmlTokenType.OpenAngle);
+ if (At(HtmlTokenType.Text))
{
nesting += ProcessTextToken(nestingSequences, nesting);
- if (CurrentSymbol != null)
+ if (CurrentToken != null)
{
AcceptAndMoveNext();
}
@@ -1810,19 +1807,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private bool AtEnd(string[] nestingSequenceComponents)
{
EnsureCurrent();
- if (string.Equals(CurrentSymbol.Content, nestingSequenceComponents[0], Comparison))
+ if (string.Equals(CurrentToken.Content, nestingSequenceComponents[0], Comparison))
{
- var bookmark = Context.Source.Position - CurrentSymbol.Content.Length;
+ var bookmark = Context.Source.Position - CurrentToken.Content.Length;
try
{
foreach (string component in nestingSequenceComponents)
{
- if (!EndOfFile && !string.Equals(CurrentSymbol.Content, component, Comparison))
+ if (!EndOfFile && !string.Equals(CurrentToken.Content, component, Comparison))
{
return false;
}
NextToken();
- while (!EndOfFile && IsSpacingToken(includeNewLines: true)(CurrentSymbol))
+ while (!EndOfFile && IsSpacingToken(includeNewLines: true)(CurrentToken))
{
NextToken();
}
@@ -1840,7 +1837,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private int ProcessTextToken(Tuple nestingSequences, int currentNesting)
{
- for (int i = 0; i < CurrentSymbol.Content.Length; i++)
+ for (int i = 0; i < CurrentToken.Content.Length; i++)
{
var nestingDelta = HandleNestingSequence(nestingSequences.Item1, i, currentNesting, 1);
if (nestingDelta == 0)
@@ -1859,22 +1856,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private int HandleNestingSequence(string sequence, int position, int currentNesting, int retIfMatched)
{
if (sequence != null &&
- CurrentSymbol.Content[position] == sequence[0] &&
- position + sequence.Length <= CurrentSymbol.Content.Length)
+ CurrentToken.Content[position] == sequence[0] &&
+ position + sequence.Length <= CurrentToken.Content.Length)
{
- var possibleStart = CurrentSymbol.Content.Substring(position, sequence.Length);
+ var possibleStart = CurrentToken.Content.Substring(position, sequence.Length);
if (string.Equals(possibleStart, sequence, Comparison))
{
- // Capture the current symbol and "put it back" (really we just want to clear CurrentSymbol)
+ // Capture the current token and "put it back" (really we just want to clear CurrentToken)
var bookmark = CurrentStart;
- var sym = CurrentSymbol;
+ var token = CurrentToken;
PutCurrentBack();
- // Carve up the symbol
- Tuple pair = Language.SplitSymbol(sym, position, HtmlSymbolType.Text);
+ // Carve up the token
+ Tuple pair = Language.SplitToken(token, position, HtmlTokenType.Text);
var preSequence = pair.Item1;
Debug.Assert(pair.Item2 != null);
- pair = Language.SplitSymbol(pair.Item2, sequence.Length, HtmlSymbolType.Text);
+ pair = Language.SplitToken(pair.Item2, sequence.Length, HtmlTokenType.Text);
var sequenceToken = pair.Item1;
var postSequence = pair.Item2;
var postSequenceBookmark = bookmark.AbsoluteIndex + preSequence.Content.Length + pair.Item1.Content.Length;
@@ -1897,7 +1894,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// This isn't the end of the last nesting sequence, accept the token and keep going
Accept(sequenceToken);
- // Position at the start of the postSequence symbol, which might be null.
+ // Position at the start of the postSequence token, which might be null.
Context.Source.Position = postSequenceBookmark;
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlSymbol.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlToken.cs
similarity index 73%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlSymbol.cs
rename to src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlToken.cs
index 217e293704..2d52ed14e2 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlSymbol.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlToken.cs
@@ -6,11 +6,11 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal class HtmlSymbol : SymbolBase
+ internal class HtmlToken : TokenBase
{
- internal static readonly HtmlSymbol Hyphen = new HtmlSymbol("-", HtmlSymbolType.Text);
+ internal static readonly HtmlToken Hyphen = new HtmlToken("-", HtmlTokenType.Text);
- public HtmlSymbol(string content, HtmlSymbolType type)
+ public HtmlToken(string content, HtmlTokenType type)
: base(content, type, RazorDiagnostic.EmptyArray)
{
if (content == null)
@@ -19,9 +19,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- public HtmlSymbol(
+ public HtmlToken(
string content,
- HtmlSymbolType type,
+ HtmlTokenType type,
IReadOnlyList errors)
: base(content, type, errors)
{
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlSymbolType.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlTokenType.cs
similarity index 96%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlSymbolType.cs
rename to src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlTokenType.cs
index bc90b98022..82d96d7ebc 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlSymbolType.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlTokenType.cs
@@ -6,7 +6,7 @@ using System;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
[Flags]
- internal enum HtmlSymbolType
+ internal enum HtmlTokenType
{
Unknown,
Text, // Text which isn't one of the below
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlTokenizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlTokenizer.cs
index df9671d1b7..99a7152447 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlTokenizer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlTokenizer.cs
@@ -7,7 +7,7 @@ using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// Tokenizer _loosely_ based on http://dev.w3.org/html5/spec/Overview.html#tokenization
- internal class HtmlTokenizer : Tokenizer
+ internal class HtmlTokenizer : Tokenizer
{
private const char TransitionChar = '@';
@@ -21,24 +21,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private new HtmlTokenizerState? CurrentState => (HtmlTokenizerState?)base.CurrentState;
- public override HtmlSymbolType RazorCommentType
+ public override HtmlTokenType RazorCommentType
{
- get { return HtmlSymbolType.RazorComment; }
+ get { return HtmlTokenType.RazorComment; }
}
- public override HtmlSymbolType RazorCommentTransitionType
+ public override HtmlTokenType RazorCommentTransitionType
{
- get { return HtmlSymbolType.RazorCommentTransition; }
+ get { return HtmlTokenType.RazorCommentTransition; }
}
- public override HtmlSymbolType RazorCommentStarType
+ public override HtmlTokenType RazorCommentStarType
{
- get { return HtmlSymbolType.RazorCommentStar; }
+ get { return HtmlTokenType.RazorCommentStar; }
}
- protected override HtmlSymbol CreateSymbol(string content, HtmlSymbolType type, IReadOnlyList errors)
+ protected override HtmlToken CreateToken(string content, HtmlTokenType type, IReadOnlyList errors)
{
- return new HtmlSymbol(content, type, errors);
+ return new HtmlToken(content, type, errors);
}
protected override StateResult Dispatch()
@@ -57,8 +57,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return RazorCommentBody();
case HtmlTokenizerState.StarAfterRazorCommentBody:
return StarAfterRazorCommentBody();
- case HtmlTokenizerState.AtSymbolAfterRazorCommentBody:
- return AtSymbolAfterRazorCommentBody();
+ case HtmlTokenizerState.AtTokenAfterRazorCommentBody:
+ return AtTokenAfterRazorCommentBody();
default:
Debug.Fail("Invalid TokenizerState");
return default(StateResult);
@@ -66,35 +66,35 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Optimize memory allocation by returning constants for the most frequent cases
- protected override string GetSymbolContent(HtmlSymbolType type)
+ protected override string GetTokenContent(HtmlTokenType type)
{
- var symbolLength = Buffer.Length;
+ var tokenLength = Buffer.Length;
- if (symbolLength == 1)
+ if (tokenLength == 1)
{
switch (type)
{
- case HtmlSymbolType.OpenAngle:
+ case HtmlTokenType.OpenAngle:
return "<";
- case HtmlSymbolType.Bang:
+ case HtmlTokenType.Bang:
return "!";
- case HtmlSymbolType.ForwardSlash:
+ case HtmlTokenType.ForwardSlash:
return "/";
- case HtmlSymbolType.QuestionMark:
+ case HtmlTokenType.QuestionMark:
return "?";
- case HtmlSymbolType.LeftBracket:
+ case HtmlTokenType.LeftBracket:
return "[";
- case HtmlSymbolType.CloseAngle:
+ case HtmlTokenType.CloseAngle:
return ">";
- case HtmlSymbolType.RightBracket:
+ case HtmlTokenType.RightBracket:
return "]";
- case HtmlSymbolType.Equals:
+ case HtmlTokenType.Equals:
return "=";
- case HtmlSymbolType.DoubleQuote:
+ case HtmlTokenType.DoubleQuote:
return "\"";
- case HtmlSymbolType.SingleQuote:
+ case HtmlTokenType.SingleQuote:
return "'";
- case HtmlSymbolType.WhiteSpace:
+ case HtmlTokenType.WhiteSpace:
if (Buffer[0] == ' ')
{
return " ";
@@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return "\t";
}
break;
- case HtmlSymbolType.NewLine:
+ case HtmlTokenType.NewLine:
if (Buffer[0] == '\n')
{
return "\n";
@@ -113,12 +113,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- if (symbolLength == 2 && type == HtmlSymbolType.NewLine)
+ if (tokenLength == 2 && type == HtmlTokenType.NewLine)
{
return "\r\n";
}
- return base.GetSymbolContent(type);
+ return base.GetTokenContent(type);
}
// http://dev.w3.org/html5/spec/Overview.html#data-state
@@ -139,21 +139,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return Transition(
HtmlTokenizerState.AfterRazorCommentTransition,
- EndSymbol(HtmlSymbolType.RazorCommentTransition));
+ EndToken(HtmlTokenType.RazorCommentTransition));
}
else if (CurrentCharacter == '@')
{
// Could be escaped comment transition
return Transition(
HtmlTokenizerState.EscapedRazorCommentTransition,
- EndSymbol(HtmlSymbolType.Transition));
+ EndToken(HtmlTokenType.Transition));
}
- return Stay(EndSymbol(HtmlSymbolType.Transition));
+ return Stay(EndToken(HtmlTokenType.Transition));
}
- else if (AtSymbol())
+ else if (AtToken())
{
- return Stay(Symbol());
+ return Stay(Token());
}
else
{
@@ -164,7 +164,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private StateResult EscapedRazorCommentTransition()
{
TakeCurrent();
- return Transition(HtmlTokenizerState.Data, EndSymbol(HtmlSymbolType.Transition));
+ return Transition(HtmlTokenizerState.Data, EndToken(HtmlTokenType.Transition));
}
private StateResult Text()
@@ -172,7 +172,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var prev = '\0';
while (!EndOfFile &&
!(ParserHelpers.IsWhitespace(CurrentCharacter) || ParserHelpers.IsNewLine(CurrentCharacter)) &&
- !AtSymbol())
+ !AtToken())
{
prev = CurrentCharacter;
TakeCurrent();
@@ -190,56 +190,56 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Output the Text token and return to the Data state to tokenize the next character (if there is one)
- return Transition(HtmlTokenizerState.Data, EndSymbol(HtmlSymbolType.Text));
+ return Transition(HtmlTokenizerState.Data, EndToken(HtmlTokenType.Text));
}
- private HtmlSymbol Symbol()
+ private HtmlToken Token()
{
- Debug.Assert(AtSymbol());
+ Debug.Assert(AtToken());
var sym = CurrentCharacter;
TakeCurrent();
switch (sym)
{
case '<':
- return EndSymbol(HtmlSymbolType.OpenAngle);
+ return EndToken(HtmlTokenType.OpenAngle);
case '!':
- return EndSymbol(HtmlSymbolType.Bang);
+ return EndToken(HtmlTokenType.Bang);
case '/':
- return EndSymbol(HtmlSymbolType.ForwardSlash);
+ return EndToken(HtmlTokenType.ForwardSlash);
case '?':
- return EndSymbol(HtmlSymbolType.QuestionMark);
+ return EndToken(HtmlTokenType.QuestionMark);
case '[':
- return EndSymbol(HtmlSymbolType.LeftBracket);
+ return EndToken(HtmlTokenType.LeftBracket);
case '>':
- return EndSymbol(HtmlSymbolType.CloseAngle);
+ return EndToken(HtmlTokenType.CloseAngle);
case ']':
- return EndSymbol(HtmlSymbolType.RightBracket);
+ return EndToken(HtmlTokenType.RightBracket);
case '=':
- return EndSymbol(HtmlSymbolType.Equals);
+ return EndToken(HtmlTokenType.Equals);
case '"':
- return EndSymbol(HtmlSymbolType.DoubleQuote);
+ return EndToken(HtmlTokenType.DoubleQuote);
case '\'':
- return EndSymbol(HtmlSymbolType.SingleQuote);
+ return EndToken(HtmlTokenType.SingleQuote);
case '-':
Debug.Assert(CurrentCharacter == '-');
TakeCurrent();
- return EndSymbol(HtmlSymbolType.DoubleHyphen);
+ return EndToken(HtmlTokenType.DoubleHyphen);
default:
- Debug.Fail("Unexpected symbol!");
- return EndSymbol(HtmlSymbolType.Unknown);
+ Debug.Fail("Unexpected token!");
+ return EndToken(HtmlTokenType.Unknown);
}
}
- private HtmlSymbol Whitespace()
+ private HtmlToken Whitespace()
{
while (ParserHelpers.IsWhitespace(CurrentCharacter))
{
TakeCurrent();
}
- return EndSymbol(HtmlSymbolType.WhiteSpace);
+ return EndToken(HtmlTokenType.WhiteSpace);
}
- private HtmlSymbol Newline()
+ private HtmlToken Newline()
{
Debug.Assert(ParserHelpers.IsNewLine(CurrentCharacter));
// CSharp Spec §2.3.1
@@ -249,10 +249,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeCurrent();
}
- return EndSymbol(HtmlSymbolType.NewLine);
+ return EndToken(HtmlTokenType.NewLine);
}
- private bool AtSymbol()
+ private bool AtToken()
{
return CurrentCharacter == '<' ||
CurrentCharacter == '<' ||
@@ -274,7 +274,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Transition((int)state, result: null);
}
- private StateResult Transition(HtmlTokenizerState state, HtmlSymbol result)
+ private StateResult Transition(HtmlTokenizerState state, HtmlToken result)
{
return Transition((int)state, result);
}
@@ -289,7 +289,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
EscapedRazorCommentTransition = RazorCommentTokenizerState.EscapedRazorCommentTransition,
RazorCommentBody = RazorCommentTokenizerState.RazorCommentBody,
StarAfterRazorCommentBody = RazorCommentTokenizerState.StarAfterRazorCommentBody,
- AtSymbolAfterRazorCommentBody = RazorCommentTokenizerState.AtSymbolAfterRazorCommentBody,
+ AtTokenAfterRazorCommentBody = RazorCommentTokenizerState.AtTokenAfterRazorCommentBody,
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ISymbol.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/IToken.cs
similarity index 91%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ISymbol.cs
rename to src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/IToken.cs
index 346a390375..2f8eb1214d 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ISymbol.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/IToken.cs
@@ -3,7 +3,7 @@
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal interface ISymbol
+ internal interface IToken
{
Span Parent { get; set; }
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ITokenizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ITokenizer.cs
index 30e5186afb..530df75dbc 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ITokenizer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ITokenizer.cs
@@ -5,6 +5,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal interface ITokenizer
{
- ISymbol NextSymbol();
+ IToken NextToken();
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ImplicitExpressionEditHandler.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ImplicitExpressionEditHandler.cs
index ababd6c7f7..b3869f00d0 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ImplicitExpressionEditHandler.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ImplicitExpressionEditHandler.cs
@@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly ISet _keywords;
private readonly IReadOnlyCollection _readOnlyKeywords;
- public ImplicitExpressionEditHandler(Func> tokenizer, ISet keywords, bool acceptTrailingDot)
+ public ImplicitExpressionEditHandler(Func> tokenizer, ISet keywords, bool acceptTrailingDot)
: base(tokenizer)
{
_keywords = keywords ?? new HashSet();
@@ -109,11 +109,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return HandleInsertion(target, lastChar.Value, change);
}
+ if (IsAcceptableInsertionInBalancedParenthesis(target, change))
+ {
+ return PartialParseResultInternal.Accepted;
+ }
+
if (IsAcceptableDeletion(target, change))
{
return HandleDeletion(target, lastChar.Value, change);
}
+ if (IsAcceptableDeletionInBalancedParenthesis(target, change))
+ {
+ return PartialParseResultInternal.Accepted;
+ }
+
return PartialParseResultInternal.Rejected;
}
@@ -160,44 +170,44 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
- for (var i = 0; i < target.Symbols.Count; i++)
+ for (var i = 0; i < target.Tokens.Count; i++)
{
- var symbol = target.Symbols[i] as CSharpSymbol;
+ var token = target.Tokens[i] as CSharpToken;
- if (symbol == null)
+ if (token == null)
{
break;
}
- var symbolStartIndex = symbol.Start.AbsoluteIndex;
- var symbolEndIndex = symbolStartIndex + symbol.Content.Length;
+ var tokenStartIndex = token.Start.AbsoluteIndex;
+ var tokenEndIndex = tokenStartIndex + token.Content.Length;
- // We're looking for the first symbol that contains the SourceChange.
- if (symbolEndIndex > change.Span.AbsoluteIndex)
+ // We're looking for the first token that contains the SourceChange.
+ if (tokenEndIndex > change.Span.AbsoluteIndex)
{
- if (symbolEndIndex >= change.Span.AbsoluteIndex + change.Span.Length && symbol.Type == CSharpSymbolType.Identifier)
+ if (tokenEndIndex >= change.Span.AbsoluteIndex + change.Span.Length && token.Type == CSharpTokenType.Identifier)
{
- // The symbol we're changing happens to be an identifier. Need to check if its transformed state is also one.
+ // The token we're changing happens to be an identifier. Need to check if its transformed state is also one.
// We do this transformation logic to capture the case that the new text change happens to not be an identifier;
// i.e. "5". Alone, it's numeric, within an identifier it's classified as identifier.
- var transformedContent = change.GetEditedContent(symbol.Content, change.Span.AbsoluteIndex - symbolStartIndex);
- var newSymbols = Tokenizer(transformedContent);
+ var transformedContent = change.GetEditedContent(token.Content, change.Span.AbsoluteIndex - tokenStartIndex);
+ var newTokens = Tokenizer(transformedContent);
- if (newSymbols.Count() != 1)
+ if (newTokens.Count() != 1)
{
- // The transformed content resulted in more than one symbol; we can only replace a single identifier with
+ // The transformed content resulted in more than one token; we can only replace a single identifier with
// another single identifier.
break;
}
- var newSymbol = (CSharpSymbol)newSymbols.First();
- if (newSymbol.Type == CSharpSymbolType.Identifier)
+ var newToken = (CSharpToken)newTokens.First();
+ if (newToken.Type == CSharpTokenType.Identifier)
{
return true;
}
}
- // Change is touching a non-identifier symbol or spans multiple symbols.
+ // Change is touching a non-identifier token or spans multiple tokens.
break;
}
@@ -216,8 +226,196 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private static bool IsAcceptableInsertion(Span target, SourceChange change)
{
return change.IsInsert &&
- (IsAcceptableEndInsertion(target, change) ||
- IsAcceptableInnerInsertion(target, change));
+ (IsAcceptableEndInsertion(target, change) ||
+ IsAcceptableInnerInsertion(target, change));
+ }
+
+ // Internal for testing
+ internal static bool IsAcceptableDeletionInBalancedParenthesis(Span target, SourceChange change)
+ {
+ if (!change.IsDelete)
+ {
+ return false;
+ }
+
+ var changeStart = change.Span.AbsoluteIndex;
+ var changeLength = change.Span.Length;
+ var changeEnd = changeStart + changeLength;
+ var tokens = target.Tokens.Cast().ToArray();
+ if (!IsInsideParenthesis(changeStart, tokens) || !IsInsideParenthesis(changeEnd, tokens))
+ {
+ // Either the start or end of the delete does not fall inside of parenthesis, unacceptable inner deletion.
+ return false;
+ }
+
+ var relativePosition = changeStart - target.Start.AbsoluteIndex;
+ var deletionContent = target.Content.Substring(relativePosition, changeLength);
+
+ if (deletionContent.IndexOfAny(new[] { '(', ')' }) >= 0)
+ {
+ // Change deleted some parenthesis
+ return false;
+ }
+
+ return true;
+ }
+
+ // Internal for testing
+ internal static bool IsAcceptableInsertionInBalancedParenthesis(Span target, SourceChange change)
+ {
+ if (!change.IsInsert)
+ {
+ return false;
+ }
+
+ if (change.NewText.IndexOfAny(new[] { '(', ')' }) >= 0)
+ {
+ // Insertions of parenthesis aren't handled by us. If someone else wants to accept it, they can.
+ return false;
+ }
+
+ var tokens = target.Tokens.Cast().ToArray();
+ if (IsInsideParenthesis(change.Span.AbsoluteIndex, tokens))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Internal for testing
+ internal static bool IsInsideParenthesis(int position, IReadOnlyList tokens)
+ {
+ var balanceCount = 0;
+ var foundInsertionPoint = false;
+ for (var i = 0; i < tokens.Count; i++)
+ {
+ var currentToken = tokens[i];
+ if (ContainsPosition(position, currentToken))
+ {
+ if (balanceCount == 0)
+ {
+ // Insertion point is outside of parenthesis, i.e. inserting at the pipe: @Foo|Baz()
+ return false;
+ }
+
+ foundInsertionPoint = true;
+ }
+
+ if (!TryUpdateBalanceCount(currentToken, ref balanceCount))
+ {
+ // Couldn't update the count. This usually occurrs when we run into a ')' outside of any parenthesis.
+ return false;
+ }
+
+ if (foundInsertionPoint && balanceCount == 0)
+ {
+ // Once parenthesis become balanced after the insertion point we return true, no need to go further.
+ // If they get unbalanced down the line the expression was already unbalanced to begin with and this
+ // change happens prior to any ambiguity.
+ return true;
+ }
+ }
+
+ // Unbalanced parenthesis
+ return false;
+ }
+
+ // Internal for testing
+ internal static bool ContainsPosition(int position, CSharpToken currentToken)
+ {
+ var tokenStart = currentToken.Start.AbsoluteIndex;
+ if (tokenStart == position)
+ {
+ // Token is exactly at the insertion point.
+ return true;
+ }
+
+ var tokenEnd = tokenStart + currentToken.Content.Length;
+ if (tokenStart < position && tokenEnd > position)
+ {
+ // Insertion point falls in the middle of the current token.
+ return true;
+ }
+
+ return false;
+ }
+
+ // Internal for testing
+ internal static bool TryUpdateBalanceCount(CSharpToken token, ref int count)
+ {
+ var updatedCount = count;
+ if (token.Type == CSharpTokenType.LeftParenthesis)
+ {
+ updatedCount++;
+ }
+ else if (token.Type == CSharpTokenType.RightParenthesis)
+ {
+ if (updatedCount == 0)
+ {
+ return false;
+ }
+
+ updatedCount--;
+ }
+ else if (token.Type == CSharpTokenType.StringLiteral)
+ {
+ var content = token.Content;
+ if (content.Length > 0 && content[content.Length - 1] != '"')
+ {
+ // Incomplete string literal may have consumed some of our parenthesis and usually occurr during auto-completion of '"' => '""'.
+ if (!TryUpdateCountFromContent(content, ref updatedCount))
+ {
+ return false;
+ }
+ }
+ }
+ else if (token.Type == CSharpTokenType.CharacterLiteral)
+ {
+ var content = token.Content;
+ if (content.Length > 0 && content[content.Length - 1] != '\'')
+ {
+ // Incomplete character literal may have consumed some of our parenthesis and usually occurr during auto-completion of "'" => "''".
+ if (!TryUpdateCountFromContent(content, ref updatedCount))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (updatedCount < 0)
+ {
+ return false;
+ }
+
+ count = updatedCount;
+ return true;
+ }
+
+ // Internal for testing
+ internal static bool TryUpdateCountFromContent(string content, ref int count)
+ {
+ var updatedCount = count;
+ for (var i = 0; i < content.Length; i++)
+ {
+ if (content[i] == '(')
+ {
+ updatedCount++;
+ }
+ else if (content[i] == ')')
+ {
+ if (updatedCount == 0)
+ {
+ // Unbalanced parenthesis, i.e. @Foo)
+ return false;
+ }
+
+ updatedCount--;
+ }
+ }
+
+ count = updatedCount;
+ return true;
}
// Accepts character insertions at the end of spans. AKA: '@foo' -> '@fooo' or '@foo' -> '@foo ' etc.
@@ -291,10 +489,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return TryAcceptChange(target, change);
}
- else
+ else if (previousChar == '(')
{
- return PartialParseResultInternal.Rejected;
+ var changeRelativePosition = change.Span.AbsoluteIndex - target.Start.AbsoluteIndex;
+ if (target.Content[changeRelativePosition] == ')')
+ {
+ return PartialParseResultInternal.Accepted | PartialParseResultInternal.Provisional;
+ }
}
+
+ return PartialParseResultInternal.Rejected;
}
private PartialParseResultInternal HandleInsertion(Span target, char previousChar, SourceChange change)
@@ -308,6 +512,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return HandleInsertionAfterIdPart(target, change);
}
+ else if (previousChar == '(')
+ {
+ return HandleInsertionAfterOpenParenthesis(target, change);
+ }
else
{
return PartialParseResultInternal.Rejected;
@@ -321,6 +529,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return TryAcceptChange(target, change);
}
+ else if (IsDoubleParenthesisInsertion(change) || IsOpenParenthesisInsertion(change))
+ {
+ // Allow inserting parens after an identifier - this is needed to support signature
+ // help intellisense in VS.
+ return TryAcceptChange(target, change);
+ }
else if (EndsWithDot(change.NewText))
{
// Accept it, possibly provisionally
@@ -337,6 +551,40 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
+ private PartialParseResultInternal HandleInsertionAfterOpenParenthesis(Span target, SourceChange change)
+ {
+ if (IsCloseParenthesisInsertion(change))
+ {
+ return TryAcceptChange(target, change);
+ }
+
+ return PartialParseResultInternal.Rejected;
+ }
+
+ private static bool IsDoubleParenthesisInsertion(SourceChange change)
+ {
+ return
+ change.IsInsert &&
+ change.NewText.Length == 2 &&
+ change.NewText == "()";
+ }
+
+ private static bool IsOpenParenthesisInsertion(SourceChange change)
+ {
+ return
+ change.IsInsert &&
+ change.NewText.Length == 1 &&
+ change.NewText == "(";
+ }
+
+ private static bool IsCloseParenthesisInsertion(SourceChange change)
+ {
+ return
+ change.IsInsert &&
+ change.NewText.Length == 1 &&
+ change.NewText == ")";
+ }
+
private static bool EndsWithDot(string content)
{
return (content.Length == 1 && content[0] == '.') ||
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/KnownSymbolType.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/KnownTokenType.cs
similarity index 92%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/KnownSymbolType.cs
rename to src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/KnownTokenType.cs
index a1ed444ac9..ca5fad7254 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/KnownSymbolType.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/KnownTokenType.cs
@@ -3,7 +3,7 @@
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal enum KnownSymbolType
+ internal enum KnownTokenType
{
WhiteSpace,
NewLine,
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/LanguageCharacteristics.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/LanguageCharacteristics.cs
index 23d5f037d0..b12bf612b9 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/LanguageCharacteristics.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/LanguageCharacteristics.cs
@@ -6,104 +6,104 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal abstract class LanguageCharacteristics
- where TSymbolType : struct
- where TTokenizer : Tokenizer
- where TSymbol : SymbolBase
+ internal abstract class LanguageCharacteristics
+ where TTokenType : struct
+ where TTokenizer : Tokenizer
+ where TToken : TokenBase
{
- public abstract string GetSample(TSymbolType type);
+ public abstract string GetSample(TTokenType type);
public abstract TTokenizer CreateTokenizer(ITextDocument source);
- public abstract TSymbolType FlipBracket(TSymbolType bracket);
- public abstract TSymbol CreateMarkerSymbol();
+ public abstract TTokenType FlipBracket(TTokenType bracket);
+ public abstract TToken CreateMarkerToken();
- public virtual IEnumerable TokenizeString(string content)
+ public virtual IEnumerable TokenizeString(string content)
{
return TokenizeString(SourceLocation.Zero, content);
}
- public virtual IEnumerable TokenizeString(SourceLocation start, string input)
+ public virtual IEnumerable TokenizeString(SourceLocation start, string input)
{
using (var reader = new SeekableTextReader(input, start.FilePath))
{
var tok = CreateTokenizer(reader);
- TSymbol sym;
- while ((sym = tok.NextSymbol()) != null)
+ TToken token;
+ while ((token = tok.NextToken()) != null)
{
- yield return sym;
+ yield return token;
}
}
}
- public virtual bool IsWhiteSpace(TSymbol symbol)
+ public virtual bool IsWhiteSpace(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.WhiteSpace);
+ return IsKnownTokenType(token, KnownTokenType.WhiteSpace);
}
- public virtual bool IsNewLine(TSymbol symbol)
+ public virtual bool IsNewLine(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.NewLine);
+ return IsKnownTokenType(token, KnownTokenType.NewLine);
}
- public virtual bool IsIdentifier(TSymbol symbol)
+ public virtual bool IsIdentifier(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.Identifier);
+ return IsKnownTokenType(token, KnownTokenType.Identifier);
}
- public virtual bool IsKeyword(TSymbol symbol)
+ public virtual bool IsKeyword(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.Keyword);
+ return IsKnownTokenType(token, KnownTokenType.Keyword);
}
- public virtual bool IsTransition(TSymbol symbol)
+ public virtual bool IsTransition(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.Transition);
+ return IsKnownTokenType(token, KnownTokenType.Transition);
}
- public virtual bool IsCommentStart(TSymbol symbol)
+ public virtual bool IsCommentStart(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.CommentStart);
+ return IsKnownTokenType(token, KnownTokenType.CommentStart);
}
- public virtual bool IsCommentStar(TSymbol symbol)
+ public virtual bool IsCommentStar(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.CommentStar);
+ return IsKnownTokenType(token, KnownTokenType.CommentStar);
}
- public virtual bool IsCommentBody(TSymbol symbol)
+ public virtual bool IsCommentBody(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.CommentBody);
+ return IsKnownTokenType(token, KnownTokenType.CommentBody);
}
- public virtual bool IsUnknown(TSymbol symbol)
+ public virtual bool IsUnknown(TToken token)
{
- return IsKnownSymbolType(symbol, KnownSymbolType.Unknown);
+ return IsKnownTokenType(token, KnownTokenType.Unknown);
}
- public virtual bool IsKnownSymbolType(TSymbol symbol, KnownSymbolType type)
+ public virtual bool IsKnownTokenType(TToken token, KnownTokenType type)
{
- return symbol != null && Equals(symbol.Type, GetKnownSymbolType(type));
+ return token != null && Equals(token.Type, GetKnownTokenType(type));
}
- public virtual Tuple SplitSymbol(TSymbol symbol, int splitAt, TSymbolType leftType)
+ public virtual Tuple SplitToken(TToken token, int splitAt, TTokenType leftType)
{
- var left = CreateSymbol(symbol.Content.Substring(0, splitAt), leftType, RazorDiagnostic.EmptyArray);
+ var left = CreateToken(token.Content.Substring(0, splitAt), leftType, RazorDiagnostic.EmptyArray);
- TSymbol right = null;
- if (splitAt < symbol.Content.Length)
+ TToken right = null;
+ if (splitAt < token.Content.Length)
{
- right = CreateSymbol(symbol.Content.Substring(splitAt), symbol.Type, symbol.Errors);
+ right = CreateToken(token.Content.Substring(splitAt), token.Type, token.Errors);
}
return Tuple.Create(left, right);
}
- public abstract TSymbolType GetKnownSymbolType(KnownSymbolType type);
+ public abstract TTokenType GetKnownTokenType(KnownTokenType type);
- public virtual bool KnowsSymbolType(KnownSymbolType type)
+ public virtual bool KnowsTokenType(KnownTokenType type)
{
- return type == KnownSymbolType.Unknown || !Equals(GetKnownSymbolType(type), GetKnownSymbolType(KnownSymbolType.Unknown));
+ return type == KnownTokenType.Unknown || !Equals(GetKnownTokenType(type), GetKnownTokenType(KnownTokenType.Unknown));
}
- protected abstract TSymbol CreateSymbol(string content, TSymbolType type, IReadOnlyList errors);
+ protected abstract TToken CreateToken(string content, TTokenType type, IReadOnlyList errors);
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/MarkupRewriter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/MarkupRewriter.cs
index 6ce51f273f..42074050ff 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/MarkupRewriter.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/MarkupRewriter.cs
@@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
builder.Kind = SpanKindInternal.Markup;
builder.ChunkGenerator = new MarkupChunkGenerator();
- foreach (ISymbol sym in HtmlLanguageCharacteristics.Instance.TokenizeString(start, content))
+ foreach (IToken sym in HtmlLanguageCharacteristics.Instance.TokenizeString(start, content))
{
builder.Accept(sym);
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParentChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParentChunkGenerator.cs
index 0d57311757..09a8c94f28 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParentChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParentChunkGenerator.cs
@@ -47,10 +47,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public void Accept(ParserVisitor visitor, Block block)
{
- for (var i = 0; i < block.Children.Count; i++)
- {
- block.Children[i].Accept(visitor);
- }
+ visitor.VisitDefault(block);
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParserHelpers.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParserHelpers.cs
index 71835178b2..fc5b31a1e3 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParserHelpers.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParserHelpers.cs
@@ -19,15 +19,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
'\u2029' // Paragraph separator
};
- public static bool IsNewLine(char value)
- {
- return NewLineCharacters.Contains(value);
- }
+ public static bool IsNewLine(char value) => Array.IndexOf(NewLineCharacters, value) != -1;
public static bool IsNewLine(string value)
{
+ // We want to handle both LF and CRLF regardless of the platform.
+ // We explicitly check for CRLF and IsNewLine() should return true for LF.
return (value.Length == 1 && (IsNewLine(value[0]))) ||
- (string.Equals(value, Environment.NewLine, StringComparison.Ordinal));
+ (string.Equals(value, "\r\n", StringComparison.Ordinal));
}
public static bool IsIdentifier(string value)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParserVisitor.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParserVisitor.cs
index 108fcf5d21..fe6031046e 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParserVisitor.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/ParserVisitor.cs
@@ -5,7 +5,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal abstract class ParserVisitor
{
- protected virtual void VisitDefault(Block block)
+ public virtual void Visit(SyntaxTreeNode node)
+ {
+ node.Accept(this);
+ }
+
+ public virtual void VisitDefault(Block block)
{
for (var i = 0; i < block.Children.Count; i++)
{
@@ -13,6 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
+ public virtual void VisitDefault(Span span)
+ {
+ }
+
public virtual void VisitBlock(Block block)
{
if (block.ChunkGenerator != null)
@@ -44,35 +53,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
VisitDefault(block);
}
- public virtual void VisitExpressionSpan(ExpressionChunkGenerator chunkGenerator, Span span)
- {
- }
-
- public virtual void VisitMarkupSpan(MarkupChunkGenerator chunkGenerator, Span span)
- {
- }
-
- public virtual void VisitImportSpan(AddImportChunkGenerator chunkGenerator, Span span)
- {
- }
-
- public virtual void VisitStatementSpan(StatementChunkGenerator chunkGenerator, Span span)
- {
- }
-
- public virtual void VisitLiteralAttributeSpan(LiteralAttributeChunkGenerator chunkGenerator, Span span)
- {
- }
-
- public virtual void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span block)
- {
- }
-
- public virtual void VisitDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block)
- {
- VisitDefault(block);
- }
-
public virtual void VisitTemplateBlock(TemplateBlockChunkGenerator chunkGenerator, Block block)
{
VisitDefault(block);
@@ -88,16 +68,54 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
VisitDefault(block);
}
+ public virtual void VisitDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block)
+ {
+ VisitDefault(block);
+ }
+
+ public virtual void VisitExpressionSpan(ExpressionChunkGenerator chunkGenerator, Span span)
+ {
+ VisitDefault(span);
+ }
+
+ public virtual void VisitMarkupSpan(MarkupChunkGenerator chunkGenerator, Span span)
+ {
+ VisitDefault(span);
+ }
+
+ public virtual void VisitImportSpan(AddImportChunkGenerator chunkGenerator, Span span)
+ {
+ VisitDefault(span);
+ }
+
+ public virtual void VisitStatementSpan(StatementChunkGenerator chunkGenerator, Span span)
+ {
+ VisitDefault(span);
+ }
+
+ public virtual void VisitLiteralAttributeSpan(LiteralAttributeChunkGenerator chunkGenerator, Span span)
+ {
+ VisitDefault(span);
+ }
+
+ public virtual void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
+ {
+ VisitDefault(span);
+ }
+
public virtual void VisitAddTagHelperSpan(AddTagHelperChunkGenerator chunkGenerator, Span span)
{
+ VisitDefault(span);
}
public virtual void VisitRemoveTagHelperSpan(RemoveTagHelperChunkGenerator chunkGenerator, Span span)
{
+ VisitDefault(span);
}
public virtual void VisitTagHelperPrefixDirectiveSpan(TagHelperPrefixDirectiveChunkGenerator chunkGenerator, Span span)
{
+ VisitDefault(span);
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RazorCommentChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RazorCommentChunkGenerator.cs
index fdf6a6f1e4..4d3d13f0b0 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RazorCommentChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RazorCommentChunkGenerator.cs
@@ -9,5 +9,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
visitor.VisitCommentBlock(this, block);
}
+
+ public override string ToString()
+ {
+ return "RazorComment";
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RazorSyntaxTreeExtensions.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RazorSyntaxTreeExtensions.cs
new file mode 100644
index 0000000000..a07082152c
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RazorSyntaxTreeExtensions.cs
@@ -0,0 +1,130 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Razor.Language.Legacy
+{
+ internal static class RazorSyntaxTreeExtensions
+ {
+ public static IReadOnlyList GetClassifiedSpans(this RazorSyntaxTree syntaxTree)
+ {
+ if (syntaxTree == null)
+ {
+ throw new ArgumentNullException(nameof(syntaxTree));
+ }
+
+ var spans = Flatten(syntaxTree);
+
+ var result = new ClassifiedSpanInternal[spans.Count];
+ for (var i = 0; i < spans.Count; i++)
+ {
+ var span = spans[i];
+ result[i] = new ClassifiedSpanInternal(
+ 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),
+ span.Kind,
+ span.Parent.Type,
+ span.EditHandler.AcceptedCharacters);
+ }
+
+ return result;
+ }
+
+ public static IReadOnlyList GetTagHelperSpans(this RazorSyntaxTree syntaxTree)
+ {
+ if (syntaxTree == null)
+ {
+ throw new ArgumentNullException(nameof(syntaxTree));
+ }
+
+ var results = new List();
+
+ var toProcess = new List();
+ var blockChildren = new List();
+ toProcess.Add(syntaxTree.Root);
+
+ for (var i = 0; i < toProcess.Count; i++)
+ {
+ var blockNode = toProcess[i];
+ if (blockNode is TagHelperBlock tagHelperNode)
+ {
+ results.Add(new TagHelperSpanInternal(
+ 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 (var child in blockNode.Children)
+ {
+ if (child is Block block)
+ {
+ blockChildren.Add(block);
+ }
+ }
+
+ if (blockChildren.Count > 0)
+ {
+ toProcess.InsertRange(i + 1, blockChildren);
+ blockChildren.Clear();
+ }
+ }
+
+ return results;
+ }
+
+ private static List Flatten(RazorSyntaxTree syntaxTree)
+ {
+ var result = new List();
+ AppendFlattenedSpans(syntaxTree.Root, result);
+ return result;
+
+ void AppendFlattenedSpans(SyntaxTreeNode node, List foundSpans)
+ {
+ if (node is Span spanNode)
+ {
+ foundSpans.Add(spanNode);
+ }
+ else
+ {
+ if (node is TagHelperBlock tagHelperNode)
+ {
+ // These aren't in document order, sort them first and then dig in
+ var attributeNodes = tagHelperNode.Attributes.Select(kvp => kvp.Value).Where(att => att != null).ToList();
+ attributeNodes.Sort((x, y) => x.Start.AbsoluteIndex.CompareTo(y.Start.AbsoluteIndex));
+
+ foreach (var attributeNode in attributeNodes)
+ {
+ AppendFlattenedSpans(attributeNode, foundSpans);
+ }
+ }
+
+ if (node is Block block)
+ {
+ foreach (var child in block.Children)
+ {
+ AppendFlattenedSpans(child, foundSpans);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RemoveTagHelperChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RemoveTagHelperChunkGenerator.cs
index 09328bb69c..58b170d33e 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RemoveTagHelperChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/RemoveTagHelperChunkGenerator.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
@@ -63,5 +64,28 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return combiner.CombinedHash;
}
+
+ public override string ToString()
+ {
+ var builder = new StringBuilder("RemoveTagHelper:{");
+ builder.Append(LookupText);
+ builder.Append(";");
+ builder.Append(DirectiveText);
+ builder.Append(";");
+ builder.Append(TypePattern);
+ builder.Append(";");
+ builder.Append(AssemblyName);
+ builder.Append("}");
+
+ if (Diagnostics.Count > 0)
+ {
+ builder.Append(" [");
+ var ids = string.Join(", ", Diagnostics.Select(diagnostic => $"{diagnostic.Id}{diagnostic.Span}"));
+ builder.Append(ids);
+ builder.Append("]");
+ }
+
+ return builder.ToString();
+ }
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SeekableTextReader.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SeekableTextReader.cs
index b95b649a67..85057cf9fb 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SeekableTextReader.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SeekableTextReader.cs
@@ -10,8 +10,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
private readonly LineTrackingStringBuffer _buffer;
private int _position = 0;
+ private int _current;
private SourceLocation _location;
- private char? _current;
public SeekableTextReader(string source, string filePath) : this(source.ToCharArray(), filePath) { }
@@ -24,8 +24,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
_buffer = new LineTrackingStringBuffer(source, filePath);
UpdateState();
-
- _location = new SourceLocation(filePath, 0, 0, 0);
}
public SourceLocation Location => _location;
@@ -47,24 +45,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public override int Read()
{
- if (_current == null)
- {
- return -1;
- }
- var chr = _current.Value;
+ var c = _current;
_position++;
UpdateState();
- return chr;
+ return c;
}
- public override int Peek()
- {
- if (_current == null)
- {
- return -1;
- }
- return _current.Value;
- }
+ public override int Peek() => _current;
private void UpdateState()
{
@@ -76,12 +63,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else if (_buffer.Length == 0)
{
- _current = null;
+ _current = -1;
_location = SourceLocation.Zero;
}
else
{
- _current = null;
+ _current = -1;
_location = _buffer.EndLocation;
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/Span.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/Span.cs
index 9b7366fc5e..fbf9f9f2c2 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/Span.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/Span.cs
@@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public ISpanChunkGenerator ChunkGenerator { get; private set; }
public SpanKindInternal Kind { get; private set; }
- public IReadOnlyList Symbols { get; private set; }
+ public IReadOnlyList Tokens { get; private set; }
// Allow test code to re-link spans
public Span Previous { get; internal set; }
@@ -42,9 +42,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var length = 0;
if (_content == null)
{
- for (var i = 0; i < Symbols.Count; i++)
+ for (var i = 0; i < Tokens.Count; i++)
{
- length += Symbols[i].Content.Length;
+ length += Tokens[i].Content.Length;
}
}
else
@@ -67,19 +67,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
if (_content == null)
{
- var symbolCount = Symbols.Count;
- if (symbolCount == 1)
+ var tokenCount = Tokens.Count;
+ if (tokenCount == 1)
{
// Perf: no StringBuilder allocation if not necessary
- _content = Symbols[0].Content;
+ _content = Tokens[0].Content;
}
else
{
var builder = new StringBuilder();
- for (var i = 0; i < symbolCount; i++)
+ for (var i = 0; i < tokenCount; i++)
{
- var symbol = Symbols[i];
- builder.Append(symbol.Content);
+ var token = Tokens[i];
+ builder.Append(token.Content);
}
_content = builder.ToString();
@@ -93,11 +93,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public void ReplaceWith(SpanBuilder builder)
{
Kind = builder.Kind;
- Symbols = builder.Symbols;
+ Tokens = builder.Tokens;
- for (var i = 0; i Gen: <");
builder.Append(ChunkGenerator.ToString());
builder.Append("> {");
- builder.Append(string.Join(";", Symbols.GroupBy(sym => sym.GetType()).Select(grp => string.Concat(grp.Key.Name, ":", grp.Count()))));
+ builder.Append(string.Join(";", Tokens.GroupBy(sym => sym.GetType()).Select(grp => string.Concat(grp.Key.Name, ":", grp.Count()))));
builder.Append("}");
return builder.ToString();
}
@@ -166,7 +166,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Kind.Equals(other.Kind) &&
EditHandler.Equals(other.EditHandler) &&
ChunkGenerator.Equals(other.ChunkGenerator) &&
- Symbols.SequenceEqual(other.Symbols);
+ Tokens.SequenceEqual(other.Tokens);
}
public override int GetHashCode()
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanBuilder.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanBuilder.cs
index b9b9e2df54..6d2e647b91 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanBuilder.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanBuilder.cs
@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
internal class SpanBuilder
{
private SourceLocation _start;
- private List _symbols;
+ private List _tokens;
private SourceLocationTracker _tracker;
public SpanBuilder(Span original)
@@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
_start = original.Start;
ChunkGenerator = original.ChunkGenerator;
- _symbols = new List(original.Symbols);
+ _tokens = new List(original.Tokens);
_tracker = new SourceLocationTracker(original.Start);
}
@@ -49,16 +49,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public SpanKindInternal Kind { get; set; }
- public IReadOnlyList Symbols
+ public IReadOnlyList Tokens
{
get
{
- if (_symbols == null)
+ if (_tokens == null)
{
- _symbols = new List();
+ _tokens = new List();
}
- return _symbols;
+ return _tokens;
}
}
@@ -68,10 +68,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// Need to potentially allocate a new list because Span.ReplaceWith takes ownership
// of the original list.
- _symbols = null;
- _symbols = new List();
+ _tokens = null;
+ _tokens = new List();
- EditHandler = SpanEditHandler.CreateDefault((content) => Enumerable.Empty());
+ EditHandler = SpanEditHandler.CreateDefault((content) => Enumerable.Empty());
ChunkGenerator = SpanChunkGenerator.Null;
Start = SourceLocation.Undefined;
}
@@ -80,23 +80,23 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
var span = new Span(this);
- for (var i = 0; i < span.Symbols.Count; i++)
+ for (var i = 0; i < span.Tokens.Count; i++)
{
- var symbol = span.Symbols[i];
- symbol.Parent = span;
+ var token = span.Tokens[i];
+ token.Parent = span;
}
return span;
}
- public void ClearSymbols()
+ public void ClearTokens()
{
- _symbols?.Clear();
+ _tokens?.Clear();
}
- public void Accept(ISymbol symbol)
+ public void Accept(IToken token)
{
- if (symbol == null)
+ if (token == null)
{
return;
}
@@ -106,8 +106,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
throw new InvalidOperationException("SpanBuilder must have a valid location");
}
- _symbols.Add(symbol);
- _tracker.UpdateLocation(symbol.Content);
+ _tokens.Add(token);
+ _tracker.UpdateLocation(token.Content);
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanChunkGenerator.cs
index 75defef805..4e15538609 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanChunkGenerator.cs
@@ -32,6 +32,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
public void Accept(ParserVisitor visitor, Span span)
{
+ visitor.VisitDefault(span);
}
public void GenerateChunk(Span target, ChunkGeneratorContext context)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanEditHandler.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanEditHandler.cs
index dd9511ce70..d5407f34dd 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanEditHandler.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SpanEditHandler.cs
@@ -11,12 +11,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
private static readonly int TypeHashCode = typeof(SpanEditHandler).GetHashCode();
- public SpanEditHandler(Func> tokenizer)
+ public SpanEditHandler(Func> tokenizer)
: this(tokenizer, AcceptedCharactersInternal.Any)
{
}
- public SpanEditHandler(Func> tokenizer, AcceptedCharactersInternal accepted)
+ public SpanEditHandler(Func> tokenizer, AcceptedCharactersInternal accepted)
{
AcceptedCharacters = accepted;
Tokenizer = tokenizer;
@@ -24,9 +24,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public AcceptedCharactersInternal AcceptedCharacters { get; set; }
- public Func> Tokenizer { get; set; }
+ public Func> Tokenizer { get; set; }
- public static SpanEditHandler CreateDefault(Func> tokenizer)
+ public static SpanEditHandler CreateDefault(Func> tokenizer)
{
return new SpanEditHandler(tokenizer);
}
@@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
var newContent = change.GetEditedContent(target);
var newSpan = new SpanBuilder(target);
- newSpan.ClearSymbols();
+ newSpan.ClearTokens();
foreach (var token in Tokenizer(newContent))
{
newSpan.Accept(token);
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SyntaxTreeNode.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SyntaxTreeNode.cs
index a20c7c318b..1b7b192db9 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SyntaxTreeNode.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SyntaxTreeNode.cs
@@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// The node to compare this node with
///
/// true if the provided node has all the same content and metadata, though the specific quantity and type of
- /// symbols may be different.
+ /// tokens may be different.
///
public abstract bool EquivalentTo(SyntaxTreeNode node);
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperBlockRewriter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperBlockRewriter.cs
index bfb31bce05..f08d0579e5 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperBlockRewriter.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperBlockRewriter.cs
@@ -42,8 +42,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// We skip the first child "" or "/>".
// The -2 accounts for both the start and end tags. If the tag does not have a valid structure then there's
// no end tag to ignore.
- var symbolOffset = validStructure ? 2 : 1;
- var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - symbolOffset);
+ var tokenOffset = validStructure ? 2 : 1;
+ var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - tokenOffset);
var processedBoundAttributeNames = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var child in attributeChildren)
@@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else
{
- // Error occured while parsing the attribute. Don't try parsing the rest to avoid misleading errors.
+ // Error occurred while parsing the attribute. Don't try parsing the rest to avoid misleading errors.
break;
}
}
@@ -149,8 +149,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Kind = span.Kind
};
- // Will contain symbols that represent a single attribute value:
- var htmlSymbols = span.Symbols.OfType().ToArray();
+ // Will contain tokens that represent a single attribute value:
+ var htmlTokens = span.Tokens.OfType().ToArray();
var capturedAttributeValueStart = false;
var attributeValueStartLocation = span.Start;
@@ -158,53 +158,53 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// TryParseBlock() variation of attribute parsing.
var attributeValueStyle = AttributeStructure.DoubleQuotes;
- // The symbolOffset is initialized to 0 to expect worst case: "class=". If a quote is found later on for
- // the attribute value the symbolOffset is adjusted accordingly.
- var symbolOffset = 0;
+ // The tokenOffset is initialized to 0 to expect worst case: "class=". If a quote is found later on for
+ // the attribute value the tokenOffset is adjusted accordingly.
+ var tokenOffset = 0;
string name = null;
- // Iterate down through the symbols to find the name and the start of the value.
- // We subtract the symbolOffset so we don't accept an ending quote of a span.
- for (var i = 0; i < htmlSymbols.Length - symbolOffset; i++)
+ // Iterate down through the tokens to find the name and the start of the value.
+ // We subtract the tokenOffset so we don't accept an ending quote of a span.
+ for (var i = 0; i < htmlTokens.Length - tokenOffset; i++)
{
- var symbol = htmlSymbols[i];
+ var token = htmlTokens[i];
if (afterEquals)
{
// We've captured all leading whitespace, the attribute name, and an equals with an optional
// quote/double quote. We're now at: " asp-for='|...'" or " asp-for=|..."
- // The goal here is to capture all symbols until the end of the attribute. Note this will not
- // consume an ending quote due to the symbolOffset.
+ // The goal here is to capture all tokens until the end of the attribute. Note this will not
+ // consume an ending quote due to the tokenOffset.
- // When symbols are accepted into SpanBuilders, their locations get altered to be offset by the
- // parent which is why we need to mark our start location prior to adding the symbol.
+ // When tokens are accepted into SpanBuilders, their locations get altered to be offset by the
+ // parent which is why we need to mark our start location prior to adding the token.
// This is needed to know the location of the attribute value start within the document.
if (!capturedAttributeValueStart)
{
capturedAttributeValueStart = true;
- attributeValueStartLocation = symbol.Start;
+ attributeValueStartLocation = token.Start;
}
- builder.Accept(symbol);
+ builder.Accept(token);
}
- else if (name == null && HtmlMarkupParser.IsValidAttributeNameSymbol(symbol))
+ else if (name == null && HtmlMarkupParser.IsValidAttributeNameToken(token))
{
// We've captured all leading whitespace prior to the attribute name.
// We're now at: " |asp-for='...'" or " |asp-for=..."
// The goal here is to capture the attribute name.
var nameBuilder = new StringBuilder();
- // Move the indexer past the attribute name symbols.
- for (var j = i; j < htmlSymbols.Length; j++)
+ // Move the indexer past the attribute name tokens.
+ for (var j = i; j < htmlTokens.Length; j++)
{
- var nameSymbol = htmlSymbols[j];
- if (!HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol))
+ var nameToken = htmlTokens[j];
+ if (!HtmlMarkupParser.IsValidAttributeNameToken(nameToken))
{
break;
}
- nameBuilder.Append(nameSymbol.Content);
+ nameBuilder.Append(nameToken.Content);
i++;
}
@@ -213,67 +213,67 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
name = nameBuilder.ToString();
attributeValueStartLocation = SourceLocationTracker.Advance(attributeValueStartLocation, name);
}
- else if (symbol.Type == HtmlSymbolType.Equals)
+ else if (token.Type == HtmlTokenType.Equals)
{
// We've captured all leading whitespace and the attribute name.
// We're now at: " asp-for|='...'" or " asp-for|=..."
// The goal here is to consume the equal sign and the optional single/double-quote.
- // The coming symbols will either be a quote or value (in the case that the value is unquoted).
+ // The coming tokens will either be a quote or value (in the case that the value is unquoted).
- SourceLocation symbolStartLocation;
+ SourceLocation tokenStartLocation;
// Skip the whitespace preceding the start of the attribute value.
do
{
- i++; // Start from the symbol after '='.
- } while (i < htmlSymbols.Length &&
- (htmlSymbols[i].Type == HtmlSymbolType.WhiteSpace ||
- htmlSymbols[i].Type == HtmlSymbolType.NewLine));
+ i++; // Start from the token after '='.
+ } while (i < htmlTokens.Length &&
+ (htmlTokens[i].Type == HtmlTokenType.WhiteSpace ||
+ htmlTokens[i].Type == HtmlTokenType.NewLine));
// Check for attribute start values, aka single or double quote
- if (i < htmlSymbols.Length && IsQuote(htmlSymbols[i]))
+ if (i < htmlTokens.Length && IsQuote(htmlTokens[i]))
{
- if (htmlSymbols[i].Type == HtmlSymbolType.SingleQuote)
+ if (htmlTokens[i].Type == HtmlTokenType.SingleQuote)
{
attributeValueStyle = AttributeStructure.SingleQuotes;
}
- symbolStartLocation = htmlSymbols[i].Start;
+ tokenStartLocation = htmlTokens[i].Start;
// If there's a start quote then there must be an end quote to be valid, skip it.
- symbolOffset = 1;
+ tokenOffset = 1;
}
else
{
- // We are at the symbol after equals. Go back to equals to ensure we don't skip past that symbol.
+ // We are at the token after equals. Go back to equals to ensure we don't skip past that token.
i--;
- symbolStartLocation = symbol.Start;
+ tokenStartLocation = token.Start;
}
attributeValueStartLocation = new SourceLocation(
- symbolStartLocation.FilePath,
- symbolStartLocation.AbsoluteIndex + 1,
- symbolStartLocation.LineIndex,
- symbolStartLocation.CharacterIndex + 1);
+ tokenStartLocation.FilePath,
+ tokenStartLocation.AbsoluteIndex + 1,
+ tokenStartLocation.LineIndex,
+ tokenStartLocation.CharacterIndex + 1);
afterEquals = true;
}
- else if (symbol.Type == HtmlSymbolType.WhiteSpace)
+ else if (token.Type == HtmlTokenType.WhiteSpace)
{
// We're at the start of the attribute, this branch may be hit on the first iterations of
- // the loop since the parser separates attributes with their spaces included as symbols.
+ // the loop since the parser separates attributes with their spaces included as tokens.
// We're at: "| asp-for='...'" or "| asp-for=..."
// Note: This will not be hit even for situations like asp-for ="..." because the core Razor
// parser currently does not know how to handle attributes in that format. This will be addressed
// by https://github.com/aspnet/Razor/issues/123.
- attributeValueStartLocation = SourceLocationTracker.Advance(attributeValueStartLocation, symbol.Content);
+ attributeValueStartLocation = SourceLocationTracker.Advance(attributeValueStartLocation, token.Content);
}
}
- // After all symbols have been added we need to set the builders start position so we do not indirectly
+ // After all tokens have been added we need to set the builders start position so we do not indirectly
// modify the span's start location.
builder.Start = attributeValueStartLocation;
@@ -341,14 +341,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return TryParseSpan(childSpan, descriptors, errorSink, processedBoundAttributeNames);
}
- var nameSymbols = childSpan
- .Symbols
- .OfType()
- .SkipWhile(symbol => !HtmlMarkupParser.IsValidAttributeNameSymbol(symbol)) // Skip prefix
- .TakeWhile(nameSymbol => HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol))
- .Select(nameSymbol => nameSymbol.Content);
+ var nameTokens = childSpan
+ .Tokens
+ .OfType()
+ .SkipWhile(token => !HtmlMarkupParser.IsValidAttributeNameToken(token)) // Skip prefix
+ .TakeWhile(nameToken => HtmlMarkupParser.IsValidAttributeNameToken(nameToken))
+ .Select(nameToken => nameToken.Content);
- var name = string.Concat(nameSymbols);
+ var name = string.Concat(nameTokens);
if (string.IsNullOrEmpty(name))
{
var location = new SourceSpan(childSpan.Start, childSpan.Length);
@@ -362,12 +362,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var result = CreateTryParseResult(name, descriptors, processedBoundAttributeNames);
var firstChild = builder.Children[0] as Span;
- if (firstChild != null && firstChild.Symbols[0] is HtmlSymbol)
+ if (firstChild != null && firstChild.Tokens[0] is HtmlToken)
{
- var htmlSymbol = firstChild.Symbols[firstChild.Symbols.Count - 1] as HtmlSymbol;
- switch (htmlSymbol.Type)
+ var htmlToken = firstChild.Tokens[firstChild.Tokens.Count - 1] as HtmlToken;
+ switch (htmlToken.Type)
{
- case HtmlSymbolType.Equals:
+ case HtmlTokenType.Equals:
if (builder.Children.Count == 2 &&
builder.Children[1] is Span value &&
value.Kind == SpanKindInternal.Markup)
@@ -385,10 +385,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
result.AttributeStructure = AttributeStructure.DoubleQuotes;
}
break;
- case HtmlSymbolType.DoubleQuote:
+ case HtmlTokenType.DoubleQuote:
result.AttributeStructure = AttributeStructure.DoubleQuotes;
break;
- case HtmlSymbolType.SingleQuote:
+ case HtmlTokenType.SingleQuote:
result.AttributeStructure = AttributeStructure.SingleQuotes;
break;
default:
@@ -407,12 +407,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var endSpan = (Span)endNode;
// In some malformed cases e.g. would cause an error because there's no
// matching end
tag in the template block scope and therefore doesn't make sense as a tag helper.
BuildMalformedTagHelpers(_trackerStack.Count - activeTrackers, errorSink);
-
- Debug.Assert(activeTrackers == _trackerStack.Count);
}
BuildCurrentlyTrackedBlock();
@@ -370,12 +368,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// We can assume the first span will always contain attributename=" and the last span will always
// contain the final quote. Therefore, if the values not quoted there's no ending quote to skip.
var childOffset = 0;
- if (childSpan.Symbols.Count > 0)
+ if (childSpan.Tokens.Count > 0)
{
- var potentialQuote = childSpan.Symbols[childSpan.Symbols.Count - 1] as HtmlSymbol;
+ var potentialQuote = childSpan.Tokens[childSpan.Tokens.Count - 1] as HtmlToken;
if (potentialQuote != null &&
- (potentialQuote.Type == HtmlSymbolType.DoubleQuote ||
- potentialQuote.Type == HtmlSymbolType.SingleQuote))
+ (potentialQuote.Type == HtmlTokenType.DoubleQuote ||
+ potentialQuote.Type == HtmlTokenType.SingleQuote))
{
childOffset = 1;
}
@@ -391,9 +389,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else
{
var valueChildSpan = (Span)valueChild;
- for (var k = 0; k < valueChildSpan.Symbols.Count; k++)
+ for (var k = 0; k < valueChildSpan.Tokens.Count; k++)
{
- _attributeValueBuilder.Append(valueChildSpan.Symbols[k].Content);
+ _attributeValueBuilder.Append(valueChildSpan.Tokens[k].Content);
}
}
}
@@ -404,43 +402,43 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var afterEquals = false;
var atValue = false;
- var endValueMarker = childSpan.Symbols.Count;
+ var endValueMarker = childSpan.Tokens.Count;
// Entire attribute is a string
for (var j = 0; j < endValueMarker; j++)
{
- var htmlSymbol = (HtmlSymbol)childSpan.Symbols[j];
+ var htmlToken = (HtmlToken)childSpan.Tokens[j];
if (!afterEquals)
{
- afterEquals = htmlSymbol.Type == HtmlSymbolType.Equals;
+ afterEquals = htmlToken.Type == HtmlTokenType.Equals;
continue;
}
if (!atValue)
{
- atValue = htmlSymbol.Type != HtmlSymbolType.WhiteSpace &&
- htmlSymbol.Type != HtmlSymbolType.NewLine;
+ atValue = htmlToken.Type != HtmlTokenType.WhiteSpace &&
+ htmlToken.Type != HtmlTokenType.NewLine;
if (atValue)
{
- if (htmlSymbol.Type == HtmlSymbolType.DoubleQuote ||
- htmlSymbol.Type == HtmlSymbolType.SingleQuote)
+ if (htmlToken.Type == HtmlTokenType.DoubleQuote ||
+ htmlToken.Type == HtmlTokenType.SingleQuote)
{
endValueMarker--;
}
else
{
- // Current symbol is considered the value (unquoted). Add its content to the
+ // Current token is considered the value (unquoted). Add its content to the
// attribute value builder before we move past it.
- _attributeValueBuilder.Append(htmlSymbol.Content);
+ _attributeValueBuilder.Append(htmlToken.Content);
}
}
continue;
}
- _attributeValueBuilder.Append(htmlSymbol.Content);
+ _attributeValueBuilder.Append(htmlToken.Content);
}
}
@@ -642,11 +640,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// If our tag end is not a markup span it means it's some sort of code SyntaxTreeNode (not a valid format)
if (tagEnd != null && tagEnd.Kind == SpanKindInternal.Markup)
{
- var endSymbol = tagEnd.Symbols.Count > 0 ?
- tagEnd.Symbols[tagEnd.Symbols.Count - 1] as HtmlSymbol :
+ var endToken = tagEnd.Tokens.Count > 0 ?
+ tagEnd.Tokens[tagEnd.Tokens.Count - 1] as HtmlToken :
null;
- if (endSymbol != null && endSymbol.Type == HtmlSymbolType.CloseAngle)
+ if (endToken != null && endToken.Type == HtmlTokenType.CloseAngle)
{
return false;
}
@@ -793,25 +791,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
var childSpan = (Span)child;
- HtmlSymbol textSymbol = null;
- for (var i = 0; i < childSpan.Symbols.Count; i++)
+ HtmlToken textToken = null;
+ for (var i = 0; i < childSpan.Tokens.Count; i++)
{
- var symbol = childSpan.Symbols[i] as HtmlSymbol;
+ var token = childSpan.Tokens[i] as HtmlToken;
- if (symbol != null &&
- (symbol.Type & (HtmlSymbolType.WhiteSpace | HtmlSymbolType.Text)) == symbol.Type)
+ if (token != null &&
+ (token.Type & (HtmlTokenType.WhiteSpace | HtmlTokenType.Text)) == token.Type)
{
- textSymbol = symbol;
+ textToken = token;
break;
}
}
- if (textSymbol == null)
+ if (textToken == null)
{
return null;
}
- return textSymbol.Type == HtmlSymbolType.WhiteSpace ? null : textSymbol.Content;
+ return textToken.Type == HtmlTokenType.WhiteSpace ? null : textToken.Content;
}
private static bool IsEndTag(Block tagBlock)
@@ -820,10 +818,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var childSpan = (Span)tagBlock.Children.First();
- // We grab the symbol that could be forward slash
- var relevantSymbol = (HtmlSymbol)childSpan.Symbols[childSpan.Symbols.Count == 1 ? 0 : 1];
+ // We grab the token that could be forward slash
+ var relevantToken = (HtmlToken)childSpan.Tokens[childSpan.Tokens.Count == 1 ? 0 : 1];
- return relevantSymbol.Type == HtmlSymbolType.ForwardSlash;
+ return relevantToken.Type == HtmlTokenType.ForwardSlash;
}
internal static bool IsComment(Span span)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperPrefixDirectiveChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperPrefixDirectiveChunkGenerator.cs
index bed1cadcc7..f559ed585c 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperPrefixDirectiveChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperPrefixDirectiveChunkGenerator.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
@@ -48,5 +49,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return combiner.CombinedHash;
}
+
+ public override string ToString()
+ {
+ var builder = new StringBuilder("TagHelperPrefix:{");
+ builder.Append(Prefix);
+ builder.Append(";");
+ builder.Append(DirectiveText);
+ builder.Append("}");
+
+ if (Diagnostics.Count > 0)
+ {
+ builder.Append(" [");
+ var ids = string.Join(", ", Diagnostics.Select(diagnostic => $"{diagnostic.Id}{diagnostic.Span}"));
+ builder.Append(ids);
+ builder.Append("]");
+ }
+
+ return builder.ToString();
+ }
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperSpanInternal.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperSpanInternal.cs
new file mode 100644
index 0000000000..8d5ff92f9d
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperSpanInternal.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.AspNetCore.Razor.Language.Legacy
+{
+ internal struct TagHelperSpanInternal
+ {
+ public TagHelperSpanInternal(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/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TemplateBlockChunkGenerator.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TemplateBlockChunkGenerator.cs
index 68212bf1b2..02940c1c31 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TemplateBlockChunkGenerator.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TemplateBlockChunkGenerator.cs
@@ -19,5 +19,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
//context.ChunkTreeBuilder.EndParentChunk();
}
+
+ public override string ToString()
+ {
+ return "Template";
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SymbolBase.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenBase.cs
similarity index 83%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SymbolBase.cs
rename to src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenBase.cs
index c1caebb988..562a41f5bc 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/SymbolBase.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenBase.cs
@@ -8,9 +8,9 @@ using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal abstract class SymbolBase : ISymbol where TType : struct
+ internal abstract class TokenBase : IToken where TType : struct
{
- protected SymbolBase(
+ protected TokenBase(
string content,
TType type,
IReadOnlyList errors)
@@ -43,15 +43,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
var tracker = new SourceLocationTracker(Parent.Start);
- for (var i = 0; i < Parent.Symbols.Count; i++)
+ for (var i = 0; i < Parent.Tokens.Count; i++)
{
- var symbol = Parent.Symbols[i];
- if (object.ReferenceEquals(this, symbol))
+ var token = Parent.Tokens[i];
+ if (object.ReferenceEquals(this, token))
{
break;
}
- tracker.UpdateLocation(symbol.Content);
+ tracker.UpdateLocation(token.Content);
}
return tracker.CurrentLocation;
@@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public override bool Equals(object obj)
{
- var other = obj as SymbolBase;
+ var other = obj as TokenBase;
return other != null &&
string.Equals(Content, other.Content, StringComparison.Ordinal) &&
Type.Equals(other.Type);
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/Tokenizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/Tokenizer.cs
index 465754f6d5..af77d2ddc6 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/Tokenizer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/Tokenizer.cs
@@ -8,9 +8,9 @@ using System.Text;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal abstract partial class Tokenizer : ITokenizer
- where TSymbolType : struct
- where TSymbol : SymbolBase
+ internal abstract partial class Tokenizer : ITokenizer
+ where TTokenType : struct
+ where TToken : TokenBase
{
protected Tokenizer(ITextDocument source)
{
@@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Source = source;
Buffer = new StringBuilder();
CurrentErrors = new List();
- StartSymbol();
+ StartToken();
}
protected List CurrentErrors { get; }
@@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected int? CurrentState { get; set; }
- protected TSymbol CurrentSymbol { get; private set; }
+ protected TToken CurrentToken { get; private set; }
public ITextDocument Source { get; private set; }
@@ -42,9 +42,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
get { return Source.Peek() == -1; }
}
- public abstract TSymbolType RazorCommentStarType { get; }
- public abstract TSymbolType RazorCommentType { get; }
- public abstract TSymbolType RazorCommentTransitionType { get; }
+ public abstract TTokenType RazorCommentStarType { get; }
+ public abstract TTokenType RazorCommentType { get; }
+ public abstract TTokenType RazorCommentTransitionType { get; }
protected bool HaveContent
{
@@ -64,27 +64,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public SourceLocation CurrentStart { get; private set; }
- protected abstract TSymbol CreateSymbol(string content, TSymbolType type, IReadOnlyList errors);
+ protected abstract TToken CreateToken(string content, TTokenType type, IReadOnlyList errors);
protected abstract StateResult Dispatch();
- ISymbol ITokenizer.NextSymbol()
+ IToken ITokenizer.NextToken()
{
- return NextSymbol();
+ return NextToken();
}
- public virtual TSymbol NextSymbol()
+ public virtual TToken NextToken()
{
// Post-Condition: Buffer should be empty at the start of Next()
Debug.Assert(Buffer.Length == 0);
- StartSymbol();
+ StartToken();
if (EndOfFile)
{
return null;
}
- var symbol = Turn();
+ var token = Turn();
// Post-Condition: Buffer should be empty at the end of Next()
Debug.Assert(Buffer.Length == 0);
@@ -92,10 +92,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Post-Condition: Token should be non-zero length unless we're at EOF.
Debug.Assert(EndOfFile || !CurrentStart.Equals(CurrentLocation));
- return symbol;
+ return token;
}
- protected virtual TSymbol Turn()
+ protected virtual TToken Turn()
{
if (CurrentState != null)
{
@@ -105,19 +105,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var next = Dispatch();
CurrentState = next.State;
- CurrentSymbol = next.Result;
+ CurrentToken = next.Result;
}
- while (CurrentState != null && CurrentSymbol == null);
+ while (CurrentState != null && CurrentToken == null);
if (CurrentState == null)
{
- return default(TSymbol); // Terminated
+ return default(TToken); // Terminated
}
- return CurrentSymbol;
+ return CurrentToken;
}
- return default(TSymbol);
+ return default(TToken);
}
public void Reset()
@@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// Returns a result containing the specified output and indicating that the next call to
/// should invoke the provided state.
///
- protected StateResult Transition(int state, TSymbol result)
+ protected StateResult Transition(int state, TToken result)
{
return new StateResult(state, result);
}
@@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return new StateResult((int)state, result: null);
}
- protected StateResult Transition(RazorCommentTokenizerState state, TSymbol result)
+ protected StateResult Transition(RazorCommentTokenizerState state, TToken result)
{
return new StateResult((int)state, result);
}
@@ -180,18 +180,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// Returns a result containing the specified output and indicating that the next call to
/// should re-invoke the current state.
///
- protected StateResult Stay(TSymbol result)
+ protected StateResult Stay(TToken result)
{
return new StateResult(CurrentState, result);
}
- protected TSymbol Single(TSymbolType type)
+ protected TToken Single(TTokenType type)
{
TakeCurrent();
- return EndSymbol(type);
+ return EndToken(type);
}
- protected void StartSymbol()
+ protected void StartToken()
{
Debug.Assert(Buffer.Length == 0);
Debug.Assert(CurrentErrors.Count == 0);
@@ -199,9 +199,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
CurrentStart = CurrentLocation;
}
- protected TSymbol EndSymbol(TSymbolType type)
+ protected TToken EndToken(TTokenType type)
{
- TSymbol symbol = null;
+ TToken token = null;
if (HaveContent)
{
// Perf: Don't allocate a new errors array unless necessary.
@@ -211,18 +211,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
errors[i] = CurrentErrors[i];
}
- var symbolContent = GetSymbolContent(type);
- Debug.Assert(string.Equals(symbolContent, Buffer.ToString(), StringComparison.Ordinal));
- symbol = CreateSymbol(symbolContent, type, errors);
+ var tokenContent = GetTokenContent(type);
+ Debug.Assert(string.Equals(tokenContent, Buffer.ToString(), StringComparison.Ordinal));
+ token = CreateToken(tokenContent, type, errors);
Buffer.Clear();
CurrentErrors.Clear();
}
- return symbol;
+ return token;
}
- protected virtual string GetSymbolContent(TSymbolType type)
+ protected virtual string GetTokenContent(TTokenType type)
{
return Buffer.ToString();
}
@@ -272,13 +272,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
if (CurrentCharacter != '*')
{
- // We've been moved since last time we were asked for a symbol... reset the state
+ // We've been moved since last time we were asked for a token... reset the state
return Transition(StartState);
}
AssertCurrent('*');
TakeCurrent();
- return Transition(1002, EndSymbol(RazorCommentStarType));
+ return Transition(1002, EndToken(RazorCommentStarType));
}
protected StateResult RazorCommentBody()
@@ -292,7 +292,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return Transition(
RazorCommentTokenizerState.StarAfterRazorCommentBody,
- EndSymbol(RazorCommentType));
+ EndToken(RazorCommentType));
}
else
{
@@ -306,7 +306,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- return Transition(StartState, EndSymbol(RazorCommentType));
+ return Transition(StartState, EndToken(RazorCommentType));
}
protected StateResult StarAfterRazorCommentBody()
@@ -314,15 +314,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AssertCurrent('*');
TakeCurrent();
return Transition(
- RazorCommentTokenizerState.AtSymbolAfterRazorCommentBody,
- EndSymbol(RazorCommentStarType));
+ RazorCommentTokenizerState.AtTokenAfterRazorCommentBody,
+ EndToken(RazorCommentStarType));
}
- protected StateResult AtSymbolAfterRazorCommentBody()
+ protected StateResult AtTokenAfterRazorCommentBody()
{
AssertCurrent('@');
TakeCurrent();
- return Transition(StartState, EndSymbol(RazorCommentTransitionType));
+ return Transition(StartState, EndToken(RazorCommentTransitionType));
}
///
@@ -392,12 +392,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
EscapedRazorCommentTransition,
RazorCommentBody,
StarAfterRazorCommentBody,
- AtSymbolAfterRazorCommentBody,
+ AtTokenAfterRazorCommentBody,
}
protected struct StateResult
{
- public StateResult(int? state, TSymbol result)
+ public StateResult(int? state, TToken result)
{
State = state;
Result = result;
@@ -405,7 +405,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public int? State { get; }
- public TSymbol Result { get; }
+ public TToken Result { get; }
}
private static LookaheadToken BeginLookahead(ITextBuffer buffer)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs
index 01fa0b11e1..af5a08831f 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs
@@ -8,20 +8,20 @@ using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal abstract partial class TokenizerBackedParser : ParserBase
- where TSymbolType : struct
- where TTokenizer : Tokenizer
- where TSymbol : SymbolBase
+ internal abstract partial class TokenizerBackedParser : ParserBase
+ where TTokenType : struct
+ where TTokenizer : Tokenizer
+ where TToken : TokenBase
{
- private readonly TokenizerView _tokenizer;
+ private readonly TokenizerView _tokenizer;
- protected TokenizerBackedParser(LanguageCharacteristics language, ParserContext context)
+ protected TokenizerBackedParser(LanguageCharacteristics language, ParserContext context)
: base(context)
{
Language = language;
var languageTokenizer = Language.CreateTokenizer(Context.Source);
- _tokenizer = new TokenizerView(languageTokenizer);
+ _tokenizer = new TokenizerView(languageTokenizer);
Span = new SpanBuilder(CurrentLocation);
}
@@ -29,12 +29,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected Action SpanConfig { get; set; }
- protected TSymbol CurrentSymbol
+ protected TToken CurrentToken
{
get { return _tokenizer.Current; }
}
- protected TSymbol PreviousSymbol { get; private set; }
+ protected TToken PreviousToken { get; private set; }
protected SourceLocation CurrentLocation => _tokenizer.Tokenizer.CurrentLocation;
@@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
get { return _tokenizer.EndOfFile; }
}
- protected LanguageCharacteristics Language { get; }
+ protected LanguageCharacteristics Language { get; }
protected virtual void HandleEmbeddedTransition()
{
@@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public override void BuildSpan(SpanBuilder span, SourceLocation start, string content)
{
- foreach (ISymbol sym in Language.TokenizeString(start, content))
+ foreach (IToken sym in Language.TokenizeString(start, content))
{
span.Accept(sym);
}
@@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- protected TSymbol Lookahead(int count)
+ protected TToken Lookahead(int count)
{
if (count < 0)
{
@@ -80,42 +80,42 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else if (count == 0)
{
- return CurrentSymbol;
+ return CurrentToken;
}
- // We add 1 in order to store the current symbol.
- var symbols = new TSymbol[count + 1];
- var currentSymbol = CurrentSymbol;
+ // We add 1 in order to store the current token.
+ var tokens = new TToken[count + 1];
+ var currentToken = CurrentToken;
- symbols[0] = currentSymbol;
+ tokens[0] = currentToken;
// We need to look forward "count" many times.
for (var i = 1; i <= count; i++)
{
NextToken();
- symbols[i] = CurrentSymbol;
+ tokens[i] = CurrentToken;
}
// Restore Tokenizer's location to where it was pointing before the look-ahead.
for (var i = count; i >= 0; i--)
{
- PutBack(symbols[i]);
+ PutBack(tokens[i]);
}
- // The PutBacks above will set CurrentSymbol to null. EnsureCurrent will set our CurrentSymbol to the
- // next symbol.
+ // The PutBacks above will set CurrentToken to null. EnsureCurrent will set our CurrentToken to the
+ // next token.
EnsureCurrent();
- return symbols[count];
+ return tokens[count];
}
///
/// Looks forward until the specified condition is met.
///
- /// A predicate accepting the symbol being evaluated and the list of symbols which have been looped through.
- /// true, if the condition was met. false - if the condition wasn't met and the last symbol has already been processed.
- /// The list of previous symbols is passed in the reverse order. So the last processed element will be the first one in the list.
- protected bool LookaheadUntil(Func, bool> condition)
+ /// A predicate accepting the token being evaluated and the list of tokens which have been looped through.
+ /// true, if the condition was met. false - if the condition wasn't met and the last token has already been processed.
+ /// The list of previous tokens is passed in the reverse order. So the last processed element will be the first one in the list.
+ protected bool LookaheadUntil(Func, bool> condition)
{
if (condition == null)
{
@@ -124,8 +124,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var matchFound = false;
- var symbols = new List();
- symbols.Add(CurrentSymbol);
+ var tokens = new List();
+ tokens.Add(CurrentToken);
while (true)
{
@@ -134,8 +134,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
break;
}
- symbols.Add(CurrentSymbol);
- if (condition(CurrentSymbol, symbols))
+ tokens.Add(CurrentToken);
+ if (condition(CurrentToken, tokens))
{
matchFound = true;
break;
@@ -143,13 +143,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Restore Tokenizer's location to where it was pointing before the look-ahead.
- for (var i = symbols.Count - 1; i >= 0; i--)
+ for (var i = tokens.Count - 1; i >= 0; i--)
{
- PutBack(symbols[i]);
+ PutBack(tokens[i]);
}
- // The PutBacks above will set CurrentSymbol to null. EnsureCurrent will set our CurrentSymbol to the
- // next symbol.
+ // The PutBacks above will set CurrentToken to null. EnsureCurrent will set our CurrentToken to the
+ // next token.
EnsureCurrent();
return matchFound;
@@ -157,57 +157,57 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal bool NextToken()
{
- PreviousSymbol = CurrentSymbol;
+ PreviousToken = CurrentToken;
return _tokenizer.Next();
}
// Helpers
[Conditional("DEBUG")]
- internal void Assert(TSymbolType expectedType)
+ internal void Assert(TTokenType expectedType)
{
- Debug.Assert(!EndOfFile && SymbolTypeEquals(CurrentSymbol.Type, expectedType));
+ Debug.Assert(!EndOfFile && TokenTypeEquals(CurrentToken.Type, expectedType));
}
- abstract protected bool SymbolTypeEquals(TSymbolType x, TSymbolType y);
+ abstract protected bool TokenTypeEquals(TTokenType x, TTokenType y);
- protected internal void PutBack(TSymbol symbol)
+ protected internal void PutBack(TToken token)
{
- if (symbol != null)
+ if (token != null)
{
- _tokenizer.PutBack(symbol);
+ _tokenizer.PutBack(token);
}
}
///
- /// Put the specified symbols back in the input stream. The provided list MUST be in the ORDER THE SYMBOLS WERE READ. The
- /// list WILL be reversed and the Putback(TSymbol) will be called on each item.
+ /// Put the specified tokens back in the input stream. The provided list MUST be in the ORDER THE TOKENS WERE READ. The
+ /// list WILL be reversed and the Putback(TToken) will be called on each item.
///
///
- /// If a document contains symbols: a, b, c, d, e, f
+ /// If a document contains tokens: a, b, c, d, e, f
/// and AcceptWhile or AcceptUntil is used to collect until d
/// the list returned by AcceptWhile/Until will contain: a, b, c IN THAT ORDER
/// that is the correct format for providing to this method. The caller of this method would,
/// in that case, want to put c, b and a back into the stream, so "a, b, c" is the CORRECT order
///
- protected internal void PutBack(IEnumerable symbols)
+ protected internal void PutBack(IEnumerable tokens)
{
- foreach (TSymbol symbol in symbols.Reverse())
+ foreach (TToken token in tokens.Reverse())
{
- PutBack(symbol);
+ PutBack(token);
}
}
protected internal void PutCurrentBack()
{
- if (!EndOfFile && CurrentSymbol != null)
+ if (!EndOfFile && CurrentToken != null)
{
- PutBack(CurrentSymbol);
+ PutBack(CurrentToken);
}
}
protected internal bool Balance(BalancingModes mode)
{
- var left = CurrentSymbol.Type;
+ var left = CurrentToken.Type;
var right = Language.FlipBracket(left);
var start = CurrentStart;
AcceptAndMoveNext();
@@ -223,13 +223,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Balance(mode, left, right, start);
}
- protected internal bool Balance(BalancingModes mode, TSymbolType left, TSymbolType right, SourceLocation start)
+ protected internal bool Balance(BalancingModes mode, TTokenType left, TTokenType right, SourceLocation start)
{
var startPosition = CurrentStart.AbsoluteIndex;
var nesting = 1;
if (!EndOfFile)
{
- var syms = new List();
+ var syms = new List();
do
{
if (IsAtEmbeddedTransition(
@@ -253,7 +253,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
if (nesting > 0)
{
- syms.Add(CurrentSymbol);
+ syms.Add(CurrentToken);
}
}
while (nesting > 0 && NextToken());
@@ -280,29 +280,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else
{
- // Accept all the symbols we saw
+ // Accept all the tokens we saw
Accept(syms);
}
}
return nesting == 0;
}
- protected internal bool NextIs(TSymbolType type)
+ protected internal bool NextIs(TTokenType type)
{
- return NextIs(sym => sym != null && SymbolTypeEquals(type, sym.Type));
+ return NextIs(sym => sym != null && TokenTypeEquals(type, sym.Type));
}
- protected internal bool NextIs(params TSymbolType[] types)
+ protected internal bool NextIs(params TTokenType[] types)
{
- return NextIs(sym => sym != null && types.Any(t => SymbolTypeEquals(t, sym.Type)));
+ return NextIs(sym => sym != null && types.Any(t => TokenTypeEquals(t, sym.Type)));
}
- protected internal bool NextIs(Func condition)
+ protected internal bool NextIs(Func condition)
{
- var cur = CurrentSymbol;
+ var cur = CurrentToken;
if (NextToken())
{
- var result = condition(CurrentSymbol);
+ var result = condition(CurrentToken);
PutCurrentBack();
PutBack(cur);
EnsureCurrent();
@@ -317,27 +317,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
- protected internal bool Was(TSymbolType type)
+ protected internal bool Was(TTokenType type)
{
- return PreviousSymbol != null && SymbolTypeEquals(PreviousSymbol.Type, type);
+ return PreviousToken != null && TokenTypeEquals(PreviousToken.Type, type);
}
- protected internal bool At(TSymbolType type)
+ protected internal bool At(TTokenType type)
{
- return !EndOfFile && CurrentSymbol != null && SymbolTypeEquals(CurrentSymbol.Type, type);
+ return !EndOfFile && CurrentToken != null && TokenTypeEquals(CurrentToken.Type, type);
}
protected internal bool AcceptAndMoveNext()
{
- Accept(CurrentSymbol);
+ Accept(CurrentToken);
return NextToken();
}
- protected TSymbol AcceptSingleWhiteSpaceCharacter()
+ protected TToken AcceptSingleWhiteSpaceCharacter()
{
- if (Language.IsWhiteSpace(CurrentSymbol))
+ if (Language.IsWhiteSpace(CurrentToken))
{
- Tuple pair = Language.SplitSymbol(CurrentSymbol, 1, Language.GetKnownSymbolType(KnownSymbolType.WhiteSpace));
+ Tuple pair = Language.SplitToken(CurrentToken, 1, Language.GetKnownTokenType(KnownTokenType.WhiteSpace));
Accept(pair.Item1);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
NextToken();
@@ -346,32 +346,32 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return null;
}
- protected internal void Accept(IEnumerable symbols)
+ protected internal void Accept(IEnumerable tokens)
{
- foreach (TSymbol symbol in symbols)
+ foreach (TToken token in tokens)
{
- Accept(symbol);
+ Accept(token);
}
}
- protected internal void Accept(TSymbol symbol)
+ protected internal void Accept(TToken token)
{
- if (symbol != null)
+ if (token != null)
{
- foreach (var error in symbol.Errors)
+ foreach (var error in token.Errors)
{
Context.ErrorSink.OnError(error);
}
- Span.Accept(symbol);
+ Span.Accept(token);
}
}
- protected internal bool AcceptAll(params TSymbolType[] types)
+ protected internal bool AcceptAll(params TTokenType[] types)
{
- foreach (TSymbolType type in types)
+ foreach (TTokenType type in types)
{
- if (CurrentSymbol == null || !SymbolTypeEquals(CurrentSymbol.Type, type))
+ if (CurrentToken == null || !TokenTypeEquals(CurrentToken.Type, type))
{
return false;
}
@@ -380,11 +380,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return true;
}
- protected internal void AddMarkerSymbolIfNecessary()
+ protected internal void AddMarkerTokenIfNecessary()
{
- if (Span.Symbols.Count == 0 && Context.Builder.LastAcceptedCharacters != AcceptedCharactersInternal.Any)
+ if (Span.Tokens.Count == 0 && Context.Builder.LastAcceptedCharacters != AcceptedCharactersInternal.Any)
{
- Accept(Language.CreateMarkerSymbol());
+ Accept(Language.CreateMarkerToken());
}
}
@@ -408,7 +408,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void Output()
{
- if (Span.Symbols.Count > 0)
+ if (Span.Tokens.Count > 0)
{
var nextStart = Span.End;
@@ -462,23 +462,23 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Initialize(Span);
}
- protected internal void Expected(KnownSymbolType type)
+ protected internal void Expected(KnownTokenType type)
{
- Expected(Language.GetKnownSymbolType(type));
+ Expected(Language.GetKnownTokenType(type));
}
- protected internal void Expected(params TSymbolType[] types)
+ protected internal void Expected(params TTokenType[] types)
{
- Debug.Assert(!EndOfFile && CurrentSymbol != null && types.Contains(CurrentSymbol.Type));
+ Debug.Assert(!EndOfFile && CurrentToken != null && types.Contains(CurrentToken.Type));
AcceptAndMoveNext();
}
- protected internal bool Optional(KnownSymbolType type)
+ protected internal bool Optional(KnownTokenType type)
{
- return Optional(Language.GetKnownSymbolType(type));
+ return Optional(Language.GetKnownTokenType(type));
}
- protected internal bool Optional(TSymbolType type)
+ protected internal bool Optional(TTokenType type)
{
if (At(type))
{
@@ -490,7 +490,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected bool EnsureCurrent()
{
- if (CurrentSymbol == null)
+ if (CurrentToken == null)
{
return NextToken();
}
@@ -498,62 +498,62 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return true;
}
- protected internal void AcceptWhile(TSymbolType type)
+ protected internal void AcceptWhile(TTokenType type)
{
- AcceptWhile(sym => SymbolTypeEquals(type, sym.Type));
+ AcceptWhile(sym => TokenTypeEquals(type, sym.Type));
}
// We want to avoid array allocations and enumeration where possible, so we use the same technique as string.Format
- protected internal void AcceptWhile(TSymbolType type1, TSymbolType type2)
+ protected internal void AcceptWhile(TTokenType type1, TTokenType type2)
{
- AcceptWhile(sym => SymbolTypeEquals(type1, sym.Type) || SymbolTypeEquals(type2, sym.Type));
+ AcceptWhile(sym => TokenTypeEquals(type1, sym.Type) || TokenTypeEquals(type2, sym.Type));
}
- protected internal void AcceptWhile(TSymbolType type1, TSymbolType type2, TSymbolType type3)
+ protected internal void AcceptWhile(TTokenType type1, TTokenType type2, TTokenType type3)
{
- AcceptWhile(sym => SymbolTypeEquals(type1, sym.Type) || SymbolTypeEquals(type2, sym.Type) || SymbolTypeEquals(type3, sym.Type));
+ AcceptWhile(sym => TokenTypeEquals(type1, sym.Type) || TokenTypeEquals(type2, sym.Type) || TokenTypeEquals(type3, sym.Type));
}
- protected internal void AcceptWhile(params TSymbolType[] types)
+ protected internal void AcceptWhile(params TTokenType[] types)
{
- AcceptWhile(sym => types.Any(expected => SymbolTypeEquals(expected, sym.Type)));
+ AcceptWhile(sym => types.Any(expected => TokenTypeEquals(expected, sym.Type)));
}
- protected internal void AcceptUntil(TSymbolType type)
+ protected internal void AcceptUntil(TTokenType type)
{
- AcceptWhile(sym => !SymbolTypeEquals(type, sym.Type));
+ AcceptWhile(sym => !TokenTypeEquals(type, sym.Type));
}
// We want to avoid array allocations and enumeration where possible, so we use the same technique as string.Format
- protected internal void AcceptUntil(TSymbolType type1, TSymbolType type2)
+ protected internal void AcceptUntil(TTokenType type1, TTokenType type2)
{
- AcceptWhile(sym => !SymbolTypeEquals(type1, sym.Type) && !SymbolTypeEquals(type2, sym.Type));
+ AcceptWhile(sym => !TokenTypeEquals(type1, sym.Type) && !TokenTypeEquals(type2, sym.Type));
}
- protected internal void AcceptUntil(TSymbolType type1, TSymbolType type2, TSymbolType type3)
+ protected internal void AcceptUntil(TTokenType type1, TTokenType type2, TTokenType type3)
{
- AcceptWhile(sym => !SymbolTypeEquals(type1, sym.Type) && !SymbolTypeEquals(type2, sym.Type) && !SymbolTypeEquals(type3, sym.Type));
+ AcceptWhile(sym => !TokenTypeEquals(type1, sym.Type) && !TokenTypeEquals(type2, sym.Type) && !TokenTypeEquals(type3, sym.Type));
}
- protected internal void AcceptUntil(params TSymbolType[] types)
+ protected internal void AcceptUntil(params TTokenType[] types)
{
- AcceptWhile(sym => types.All(expected => !SymbolTypeEquals(expected, sym.Type)));
+ AcceptWhile(sym => types.All(expected => !TokenTypeEquals(expected, sym.Type)));
}
- protected internal void AcceptWhile(Func condition)
+ protected internal void AcceptWhile(Func condition)
{
Accept(ReadWhileLazy(condition));
}
- protected internal IEnumerable ReadWhile(Func condition)
+ protected internal IEnumerable ReadWhile(Func condition)
{
return ReadWhileLazy(condition).ToList();
}
- protected TSymbol AcceptWhiteSpaceInLines()
+ protected TToken AcceptWhiteSpaceInLines()
{
- TSymbol lastWs = null;
- while (Language.IsWhiteSpace(CurrentSymbol) || Language.IsNewLine(CurrentSymbol))
+ TToken lastWs = null;
+ while (Language.IsWhiteSpace(CurrentToken) || Language.IsNewLine(CurrentToken))
{
// Capture the previous whitespace node
if (lastWs != null)
@@ -561,14 +561,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Accept(lastWs);
}
- if (Language.IsWhiteSpace(CurrentSymbol))
+ if (Language.IsWhiteSpace(CurrentToken))
{
- lastWs = CurrentSymbol;
+ lastWs = CurrentToken;
}
- else if (Language.IsNewLine(CurrentSymbol))
+ else if (Language.IsNewLine(CurrentToken))
{
// Accept newline and reset last whitespace tracker
- Accept(CurrentSymbol);
+ Accept(CurrentToken);
lastWs = null;
}
@@ -579,18 +579,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected bool AtIdentifier(bool allowKeywords)
{
- return CurrentSymbol != null &&
- (Language.IsIdentifier(CurrentSymbol) ||
- (allowKeywords && Language.IsKeyword(CurrentSymbol)));
+ return CurrentToken != null &&
+ (Language.IsIdentifier(CurrentToken) ||
+ (allowKeywords && Language.IsKeyword(CurrentToken)));
}
// Don't open this to sub classes because it's lazy but it looks eager.
// You have to advance the Enumerable to read the next characters.
- internal IEnumerable ReadWhileLazy(Func condition)
+ internal IEnumerable ReadWhileLazy(Func condition)
{
- while (EnsureCurrent() && condition(CurrentSymbol))
+ while (EnsureCurrent() && condition(CurrentToken))
{
- yield return CurrentSymbol;
+ yield return CurrentToken;
NextToken();
}
}
@@ -620,9 +620,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected void RazorComment()
{
- if (!Language.KnowsSymbolType(KnownSymbolType.CommentStart) ||
- !Language.KnowsSymbolType(KnownSymbolType.CommentStar) ||
- !Language.KnowsSymbolType(KnownSymbolType.CommentBody))
+ if (!Language.KnowsTokenType(KnownTokenType.CommentStart) ||
+ !Language.KnowsTokenType(KnownTokenType.CommentStar) ||
+ !Language.KnowsTokenType(KnownTokenType.CommentBody))
{
throw new InvalidOperationException(Resources.Language_Does_Not_Support_RazorComment);
}
@@ -634,18 +634,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Context.Builder.CurrentBlock.ChunkGenerator = new RazorCommentChunkGenerator();
var start = CurrentStart;
- Expected(KnownSymbolType.CommentStart);
+ Expected(KnownTokenType.CommentStart);
Output(SpanKindInternal.Transition, AcceptedCharactersInternal.None);
- Expected(KnownSymbolType.CommentStar);
+ Expected(KnownTokenType.CommentStar);
Output(SpanKindInternal.MetaCode, AcceptedCharactersInternal.None);
- Optional(KnownSymbolType.CommentBody);
- AddMarkerSymbolIfNecessary();
+ Optional(KnownTokenType.CommentBody);
+ AddMarkerTokenIfNecessary();
Output(SpanKindInternal.Comment);
var errorReported = false;
- if (!Optional(KnownSymbolType.CommentStar))
+ if (!Optional(KnownTokenType.CommentStar))
{
errorReported = true;
Context.ErrorSink.OnError(
@@ -657,7 +657,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Output(SpanKindInternal.MetaCode, AcceptedCharactersInternal.None);
}
- if (!Optional(KnownSymbolType.CommentStart))
+ if (!Optional(KnownTokenType.CommentStart))
{
if (!errorReported)
{
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerView.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerView.cs
index b85f0cd10b..2b290bda5c 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerView.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerView.cs
@@ -3,10 +3,10 @@
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
- internal class TokenizerView
- where TSymbolType : struct
- where TTokenizer : Tokenizer
- where TSymbol : SymbolBase
+ internal class TokenizerView
+ where TTokenType : struct
+ where TTokenizer : Tokenizer
+ where TToken : TokenBase
{
public TokenizerView(TTokenizer tokenizer)
{
@@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public TTokenizer Tokenizer { get; private set; }
public bool EndOfFile { get; private set; }
- public TSymbol Current { get; private set; }
+ public TToken Current { get; private set; }
public ITextDocument Source
{
@@ -24,14 +24,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public bool Next()
{
- Current = Tokenizer.NextSymbol();
+ Current = Tokenizer.NextToken();
EndOfFile = (Current == null);
return !EndOfFile;
}
- public void PutBack(TSymbol symbol)
+ public void PutBack(TToken token)
{
- Source.Position -= symbol.Content.Length;
+ Source.Position -= token.Content.Length;
Current = null;
EndOfFile = Source.Position >= Source.Length;
Tokenizer.Reset();
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/WhiteSpaceRewriter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/WhiteSpaceRewriter.cs
index 766f57d4bf..cf71b0a024 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/WhiteSpaceRewriter.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/WhiteSpaceRewriter.cs
@@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// Add this node to the parent
var builder = new SpanBuilder(ws);
- builder.ClearSymbols();
+ builder.ClearTokens();
FillSpan(builder, ws.Start, ws.Content);
parent.Children.Add(builder.Build());
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Properties/Resources.Designer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Properties/Resources.Designer.cs
index a785a39bca..773fb6de9c 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Properties/Resources.Designer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Properties/Resources.Designer.cs
@@ -139,128 +139,128 @@ namespace Microsoft.AspNetCore.Razor.Language
///
/// <<character literal>>
///
- internal static string CSharpSymbol_CharacterLiteral
+ internal static string CSharpToken_CharacterLiteral
{
- get => GetString("CSharpSymbol_CharacterLiteral");
+ get => GetString("CSharpToken_CharacterLiteral");
}
///
/// <<character literal>>
///
- internal static string FormatCSharpSymbol_CharacterLiteral()
- => GetString("CSharpSymbol_CharacterLiteral");
+ internal static string FormatCSharpToken_CharacterLiteral()
+ => GetString("CSharpToken_CharacterLiteral");
///
/// <<comment>>
///
- internal static string CSharpSymbol_Comment
+ internal static string CSharpToken_Comment
{
- get => GetString("CSharpSymbol_Comment");
+ get => GetString("CSharpToken_Comment");
}
///
/// <<comment>>
///
- internal static string FormatCSharpSymbol_Comment()
- => GetString("CSharpSymbol_Comment");
+ internal static string FormatCSharpToken_Comment()
+ => GetString("CSharpToken_Comment");
///
/// <<identifier>>
///
- internal static string CSharpSymbol_Identifier
+ internal static string CSharpToken_Identifier
{
- get => GetString("CSharpSymbol_Identifier");
+ get => GetString("CSharpToken_Identifier");
}
///
/// <<identifier>>
///
- internal static string FormatCSharpSymbol_Identifier()
- => GetString("CSharpSymbol_Identifier");
+ internal static string FormatCSharpToken_Identifier()
+ => GetString("CSharpToken_Identifier");
///
/// <<integer literal>>
///
- internal static string CSharpSymbol_IntegerLiteral
+ internal static string CSharpToken_IntegerLiteral
{
- get => GetString("CSharpSymbol_IntegerLiteral");
+ get => GetString("CSharpToken_IntegerLiteral");
}
///
/// <<integer literal>>
///
- internal static string FormatCSharpSymbol_IntegerLiteral()
- => GetString("CSharpSymbol_IntegerLiteral");
+ internal static string FormatCSharpToken_IntegerLiteral()
+ => GetString("CSharpToken_IntegerLiteral");
///
/// <<keyword>>
///
- internal static string CSharpSymbol_Keyword
+ internal static string CSharpToken_Keyword
{
- get => GetString("CSharpSymbol_Keyword");
+ get => GetString("CSharpToken_Keyword");
}
///
/// <<keyword>>
///
- internal static string FormatCSharpSymbol_Keyword()
- => GetString("CSharpSymbol_Keyword");
+ internal static string FormatCSharpToken_Keyword()
+ => GetString("CSharpToken_Keyword");
///
/// <<newline sequence>>
///
- internal static string CSharpSymbol_Newline
+ internal static string CSharpToken_Newline
{
- get => GetString("CSharpSymbol_Newline");
+ get => GetString("CSharpToken_Newline");
}
///
/// <<newline sequence>>
///
- internal static string FormatCSharpSymbol_Newline()
- => GetString("CSharpSymbol_Newline");
+ internal static string FormatCSharpToken_Newline()
+ => GetString("CSharpToken_Newline");
///
/// <<real literal>>
///
- internal static string CSharpSymbol_RealLiteral
+ internal static string CSharpToken_RealLiteral
{
- get => GetString("CSharpSymbol_RealLiteral");
+ get => GetString("CSharpToken_RealLiteral");
}
///
/// <<real literal>>
///
- internal static string FormatCSharpSymbol_RealLiteral()
- => GetString("CSharpSymbol_RealLiteral");
+ internal static string FormatCSharpToken_RealLiteral()
+ => GetString("CSharpToken_RealLiteral");
///
/// <<string literal>>
///
- internal static string CSharpSymbol_StringLiteral
+ internal static string CSharpToken_StringLiteral
{
- get => GetString("CSharpSymbol_StringLiteral");
+ get => GetString("CSharpToken_StringLiteral");
}
///
/// <<string literal>>
///
- internal static string FormatCSharpSymbol_StringLiteral()
- => GetString("CSharpSymbol_StringLiteral");
+ internal static string FormatCSharpToken_StringLiteral()
+ => GetString("CSharpToken_StringLiteral");
///
/// <<white space>>
///
- internal static string CSharpSymbol_Whitespace
+ internal static string CSharpToken_Whitespace
{
- get => GetString("CSharpSymbol_Whitespace");
+ get => GetString("CSharpToken_Whitespace");
}
///
/// <<white space>>
///
- internal static string FormatCSharpSymbol_Whitespace()
- => GetString("CSharpSymbol_Whitespace");
+ internal static string FormatCSharpToken_Whitespace()
+ => GetString("CSharpToken_Whitespace");
///
/// The document type '{0}' does not support the extension '{1}'.
@@ -489,58 +489,58 @@ namespace Microsoft.AspNetCore.Razor.Language
///
/// <<newline sequence>>
///
- internal static string HtmlSymbol_NewLine
+ internal static string HtmlToken_NewLine
{
- get => GetString("HtmlSymbol_NewLine");
+ get => GetString("HtmlToken_NewLine");
}
///
/// <<newline sequence>>
///
- internal static string FormatHtmlSymbol_NewLine()
- => GetString("HtmlSymbol_NewLine");
+ internal static string FormatHtmlToken_NewLine()
+ => GetString("HtmlToken_NewLine");
///
/// <<razor comment>>
///
- internal static string HtmlSymbol_RazorComment
+ internal static string HtmlToken_RazorComment
{
- get => GetString("HtmlSymbol_RazorComment");
+ get => GetString("HtmlToken_RazorComment");
}
///
/// <<razor comment>>
///
- internal static string FormatHtmlSymbol_RazorComment()
- => GetString("HtmlSymbol_RazorComment");
+ internal static string FormatHtmlToken_RazorComment()
+ => GetString("HtmlToken_RazorComment");
///
/// <<text>>
///
- internal static string HtmlSymbol_Text
+ internal static string HtmlToken_Text
{
- get => GetString("HtmlSymbol_Text");
+ get => GetString("HtmlToken_Text");
}
///
/// <<text>>
///
- internal static string FormatHtmlSymbol_Text()
- => GetString("HtmlSymbol_Text");
+ internal static string FormatHtmlToken_Text()
+ => GetString("HtmlToken_Text");
///
/// <<white space>>
///
- internal static string HtmlSymbol_WhiteSpace
+ internal static string HtmlToken_WhiteSpace
{
- get => GetString("HtmlSymbol_WhiteSpace");
+ get => GetString("HtmlToken_WhiteSpace");
}
///
/// <<white space>>
///
- internal static string FormatHtmlSymbol_WhiteSpace()
- => GetString("HtmlSymbol_WhiteSpace");
+ internal static string FormatHtmlToken_WhiteSpace()
+ => GetString("HtmlToken_WhiteSpace");
///
/// Specify the base class for the current document.
@@ -711,7 +711,7 @@ namespace Microsoft.AspNetCore.Razor.Language
=> GetString("KeyMustNotBeNull");
///
- /// Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known symbol types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment
+ /// Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known token types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment
///
internal static string Language_Does_Not_Support_RazorComment
{
@@ -719,7 +719,7 @@ namespace Microsoft.AspNetCore.Razor.Language
}
///
- /// Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known symbol types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment
+ /// Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known token types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment
///
internal static string FormatLanguage_Does_Not_Support_RazorComment()
=> GetString("Language_Does_Not_Support_RazorComment");
@@ -1351,16 +1351,16 @@ namespace Microsoft.AspNetCore.Razor.Language
///
/// <<unknown>>
///
- internal static string Symbol_Unknown
+ internal static string Token_Unknown
{
- get => GetString("Symbol_Unknown");
+ get => GetString("Token_Unknown");
}
///
/// <<unknown>>
///
- internal static string FormatSymbol_Unknown()
- => GetString("Symbol_Unknown");
+ internal static string FormatToken_Unknown()
+ => GetString("Token_Unknown");
///
/// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character.
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorCodeDocumentExtensions.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorCodeDocumentExtensions.cs
index 85df33837a..dafc33983f 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorCodeDocumentExtensions.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorCodeDocumentExtensions.cs
@@ -29,6 +29,26 @@ namespace Microsoft.AspNetCore.Razor.Language
document.Items[typeof(TagHelperDocumentContext)] = context;
}
+ internal static IReadOnlyList GetTagHelpers(this RazorCodeDocument document)
+ {
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ return (document.Items[typeof(TagHelpersHolder)] as TagHelpersHolder)?.TagHelpers;
+ }
+
+ internal static void SetTagHelpers(this RazorCodeDocument document, IReadOnlyList tagHelpers)
+ {
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ document.Items[typeof(TagHelpersHolder)] = new TagHelpersHolder(tagHelpers);
+ }
+
public static RazorSyntaxTree GetSyntaxTree(this RazorCodeDocument document)
{
if (document == null)
@@ -168,5 +188,15 @@ namespace Microsoft.AspNetCore.Razor.Language
public IReadOnlyList SyntaxTrees { get; }
}
+
+ private class TagHelpersHolder
+ {
+ public TagHelpersHolder(IReadOnlyList tagHelpers)
+ {
+ TagHelpers = tagHelpers;
+ }
+
+ public IReadOnlyList TagHelpers { get; }
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorConfiguration.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorConfiguration.cs
index e8ef287c0c..1094b0a432 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorConfiguration.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorConfiguration.cs
@@ -4,10 +4,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language
{
- public abstract class RazorConfiguration
+ public abstract class RazorConfiguration : IEquatable
{
public static readonly RazorConfiguration Default = new DefaultRazorConfiguration(
RazorLanguageVersion.Latest,
@@ -43,6 +44,58 @@ namespace Microsoft.AspNetCore.Razor.Language
public abstract RazorLanguageVersion LanguageVersion { get; }
+ public override bool Equals(object obj)
+ {
+ return base.Equals(obj as RazorConfiguration);
+ }
+
+ public virtual bool Equals(RazorConfiguration other)
+ {
+ if (object.ReferenceEquals(other, null))
+ {
+ return false;
+ }
+
+ if (LanguageVersion != other.LanguageVersion)
+ {
+ return false;
+ }
+
+ if (ConfigurationName != other.ConfigurationName)
+ {
+ return false;
+ }
+
+ if (Extensions.Count != other.Extensions.Count)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < Extensions.Count; i++)
+ {
+ if (Extensions[i].ExtensionName != other.Extensions[i].ExtensionName)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ var hash = new HashCodeCombiner();
+ hash.Add(LanguageVersion);
+ hash.Add(ConfigurationName);
+
+ for (var i = 0; i < Extensions.Count; i++)
+ {
+ hash.Add(Extensions[i].ExtensionName);
+ }
+
+ return hash;
+ }
+
private class DefaultRazorConfiguration : RazorConfiguration
{
public DefaultRazorConfiguration(
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorDiagnosticFactory.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorDiagnosticFactory.cs
index cf1ad34dd6..ef111ac0ae 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorDiagnosticFactory.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorDiagnosticFactory.cs
@@ -240,16 +240,6 @@ namespace Microsoft.AspNetCore.Razor.Language
return RazorDiagnostic.Create(Parsing_InvalidTagHelperPrefixValue, location, directiveName, character, prefix);
}
- internal static readonly RazorDiagnosticDescriptor Parsing_InvalidTagHelperLookupText =
- new RazorDiagnosticDescriptor(
- $"{DiagnosticPrefix}1020",
- () => Resources.InvalidTagHelperLookupText,
- RazorDiagnosticSeverity.Error);
- public static RazorDiagnostic CreateParsing_InvalidTagHelperLookupText(SourceSpan location, string lookupText)
- {
- return RazorDiagnostic.Create(Parsing_InvalidTagHelperLookupText, location, lookupText);
- }
-
internal static readonly RazorDiagnosticDescriptor Parsing_MarkupBlockMustStartWithTag =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}1021",
@@ -417,6 +407,16 @@ namespace Microsoft.AspNetCore.Razor.Language
return diagnostic;
}
+ internal static readonly RazorDiagnosticDescriptor Parsing_InvalidTagHelperLookupText =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}1036",
+ () => Resources.InvalidTagHelperLookupText,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateParsing_InvalidTagHelperLookupText(SourceSpan location, string lookupText)
+ {
+ return RazorDiagnostic.Create(Parsing_InvalidTagHelperLookupText, location, lookupText);
+ }
+
#endregion
#region Semantic Errors
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngine.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngine.cs
index 07ee624370..a36b80d4b0 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngine.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngine.cs
@@ -35,6 +35,18 @@ namespace Microsoft.AspNetCore.Razor.Language
return codeDocument;
}
+ internal virtual RazorCodeDocument Process(RazorSourceDocument source, IReadOnlyList importSources, IReadOnlyList tagHelpers)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ var codeDocument = CreateCodeDocumentCore(source, importSources, tagHelpers);
+ ProcessCore(codeDocument);
+ return codeDocument;
+ }
+
public virtual RazorCodeDocument ProcessDesignTime(RazorProjectItem projectItem)
{
if (projectItem == null)
@@ -47,10 +59,32 @@ namespace Microsoft.AspNetCore.Razor.Language
return codeDocument;
}
+ internal virtual RazorCodeDocument ProcessDesignTime(RazorSourceDocument source, IReadOnlyList importSources, IReadOnlyList tagHelpers)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ var codeDocument = CreateCodeDocumentDesignTimeCore(source, importSources, tagHelpers);
+ ProcessCore(codeDocument);
+ return codeDocument;
+ }
+
protected abstract RazorCodeDocument CreateCodeDocumentCore(RazorProjectItem projectItem);
+ internal virtual RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument source, IReadOnlyList importSources, IReadOnlyList tagHelpers)
+ {
+ return RazorCodeDocument.Create(source, importSources);
+ }
+
protected abstract RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem);
+ internal virtual RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument source, IReadOnlyList importSources, IReadOnlyList tagHelpers)
+ {
+ return RazorCodeDocument.Create(source, importSources);
+ }
+
protected abstract void ProcessCore(RazorCodeDocument codeDocument);
internal static RazorProjectEngine CreateEmpty(Action configure = null)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptorComparer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptorComparer.cs
index 78186cfb49..149f6a0c3d 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptorComparer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptorComparer.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language
@@ -58,26 +57,21 @@ namespace Microsoft.AspNetCore.Razor.Language
return false;
}
- return descriptorX != null &&
+ return
descriptorX.NameComparison == descriptorY.NameComparison &&
descriptorX.ValueComparison == descriptorY.ValueComparison &&
string.Equals(descriptorX.Name, descriptorY.Name, _stringComparison) &&
string.Equals(descriptorX.Value, descriptorY.Value, StringComparison.Ordinal) &&
- string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
- Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics);
+ string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal);
}
///
public virtual int GetHashCode(RequiredAttributeDescriptor descriptor)
{
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.NameComparison);
- hashCodeCombiner.Add(descriptor.ValueComparison);
- hashCodeCombiner.Add(descriptor.Name, _stringComparer);
- hashCodeCombiner.Add(descriptor.Value, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.DisplayName, StringComparer.Ordinal);
+ var hash = HashCodeCombiner.Start();
+ hash.Add(descriptor.Name, _stringComparer);
- return hashCodeCombiner.CombinedHash;
+ return hash.CombinedHash;
}
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Resources.resx b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Resources.resx
index 195a9d791b..e23e66c297 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Resources.resx
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Resources.resx
@@ -146,31 +146,31 @@
Invalid newline sequence '{0}'. Support newline sequences are '\r\n' and '\n'.
-
+
<<character literal>>
-
+
<<comment>>
-
+
<<identifier>>
-
+
<<integer literal>>
-
+
<<keyword>>
-
+
<<newline sequence>>
-
+
<<real literal>>
-
+
<<string literal>>
-
+
<<white space>>
@@ -221,16 +221,16 @@
Specify a C# code block.
-
+
<<newline sequence>>
-
+
<<razor comment>>
-
+
<<text>>
-
+
<<white space>>
@@ -270,7 +270,7 @@
The key must not be null.
- Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known symbol types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment
+ Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known token types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment
The specified encoding '{0}' does not match the content's encoding '{1}'.
@@ -423,7 +423,7 @@ Instead, wrap the contents of the block in "{{}}":
@section Header { ... }
In CSHTML, the @section keyword is case-sensitive and lowercase (as with all C# keywords)
-
+
<<unknown>>
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorComparer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorComparer.cs
index 90cead0e50..5ca9c0fe91 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorComparer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorComparer.cs
@@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Razor.Language
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
{
- if (descriptorX == descriptorY)
+ if (object.ReferenceEquals(descriptorX, descriptorY))
{
return true;
}
@@ -59,31 +59,81 @@ namespace Microsoft.AspNetCore.Razor.Language
return false;
}
- return descriptorX != null &&
- string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal) &&
- string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) &&
- Enumerable.SequenceEqual(
- descriptorX.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
- descriptorY.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
- _boundAttributeComparer) &&
- Enumerable.SequenceEqual(
- descriptorX.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
- descriptorY.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
- _tagMatchingRuleComparer) &&
- (descriptorX.AllowedChildTags == descriptorY.AllowedChildTags ||
+ if (descriptorX == null)
+ {
+ return false;
+ }
+
+ if (!string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ if (!string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ if (!string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ if (!Enumerable.SequenceEqual(
+ descriptorX.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
+ descriptorY.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
+ _boundAttributeComparer))
+ {
+ return false;
+ }
+
+ if (!Enumerable.SequenceEqual(
+ descriptorX.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
+ descriptorY.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
+ _tagMatchingRuleComparer))
+ {
+ return false;
+ }
+
+ if (!(descriptorX.AllowedChildTags == descriptorY.AllowedChildTags ||
(descriptorX.AllowedChildTags != null &&
descriptorY.AllowedChildTags != null &&
Enumerable.SequenceEqual(
descriptorX.AllowedChildTags.OrderBy(childTag => childTag.Name, _stringComparer),
descriptorY.AllowedChildTags.OrderBy(childTag => childTag.Name, _stringComparer),
- _AllowedChildTagDescriptorComparer))) &&
- string.Equals(descriptorX.Documentation, descriptorY.Documentation, StringComparison.Ordinal) &&
- string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
- string.Equals(descriptorX.TagOutputHint, descriptorY.TagOutputHint, _stringComparison) &&
- Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics) &&
- Enumerable.SequenceEqual(
- descriptorX.Metadata.OrderBy(metadataX => metadataX.Key, StringComparer.Ordinal),
- descriptorY.Metadata.OrderBy(metadataY => metadataY.Key, StringComparer.Ordinal));
+ _AllowedChildTagDescriptorComparer))))
+ {
+ return false;
+ }
+
+ if (!string.Equals(descriptorX.Documentation, descriptorY.Documentation, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ if (!string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ if (!string.Equals(descriptorX.TagOutputHint, descriptorY.TagOutputHint, _stringComparison))
+ {
+ return false;
+ }
+
+ if (!Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics))
+ {
+ return false;
+ }
+
+ if (!Enumerable.SequenceEqual(
+ descriptorX.Metadata.OrderBy(metadataX => metadataX.Key, StringComparer.Ordinal),
+ descriptorY.Metadata.OrderBy(metadataY => metadataY.Key, StringComparer.Ordinal)))
+ {
+ return false;
+ }
+
+ return true;
}
///
@@ -94,33 +144,12 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(descriptor));
}
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.Kind);
- hashCodeCombiner.Add(descriptor.AssemblyName, StringComparer.Ordinal);
+ var hash = HashCodeCombiner.Start();
+ hash.Add(descriptor.Kind, StringComparer.Ordinal);
+ hash.Add(descriptor.AssemblyName, StringComparer.Ordinal);
+ hash.Add(descriptor.Name, StringComparer.Ordinal);
- var childTags = descriptor.AllowedChildTags.OrderBy(childTag => childTag.Name, _stringComparer);
- foreach (var childTag in childTags)
- {
- hashCodeCombiner.Add(_AllowedChildTagDescriptorComparer.GetHashCode(childTag));
- }
-
- var boundAttributes = descriptor.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer);
- foreach (var attribute in boundAttributes)
- {
- hashCodeCombiner.Add(_boundAttributeComparer.GetHashCode(attribute));
- }
-
- var rules = descriptor.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer);
- foreach (var rule in rules)
- {
- hashCodeCombiner.Add(_tagMatchingRuleComparer.GetHashCode(rule));
- }
-
- hashCodeCombiner.Add(descriptor.Documentation, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.DisplayName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.TagOutputHint, _stringComparer);
-
- return hashCodeCombiner.CombinedHash;
+ return hash.CombinedHash;
}
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorComparer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorComparer.cs
index c77bdfbddb..734485b3c3 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorComparer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorComparer.cs
@@ -54,12 +54,11 @@ namespace Microsoft.AspNetCore.Razor.Language
return false;
}
- return ruleX != null &&
+ return
string.Equals(ruleX.TagName, ruleY.TagName, _stringComparison) &&
string.Equals(ruleX.ParentTag, ruleY.ParentTag, _stringComparison) &&
ruleX.TagStructure == ruleY.TagStructure &&
- Enumerable.SequenceEqual(ruleX.Attributes, ruleY.Attributes, _requiredAttributeComparer) &&
- Enumerable.SequenceEqual(ruleX.Diagnostics, ruleY.Diagnostics);
+ Enumerable.SequenceEqual(ruleX.Attributes, ruleY.Attributes, _requiredAttributeComparer);
}
public virtual int GetHashCode(TagMatchingRuleDescriptor rule)
@@ -69,18 +68,10 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(rule));
}
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(rule.TagName, _stringComparer);
- hashCodeCombiner.Add(rule.ParentTag, _stringComparer);
- hashCodeCombiner.Add(rule.TagStructure);
+ var hash = HashCodeCombiner.Start();
+ hash.Add(rule.TagName, _stringComparer);
- var attributes = rule.Attributes.OrderBy(attribute => attribute.Name, _stringComparer);
- foreach (var attribute in attributes)
- {
- hashCodeCombiner.Add(_requiredAttributeComparer.GetHashCode(attribute));
- }
-
- return hashCodeCombiner.CombinedHash;
+ return hash.CombinedHash;
}
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/breakingchanges.netcore.json b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/breakingchanges.netcore.json
new file mode 100644
index 0000000000..a753f1bfc8
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/breakingchanges.netcore.json
@@ -0,0 +1,15 @@
+[
+ {
+ "TypeId": "protected enum Microsoft.AspNetCore.Razor.Language.Legacy.Tokenizer+RazorCommentTokenizerState where T0 : Microsoft.AspNetCore.Razor.Language.Legacy.SymbolBase where T1 : struct",
+ "Kind": "Removal"
+ },
+ {
+ "TypeId": "protected struct Microsoft.AspNetCore.Razor.Language.Legacy.Tokenizer+StateResult where T0 : Microsoft.AspNetCore.Razor.Language.Legacy.SymbolBase where T1 : struct",
+ "Kind": "Removal"
+ },
+ {
+ "TypeId": "protected class Microsoft.AspNetCore.Razor.Language.Legacy.CSharpCodeParser+Block",
+ "MemberId": "public .ctor(Microsoft.AspNetCore.Razor.Language.Legacy.CSharpSymbol symbol, Microsoft.AspNetCore.Razor.Language.SourceLocation start)",
+ "Kind": "Removal"
+ }
+]
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/breakingchanges.netframework.json b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/breakingchanges.netframework.json
new file mode 100644
index 0000000000..a753f1bfc8
--- /dev/null
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/breakingchanges.netframework.json
@@ -0,0 +1,15 @@
+[
+ {
+ "TypeId": "protected enum Microsoft.AspNetCore.Razor.Language.Legacy.Tokenizer+RazorCommentTokenizerState where T0 : Microsoft.AspNetCore.Razor.Language.Legacy.SymbolBase where T1 : struct",
+ "Kind": "Removal"
+ },
+ {
+ "TypeId": "protected struct Microsoft.AspNetCore.Razor.Language.Legacy.Tokenizer+StateResult where T0 : Microsoft.AspNetCore.Razor.Language.Legacy.SymbolBase where T1 : struct",
+ "Kind": "Removal"
+ },
+ {
+ "TypeId": "protected class Microsoft.AspNetCore.Razor.Language.Legacy.CSharpCodeParser+Block",
+ "MemberId": "public .ctor(Microsoft.AspNetCore.Razor.Language.Legacy.CSharpSymbol symbol, Microsoft.AspNetCore.Razor.Language.SourceLocation start)",
+ "Kind": "Removal"
+ }
+]
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs
index 030e898b44..140c90597e 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs
@@ -258,8 +258,14 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
if (childContent == null)
{
_startTagHelperWritingScope(null);
- await _executeChildContentAsync();
- childContent = _endTagHelperWritingScope();
+ try
+ {
+ await _executeChildContentAsync();
+ }
+ finally
+ {
+ childContent = _endTagHelperWritingScope();
+ }
}
Debug.Assert(!Output.IsContentModified);
@@ -292,8 +298,14 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
if (!useCachedResult || childContent == null)
{
_startTagHelperWritingScope(encoder);
- await _executeChildContentAsync();
- childContent = _endTagHelperWritingScope();
+ try
+ {
+ await _executeChildContentAsync();
+ }
+ finally
+ {
+ childContent = _endTagHelperWritingScope();
+ }
if (encoder == null)
{
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Tasks/Microsoft.AspNetCore.Razor.Tasks.csproj b/src/Razor/src/Microsoft.AspNetCore.Razor.Tasks/Microsoft.AspNetCore.Razor.Tasks.csproj
deleted file mode 100644
index 79eefc909b..0000000000
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Tasks/Microsoft.AspNetCore.Razor.Tasks.csproj
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
- Razor is a markup syntax for adding server-side logic to web pages. This assembly contains infrastructure supporting Razor MSBuild integration.
-
-
- net46;netstandard2.0
-
-
- false
- false
-
-
-
-
-
-
-
-
- Shared\ServerProtocol\%(FileName)
-
-
- Shared\PipeName.cs
-
-
- Shared\MutexName.cs
-
-
- Shared\Client.cs
-
-
-
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/Application.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/Application.cs
index ba043a2b40..9c63203f66 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/Application.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/Application.cs
@@ -57,8 +57,11 @@ namespace Microsoft.AspNetCore.Razor.Tools
}
catch (AggregateException ex) when (ex.InnerException != null)
{
- Error.WriteLine(ex.InnerException.Message);
- Error.WriteLine(ex.InnerException.StackTrace);
+ foreach (var innerException in ex.Flatten().InnerExceptions)
+ {
+ Error.WriteLine(innerException.Message);
+ Error.WriteLine(innerException.StackTrace);
+ }
return 1;
}
catch (CommandParsingException ex)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/ServerCommand.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/ServerCommand.cs
index 44bb594b1c..a4aee01b3d 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/ServerCommand.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/ServerCommand.cs
@@ -25,7 +25,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
internal ServerCommand(Application parent, string pipeName, int? keepAlive = null)
: this(parent)
{
- Pipe.Values.Add(pipeName);
+ if (!string.IsNullOrEmpty(pipeName))
+ {
+ Pipe.Values.Add(pipeName);
+ }
if (keepAlive.HasValue)
{
@@ -119,8 +122,21 @@ namespace Microsoft.AspNetCore.Razor.Tools
dispatcher.Run();
}
- internal FileStream WritePidFile()
+ protected virtual FileStream WritePidFile()
{
+ var path = GetPidFilePath(env => Environment.GetEnvironmentVariable(env));
+ return WritePidFile(path);
+ }
+
+ // Internal for testing.
+ internal virtual FileStream WritePidFile(string directoryPath)
+ {
+ if (string.IsNullOrEmpty(directoryPath))
+ {
+ // Invalid path. Bail.
+ return null;
+ }
+
// To make all the running rzc servers more discoverable, We want to write the process Id and pipe name to a file.
// The file contents will be in the following format,
//
@@ -133,24 +149,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
var processId = Process.GetCurrentProcess().Id;
var fileName = $"rzc-{processId}";
- var path = Environment.GetEnvironmentVariable("DOTNET_BUILD_PIDFILE_DIRECTORY");
- if (string.IsNullOrEmpty(path))
- {
- var homeEnvVariable = PlatformInformation.IsWindows ? "USERPROFILE" : "HOME";
- var homePath = Environment.GetEnvironmentVariable(homeEnvVariable);
- if (string.IsNullOrEmpty(homePath))
- {
- // Couldn't locate the user profile directory. Bail.
- return null;
- }
-
- path = Path.Combine(homePath, ".dotnet", "pids", "build");
- }
-
// Make sure the directory exists.
- Directory.CreateDirectory(path);
+ Directory.CreateDirectory(directoryPath);
- path = Path.Combine(path, fileName);
+ var path = Path.Combine(directoryPath, fileName);
var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.DeleteOnClose);
using (var writer = new StreamWriter(fileStream, Encoding.UTF8, DefaultBufferSize, leaveOpen: true))
@@ -162,5 +164,25 @@ namespace Microsoft.AspNetCore.Razor.Tools
return fileStream;
}
+
+ // Internal for testing.
+ internal virtual string GetPidFilePath(Func getEnvironmentVariable)
+ {
+ var path = getEnvironmentVariable("DOTNET_BUILD_PIDFILE_DIRECTORY");
+ if (string.IsNullOrEmpty(path))
+ {
+ var homeEnvVariable = PlatformInformation.IsWindows ? "USERPROFILE" : "HOME";
+ var homePath = getEnvironmentVariable(homeEnvVariable);
+ if (string.IsNullOrEmpty(homePath))
+ {
+ // Couldn't locate the user profile directory. Bail.
+ return null;
+ }
+
+ path = Path.Combine(homePath, ".dotnet", "pids", "build");
+ }
+
+ return path;
+ }
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerConnection.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerConnection.cs
index d9fb8d4b11..d254be4e60 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerConnection.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerConnection.cs
@@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
}
catch
{
- // In the case an exception occured trying to open the Mutex then
+ // In the case an exception occurred trying to open the Mutex then
// the assumption is that it's not open.
}
@@ -387,4 +387,4 @@ namespace Microsoft.AspNetCore.Razor.Tools
public static bool IsWindows => Path.DirectorySeparatorChar == '\\';
public static bool IsUnix => Path.DirectorySeparatorChar == '/';
}
-}
\ No newline at end of file
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor/TagHelpers/DefaultTagHelperContent.cs b/src/Razor/src/Microsoft.AspNetCore.Razor/TagHelpers/DefaultTagHelperContent.cs
index c4cdc3750f..f7185ba8a6 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor/TagHelpers/DefaultTagHelperContent.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor/TagHelpers/DefaultTagHelperContent.cs
@@ -221,8 +221,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
return;
}
- var stringValue = entry as string;
- if (stringValue != null)
+ if (entry is string stringValue)
{
encoder.Encode(writer, stringValue);
}
@@ -239,13 +238,11 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
return;
}
- string entryAsString;
- IHtmlContentContainer entryAsContainer;
- if ((entryAsString = entry as string) != null)
+ if (entry is string entryAsString)
{
destination.Append(entryAsString);
}
- else if ((entryAsContainer = entry as IHtmlContentContainer) != null)
+ else if (entry is IHtmlContentContainer entryAsContainer)
{
entryAsContainer.CopyTo(destination);
}
@@ -262,13 +259,11 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
return;
}
- string entryAsString;
- IHtmlContentContainer entryAsContainer;
- if ((entryAsString = entry as string) != null)
+ if (entry is string entryAsString)
{
destination.Append(entryAsString);
}
- else if ((entryAsContainer = entry as IHtmlContentContainer) != null)
+ else if (entry is IHtmlContentContainer entryAsContainer)
{
entryAsContainer.MoveTo(destination);
}
@@ -282,29 +277,19 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
{
if (entry == null)
{
- return false;
+ return true;
}
- var stringValue = entry as string;
- if (stringValue != null)
+ if (entry is string stringValue)
{
// Do not encode the string because encoded value remains whitespace from user's POV.
- if (!string.IsNullOrWhiteSpace(stringValue))
- {
- return false;
- }
- }
- else
- {
- // Use NullHtmlEncoder to avoid treating encoded whitespace as non-whitespace e.g. "\t" as " ".
- ((IHtmlContent)entry).WriteTo(writer, NullHtmlEncoder.Default);
- if (!writer.IsEmptyOrWhiteSpace)
- {
- return false;
- }
+ return string.IsNullOrWhiteSpace(stringValue);
}
- return true;
+ // Use NullHtmlEncoder to avoid treating encoded whitespace as non-whitespace e.g. "\t" as " ".
+ ((IHtmlContent)entry).WriteTo(writer, NullHtmlEncoder.Default);
+
+ return writer.IsEmptyOrWhiteSpace;
}
private TagHelperContent AppendCore(object entry)
@@ -329,7 +314,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
{
return GetContent();
}
-
+
// Overrides Write(string) to find if the content written is empty/whitespace.
private class EmptyOrWhiteSpaceWriter : TextWriter
{
@@ -360,4 +345,4 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultProjectSnapshotProjectEngineFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultProjectSnapshotProjectEngineFactory.cs
new file mode 100644
index 0000000000..cd9ac1ee51
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultProjectSnapshotProjectEngineFactory.cs
@@ -0,0 +1,102 @@
+// 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.Razor.ProjectSystem;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+ internal class DefaultProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
+ {
+ private readonly static RazorConfiguration DefaultConfiguration = FallbackRazorConfiguration.MVC_2_1;
+
+ private readonly IFallbackProjectEngineFactory _fallback;
+ private readonly Lazy[] _factories;
+
+ public DefaultProjectSnapshotProjectEngineFactory(
+ IFallbackProjectEngineFactory fallback,
+ Lazy[] factories)
+ {
+ if (fallback == null)
+ {
+ throw new ArgumentNullException(nameof(fallback));
+ }
+
+ if (factories == null)
+ {
+ throw new ArgumentNullException(nameof(factories));
+ }
+
+ _fallback = fallback;
+ _factories = factories;
+ }
+
+ public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action configure)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ if (fileSystem == null)
+ {
+ throw new ArgumentNullException(nameof(fileSystem));
+ }
+
+ // When we're running in the editor, the editor provides a configure delegate that will include
+ // the editor settings and tag helpers.
+ //
+ // This service is only used in process in Visual Studio, and any other callers should provide these
+ // things also.
+ configure = configure ?? ((b) => { });
+
+ // The default configuration currently matches the newest MVC configuration.
+ //
+ // We typically want this because the language adds features over time - we don't want to a bunch of errors
+ // to show up when a document is first opened, and then go away when the configuration loads, we'd prefer the opposite.
+ var configuration = project.Configuration ?? DefaultConfiguration;
+
+ // If there's no factory to handle the configuration then fall back to a very basic configuration.
+ //
+ // This will stop a crash from happening in this case (misconfigured project), but will still make
+ // it obvious to the user that something is wrong.
+ var factory = SelectFactory(configuration) ?? _fallback;
+ return factory.Create(configuration, fileSystem, configure);
+ }
+
+ public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ return SelectFactory(project.Configuration ?? DefaultConfiguration, requireSerializable: false);
+ }
+
+ public override IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ return SelectFactory(project.Configuration ?? DefaultConfiguration, requireSerializable: true);
+ }
+
+ private IProjectEngineFactory SelectFactory(RazorConfiguration configuration, bool requireSerializable = false)
+ {
+ for (var i = 0; i < _factories.Length; i++)
+ {
+ var factory = _factories[i];
+ if (string.Equals(configuration.ConfigurationName, factory.Metadata.ConfigurationName))
+ {
+ return requireSerializable && !factory.Metadata.SupportsSerialization ? null : factory.Value;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultProjectSnapshotProjectEngineFactoryFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultProjectSnapshotProjectEngineFactoryFactory.cs
new file mode 100644
index 0000000000..663bd8f2bc
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultProjectSnapshotProjectEngineFactoryFactory.cs
@@ -0,0 +1,46 @@
+// 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.Composition;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+
+namespace Microsoft.CodeAnalysis.Razor.Workspaces
+{
+ [ExportWorkspaceServiceFactory(typeof(ProjectSnapshotProjectEngineFactory))]
+ internal class DefaultProjectSnapshotProjectEngineFactoryFactory : IWorkspaceServiceFactory
+ {
+ private readonly IFallbackProjectEngineFactory _fallback;
+ private readonly Lazy[] _factories;
+
+ [ImportingConstructor]
+ public DefaultProjectSnapshotProjectEngineFactoryFactory(
+ IFallbackProjectEngineFactory fallback,
+ [ImportMany] Lazy[] factories)
+ {
+ if (fallback == null)
+ {
+ throw new ArgumentNullException(nameof(fallback));
+ }
+
+ if (factories == null)
+ {
+ throw new ArgumentNullException(nameof(factories));
+ }
+
+ _fallback = fallback;
+ _factories = factories;
+ }
+
+ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
+ {
+ if (workspaceServices == null)
+ {
+ throw new ArgumentNullException(nameof(workspaceServices));
+ }
+
+ return new DefaultProjectSnapshotProjectEngineFactory(_fallback, _factories);
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentGenerator/BackgroundDocumentGenerator.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentGenerator/BackgroundDocumentGenerator.cs
new file mode 100644
index 0000000000..eba3c12e25
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentGenerator/BackgroundDocumentGenerator.cs
@@ -0,0 +1,275 @@
+// 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.Composition;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Razor.ProjectSystem;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+ // Deliberately not exported for now, until this feature is working end to end.
+ // [Export(typeof(ProjectSnapshotChangeTrigger))]
+ internal class BackgroundDocumentGenerator : ProjectSnapshotChangeTrigger
+ {
+ private ForegroundDispatcher _foregroundDispatcher;
+ private ProjectSnapshotManagerBase _projectManager;
+
+ private readonly Dictionary _files;
+ private Timer _timer;
+
+ [ImportingConstructor]
+ public BackgroundDocumentGenerator(ForegroundDispatcher foregroundDispatcher)
+ {
+ if (foregroundDispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(foregroundDispatcher));
+ }
+
+ _foregroundDispatcher = foregroundDispatcher;
+
+ _files = new Dictionary();
+ }
+
+ public bool HasPendingNotifications
+ {
+ get
+ {
+ lock (_files)
+ {
+ return _files.Count > 0;
+ }
+ }
+ }
+
+ // Used in unit tests to control the timer delay.
+ public TimeSpan Delay { get; set; } = TimeSpan.FromSeconds(2);
+
+ public bool IsScheduledOrRunning => _timer != null;
+
+ // Used in unit tests to ensure we can control when background work starts.
+ public ManualResetEventSlim BlockBackgroundWorkStart { get; set; }
+
+ // Used in unit tests to ensure we can know when background work finishes.
+ public ManualResetEventSlim NotifyBackgroundWorkStarting { get; set; }
+
+ // Used in unit tests to ensure we can know when background has captured its current workload.
+ public ManualResetEventSlim NotifyBackgroundCapturedWorkload { get; set; }
+
+ // Used in unit tests to ensure we can control when background work completes.
+ public ManualResetEventSlim BlockBackgroundWorkCompleting { get; set; }
+
+ // Used in unit tests to ensure we can know when background work finishes.
+ public ManualResetEventSlim NotifyBackgroundWorkCompleted { get; set; }
+
+ private void OnStartingBackgroundWork()
+ {
+ if (BlockBackgroundWorkStart != null)
+ {
+ BlockBackgroundWorkStart.Wait();
+ BlockBackgroundWorkStart.Reset();
+ }
+
+ if (NotifyBackgroundWorkStarting != null)
+ {
+ NotifyBackgroundWorkStarting.Set();
+ }
+ }
+
+ private void OnCompletingBackgroundWork()
+ {
+ if (BlockBackgroundWorkCompleting != null)
+ {
+ BlockBackgroundWorkCompleting.Wait();
+ BlockBackgroundWorkCompleting.Reset();
+ }
+ }
+
+ private void OnCompletedBackgroundWork()
+ {
+ if (NotifyBackgroundWorkCompleted != null)
+ {
+ NotifyBackgroundWorkCompleted.Set();
+ }
+ }
+
+ private void OnBackgroundCapturedWorkload()
+ {
+ if (NotifyBackgroundCapturedWorkload != null)
+ {
+ NotifyBackgroundCapturedWorkload.Set();
+ }
+ }
+
+ public override void Initialize(ProjectSnapshotManagerBase projectManager)
+ {
+ if (projectManager == null)
+ {
+ throw new ArgumentNullException(nameof(projectManager));
+ }
+
+ _projectManager = projectManager;
+ _projectManager.Changed += ProjectManager_Changed;
+ }
+
+ protected virtual Task ProcessDocument(DocumentSnapshot document)
+ {
+ return document.GetGeneratedOutputAsync();
+ }
+
+ public void Enqueue(ProjectSnapshot project, DocumentSnapshot document)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+
+ lock (_files)
+ {
+ // We only want to store the last 'seen' version of any given document. That way when we pick one to process
+ // it's always the best version to use.
+ _files[new DocumentKey(project.FilePath, document.FilePath)] = document;
+
+ StartWorker();
+ }
+ }
+
+ protected virtual void StartWorker()
+ {
+ // Access to the timer is protected by the lock in Enqueue and in Timer_Tick
+ if (_timer == null)
+ {
+
+ // Timer will fire after a fixed delay, but only once.
+ _timer = NonCapturingTimer.Create(state => ((BackgroundDocumentGenerator)state).Timer_Tick(), this, Delay, Timeout.InfiniteTimeSpan);
+ }
+ }
+
+ private void Timer_Tick()
+ {
+ _ = TimerTick();
+ }
+
+ private async Task TimerTick()
+ {
+ try
+ {
+ _foregroundDispatcher.AssertBackgroundThread();
+
+ // Timer is stopped.
+ _timer.Change(Timeout.Infinite, Timeout.Infinite);
+
+ OnStartingBackgroundWork();
+
+ DocumentSnapshot[] work;
+ lock (_files)
+ {
+ work = _files.Values.ToArray();
+ _files.Clear();
+ }
+
+ OnBackgroundCapturedWorkload();
+
+ for (var i = 0; i < work.Length; i++)
+ {
+ var document = work[i];
+ try
+ {
+ await ProcessDocument(document);
+ }
+ catch (Exception ex)
+ {
+ ReportError(document, ex);
+ }
+ }
+
+ OnCompletingBackgroundWork();
+
+ lock (_files)
+ {
+ // Resetting the timer allows another batch of work to start.
+ _timer.Dispose();
+ _timer = null;
+
+ // If more work came in while we were running start the worker again.
+ if (_files.Count > 0)
+ {
+ StartWorker();
+ }
+ }
+
+ OnCompletedBackgroundWork();
+ }
+ catch (Exception ex)
+ {
+ // This is something totally unexpected, let's just send it over to the workspace.
+ await Task.Factory.StartNew(
+ (p) => ((ProjectSnapshotManagerBase)p).ReportError(ex),
+ _projectManager,
+ CancellationToken.None,
+ TaskCreationOptions.None,
+ _foregroundDispatcher.ForegroundScheduler);
+ }
+ }
+
+ private void ReportError(DocumentSnapshot document, Exception ex)
+ {
+ GC.KeepAlive(Task.Factory.StartNew(
+ (p) => ((ProjectSnapshotManagerBase)p).ReportError(ex),
+ _projectManager,
+ CancellationToken.None,
+ TaskCreationOptions.None,
+ _foregroundDispatcher.ForegroundScheduler));
+ }
+
+ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
+ {
+ switch (e.Kind)
+ {
+ case ProjectChangeKind.ProjectAdded:
+ case ProjectChangeKind.ProjectChanged:
+ {
+ var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
+ foreach (var documentFilePath in project.DocumentFilePaths)
+ {
+ Enqueue(project, project.GetDocument(documentFilePath));
+ }
+
+ break;
+ }
+
+ case ProjectChangeKind.ProjectRemoved:
+ // ignore
+ break;
+
+ case ProjectChangeKind.DocumentAdded:
+ case ProjectChangeKind.DocumentChanged:
+ {
+ var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
+ Enqueue(project, project.GetDocument(e.DocumentFilePath));
+
+ break;
+ }
+
+
+ case ProjectChangeKind.DocumentRemoved:
+ // ignore
+ break;
+
+ default:
+ throw new InvalidOperationException($"Unknown ProjectChangeKind {e.Kind}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentKey.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentKey.cs
new file mode 100644
index 0000000000..2fe707a707
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentKey.cs
@@ -0,0 +1,41 @@
+// 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.Extensions.Internal;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+ public struct DocumentKey : IEquatable
+ {
+ public DocumentKey(string projectFilePath, string documentFilePath)
+ {
+ ProjectFilePath = projectFilePath;
+ DocumentFilePath = documentFilePath;
+ }
+
+ public string ProjectFilePath { get; }
+
+ public string DocumentFilePath { get; }
+
+ public bool Equals(DocumentKey other)
+ {
+ return
+ FilePathComparer.Instance.Equals(ProjectFilePath, other.ProjectFilePath) &&
+ FilePathComparer.Instance.Equals(DocumentFilePath, other.DocumentFilePath);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is DocumentKey key ? Equals(key) : false;
+ }
+
+ public override int GetHashCode()
+ {
+ var hash = new HashCodeCombiner();
+ hash.Add(ProjectFilePath, FilePathComparer.Instance);
+ hash.Add(DocumentFilePath, FilePathComparer.Instance);
+ return hash;
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Microsoft.CodeAnalysis.Razor.Workspaces.csproj b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Microsoft.CodeAnalysis.Razor.Workspaces.csproj
index e7f29a2fb0..ca09c13e78 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Microsoft.CodeAnalysis.Razor.Workspaces.csproj
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Microsoft.CodeAnalysis.Razor.Workspaces.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSnapshotProjectEngineFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSnapshotProjectEngineFactory.cs
new file mode 100644
index 0000000000..cf2341878d
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSnapshotProjectEngineFactory.cs
@@ -0,0 +1,51 @@
+// 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.IO;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Razor.ProjectSystem;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+ internal abstract class ProjectSnapshotProjectEngineFactory : IWorkspaceService
+ {
+ public abstract IProjectEngineFactory FindFactory(ProjectSnapshot project);
+
+ public abstract IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project);
+
+ public RazorProjectEngine Create(ProjectSnapshot project)
+ {
+ return Create(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), null);
+ }
+
+ public RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ if (fileSystem == null)
+ {
+ throw new ArgumentNullException(nameof(fileSystem));
+ }
+
+ return Create(project, fileSystem, null);
+ }
+
+ public RazorProjectEngine Create(ProjectSnapshot project, Action configure)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ return Create(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), configure);
+ }
+
+ public abstract RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action configure);
+
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultDocumentSnapshot.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultDocumentSnapshot.cs
new file mode 100644
index 0000000000..d25b38ddbe
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultDocumentSnapshot.cs
@@ -0,0 +1,81 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
+{
+ internal class DefaultDocumentSnapshot : DocumentSnapshot
+ {
+ public DefaultDocumentSnapshot(DefaultProjectSnapshot project, DocumentState state)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ if (state == null)
+ {
+ throw new ArgumentNullException(nameof(state));
+ }
+
+ Project = project;
+ State = state;
+ }
+
+ public DefaultProjectSnapshot Project { get; }
+
+ public DocumentState State { get; }
+
+ public override string FilePath => State.HostDocument.FilePath;
+
+ public override string TargetPath => State.HostDocument.TargetPath;
+
+ public override IReadOnlyList GetImports()
+ {
+ return State.Imports.GetImports(Project, this);
+ }
+
+ public override Task GetTextAsync()
+ {
+ return State.GetTextAsync();
+ }
+
+ public override Task GetTextVersionAsync()
+ {
+ return State.GetTextVersionAsync();
+ }
+
+ public override Task GetGeneratedOutputAsync()
+ {
+ // IMPORTANT: Don't put more code here. We want this to return a cached task.
+ return State.GeneratedOutput.GetGeneratedOutputInitializationTask(Project, this);
+ }
+
+ public override bool TryGetText(out SourceText result)
+ {
+ return State.TryGetText(out result);
+ }
+
+ public override bool TryGetTextVersion(out VersionStamp result)
+ {
+ return State.TryGetTextVersion(out result);
+ }
+
+ public override bool TryGetGeneratedOutput(out RazorCodeDocument result)
+ {
+ if (State.GeneratedOutput.IsResultAvailable)
+ {
+ result = State.GeneratedOutput.GetGeneratedOutputInitializationTask(Project, this).Result;
+ return true;
+ }
+
+ result = null;
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshot.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshot.cs
index b8646429c6..aba5e7a916 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshot.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshot.cs
@@ -3,182 +3,82 @@
using System;
using System.Collections.Generic;
-using System.Linq;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
- // All of the public state of this is immutable - we create a new instance and notify subscribers
- // when it changes.
- //
- // However we use the private state to track things like dirty/clean.
- //
- // See the private constructors... When we update the snapshot we either are processing a Workspace
- // change (Project) or updating the computed state (ProjectSnapshotUpdateContext). We don't do both
- // at once.
internal class DefaultProjectSnapshot : ProjectSnapshot
{
- public DefaultProjectSnapshot(HostProject hostProject, Project workspaceProject, VersionStamp? version = null)
+ private readonly object _lock;
+
+ private Dictionary _documents;
+
+ public DefaultProjectSnapshot(ProjectState state)
{
- if (hostProject == null)
+ if (state == null)
{
- throw new ArgumentNullException(nameof(hostProject));
+ throw new ArgumentNullException(nameof(state));
}
- HostProject = hostProject;
- WorkspaceProject = workspaceProject; // Might be null
-
- FilePath = hostProject.FilePath;
- Version = version ?? VersionStamp.Default;
+ State = state;
+
+ _lock = new object();
+ _documents = new Dictionary(FilePathComparer.Instance);
}
- private DefaultProjectSnapshot(HostProject hostProject, DefaultProjectSnapshot other)
- {
- if (hostProject == null)
- {
- throw new ArgumentNullException(nameof(hostProject));
- }
-
- if (other == null)
- {
- throw new ArgumentNullException(nameof(other));
- }
-
- ComputedVersion = other.ComputedVersion;
-
- FilePath = other.FilePath;
- TagHelpers = other.TagHelpers;
- HostProject = hostProject;
- WorkspaceProject = other.WorkspaceProject;
-
- Version = other.Version.GetNewerVersion();
- }
-
- private DefaultProjectSnapshot(Project workspaceProject, DefaultProjectSnapshot other)
- {
- if (workspaceProject == null)
- {
- throw new ArgumentNullException(nameof(workspaceProject));
- }
-
- if (other == null)
- {
- throw new ArgumentNullException(nameof(other));
- }
-
- ComputedVersion = other.ComputedVersion;
-
- FilePath = other.FilePath;
- TagHelpers = other.TagHelpers;
- HostProject = other.HostProject;
- WorkspaceProject = workspaceProject;
-
- Version = other.Version.GetNewerVersion();
- }
-
- private DefaultProjectSnapshot(ProjectSnapshotUpdateContext update, DefaultProjectSnapshot other)
- {
- if (update == null)
- {
- throw new ArgumentNullException(nameof(update));
- }
-
- if (other == null)
- {
- throw new ArgumentNullException(nameof(other));
- }
-
- ComputedVersion = update.Version;
-
- FilePath = other.FilePath;
- HostProject = other.HostProject;
- TagHelpers = update.TagHelpers ?? Array.Empty();
- WorkspaceProject = other.WorkspaceProject;
-
- // This doesn't represent a new version of the underlying data. Keep the same version.
- Version = other.Version;
- }
+ public ProjectState State { get; }
public override RazorConfiguration Configuration => HostProject.Configuration;
- public override string FilePath { get; }
+ public override IEnumerable DocumentFilePaths => State.Documents.Keys;
- public override HostProject HostProject { get; }
+ public override string FilePath => State.HostProject.FilePath;
+
+ public HostProject HostProject => State.HostProject;
public override bool IsInitialized => WorkspaceProject != null;
- public override VersionStamp Version { get; }
+ public override VersionStamp Version => State.Version;
- public override Project WorkspaceProject { get; }
+ public override Project WorkspaceProject => State.WorkspaceProject;
- public override IReadOnlyList TagHelpers { get; } = Array.Empty();
-
- // This is the version that the computed state is based on.
- public VersionStamp? ComputedVersion { get; set; }
-
- // We know the project is dirty if we don't have a computed result, or it was computed for a different version.
- // Since the PSM updates the snapshots synchronously, the snapshot can never be older than the computed state.
- public bool IsDirty => ComputedVersion == null || ComputedVersion.Value != Version;
-
- public ProjectSnapshotUpdateContext CreateUpdateContext()
+ public override DocumentSnapshot GetDocument(string filePath)
{
- return new ProjectSnapshotUpdateContext(FilePath, HostProject, WorkspaceProject, Version);
+ lock (_lock)
+ {
+ if (!_documents.TryGetValue(filePath, out var result) &&
+ State.Documents.TryGetValue(filePath, out var state))
+ {
+ result = new DefaultDocumentSnapshot(this, state);
+ _documents.Add(filePath, result);
+ }
+
+ return result;
+ }
}
- public DefaultProjectSnapshot WithHostProject(HostProject hostProject)
+ public override RazorProjectEngine GetProjectEngine()
{
- if (hostProject == null)
+ return State.ProjectEngine.GetProjectEngine(this);
+ }
+
+ public override Task> GetTagHelpersAsync()
+ {
+ // IMPORTANT: Don't put more code here. We want this to return a cached task.
+ return State.TagHelpers.GetTagHelperInitializationTask(this);
+ }
+
+ public override bool TryGetTagHelpers(out IReadOnlyList result)
+ {
+ if (State.TagHelpers.IsResultAvailable)
{
- throw new ArgumentNullException(nameof(hostProject));
+ result = State.TagHelpers.GetTagHelperInitializationTask(this).Result;
+ return true;
}
- return new DefaultProjectSnapshot(hostProject, this);
- }
-
- public DefaultProjectSnapshot RemoveWorkspaceProject()
- {
- // We want to get rid of all of the computed state since it's not really valid.
- return new DefaultProjectSnapshot(HostProject, null, Version.GetNewerVersion());
- }
-
- public DefaultProjectSnapshot WithWorkspaceProject(Project workspaceProject)
- {
- if (workspaceProject == null)
- {
- throw new ArgumentNullException(nameof(workspaceProject));
- }
-
- return new DefaultProjectSnapshot(workspaceProject, this);
- }
-
- public DefaultProjectSnapshot WithComputedUpdate(ProjectSnapshotUpdateContext update)
- {
- if (update == null)
- {
- throw new ArgumentNullException(nameof(update));
- }
-
- return new DefaultProjectSnapshot(update, this);
- }
-
- public bool HasConfigurationChanged(DefaultProjectSnapshot original)
- {
- if (original == null)
- {
- throw new ArgumentNullException(nameof(original));
- }
-
- return !object.Equals(Configuration, original.Configuration);
- }
-
- public bool HaveTagHelpersChanged(ProjectSnapshot original)
- {
- if (original == null)
- {
- throw new ArgumentNullException(nameof(original));
- }
-
- return !Enumerable.SequenceEqual(TagHelpers, original.TagHelpers);
+ result = null;
+ return false;
}
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManager.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManager.cs
index c246fa14d7..5a810cc803 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManager.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManager.cs
@@ -3,8 +3,10 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
@@ -31,15 +33,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private readonly ErrorReporter _errorReporter;
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly ProjectSnapshotChangeTrigger[] _triggers;
- private readonly ProjectSnapshotWorkerQueue _workerQueue;
- private readonly ProjectSnapshotWorker _worker;
- private readonly Dictionary _projects;
+ // Each entry holds a ProjectState and an optional ProjectSnapshot. ProjectSnapshots are
+ // created lazily.
+ private readonly Dictionary _projects;
+ private readonly HashSet _openDocuments;
public DefaultProjectSnapshotManager(
ForegroundDispatcher foregroundDispatcher,
ErrorReporter errorReporter,
- ProjectSnapshotWorker worker,
IEnumerable triggers,
Workspace workspace)
{
@@ -53,11 +55,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
throw new ArgumentNullException(nameof(errorReporter));
}
- if (worker == null)
- {
- throw new ArgumentNullException(nameof(worker));
- }
-
if (triggers == null)
{
throw new ArgumentNullException(nameof(triggers));
@@ -70,13 +67,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
_foregroundDispatcher = foregroundDispatcher;
_errorReporter = errorReporter;
- _worker = worker;
_triggers = triggers.ToArray();
Workspace = workspace;
- _projects = new Dictionary(FilePathComparer.Instance);
-
- _workerQueue = new ProjectSnapshotWorkerQueue(_foregroundDispatcher, this, worker);
+ _projects = new Dictionary(FilePathComparer.Instance);
+ _openDocuments = new HashSet(FilePathComparer.Instance);
for (var i = 0; i < _triggers.Length; i++)
{
@@ -89,43 +84,305 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
get
{
_foregroundDispatcher.AssertForegroundThread();
- return _projects.Values.ToArray();
+
+
+ var i = 0;
+ var projects = new ProjectSnapshot[_projects.Count];
+ foreach (var entry in _projects)
+ {
+ if (entry.Value.Snapshot == null)
+ {
+ entry.Value.Snapshot = new DefaultProjectSnapshot(entry.Value.State);
+ }
+
+ projects[i++] = entry.Value.Snapshot;
+ }
+
+ return projects;
}
}
public override Workspace Workspace { get; }
- public override void ProjectUpdated(ProjectSnapshotUpdateContext update)
+ public override ProjectSnapshot GetLoadedProject(string filePath)
{
- if (update == null)
+ if (filePath == null)
{
- throw new ArgumentNullException(nameof(update));
+ throw new ArgumentNullException(nameof(filePath));
}
_foregroundDispatcher.AssertForegroundThread();
- if (_projects.TryGetValue(update.WorkspaceProject.FilePath, out var original))
+ if (_projects.TryGetValue(filePath, out var entry))
{
- if (!original.IsInitialized)
+ if (entry.Snapshot == null)
{
- // If the project has been uninitialized, just ignore the update.
- return;
+ entry.Snapshot = new DefaultProjectSnapshot(entry.State);
}
- // This is an update to the project's computed values, so everything should be overwritten
- var snapshot = original.WithComputedUpdate(update);
- _projects[update.WorkspaceProject.FilePath] = snapshot;
+ return entry.Snapshot;
+ }
- if (snapshot.IsDirty)
+ return null;
+ }
+
+ public override ProjectSnapshot GetOrCreateProject(string filePath)
+ {
+ if (filePath == null)
+ {
+ throw new ArgumentNullException(nameof(filePath));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+
+ return GetLoadedProject(filePath) ?? new EphemeralProjectSnapshot(Workspace.Services, filePath);
+ }
+
+ public override bool IsDocumentOpen(string documentFilePath)
+ {
+ if (documentFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(documentFilePath));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+
+ return _openDocuments.Contains(documentFilePath);
+ }
+
+ public override void DocumentAdded(HostProject hostProject, HostDocument document, TextLoader textLoader)
+ {
+ if (hostProject == null)
+ {
+ throw new ArgumentNullException(nameof(hostProject));
+ }
+
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+
+ if (_projects.TryGetValue(hostProject.FilePath, out var entry))
+ {
+ var loader = textLoader == null ? DocumentState.EmptyLoader : (Func>)(() =>
{
- // It's possible that the snapshot can still be dirty if we got a project update while computing state in
- // the background. We need to trigger the background work to asynchronously compute the effect of the updates.
- NotifyBackgroundWorker(snapshot.CreateUpdateContext());
+ return textLoader.LoadTextAndVersionAsync(Workspace, null, CancellationToken.None);
+ });
+ var state = entry.State.WithAddedHostDocument(document, loader);
+
+ // Document updates can no-op.
+ if (!object.ReferenceEquals(state, entry.State))
+ {
+ _projects[hostProject.FilePath] = new Entry(state);
+ NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentAdded));
}
-
- if (!object.Equals(snapshot.ComputedVersion, original.ComputedVersion))
+ }
+ }
+
+ public override void DocumentRemoved(HostProject hostProject, HostDocument document)
+ {
+ if (hostProject == null)
+ {
+ throw new ArgumentNullException(nameof(hostProject));
+ }
+
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+ if (_projects.TryGetValue(hostProject.FilePath, out var entry))
+ {
+ var state = entry.State.WithRemovedHostDocument(document);
+
+ // Document updates can no-op.
+ if (!object.ReferenceEquals(state, entry.State))
{
- NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.TagHelpersChanged));
+ _projects[hostProject.FilePath] = new Entry(state);
+ NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentRemoved));
+ }
+ }
+ }
+
+ public override void DocumentOpened(string projectFilePath, string documentFilePath, SourceText sourceText)
+ {
+ if (projectFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(projectFilePath));
+ }
+
+ if (documentFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(documentFilePath));
+ }
+
+ if (sourceText == null)
+ {
+ throw new ArgumentNullException(nameof(sourceText));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+ if (_projects.TryGetValue(projectFilePath, out var entry) &&
+ entry.State.Documents.TryGetValue(documentFilePath, out var older))
+ {
+ ProjectState state;
+ SourceText olderText;
+ VersionStamp olderVersion;
+
+ var currentText = sourceText;
+ if (older.TryGetText(out olderText) &&
+ older.TryGetTextVersion(out olderVersion))
+ {
+ var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
+ state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
+ }
+ else
+ {
+ state = entry.State.WithChangedHostDocument(older.HostDocument, async () =>
+ {
+ olderText = await older.GetTextAsync().ConfigureAwait(false);
+ olderVersion = await older.GetTextVersionAsync().ConfigureAwait(false);
+
+ var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
+ return TextAndVersion.Create(currentText, version, documentFilePath);
+ });
+ }
+
+ _openDocuments.Add(documentFilePath);
+
+ // Document updates can no-op.
+ if (!object.ReferenceEquals(state, entry.State))
+ {
+ _projects[projectFilePath] = new Entry(state);
+ NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
+ }
+ }
+ }
+
+ public override void DocumentClosed(string projectFilePath, string documentFilePath, TextLoader textLoader)
+ {
+ if (projectFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(projectFilePath));
+ }
+
+ if (documentFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(documentFilePath));
+ }
+
+ if (textLoader == null)
+ {
+ throw new ArgumentNullException(nameof(textLoader));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+ if (_projects.TryGetValue(projectFilePath, out var entry) &&
+ entry.State.Documents.TryGetValue(documentFilePath, out var older))
+ {
+ var state = entry.State.WithChangedHostDocument(older.HostDocument, async () =>
+ {
+ return await textLoader.LoadTextAndVersionAsync(Workspace, default, default);
+ });
+
+ _openDocuments.Remove(documentFilePath);
+
+ // Document updates can no-op.
+ if (!object.ReferenceEquals(state, entry.State))
+ {
+ _projects[projectFilePath] = new Entry(state);
+ NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
+ }
+ }
+ }
+
+ public override void DocumentChanged(string projectFilePath, string documentFilePath, SourceText sourceText)
+ {
+ if (projectFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(projectFilePath));
+ }
+
+ if (documentFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(documentFilePath));
+ }
+
+ if (sourceText == null)
+ {
+ throw new ArgumentNullException(nameof(sourceText));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+ if (_projects.TryGetValue(projectFilePath, out var entry) &&
+ entry.State.Documents.TryGetValue(documentFilePath, out var older))
+ {
+ ProjectState state;
+ SourceText olderText;
+ VersionStamp olderVersion;
+
+ var currentText = sourceText;
+ if (older.TryGetText(out olderText) &&
+ older.TryGetTextVersion(out olderVersion))
+ {
+ var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
+ state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
+ }
+ else
+ {
+ state = entry.State.WithChangedHostDocument(older.HostDocument, async () =>
+ {
+ olderText = await older.GetTextAsync().ConfigureAwait(false);
+ olderVersion = await older.GetTextVersionAsync().ConfigureAwait(false);
+
+ var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
+ return TextAndVersion.Create(currentText, version, documentFilePath);
+ });
+ }
+
+ // Document updates can no-op.
+ if (!object.ReferenceEquals(state, entry.State))
+ {
+ _projects[projectFilePath] = new Entry(state);
+ NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
+ }
+ }
+ }
+
+ public override void DocumentChanged(string projectFilePath, string documentFilePath, TextLoader textLoader)
+ {
+ if (projectFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(projectFilePath));
+ }
+
+ if (documentFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(documentFilePath));
+ }
+
+ if (textLoader == null)
+ {
+ throw new ArgumentNullException(nameof(textLoader));
+ }
+
+ _foregroundDispatcher.AssertForegroundThread();
+ if (_projects.TryGetValue(projectFilePath, out var entry) &&
+ entry.State.Documents.TryGetValue(documentFilePath, out var older))
+ {
+ var state = entry.State.WithChangedHostDocument(older.HostDocument, async () =>
+ {
+ return await textLoader.LoadTextAndVersionAsync(Workspace, default, default);
+ });
+
+ // Document updates can no-op.
+ if (!object.ReferenceEquals(state, entry.State))
+ {
+ _projects[projectFilePath] = new Entry(state);
+ NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@@ -149,17 +406,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// So if possible find a WorkspaceProject.
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
- var snapshot = new DefaultProjectSnapshot(hostProject, workspaceProject);
- _projects[hostProject.FilePath] = snapshot;
-
- if (snapshot.IsInitialized && snapshot.IsDirty)
- {
- // Start computing background state if the project is fully initialized.
- NotifyBackgroundWorker(snapshot.CreateUpdateContext());
- }
+ var state = ProjectState.Create(Workspace.Services, hostProject, workspaceProject);
+ _projects[hostProject.FilePath] = new Entry(state);
// We need to notify listeners about every project add.
- NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Added));
+ NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectAdded));
}
public override void HostProjectChanged(HostProject hostProject)
@@ -171,22 +422,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
_foregroundDispatcher.AssertForegroundThread();
- if (_projects.TryGetValue(hostProject.FilePath, out var original))
+ if (_projects.TryGetValue(hostProject.FilePath, out var entry))
{
- // Doing an update to the project should keep computed values, but mark the project as dirty if the
- // underlying project is newer.
- var snapshot = original.WithHostProject(hostProject);
- _projects[hostProject.FilePath] = snapshot;
+ var state = entry.State.WithHostProject(hostProject);
- if (snapshot.IsInitialized && snapshot.IsDirty)
+ // HostProject updates can no-op.
+ if (!object.ReferenceEquals(state, entry.State))
{
- // Start computing background state if the project is fully initialized.
- NotifyBackgroundWorker(snapshot.CreateUpdateContext());
- }
+ _projects[hostProject.FilePath] = new Entry(state);
- // Notify listeners right away because if the HostProject changes then it's likely that the Razor
- // configuration changed.
- NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
+ NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectChanged));
+ }
}
}
@@ -204,37 +450,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
_projects.Remove(hostProject.FilePath);
// We need to notify listeners about every project removal.
- NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Removed));
- }
- }
-
- public override void HostProjectBuildComplete(HostProject hostProject)
- {
- if (hostProject == null)
- {
- throw new ArgumentNullException(nameof(hostProject));
- }
-
- _foregroundDispatcher.AssertForegroundThread();
-
- if (_projects.TryGetValue(hostProject.FilePath, out var original))
- {
- var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
- if (workspaceProject == null)
- {
- // Host project was built prior to a workspace project being associated. We have nothing to do without
- // a workspace project so we short circuit.
- return;
- }
-
- // Doing an update to the project should keep computed values, but mark the project as dirty if the
- // underlying project is newer.
- var snapshot = original.WithWorkspaceProject(workspaceProject);
-
- _projects[hostProject.FilePath] = snapshot;
-
- // Notify the background worker so it can trigger tag helper discovery.
- NotifyBackgroundWorker(snapshot.CreateUpdateContext());
+ NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectRemoved));
}
}
@@ -254,25 +470,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// The WorkspaceProject initialization never triggers a "Project Add" from out point of view, we
// only care if the new WorkspaceProject matches an existing HostProject.
- if (_projects.TryGetValue(workspaceProject.FilePath, out var original))
+ if (_projects.TryGetValue(workspaceProject.FilePath, out var entry))
{
// If this is a multi-targeting project then we are only interested in a single workspace project. If we already
// found one in the past just ignore this one.
- if (original.WorkspaceProject == null)
+ if (entry.State.WorkspaceProject == null)
{
- var snapshot = original.WithWorkspaceProject(workspaceProject);
- _projects[workspaceProject.FilePath] = snapshot;
+ var state = entry.State.WithWorkspaceProject(workspaceProject);
+ _projects[workspaceProject.FilePath] = new Entry(state);
- if (snapshot.IsInitialized && snapshot.IsDirty)
- {
- // We don't need to notify listeners yet because we don't have any **new** computed state.
- //
- // However we do need to trigger the background work to asynchronously compute the effect of the updates.
- NotifyBackgroundWorker(snapshot.CreateUpdateContext());
- }
-
- // Notify listeners right away since WorkspaceProject was just added, the project is now initialized.
- NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
+ NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
}
}
}
@@ -293,25 +500,19 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// We also need to check the projectId here. If this is a multi-targeting project then we are only interested
// in a single workspace project. Just use the one that showed up first.
- if (_projects.TryGetValue(workspaceProject.FilePath, out var original) &&
- (original.WorkspaceProject == null ||
- original.WorkspaceProject.Id == workspaceProject.Id))
+ if (_projects.TryGetValue(workspaceProject.FilePath, out var entry) &&
+ (entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Id == workspaceProject.Id) &&
+ (entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Version.GetNewerVersion(workspaceProject.Version) == workspaceProject.Version))
{
- // Doing an update to the project should keep computed values, but mark the project as dirty if the
- // underlying project is newer.
- var snapshot = original.WithWorkspaceProject(workspaceProject);
- _projects[workspaceProject.FilePath] = snapshot;
-
- if (snapshot.IsInitialized && snapshot.IsDirty)
+ var state = entry.State.WithWorkspaceProject(workspaceProject);
+
+ // WorkspaceProject updates can no-op. This can be the case if a build is triggered, but we've
+ // already seen the update.
+ if (!object.ReferenceEquals(state, entry.State))
{
- // We don't need to notify listeners yet because we don't have any **new** computed state. However we do
- // need to trigger the background work to asynchronously compute the effect of the updates.
- NotifyBackgroundWorker(snapshot.CreateUpdateContext());
- }
+ _projects[workspaceProject.FilePath] = new Entry(state);
- if (snapshot.HaveTagHelpersChanged(original))
- {
- NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.TagHelpersChanged));
+ NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
}
}
}
@@ -330,16 +531,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return;
}
- if (_projects.TryGetValue(workspaceProject.FilePath, out var original))
+ if (_projects.TryGetValue(workspaceProject.FilePath, out var entry))
{
// We also need to check the projectId here. If this is a multi-targeting project then we are only interested
// in a single workspace project. Make sure the WorkspaceProject we're using is the one that's being removed.
- if (original.WorkspaceProject?.Id != workspaceProject.Id)
+ if (entry.State.WorkspaceProject?.Id != workspaceProject.Id)
{
return;
}
- DefaultProjectSnapshot snapshot;
+ ProjectState state;
// So if the WorkspaceProject got removed, we should double check to make sure that there aren't others
// hanging around. This could happen if a project is multi-targeting and one of the TFMs is removed.
@@ -347,30 +548,19 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
if (otherWorkspaceProject != null && otherWorkspaceProject.Id != workspaceProject.Id)
{
// OK there's another WorkspaceProject, use that.
- //
- // Doing an update to the project should keep computed values, but mark the project as dirty if the
- // underlying project is newer.
- snapshot = original.WithWorkspaceProject(otherWorkspaceProject);
- _projects[workspaceProject.FilePath] = snapshot;
+ state = entry.State.WithWorkspaceProject(otherWorkspaceProject);
+ _projects[otherWorkspaceProject.FilePath] = new Entry(state);
- if (snapshot.IsInitialized && snapshot.IsDirty)
- {
- // We don't need to notify listeners yet because we don't have any **new** computed state. However we do
- // need to trigger the background work to asynchronously compute the effect of the updates.
- NotifyBackgroundWorker(snapshot.CreateUpdateContext());
- }
-
- // Notify listeners of a change because it's a different WorkspaceProject.
- NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
-
- return;
+ NotifyListeners(new ProjectChangeEventArgs(otherWorkspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
}
+ else
+ {
+ state = entry.State.WithWorkspaceProject(null);
+ _projects[workspaceProject.FilePath] = new Entry(state);
- snapshot = original.RemoveWorkspaceProject();
- _projects[workspaceProject.FilePath] = snapshot;
-
- // Notify listeners of a change because we've removed computed state.
- NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
+ // Notify listeners of a change because we've removed computed state.
+ NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
+ }
}
}
@@ -401,8 +591,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
throw new ArgumentNullException(nameof(exception));
}
- var project = hostProject?.FilePath == null ? null : this.GetProjectWithFilePath(hostProject.FilePath);
- _errorReporter.ReportError(exception, project);
+ var snapshot = hostProject?.FilePath == null ? null : GetLoadedProject(hostProject.FilePath);
+ _errorReporter.ReportError(exception, snapshot);
}
public override void ReportError(Exception exception, Project workspaceProject)
@@ -411,7 +601,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
throw new ArgumentNullException(nameof(exception));
}
-
+
_errorReporter.ReportError(exception, workspaceProject);
}
@@ -440,14 +630,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return null;
}
- // virtual so it can be overridden in tests
- protected virtual void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
- {
- _foregroundDispatcher.AssertForegroundThread();
-
- _workerQueue.Enqueue(context);
- }
-
// virtual so it can be overridden in tests
protected virtual void NotifyListeners(ProjectChangeEventArgs e)
{
@@ -459,5 +641,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
handler(this, e);
}
}
+
+ private class Entry
+ {
+ public ProjectSnapshot Snapshot;
+ public readonly ProjectState State;
+
+ public Entry(ProjectState state)
+ {
+ State = state;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManagerFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManagerFactory.cs
index d82d82cd7c..b092174aa6 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManagerFactory.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManagerFactory.cs
@@ -45,8 +45,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return new DefaultProjectSnapshotManager(
_foregroundDispatcher,
languageServices.WorkspaceServices.GetRequiredService(),
- languageServices.GetRequiredService(),
- _triggers,
+ _triggers,
languageServices.WorkspaceServices.Workspace);
}
}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotWorker.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotWorker.cs
deleted file mode 100644
index 1b7480f940..0000000000
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotWorker.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.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
-{
- internal class DefaultProjectSnapshotWorker : ProjectSnapshotWorker
- {
- private readonly ForegroundDispatcher _foregroundDispatcher;
- private readonly TagHelperResolver _tagHelperResolver;
-
- public DefaultProjectSnapshotWorker(ForegroundDispatcher foregroundDispatcher, TagHelperResolver tagHelperResolver)
- {
- if (foregroundDispatcher == null)
- {
- throw new ArgumentNullException(nameof(foregroundDispatcher));
- }
-
- if (tagHelperResolver == null)
- {
- throw new ArgumentNullException(nameof(tagHelperResolver));
- }
-
- _foregroundDispatcher = foregroundDispatcher;
- _tagHelperResolver = tagHelperResolver;
- }
-
- public override Task ProcessUpdateAsync(ProjectSnapshotUpdateContext update, CancellationToken cancellationToken = default(CancellationToken))
- {
- if (update == null)
- {
- throw new ArgumentNullException(nameof(update));
- }
-
- // Don't block the main thread
- if (_foregroundDispatcher.IsForegroundThread)
- {
- return Task.Factory.StartNew(ProjectUpdatesCoreAsync, update, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.BackgroundScheduler);
- }
-
- return ProjectUpdatesCoreAsync(update);
- }
-
- protected virtual void OnProcessingUpdate()
- {
- }
-
- private async Task ProjectUpdatesCoreAsync(object state)
- {
- var update = (ProjectSnapshotUpdateContext)state;
-
- OnProcessingUpdate();
-
- var snapshot = new DefaultProjectSnapshot(update.HostProject, update.WorkspaceProject, update.Version);
- var result = await _tagHelperResolver.GetTagHelpersAsync(snapshot, CancellationToken.None);
- update.TagHelpers = result.Descriptors;
- }
- }
-}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotWorkerFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotWorkerFactory.cs
deleted file mode 100644
index bd36bf361d..0000000000
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotWorkerFactory.cs
+++ /dev/null
@@ -1,32 +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.ProjectSystem
-{
- [Shared]
- [ExportLanguageServiceFactory(typeof(ProjectSnapshotWorker), RazorLanguage.Name)]
- internal class DefaultProjectSnapshotWorkerFactory : ILanguageServiceFactory
- {
- private readonly ForegroundDispatcher _foregroundDispatcher;
-
- [ImportingConstructor]
- public DefaultProjectSnapshotWorkerFactory(ForegroundDispatcher foregroundDispatcher)
- {
- if (foregroundDispatcher == null)
- {
- throw new System.ArgumentNullException(nameof(foregroundDispatcher));
- }
-
- _foregroundDispatcher = foregroundDispatcher;
- }
-
- public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
- {
- return new DefaultProjectSnapshotWorker(_foregroundDispatcher, languageServices.GetRequiredService());
- }
- }
-}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentGeneratedOutputTracker.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentGeneratedOutputTracker.cs
new file mode 100644
index 0000000000..a3e11da04f
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentGeneratedOutputTracker.cs
@@ -0,0 +1,172 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
+{
+ internal class DocumentGeneratedOutputTracker
+ {
+ private readonly object _lock;
+
+ private DocumentGeneratedOutputTracker _older;
+ private Task _task;
+
+ private IReadOnlyList _tagHelpers;
+ private IReadOnlyList _imports;
+
+ public DocumentGeneratedOutputTracker(DocumentGeneratedOutputTracker older)
+ {
+ _older = older;
+
+ _lock = new object();
+ }
+
+ public bool IsResultAvailable => _task?.IsCompleted == true;
+
+ public DocumentGeneratedOutputTracker Older => _older;
+
+ public Task GetGeneratedOutputInitializationTask(ProjectSnapshot project, DocumentSnapshot document)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ if (_task == null)
+ {
+ lock (_lock)
+ {
+ if (_task == null)
+ {
+ _task = GetGeneratedOutputInitializationTaskCore(project, document);
+ }
+ }
+ }
+
+ return _task;
+ }
+
+ public DocumentGeneratedOutputTracker Fork()
+ {
+ return new DocumentGeneratedOutputTracker(this);
+ }
+
+ private async Task GetGeneratedOutputInitializationTaskCore(ProjectSnapshot project, DocumentSnapshot document)
+ {
+ var tagHelpers = await project.GetTagHelpersAsync().ConfigureAwait(false);
+ var imports = await GetImportsAsync(project, document);
+
+ if (_older != null && _older.IsResultAvailable)
+ {
+ var tagHelperDifference = new HashSet(TagHelperDescriptorComparer.Default);
+ tagHelperDifference.UnionWith(_older._tagHelpers);
+ tagHelperDifference.SymmetricExceptWith(tagHelpers);
+
+ var importDifference = new HashSet();
+ importDifference.UnionWith(_older._imports);
+ importDifference.SymmetricExceptWith(imports);
+
+ if (tagHelperDifference.Count == 0 && importDifference.Count == 0)
+ {
+ // We can use the cached result.
+ var result = _older._task.Result;
+
+ // Drop reference so it can be GC'ed
+ _older = null;
+
+ // Cache the tag helpers and imports so the next version can use them
+ _tagHelpers = tagHelpers;
+ _imports = imports;
+
+ return result;
+ }
+ }
+
+ // Drop reference so it can be GC'ed
+ _older = null;
+
+ // Cache the tag helpers and imports so the next version can use them
+ _tagHelpers = tagHelpers;
+ _imports = imports;
+
+ var importSources = new List();
+ foreach (var item in imports)
+ {
+ var sourceDocument = await GetRazorSourceDocumentAsync(item.Import);
+ importSources.Add(sourceDocument);
+ }
+
+ var documentSource = await GetRazorSourceDocumentAsync(document);
+
+ var projectEngine = project.GetProjectEngine();
+
+ return projectEngine.ProcessDesignTime(documentSource, importSources, tagHelpers);
+ }
+
+ private async Task GetRazorSourceDocumentAsync(DocumentSnapshot document)
+ {
+ var sourceText = await document.GetTextAsync();
+
+ return sourceText.GetRazorSourceDocument(document.FilePath);
+ }
+
+ private async Task> GetImportsAsync(ProjectSnapshot project, DocumentSnapshot document)
+ {
+ var imports = new List();
+ foreach (var snapshot in document.GetImports())
+ {
+ var versionStamp = await snapshot.GetTextVersionAsync();
+ imports.Add(new ImportItem(snapshot.FilePath, versionStamp, snapshot));
+ }
+
+ return imports;
+ }
+
+ private struct ImportItem : IEquatable
+ {
+ public ImportItem(string filePath, VersionStamp versionStamp, DocumentSnapshot import)
+ {
+ FilePath = filePath;
+ VersionStamp = versionStamp;
+ Import = import;
+ }
+
+ public string FilePath { get; }
+
+ public VersionStamp VersionStamp { get; }
+
+ public DocumentSnapshot Import { get; }
+
+ public bool Equals(ImportItem other)
+ {
+ return
+ FilePathComparer.Instance.Equals(FilePath, other.FilePath) &&
+ VersionStamp == other.VersionStamp;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is ImportItem item ? Equals(item) : false;
+ }
+
+ public override int GetHashCode()
+ {
+ var hash = new HashCodeCombiner();
+ hash.Add(FilePath, FilePathComparer.Instance);
+ hash.Add(VersionStamp);
+ return hash;
+ }
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentImportsTracker.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentImportsTracker.cs
new file mode 100644
index 0000000000..96b927e42f
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentImportsTracker.cs
@@ -0,0 +1,165 @@
+// 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.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
+{
+ internal class DocumentImportsTracker
+ {
+ private readonly object _lock;
+
+ private IReadOnlyList _imports;
+
+ public DocumentImportsTracker()
+ {
+ _lock = new object();
+ }
+
+ public IReadOnlyList GetImports(ProjectSnapshot project, DocumentSnapshot document)
+ {
+ if (project == null)
+ {
+ throw new ArgumentNullException(nameof(project));
+ }
+
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ if (_imports == null)
+ {
+ lock (_lock)
+ {
+ if (_imports == null)
+ {
+ _imports = GetImportsCore(project, document);
+ }
+ }
+ }
+
+ return _imports;
+ }
+
+ private IReadOnlyList GetImportsCore(ProjectSnapshot project, DocumentSnapshot document)
+ {
+ var projectEngine = project.GetProjectEngine();
+ var importFeature = projectEngine.ProjectFeatures.OfType().FirstOrDefault();
+ var projectItem = projectEngine.FileSystem.GetItem(document.FilePath);
+ var importItems = importFeature?.GetImports(projectItem).Where(i => i.Exists);
+ if (importItems == null)
+ {
+ return Array.Empty();
+ }
+
+ var imports = new List();
+ foreach (var item in importItems)
+ {
+ if (item.PhysicalPath == null)
+ {
+ // This is a default import.
+ var defaultImport = new DefaultImportDocumentSnapshot(project, item);
+ imports.Add(defaultImport);
+ }
+ else
+ {
+ var import = project.GetDocument(item.PhysicalPath);
+ if (import == null)
+ {
+ // We are not tracking this document in this project. So do nothing.
+ continue;
+ }
+
+ imports.Add(import);
+ }
+ }
+
+ return imports;
+ }
+
+ private class DefaultImportDocumentSnapshot : DocumentSnapshot
+ {
+ private ProjectSnapshot _project;
+ private RazorProjectItem _importItem;
+ private SourceText _sourceText;
+ private VersionStamp _version;
+ private DocumentGeneratedOutputTracker _generatedOutput;
+
+ public DefaultImportDocumentSnapshot(ProjectSnapshot project, RazorProjectItem item)
+ {
+ _project = project;
+ _importItem = item;
+ _version = VersionStamp.Default;
+ _generatedOutput = new DocumentGeneratedOutputTracker(null);
+ }
+
+ public override string FilePath => null;
+
+ public override string TargetPath => null;
+
+ public override Task GetGeneratedOutputAsync()
+ {
+ return _generatedOutput.GetGeneratedOutputInitializationTask(_project, this);
+ }
+
+ public override IReadOnlyList GetImports()
+ {
+ return Array.Empty();
+ }
+
+ public async override Task GetTextAsync()
+ {
+ using (var stream = _importItem.Read())
+ using (var reader = new StreamReader(stream))
+ {
+ var content = await reader.ReadToEndAsync();
+ _sourceText = SourceText.From(content);
+ }
+
+ return _sourceText;
+ }
+
+ public override Task GetTextVersionAsync()
+ {
+ return Task.FromResult(_version);
+ }
+
+ public override bool TryGetText(out SourceText result)
+ {
+ if (_sourceText != null)
+ {
+ result = _sourceText;
+ return true;
+ }
+
+ result = null;
+ return false;
+ }
+
+ public override bool TryGetTextVersion(out VersionStamp result)
+ {
+ result = _version;
+ return true;
+ }
+
+ public override bool TryGetGeneratedOutput(out RazorCodeDocument result)
+ {
+ if (_generatedOutput.IsResultAvailable)
+ {
+ result = GetGeneratedOutputAsync().Result;
+ return true;
+ }
+
+ result = null;
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentSnapshot.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentSnapshot.cs
new file mode 100644
index 0000000000..0cc9da8f04
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentSnapshot.cs
@@ -0,0 +1,31 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
+{
+ internal abstract class DocumentSnapshot
+ {
+ public abstract string FilePath { get; }
+
+ public abstract string TargetPath { get; }
+
+ public abstract IReadOnlyList GetImports();
+
+ public abstract Task GetTextAsync();
+
+ public abstract Task GetTextVersionAsync();
+
+ public abstract Task GetGeneratedOutputAsync();
+
+ public abstract bool TryGetText(out SourceText result);
+
+ public abstract bool TryGetTextVersion(out VersionStamp result);
+
+ public abstract bool TryGetGeneratedOutput(out RazorCodeDocument result);
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentState.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentState.cs
new file mode 100644
index 0000000000..d1fb05b67a
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DocumentState.cs
@@ -0,0 +1,219 @@
+// 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.Threading.Tasks;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
+{
+ internal class DocumentState
+ {
+ private static readonly TextAndVersion EmptyText = TextAndVersion.Create(
+ SourceText.From(string.Empty),
+ VersionStamp.Default);
+
+ public static readonly Func> EmptyLoader = () => Task.FromResult(EmptyText);
+
+ private readonly object _lock;
+
+ private Func> _loader;
+ private Task _loaderTask;
+ private SourceText _sourceText;
+ private VersionStamp? _version;
+
+ private DocumentGeneratedOutputTracker _generatedOutput;
+ private DocumentImportsTracker _imports;
+
+ public static DocumentState Create(
+ HostWorkspaceServices services,
+ HostDocument hostDocument,
+ Func> loader)
+ {
+ if (services == null)
+ {
+ throw new ArgumentNullException(nameof(services));
+ }
+
+ if (hostDocument == null)
+ {
+ throw new ArgumentNullException(nameof(hostDocument));
+ }
+
+ loader = loader ?? EmptyLoader;
+ return new DocumentState(services, hostDocument, null, null, loader);
+ }
+
+ // Internal for testing
+ internal DocumentState(
+ HostWorkspaceServices services,
+ HostDocument hostDocument,
+ SourceText text,
+ VersionStamp? version,
+ Func> loader)
+ {
+ Services = services;
+ HostDocument = hostDocument;
+ _sourceText = text;
+ _version = version;
+ _loader = loader;
+ _lock = new object();
+ }
+
+ public HostDocument HostDocument { get; }
+
+ public HostWorkspaceServices Services { get; }
+
+ public DocumentGeneratedOutputTracker GeneratedOutput
+ {
+ get
+ {
+ if (_generatedOutput == null)
+ {
+ lock (_lock)
+ {
+ if (_generatedOutput == null)
+ {
+ _generatedOutput = new DocumentGeneratedOutputTracker(null);
+ }
+ }
+ }
+
+ return _generatedOutput;
+ }
+ }
+
+ public DocumentImportsTracker Imports
+ {
+ get
+ {
+ if (_imports == null)
+ {
+ lock (_lock)
+ {
+ if (_imports == null)
+ {
+ _imports = new DocumentImportsTracker();
+ }
+ }
+ }
+
+ return _imports;
+ }
+ }
+
+ public async Task GetTextAsync()
+ {
+ if (TryGetText(out var text))
+ {
+ return text;
+ }
+
+ lock (_lock)
+ {
+ _loaderTask = _loader();
+ }
+
+ return (await _loaderTask.ConfigureAwait(false)).Text;
+ }
+
+ public async Task GetTextVersionAsync()
+ {
+ if (TryGetTextVersion(out var version))
+ {
+ return version;
+ }
+
+ lock (_lock)
+ {
+ _loaderTask = _loader();
+ }
+
+ return (await _loaderTask.ConfigureAwait(false)).Version;
+ }
+
+ public bool TryGetText(out SourceText result)
+ {
+ if (_sourceText != null)
+ {
+ result = _sourceText;
+ return true;
+ }
+
+ if (_loaderTask != null && _loaderTask.IsCompleted)
+ {
+ result = _loaderTask.Result.Text;
+ return true;
+ }
+
+ result = null;
+ return false;
+ }
+
+ public bool TryGetTextVersion(out VersionStamp result)
+ {
+ if (_version != null)
+ {
+ result = _version.Value;
+ return true;
+ }
+
+ if (_loaderTask != null && _loaderTask.IsCompleted)
+ {
+ result = _loaderTask.Result.Version;
+ return true;
+ }
+
+ result = default;
+ return false;
+ }
+
+ public virtual DocumentState WithConfigurationChange()
+ {
+ var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);
+
+ // The source could not have possibly changed.
+ state._sourceText = _sourceText;
+ state._version = _version;
+ state._loaderTask = _loaderTask;
+
+ return state;
+ }
+
+ public virtual DocumentState WithWorkspaceProjectChange()
+ {
+ var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);
+
+ // The source could not have possibly changed.
+ state._sourceText = _sourceText;
+ state._version = _version;
+ state._loaderTask = _loaderTask;
+
+ // Opportunistically cache the generated code
+ state._generatedOutput = _generatedOutput?.Fork();
+
+ return state;
+ }
+
+ public virtual DocumentState WithText(SourceText sourceText, VersionStamp version)
+ {
+ if (sourceText == null)
+ {
+ throw new ArgumentNullException(nameof(sourceText));
+ }
+
+ return new DocumentState(Services, HostDocument, sourceText, version, null);
+ }
+
+ public virtual DocumentState WithTextLoader(Func> loader)
+ {
+ if (loader == null)
+ {
+ throw new ArgumentNullException(nameof(loader));
+ }
+
+ return new DocumentState(Services, HostDocument, null, null, loader);
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/EphemeralProjectSnapshot.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/EphemeralProjectSnapshot.cs
new file mode 100644
index 0000000000..cd87c074de
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/EphemeralProjectSnapshot.cs
@@ -0,0 +1,81 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Host;
+
+namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
+{
+ internal class EphemeralProjectSnapshot : ProjectSnapshot
+ {
+ private static readonly Task> EmptyTagHelpers = Task.FromResult>(Array.Empty());
+
+ private readonly HostWorkspaceServices _services;
+ private readonly Lazy _projectEngine;
+
+ public EphemeralProjectSnapshot(HostWorkspaceServices services, string filePath)
+ {
+ if (services == null)
+ {
+ throw new ArgumentNullException(nameof(services));
+ }
+
+ if (filePath == null)
+ {
+ throw new ArgumentNullException(nameof(filePath));
+ }
+
+ _services = services;
+ FilePath = filePath;
+
+ _projectEngine = new Lazy(CreateProjectEngine);
+ }
+
+ public override RazorConfiguration Configuration => FallbackRazorConfiguration.MVC_2_1;
+
+ public override IEnumerable DocumentFilePaths => Array.Empty();
+
+ public override string FilePath { get; }
+
+ public override bool IsInitialized => false;
+
+ public override VersionStamp Version { get; } = VersionStamp.Default;
+
+ public override Project WorkspaceProject => null;
+
+ public override DocumentSnapshot GetDocument(string filePath)
+ {
+ if (filePath == null)
+ {
+ throw new ArgumentNullException(nameof(filePath));
+ }
+
+ return null;
+ }
+
+ public override RazorProjectEngine GetProjectEngine()
+ {
+ return _projectEngine.Value;
+ }
+
+ public override Task> GetTagHelpersAsync()
+ {
+ return EmptyTagHelpers;
+ }
+
+ public override bool TryGetTagHelpers(out IReadOnlyList result)
+ {
+ result = EmptyTagHelpers.Result;
+ return true;
+ }
+
+ private RazorProjectEngine CreateProjectEngine()
+ {
+ var factory = _services.GetRequiredService();
+ return factory.Create(this);
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/HostDocument.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/HostDocument.cs
new file mode 100644
index 0000000000..859cc0df32
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/HostDocument.cs
@@ -0,0 +1,30 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
+{
+ internal class HostDocument
+ {
+ public HostDocument(string filePath, string targetPath)
+ {
+ if (filePath == null)
+ {
+ throw new ArgumentNullException(nameof(filePath));
+ }
+
+ if (targetPath == null)
+ {
+ throw new ArgumentNullException(nameof(targetPath));
+ }
+
+ FilePath = filePath;
+ TargetPath = targetPath;
+ }
+
+ public string FilePath { get; }
+
+ public string TargetPath { get; }
+ }
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/LiveShareProjectSnapshotBase.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/LiveShareProjectSnapshotBase.cs
new file mode 100644
index 0000000000..f03a31b0ae
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/LiveShareProjectSnapshotBase.cs
@@ -0,0 +1,36 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Razor.ProjectSystem;
+
+namespace Microsoft.CodeAnalysis.Razor.Workspaces.ProjectSystem
+{
+ // This is a marker class to allow us to know when potential breaking changes might impact live share.
+ // This is intentionally not abstract so any API changes that happen to ProjectSnapshot will break this.
+ internal class LiveShareProjectSnapshotBase : ProjectSnapshot
+ {
+ public override RazorConfiguration Configuration => throw new NotImplementedException();
+
+ public override IEnumerable