Merge remote-tracking branch 'Razor/rybrande/release22ToSrc' into rybrande/Mondo2.2

This commit is contained in:
Ryan Brandenburg 2018-11-27 12:26:29 -08:00
commit e46623e256
3127 changed files with 57550 additions and 22453 deletions

View File

@ -14,9 +14,8 @@
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)build\Key.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AssemblySigningCertName>Microsoft</AssemblySigningCertName>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -1,7 +1,10 @@
<Project>
<Project>
<PropertyGroup>
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">$(MicrosoftNETCoreApp20PackageVersion)</RuntimeFrameworkVersion>
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">$(MicrosoftNETCoreApp21PackageVersion)</RuntimeFrameworkVersion>
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.2' ">$(MicrosoftNETCoreApp22PackageVersion)</RuntimeFrameworkVersion>
<NETStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard2.0' ">$(NETStandardLibrary20PackageVersion)</NETStandardImplicitPackageVersion>
<!-- aspnet/BuildTools#662 Don't police what version of NetCoreApp we use -->
<NETCoreAppMaximumVersion>99.9</NETCoreAppMaximumVersion>
</PropertyGroup>
</Project>

View File

@ -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": {},

View File

@ -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}

View File

@ -17,6 +17,18 @@
<Compile Include="..\..\src\Microsoft.VisualStudio.LanguageServices.Razor\Serialization\*.cs">
<Link>Serialization\%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestServices.cs">
<Link>TestServices\%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestWorkspace.cs">
<Link>TestServices\%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestLanguageServices.cs">
<Link>TestServices\%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestWorkspaceServices.cs">
<Link>TestServices\%(FileName)%(Extension)</Link>
</Compile>
</ItemGroup>
<ItemGroup>

View File

@ -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<Task> Tasks { get; } = new List<Task>();
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());
}
}
}

View File

@ -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]);
}
}
}
}

View File

@ -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<ProjectSnapshotChangeTrigger>(),
new AdhocWorkspace(services));
}
private static IReadOnlyList<TagHelperDescriptor> 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<IReadOnlyList<TagHelperDescriptor>>(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<TagHelperDescriptor> _tagHelpers;
public StaticTagHelperResolver(IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
this._tagHelpers = tagHelpers;
}
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
{
return Task.FromResult(new TagHelperResolutionResult(_tagHelpers, Array.Empty<RazorDiagnostic>()));
}
}
private class StaticProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
{
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> 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();
}
}
}
}

View File

@ -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<IRazorDirectiveFeature>().FirstOrDefault();
Directives = directiveFeature?.Directives.ToArray() ?? Array.Empty<DirectiveDescriptor>();
}
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));
}
}
}
}

View File

@ -30,7 +30,9 @@
<Category>$(MPackArtifactCategory)</Category>
</ArtifactInfo>
<FilesToExcludeFromSigning Include="$(MPackOutputPath)" />
<!-- Certificate=None means don't sign the .mpack file itself, but sign its contents. -->
<FilesToSign Include="$(MPackOutputPath)" Certificate="None" />
<FilesToSign Include="Microsoft.VisualStudio.Mac.RazorAddin.dll" Certificate="$(AssemblySigningCertName)" Container="$(MPackOutputPath)" />
</ItemGroup>
</Target>

View File

@ -42,7 +42,8 @@
<PackageId>$(VSIXName)</PackageId>
</ArtifactInfo>
<FilesToSign Include="$(VSIXOutputPath)" Certificate="Vsix" />
<FilesToSign Include="$(VSIXOutputPath)" Certificate="$(VsixSigningCertName)" />
<FilesToSign Include="Microsoft.VisualStudio.RazorExtension.dll" Certificate="$(AssemblySigningCertName)" Container="$(VSIXOutputPath)" />
<FilesToExcludeFromSigning Include="$(VSIXManifestOutputPath)" />
<FilesToExcludeFromSigning Include="$(VSIXSymbolsOutputPath)" />
@ -63,7 +64,6 @@
<MSBuildArguments Include="
$(VSIXProject);
/t:Restore;
/m;
/v:m;
/p:Configuration=$(Configuration);
/p:BuildNumber=$(BuildNumber);" />
@ -96,7 +96,6 @@
<MSBuildArguments Remove="@(MSBuildArguments)" />
<MSBuildArguments Include="
$(VSIXProject);
/m;
/v:M;
/fl;
/flp:LogFile=$(VSIXLogFilePath);
@ -104,6 +103,8 @@
/p:TargetVSIXContainer=$(VSIXOutputPath);
/p:SymbolsPublishDir=$(BuildDir);
/p:Configuration=$(Configuration);
/p:FeatureBranchVersionSuffix=$(FeatureBranchVersionSuffix);
/p:BuildNumber=$(BuildNumber);
/p:LangVersion=7.1" />
</ItemGroup>

View File

@ -2,19 +2,26 @@
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>
<!-- These package versions may be overridden or updated by automation. -->
<PropertyGroup Label="Package Versions: Auto">
<PropertyGroup Label="Package Versions">
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.1.3-rtm-15802</InternalAspNetCoreSdkPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview2-20181108.4</InternalAspNetCoreSdkPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-rtm-181106-13</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.2.0-rtm-35661</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-rtm-181106-13</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftBuildFrameworkPackageVersion>15.6.82</MicrosoftBuildFrameworkPackageVersion>
<MicrosoftBuildPackageVersion>15.6.82</MicrosoftBuildPackageVersion>
<MicrosoftBuildUtilitiesCorePackageVersion>15.6.82</MicrosoftBuildUtilitiesCorePackageVersion>
<MicrosoftCodeAnalysisCommonPackageVersion>2.8.0</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>2.8.0</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.2.0-rtm-181106-13</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.2.0-rtm-181106-13</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0</MicrosoftExtensionsDependencyModelPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.2</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.2.0-rtm-181106-13</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>2.2.0-rtm-181106-13</MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>2.2.0-rtm-35661</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.3</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETCoreApp22PackageVersion>2.2.0-rtm-27105-02</MicrosoftNETCoreApp22PackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.0.26606</MicrosoftVisualStudioComponentModelHostPackageVersion>
<MicrosoftVisualStudioEditorPackageVersion>15.6.161-preview</MicrosoftVisualStudioEditorPackageVersion>
@ -33,7 +40,7 @@
<MicrosoftVisualStudioTextUIPackageVersion>15.6.161-preview</MicrosoftVisualStudioTextUIPackageVersion>
<MonoAddinsPackageVersion>1.3.8</MonoAddinsPackageVersion>
<MonoDevelopSdkPackageVersion>1.0.1</MonoDevelopSdkPackageVersion>
<MoqPackageVersion>4.7.49</MoqPackageVersion>
<MoqPackageVersion>4.10.0</MoqPackageVersion>
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
<StreamJsonRpcPackageVersion>1.1.92</StreamJsonRpcPackageVersion>
@ -41,32 +48,21 @@
<SystemRuntimeInteropServicesRuntimeInformationPackageVersion>4.3.0</SystemRuntimeInteropServicesRuntimeInformationPackageVersion>
<SystemValueTuplePackageVersion>4.5.0</SystemValueTuplePackageVersion>
<VisualStudio_NewtonsoftJsonPackageVersion>9.0.1</VisualStudio_NewtonsoftJsonPackageVersion>
<VSIX_MicrosoftCodeAnalysisCommonPackageVersion>2.8.0</VSIX_MicrosoftCodeAnalysisCommonPackageVersion>
<VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>2.8.0</VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<VSIX_MicrosoftCodeAnalysisCSharpPackageVersion>2.8.0</VSIX_MicrosoftCodeAnalysisCSharpPackageVersion>
<VSIX_MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>2.8.0</VSIX_MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>2.8.0</VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
<VSIX_MicrosoftCodeAnalysisRemoteRazorServiceHubPackageVersion>2.8.0-beta2-62721-09</VSIX_MicrosoftCodeAnalysisRemoteRazorServiceHubPackageVersion>
<VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>2.8.0</VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
<VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>2.8.0</VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
<VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion>2.8.0</VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion>
<VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion>2.8.0-beta2-62721-09</VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion>
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
<VSIX_MicrosoftCodeAnalysisCommonPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCommonPackageVersion>
<VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<VSIX_MicrosoftCodeAnalysisCSharpPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCSharpPackageVersion>
<VSIX_MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
<VSIX_MicrosoftCodeAnalysisRemoteRazorServiceHubPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisRemoteRazorServiceHubPackageVersion>
<VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
<VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
<VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion>
<VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion>
<XunitAnalyzersPackageVersion>0.10.0</XunitAnalyzersPackageVersion>
<XunitPackageVersion>2.3.1</XunitPackageVersion>
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
<XunitRunnerVisualStudioPackageVersion>2.4.0</XunitRunnerVisualStudioPackageVersion>
</PropertyGroup>
<PropertyGroup Label="Package Versions: Pinned" />
<!-- This may import a generated file which may override the variables above. -->
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
<!-- These are package versions that should not be overridden or updated by automation. -->
<PropertyGroup Label="Package Versions: Pinned">
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.1</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.1</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.1</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.1</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
</PropertyGroup>
</Project>
</Project>

View File

@ -21,12 +21,13 @@
<PropertyGroup>
<!-- These properties are use by the automation that updates dependencies.props -->
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
<LineupPackageVersion>2.1.0-rc1-*</LineupPackageVersion>
<LineupPackageVersion>2.2.0-*</LineupPackageVersion>
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json</LineupPackageRestoreSource>
</PropertyGroup>
<ItemGroup>
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp20PackageVersion)" />
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp21PackageVersion)" />
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp22PackageVersion)" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,7 @@
<Project>
<Import Project="VSIX.targets" />
<Import Project="MPack.targets" />
<ItemGroup>
<Solutions Update="$(RepositoryRoot)Razor.sln">
<!-- the 'DebugNoVSIX' and 'ReleaseNoVSIX' configurations exclude the VSIX project, which doesn't build with Microsoft.NET.Sdk yet. -->
@ -21,7 +22,11 @@
Outputs="$(MSBuildLocationFileOutput)">
<PropertyGroup>
<TemplateProperties>MSBuildLocation=$(VisualStudioMSBuildx86Path)</TemplateProperties>
<TemplateProperties>
MSBuildLocation=$(VisualStudioMSBuildx86Path);
MicrosoftNETCoreAppPackageVersion=$(MicrosoftNETCoreApp22PackageVersion);
NETStandardLibraryPackageVersion=$(NETStandardLibrary20PackageVersion)
</TemplateProperties>
</PropertyGroup>
<GenerateFileFromTemplate

View File

@ -47,5 +47,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
extension.WriteInjectProperty(context, this);
}
public override void FormatNode(IntermediateNodeFormatter formatter)
{
formatter.WriteContent(MemberName);
formatter.WriteProperty(nameof(MemberName), MemberName);
formatter.WriteProperty(nameof(TypeName), TypeName);
}
}
}

View File

@ -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.Version1_X
@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<TModel>";
@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();
}
}
}

View File

@ -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<INamedTypeSymbol>();
var visitor = ViewComponentTypeVisitor.Create(compilation, types);
var visitor = new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, types);
// We always visit the global namespace.
visitor.Visit(compilation.Assembly.GlobalNamespace);

View File

@ -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);
}
}
}

View File

@ -16,13 +16,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
private readonly INamedTypeSymbol _nonViewComponentAttribute;
private readonly List<INamedTypeSymbol> _results;
public static ViewComponentTypeVisitor Create(Compilation compilation, List<INamedTypeSymbol> 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 ||

View File

@ -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);
}
}
}

View File

@ -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()

View File

@ -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<TModel>";
@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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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<IMethodSymbol>()
.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<IMethodSymbol> GetInvokeMethods(INamedTypeSymbol type)
{
var methods = new List<IMethodSymbol>();
while (type != null)
{
var currentTypeMethods = type.GetMembers()
.OfType<IMethodSymbol>()
.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<IParameterSymbol> 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;
});
}
}

View File

@ -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<INamedTypeSymbol>();
var visitor = ViewComponentTypeVisitor.Create(compilation, types);
var visitor = new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, types);
// We always visit the global namespace.
visitor.Visit(compilation.Assembly.GlobalNamespace);

View File

@ -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);
}
}
}

View File

@ -13,13 +13,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
private readonly INamedTypeSymbol _nonViewComponentAttribute;
private readonly List<INamedTypeSymbol> _results;
public static ViewComponentTypeVisitor Create(Compilation compilation, List<INamedTypeSymbol> 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,

View File

@ -1,103 +1,56 @@
<Project>
<!-- Using explicit SDK imports here because the default way conflicts with the AfterBuild target -->
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains MSBuild support for Razor.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<!-- This project doesn't have any code, so don't include it in the .nupkg -->
<IncludeBuildOutput>false</IncludeBuildOutput>
<EnableDefaultItems>false</EnableDefaultItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
</PropertyGroup>
<!--
Building this package is somewhat complicated because we need to Build or Publish some other projects
that have different TFM's including one with multiple TFMs.
<!--
Building this package is somewhat complicated because we need to Publish some other projects
that have different TFM's including one with multiple TFMs.
We then need to include the output of those projects in our output directory (where it will be used
by tests) and in the nukpg.
-->
<!-- This is the tasks project that needs to be included in the package. -->
<ItemGroup>
<TaskProject Include="..\Microsoft.AspNetCore.Razor.Tasks\Microsoft.AspNetCore.Razor.Tasks.csproj" />
</ItemGroup>
<!-- These are tools that need to be included in the package. -->
<ItemGroup>
<ToolProject Include="..\Microsoft.AspNetCore.Razor.Tools\Microsoft.AspNetCore.Razor.Tools.csproj" />
</ItemGroup>
<!-- Using explicit SDK imports here because the default way conflicts with the AfterBuild target -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<GenerateNuspecDependsOn>_BuildDependencyProjects;$(GenerateNuspecDependsOn)</GenerateNuspecDependsOn>
<BuildDependsOn>_BuildDependencyProjects;$(BuildDependsOn)</BuildDependsOn>
<ToolProject>..\Microsoft.AspNetCore.Razor.Tools\Microsoft.AspNetCore.Razor.Tools.csproj</ToolProject>
</PropertyGroup>
<Target Name="_BuildDependencyProjects">
<!--
The Microsoft.AspNetCore.Razor.Tasks package needs to support both net46 and netstandard2.0 for desktop vs
coreclr MSBuild - so we have to build it twice.
<ItemGroup>
<ProjectReference Include="$(ToolProject)" ReferenceOutputAssembly="false" Condition="'$(TargetFramework)' == 'netcoreapp2.0'" />
</ItemGroup>
We're careful here to avoid setting properties when building the other projects. This can create problems
with concurrency.
<ItemGroup Condition="'$(TargetFramework)'==''">
<!-- Binaries produced in this project -->
<SignedPackageFile Include="$(MSBuildProjectDirectory)\$(OutputPath)\tools/rzc.dll" Certificate="$(AssemblySigningCertName)" />
First, build the project, then copy it to the ouput directory, then add it as packable content.
-->
<MSBuild Projects="@(TaskProject)" />
<!-- Third-party assemblies -->
<SignedPackageFile Include="$(MSBuildProjectDirectory)\$(OutputPath)\tools/Newtonsoft.Json.dll" Certificate="$(AssemblySigning3rdPartyCertName)" />
<MSBuild Projects="@(TaskProject)" Properties="TargetFramework=net46" Targets="BuiltProjectOutputGroup">
<Output TaskParameter="TargetOutputs" ItemName="TaskAssemblyNet46" />
</MSBuild>
<!-- Binaries that should be signed by corefx/roslyn -->
<ExcludePackageFileFromSigning Include="$(MSBuildProjectDirectory)\$(OutputPath)\tools/Microsoft.CodeAnalysis.CSharp.dll" />
<ExcludePackageFileFromSigning Include="$(MSBuildProjectDirectory)\$(OutputPath)\tools/Microsoft.CodeAnalysis.dll" />
<ExcludePackageFileFromSigning Include="$(MSBuildProjectDirectory)\$(OutputPath)\tools/runtimes/unix/lib/netstandard1.3/System.Text.Encoding.CodePages.dll" />
<ExcludePackageFileFromSigning Include="$(MSBuildProjectDirectory)\$(OutputPath)\tools/runtimes/win/lib/netstandard1.3/System.Text.Encoding.CodePages.dll" />
</ItemGroup>
<MSBuild Projects="@(TaskProject)" Properties="TargetFramework=net46" Targets="DebugSymbolsProjectOutputGroup">
<Output TaskParameter="TargetOutputs" ItemName="TaskSymbolNet46" />
</MSBuild>
<MSBuild Projects="@(TaskProject)" Properties="TargetFramework=netstandard2.0" Targets="BuiltProjectOutputGroup">
<Output TaskParameter="TargetOutputs" ItemName="TaskAssemblyNetStandard" />
</MSBuild>
<MSBuild Projects="@(TaskProject)" Properties="TargetFramework=netstandard2.0" Targets="DebugSymbolsProjectOutputGroup">
<Output TaskParameter="TargetOutputs" ItemName="TaskSymbolNetStandard" />
</MSBuild>
<Copy SourceFiles="@(TaskAssemblyNet46)" DestinationFolder="$(OutputPath)\tasks\net46\">
<Output TaskParameter="CopiedFiles" ItemName="FileWrites" />
</Copy>
<Copy SourceFiles="@(TaskAssemblyNetStandard)" DestinationFolder="$(OutputPath)\tasks\netstandard2.0\">
<Output TaskParameter="CopiedFiles" ItemName="FileWrites" />
</Copy>
<Error Text="TaskAssemblyNet46 is empty. This is a bug" Condition="'@(TaskAssemblyNet46)'==''" />
<Error Text="TaskAssemblyNetStandard is empty. This is a bug" Condition="'@(TaskAssemblyNetStandard)'==''" />
<!--
Next we need to build the netcoreapp2.0 tools. In this case we need to do a publish, because we need
all of the output to put in the package.
-->
<RemoveDir Directories="tools\" />
<MSBuild Projects="@(ToolProject)" />
<MSBuild Projects="@(ToolProject)" Properties="PublishDir=$(MSBuildProjectDirectory)\$(OutputPath)tools\" Targets="Publish" />
<ItemGroup>
<_RazorTool Include="$(OutputPath)tools\**\*" />
</ItemGroup>
<Error Text="_RazorTool is empty. This is a bug" Condition="'@(_RazorTool)'==''" />
</Target>
<Target Name="PopulateNuspec" BeforeTargets="GenerateNuspec" DependsOnTargets="BuiltProjectOutputGroup;DebugSymbolsProjectOutputGroup;_BuildDependencyProjects">
<Target Name="PopulateNuspec" BeforeTargets="GenerateNuspec">
<PropertyGroup>
<!-- Make sure we create a symbols.nupkg. -->
<IncludeSymbols>true</IncludeSymbols>
<!-- RepositoryCommit is only available when "build" runs, but not during dotnet pack -->
<RepositoryCommit Condition="'$(RepositoryCommit)' == ''">unknown</RepositoryCommit>
<NuspecProperties>
id=$(PackageId);
version=$(PackageVersion);
@ -111,15 +64,21 @@
repositoryCommit=$(RepositoryCommit);
copyright=$(Copyright);
<!-- Include the assembly and symbols from the tasks project -->
TaskAssemblyNet46=@(TaskAssemblyNet46);
TaskSymbolNet46=@(TaskSymbolNet46);
TaskAssemblyNetStandard=@(TaskAssemblyNetStandard);
TaskSymbolNetStandard=@(TaskSymbolNetStandard);
<!-- Include the assembly and symbols from the tools project -->
ToolAssembly=$(OutputPath)tools\**\*;
ToolFiles=$(OutputPath)tools\**\*;
</NuspecProperties>
</PropertyGroup>
</Target>
<Target Name="LayoutDependencies" AfterTargets="Build" BeforeTargets="PopulateNuspec">
<RemoveDir Directories="$(OutputPath)tools\" />
<MSBuild Projects="$(ToolProject)" Properties="PublishDir=$(MSBuildProjectDirectory)\$(OutputPath)tools\;TargetFramework=netcoreapp2.0" Targets="Publish" />
<ItemGroup>
<_RazorTool Include="$(OutputPath)tools\**\*" />
</ItemGroup>
<Error Text="_RazorTool is empty. This is a bug" Condition="'@(_RazorTool)'==''" />
</Target>
</Project>

View File

@ -26,6 +26,6 @@
<file src="$TaskSymbolNet46$" target="tasks\net46\" />
<file src="$TaskAssemblyNetStandard$" target="tasks\netstandard2.0\" />
<file src="$TaskSymbolNetStandard$" target="tasks\netstandard2.0\" />
<file src="$ToolAssembly$" target="tools\" exclude="**\*.xml" />
<file src="$ToolFiles$" target="tools\" exclude="**\*.xml" />
</files>
</package>
</package>

View File

@ -4,6 +4,16 @@
This target is explicitly imported by Razor SDK.
-->
<UsingTask
TaskName="Microsoft.AspNetCore.Razor.Tasks.RazorGenerate"
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
Condition="'$(RazorSdkBuildTasksAssembly)' != ''" />
<UsingTask
TaskName="Microsoft.AspNetCore.Razor.Tasks.RazorTagHelper"
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
Condition="'$(RazorSdkBuildTasksAssembly)' != ''" />
<!--
Consider these properties to be private to this targets file. The main Razor SDK should define all of the properties
that we use to pass data back and forth.
@ -48,7 +58,7 @@
<Target
Name="ResolveTagHelperRazorGenerateInputs"
DependsOnTargets="Compile"
DependsOnTargets="_EnsureRazorTasksAssemblyDefined;Compile"
Inputs="$(MSBuildAllProjects);@(RazorReferencePath)"
Outputs="$(_RazorTagHelperInputCache)"
Condition="'@(RazorGenerateWithTargetPath)' != ''">
@ -72,7 +82,6 @@
ToolAssembly="$(_RazorToolAssembly)"
UseServer="$(UseRazorBuildServer)"
ForceServer="$(_RazorForceBuildServer)"
SuppressCurrentUserOnlyPipeOptions="$(_RazorSuppressCurrentUserOnlyPipeOptions)"
PipeName="$(_RazorBuildServerPipeName)"
Version="$(RazorLangVersion)"
Configuration="@(ResolvedRazorConfiguration)"
@ -98,6 +107,7 @@
<PropertyGroup>
<RazorCoreGenerateDependsOn>
_EnsureRazorTasksAssemblyDefined;
_HashRazorGenerateInputs;
_ResolveRazorGenerateOutputs;
</RazorCoreGenerateDependsOn>
@ -124,7 +134,6 @@
ToolAssembly="$(_RazorToolAssembly)"
UseServer="$(UseRazorBuildServer)"
ForceServer="$(_RazorForceBuildServer)"
SuppressCurrentUserOnlyPipeOptions="$(_RazorSuppressCurrentUserOnlyPipeOptions)"
PipeName="$(_RazorBuildServerPipeName)"
Version="$(RazorLangVersion)"
Configuration="@(ResolvedRazorConfiguration)"
@ -148,4 +157,10 @@
</ItemGroup>
</Target>
<Target Name="_EnsureRazorTasksAssemblyDefined">
<Error
Text="Assembly location for Razor SDK Tasks was not specified. The most likely cause is an older incompatible version of Microsoft.NET.Sdk.Razor, or Microsoft.NET.Sdk.Web used by this project. Please target a newer version of the .NET Core SDK."
Condition="'$(RazorSdkBuildTasksAssembly)' == ''" />
</Target>
</Project>

View File

@ -30,11 +30,6 @@
<!-- Override this to hijack the tasks and targets. Used by tests. -->
<_RazorMSBuildRoot Condition="'$(_RazorMSBuildRoot)'==''">$(MSBuildThisFileDirectory)..\..\</_RazorMSBuildRoot>
<_RazorTaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' ">netstandard2.0</_RazorTaskFolder>
<_RazorTaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' ">net46</_RazorTaskFolder>
<_RazorTaskAssembly Condition="'$(_RazorTaskAssembly)'==''">$(_RazorMSBuildRoot)\tasks\$(_RazorTaskFolder)\Microsoft.AspNetCore.Razor.Tasks.dll</_RazorTaskAssembly>
<!-- Used to locate our tools -->
<_RazorToolAssembly Condition="'$(_RazorToolAssembly)'==''">$(_RazorMSBuildRoot)tools\rzc.dll</_RazorToolAssembly>
</PropertyGroup>
@ -47,6 +42,4 @@
<ProjectCapability Include="DotNetCoreRazorConfiguration"/>
</ItemGroup>
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.RazorGenerate" AssemblyFile="$(_RazorTaskAssembly)" />
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.RazorTagHelper" AssemblyFile="$(_RazorTaskAssembly)" />
</Project>

View File

@ -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);
}
/// <inheritdoc />
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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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++)
{

View File

@ -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();
}
}

View File

@ -34,13 +34,13 @@ namespace Microsoft.AspNetCore.Razor.Language
document.Options = codeDocument.GetCodeGenerationOptions() ?? _optionsFeature.GetOptions();
var namespaces = new Dictionary<string, SourceSpan?>(StringComparer.Ordinal);
IReadOnlyList<UsingReference> importedUsings = Array.Empty<UsingReference>();
// 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<UsingReference>(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<UsingReference>
{
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<string, SourceSpan?> _namespaces;
protected readonly List<UsingReference> _usings;
protected readonly RazorParserFeatureFlags _featureFlags;
public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces, RazorParserFeatureFlags featureFlags)
public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, RazorParserFeatureFlags featureFlags)
{
_document = document;
_builder = builder;
_namespaces = namespaces;
_usings = new List<UsingReference>();
_featureFlags = featureFlags;
}
public IReadOnlyList<UsingReference> 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<string, SourceSpan?> 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<string, SourceSpan?> namespaces, RazorParserFeatureFlags featureFlags)
: base(document, new ImportBuilder(builder), namespaces, featureFlags)
public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, RazorParserFeatureFlags featureFlags)
: base(document, new ImportBuilder(builder), featureFlags)
{
}

View File

@ -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<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
if (sourceDocument == null)
{
throw new ArgumentNullException(nameof(sourceDocument));
}
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(ConfigureParserOptions);
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().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<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
if (sourceDocument == null)
{
throw new ArgumentNullException(nameof(sourceDocument));
}
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(ConfigureDesignTimeParserOptions);
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().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)

View File

@ -15,18 +15,23 @@ namespace Microsoft.AspNetCore.Razor.Language
var syntaxTree = codeDocument.GetSyntaxTree();
ThrowForMissingDocumentDependency(syntaxTree);
var feature = Engine.Features.OfType<ITagHelperFeature>().FirstOrDefault();
if (feature == null)
var descriptors = codeDocument.GetTagHelpers();
if (descriptors == null)
{
// No feature, nothing to do.
return;
var feature = Engine.Features.OfType<ITagHelperFeature>().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)

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{
internal class DirectiveTokenEditHandler : SpanEditHandler
{
public DirectiveTokenEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer) : base(tokenizer)
public DirectiveTokenEditHandler(Func<string, IEnumerable<IToken>> tokenizer) : base(tokenizer)
{
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}
}

View File

@ -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);
}
}
}

View File

@ -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}";

View File

@ -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;

View File

@ -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(" ");

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -18,5 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitCSharpCode(this);
}
public override void FormatNode(IntermediateNodeFormatter formatter)
{
formatter.WriteChildren(Children);
}
}
}

View File

@ -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);
}
}
}

View File

@ -18,5 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitCSharpExpression(this);
}
public override void FormatNode(IntermediateNodeFormatter formatter)
{
formatter.WriteChildren(Children);
}
}
}

View File

@ -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)));
}
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -18,5 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitHtml(this);
}
public override void FormatNode(IntermediateNodeFormatter formatter)
{
formatter.WriteChildren(Children);
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -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);
}
}

View File

@ -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<string, string> _properties = new Dictionary<string, string>(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;
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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 <parameter.Modifiers.Count; i++)
{
builder.Append(parameter.Modifiers[i]);
builder.Append(" ");
}
builder.Append(parameter.TypeName);
builder.Append(" ");
builder.Append(parameter.ParameterName);
return builder.ToString();
}
}
}

View File

@ -20,5 +20,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitNamespaceDeclaration(this);
}
public override void FormatNode(IntermediateNodeFormatter formatter)
{
formatter.WriteContent(Content);
formatter.WriteProperty(nameof(Content), Content);
}
}
}

View File

@ -22,5 +22,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitTagHelperHtmlAttribute(this);
}
public override void FormatNode(IntermediateNodeFormatter formatter)
{
formatter.WriteContent(AttributeName);
formatter.WriteProperty(nameof(AttributeName), AttributeName);
formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
}
}
}

View File

@ -44,5 +44,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
visitor.VisitTagHelper(this);
}
public override void FormatNode(IntermediateNodeFormatter formatter)
{
formatter.WriteContent(TagName);
formatter.WriteProperty(nameof(TagHelpers), string.Join(", ", TagHelpers.Select(t => t.DisplayName)));
formatter.WriteProperty(nameof(TagMode), TagMode.ToString());
formatter.WriteProperty(nameof(TagName), TagName);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}

View File

@ -11,18 +11,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
private static readonly int TypeHashCode = typeof(AutoCompleteEditHandler).GetHashCode();
public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer)
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer)
: base(tokenizer)
{
}
public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, bool autoCompleteAtEndOfSpan)
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer, bool autoCompleteAtEndOfSpan)
: this(tokenizer)
{
AutoCompleteAtEndOfSpan = autoCompleteAtEndOfSpan;
}
public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, AcceptedCharactersInternal accepted)
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer, AcceptedCharactersInternal accepted)
: base(tokenizer, accepted)
{
}

View File

@ -6,61 +6,61 @@ using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class CSharpLanguageCharacteristics : LanguageCharacteristics<CSharpTokenizer, CSharpSymbol, CSharpSymbolType>
internal class CSharpLanguageCharacteristics : LanguageCharacteristics<CSharpTokenizer, CSharpToken, CSharpTokenType>
{
private static readonly CSharpLanguageCharacteristics _instance = new CSharpLanguageCharacteristics();
private static Dictionary<CSharpSymbolType, string> _symbolSamples = new Dictionary<CSharpSymbolType, string>()
private static Dictionary<CSharpTokenType, string> _tokenSamples = new Dictionary<CSharpTokenType, string>()
{
{ 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<RazorDiagnostic> errors)
protected override CSharpToken CreateToken(string content, CSharpTokenType type, IReadOnlyList<RazorDiagnostic> 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;
}
}

View File

@ -6,11 +6,11 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class CSharpSymbol : SymbolBase<CSharpSymbolType>
internal class CSharpToken : TokenBase<CSharpTokenType>
{
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<RazorDiagnostic> 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;
}

View File

@ -3,7 +3,7 @@
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal enum CSharpSymbolType
internal enum CSharpTokenType
{
Unknown,
Identifier,

View File

@ -8,9 +8,9 @@ using System.Globalization;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class CSharpTokenizer : Tokenizer<CSharpSymbol, CSharpSymbolType>
internal class CSharpTokenizer : Tokenizer<CSharpToken, CSharpTokenType>
{
private Dictionary<char, Func<CSharpSymbolType>> _operatorHandlers;
private Dictionary<char, Func<CSharpTokenType>> _operatorHandlers;
private static readonly Dictionary<string, CSharpKeyword> _keywords = new Dictionary<string, CSharpKeyword>(StringComparer.Ordinal)
{
@ -100,31 +100,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
base.CurrentState = StartState;
_operatorHandlers = new Dictionary<char, Func<CSharpSymbolType>>()
_operatorHandlers = new Dictionary<char, Func<CSharpTokenType>>()
{
{ '-', 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<RazorDiagnostic> errors)
protected override CSharpToken CreateToken(string content, CSharpTokenType type, IReadOnlyList<RazorDiagnostic> 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<CSharpSymbolType> handler;
Func<CSharpTokenType> 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<CSharpSymbolType> CreateTwoCharOperatorHandler(CSharpSymbolType typeIfOnlyFirst, char second, CSharpSymbolType typeIfBoth)
private Func<CSharpTokenType> CreateTwoCharOperatorHandler(CSharpTokenType typeIfOnlyFirst, char second, CSharpTokenType typeIfBoth)
{
return () =>
{
@ -513,7 +513,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
};
}
private Func<CSharpSymbolType> CreateTwoCharOperatorHandler(CSharpSymbolType typeIfOnlyFirst, char option1, CSharpSymbolType typeIfOption1, char option2, CSharpSymbolType typeIfOption2)
private Func<CSharpTokenType> 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,
}
}
}

View File

@ -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; }
}
}

View File

@ -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<string, IEnumerable<IToken>> 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();
}
}

View File

@ -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;
}
}
}

View File

@ -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("]");
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -6,7 +6,7 @@ using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class HtmlLanguageCharacteristics : LanguageCharacteristics<HtmlTokenizer, HtmlSymbol, HtmlSymbolType>
internal class HtmlLanguageCharacteristics : LanguageCharacteristics<HtmlTokenizer, HtmlToken, HtmlTokenType>
{
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<RazorDiagnostic> errors)
protected override HtmlToken CreateToken(string content, HtmlTokenType type, IReadOnlyList<RazorDiagnostic> errors)
{
return new HtmlSymbol(content, type, errors);
return new HtmlToken(content, type, errors);
}
}
}

View File

@ -6,11 +6,11 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class HtmlSymbol : SymbolBase<HtmlSymbolType>
internal class HtmlToken : TokenBase<HtmlTokenType>
{
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<RazorDiagnostic> errors)
: base(content, type, errors)
{

View File

@ -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

View File

@ -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<HtmlSymbol, HtmlSymbolType>
internal class HtmlTokenizer : Tokenizer<HtmlToken, HtmlTokenType>
{
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<RazorDiagnostic> errors)
protected override HtmlToken CreateToken(string content, HtmlTokenType type, IReadOnlyList<RazorDiagnostic> 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,
}
}
}

View File

@ -3,7 +3,7 @@
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal interface ISymbol
internal interface IToken
{
Span Parent { get; set; }

View File

@ -5,6 +5,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal interface ITokenizer
{
ISymbol NextSymbol();
IToken NextToken();
}
}

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly ISet<string> _keywords;
private readonly IReadOnlyCollection<string> _readOnlyKeywords;
public ImplicitExpressionEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, ISet<string> keywords, bool acceptTrailingDot)
public ImplicitExpressionEditHandler(Func<string, IEnumerable<IToken>> tokenizer, ISet<string> keywords, bool acceptTrailingDot)
: base(tokenizer)
{
_keywords = keywords ?? new HashSet<string>();
@ -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<CSharpToken>().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<CSharpToken>().ToArray();
if (IsInsideParenthesis(change.Span.AbsoluteIndex, tokens))
{
return true;
}
return false;
}
// Internal for testing
internal static bool IsInsideParenthesis(int position, IReadOnlyList<CSharpToken> 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] == '.') ||

View File

@ -3,7 +3,7 @@
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal enum KnownSymbolType
internal enum KnownTokenType
{
WhiteSpace,
NewLine,

View File

@ -6,104 +6,104 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal abstract class LanguageCharacteristics<TTokenizer, TSymbol, TSymbolType>
where TSymbolType : struct
where TTokenizer : Tokenizer<TSymbol, TSymbolType>
where TSymbol : SymbolBase<TSymbolType>
internal abstract class LanguageCharacteristics<TTokenizer, TToken, TTokenType>
where TTokenType : struct
where TTokenizer : Tokenizer<TToken, TTokenType>
where TToken : TokenBase<TTokenType>
{
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<TSymbol> TokenizeString(string content)
public virtual IEnumerable<TToken> TokenizeString(string content)
{
return TokenizeString(SourceLocation.Zero, content);
}
public virtual IEnumerable<TSymbol> TokenizeString(SourceLocation start, string input)
public virtual IEnumerable<TToken> 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<TSymbol, TSymbol> SplitSymbol(TSymbol symbol, int splitAt, TSymbolType leftType)
public virtual Tuple<TToken, TToken> 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<RazorDiagnostic> errors);
protected abstract TToken CreateToken(string content, TTokenType type, IReadOnlyList<RazorDiagnostic> errors);
}
}

View File

@ -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);
}

Some files were not shown because too many files have changed in this diff Show More