Merge branch 'master' into merge/release/2.2-to-master

This commit is contained in:
Pranav K 2018-10-25 09:35:47 -07:00 committed by GitHub
commit 70aad7cb70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1569 changed files with 34084 additions and 20328 deletions

View File

@ -12,7 +12,7 @@ os:
osx_image: xcode8.2
branches:
only:
- dev
- master
- /^release\/.*$/
- /^(.*\/)?ci-.*$/
before_install:

View File

@ -7,7 +7,7 @@ resources:
- repository: buildtools
type: git
name: aspnet-BuildTools
ref: refs/heads/release/2.2
ref: refs/heads/master
phases:
- template: .vsts-pipelines/templates/project-ci.yml@buildtools

View File

@ -1,4 +1,4 @@
Contributing
======
Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/dev/CONTRIBUTING.md) in the Home repo.
Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo.

View File

@ -1,14 +1,201 @@
Copyright (c) .NET Foundation and Contributors
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
All rights reserved.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
1. Definitions.
http://www.apache.org/licenses/LICENSE-2.0
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) .NET Foundation and Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -132,14 +132,6 @@ namespace Microsoft.AspNetCore.Razor.Performance
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();
@ -149,6 +141,14 @@ namespace Microsoft.AspNetCore.Razor.Performance
{
throw new NotImplementedException();
}
public override RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
RazorExtensions.Register(b);
});
}
}
}
}

View File

@ -105,7 +105,7 @@
/p:Configuration=$(Configuration);
/p:FeatureBranchVersionSuffix=$(FeatureBranchVersionSuffix);
/p:BuildNumber=$(BuildNumber);
/p:LangVersion=7.1" />
/p:LangVersion=7.2" />
</ItemGroup>
<MakeDir Directories="$(LogOutputDir)" />

View File

@ -4,49 +4,52 @@
</PropertyGroup>
<PropertyGroup Label="Package Versions">
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview2-20181019.2</InternalAspNetCoreSdkPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftBuildFrameworkPackageVersion>15.6.82</MicrosoftBuildFrameworkPackageVersion>
<MicrosoftBuildPackageVersion>15.6.82</MicrosoftBuildPackageVersion>
<MicrosoftBuildUtilitiesCorePackageVersion>15.6.82</MicrosoftBuildUtilitiesCorePackageVersion>
<InternalAspNetCoreSdkPackageVersion>3.0.0-alpha1-20181017.7</InternalAspNetCoreSdkPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>3.0.0-alpha1-10549</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>3.0.0-alpha1-10549</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftBuildFrameworkPackageVersion>15.8.166</MicrosoftBuildFrameworkPackageVersion>
<MicrosoftBuildPackageVersion>15.8.166</MicrosoftBuildPackageVersion>
<MicrosoftBuildUtilitiesCorePackageVersion>15.8.166</MicrosoftBuildUtilitiesCorePackageVersion>
<MicrosoftCodeAnalysisCommonPackageVersion>2.8.0</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>2.8.0</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0</MicrosoftExtensionsDependencyModelPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
<MicrosoftExtensionsDependencyModelPackageVersion>3.0.0-preview1-26907-05</MicrosoftExtensionsDependencyModelPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.3</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview3-27014-02</MicrosoftNETCoreApp22PackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.0.26606</MicrosoftVisualStudioComponentModelHostPackageVersion>
<MicrosoftVisualStudioEditorPackageVersion>15.6.161-preview</MicrosoftVisualStudioEditorPackageVersion>
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>15.6.161-preview</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
<MicrosoftVisualStudioOLEInteropPackageVersion>7.10.6070</MicrosoftVisualStudioOLEInteropPackageVersion>
<MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>15.3.224</MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.8.525</MicrosoftVisualStudioComponentModelHostPackageVersion>
<MicrosoftVisualStudioImageCatalogPackageVersion>15.8.28010</MicrosoftVisualStudioImageCatalogPackageVersion>
<MicrosoftVisualStudioEditorPackageVersion>16.0.142-g25b7188c54</MicrosoftVisualStudioEditorPackageVersion>
<MicrosoftVisualStudioLanguagePackageVersion>16.0.142-g25b7188c54</MicrosoftVisualStudioLanguagePackageVersion>
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>16.0.142-g25b7188c54</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
<MicrosoftVisualStudioOLEInteropPackageVersion>7.10.6071</MicrosoftVisualStudioOLEInteropPackageVersion>
<MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>15.8.243</MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>
<MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>2.0.6142705</MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>
<MicrosoftVisualStudioProjectSystemSDKPackageVersion>15.3.224</MicrosoftVisualStudioProjectSystemSDKPackageVersion>
<MicrosoftVisualStudioShell150PackageVersion>15.0.26606</MicrosoftVisualStudioShell150PackageVersion>
<MicrosoftVisualStudioShellInterop100PackageVersion>10.0.30319</MicrosoftVisualStudioShellInterop100PackageVersion>
<MicrosoftVisualStudioShellInterop110PackageVersion>11.0.61030</MicrosoftVisualStudioShellInterop110PackageVersion>
<MicrosoftVisualStudioShellInterop120PackageVersion>12.0.30110</MicrosoftVisualStudioShellInterop120PackageVersion>
<MicrosoftVisualStudioShellInterop80PackageVersion>8.0.50727</MicrosoftVisualStudioShellInterop80PackageVersion>
<MicrosoftVisualStudioShellInterop90PackageVersion>9.0.30729</MicrosoftVisualStudioShellInterop90PackageVersion>
<MicrosoftVisualStudioShellInteropPackageVersion>7.10.6071</MicrosoftVisualStudioShellInteropPackageVersion>
<MicrosoftVisualStudioTextUIPackageVersion>15.6.161-preview</MicrosoftVisualStudioTextUIPackageVersion>
<MicrosoftVisualStudioProjectSystemSDKPackageVersion>15.8.243</MicrosoftVisualStudioProjectSystemSDKPackageVersion>
<MicrosoftVisualStudioShell150PackageVersion>15.8.28010</MicrosoftVisualStudioShell150PackageVersion>
<MicrosoftVisualStudioShellInterop100PackageVersion>10.0.30320</MicrosoftVisualStudioShellInterop100PackageVersion>
<MicrosoftVisualStudioShellInterop110PackageVersion>11.0.61031</MicrosoftVisualStudioShellInterop110PackageVersion>
<MicrosoftVisualStudioShellInterop120PackageVersion>12.0.30111</MicrosoftVisualStudioShellInterop120PackageVersion>
<MicrosoftVisualStudioShellInterop80PackageVersion>8.0.50728</MicrosoftVisualStudioShellInterop80PackageVersion>
<MicrosoftVisualStudioShellInterop90PackageVersion>9.0.30730</MicrosoftVisualStudioShellInterop90PackageVersion>
<MicrosoftVisualStudioShellInteropPackageVersion>7.10.6072</MicrosoftVisualStudioShellInteropPackageVersion>
<MicrosoftVisualStudioTextUIPackageVersion>16.0.142-g25b7188c54</MicrosoftVisualStudioTextUIPackageVersion>
<MicrosoftVisualStudioThreadingPackageVersion>15.8.168</MicrosoftVisualStudioThreadingPackageVersion>
<MonoAddinsPackageVersion>1.3.8</MonoAddinsPackageVersion>
<MonoDevelopSdkPackageVersion>1.0.1</MonoDevelopSdkPackageVersion>
<MoqPackageVersion>4.10.0</MoqPackageVersion>
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
<StreamJsonRpcPackageVersion>1.1.92</StreamJsonRpcPackageVersion>
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0</SystemDiagnosticsDiagnosticSourcePackageVersion>
<StreamJsonRpcPackageVersion>1.3.23</StreamJsonRpcPackageVersion>
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.6.0-preview1-26907-04</SystemDiagnosticsDiagnosticSourcePackageVersion>
<SystemRuntimeInteropServicesRuntimeInformationPackageVersion>4.3.0</SystemRuntimeInteropServicesRuntimeInformationPackageVersion>
<SystemValueTuplePackageVersion>4.5.0</SystemValueTuplePackageVersion>
<SystemValueTuplePackageVersion>4.6.0-preview1-26829-04</SystemValueTuplePackageVersion>
<VisualStudio_NewtonsoftJsonPackageVersion>9.0.1</VisualStudio_NewtonsoftJsonPackageVersion>
<VSIX_MicrosoftCodeAnalysisCommonPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCommonPackageVersion>
<VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>

View File

@ -21,7 +21,6 @@
<PropertyGroup>
<!-- These properties are use by the automation that updates dependencies.props -->
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
<LineupPackageVersion>2.2.0-*</LineupPackageVersion>
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json</LineupPackageRestoreSource>
</PropertyGroup>

View File

@ -1,2 +1,2 @@
version:2.2.0-preview2-20181019.2
commithash:72ba316025aca165c09cb97b407965d944bad93f
version:3.0.0-alpha1-20181017.7
commithash:f4082c290c6c7610e7cb9d787072004453fc175f

View File

@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/release/2.2/tools/korebuild.schema.json",
"channel": "release/2.2",
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json",
"channel": "master",
"toolsets": {
"visualstudio": {
"required": false,

View File

@ -595,9 +595,9 @@ namespace Microsoft.AspNetCore.Razor.Language
{
if (span.Tokens.Count == 1)
{
var token = span.Tokens[0] as HtmlToken;
var token = span.Tokens[0];
if (token != null &&
token.Type == HtmlTokenType.Unknown &&
token.Kind == SyntaxKind.Unknown &&
token.Content.Length == 0)
{
// We don't want to create IR nodes for marker tokens.

View File

@ -49,6 +49,10 @@ namespace Microsoft.AspNetCore.Razor.Language
var absolutePath = NormalizeAndEnsureValidPath(path);
var file = new FileInfo(absolutePath);
if (!absolutePath.StartsWith(absoluteBasePath))
{
throw new InvalidOperationException($"The file '{file.FullName}' is not a descendent of the base path '{absoluteBasePath}'.");
}
var relativePhysicalPath = file.FullName.Substring(absoluteBasePath.Length + 1); // Include leading separator
var filePath = "/" + relativePhysicalPath.Replace(Path.DirectorySeparatorChar, '/');

View File

@ -4,18 +4,19 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DirectiveTokenEditHandler : SpanEditHandler
{
public DirectiveTokenEditHandler(Func<string, IEnumerable<IToken>> tokenizer) : base(tokenizer)
public DirectiveTokenEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer) : base(tokenizer)
{
}
protected override PartialParseResultInternal CanAcceptChange(Span target, SourceChange change)
{
if (AcceptedCharacters == AcceptedCharactersInternal.NonWhiteSpace)
if (AcceptedCharacters == AcceptedCharactersInternal.NonWhitespace)
{
var originalText = change.GetOriginalText(target);
var editedContent = change.GetEditedContent(target);

View File

@ -65,15 +65,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
}
// {node.Content} __typeHelper = default({node.Content});
context.AddSourceMappingFor(node);
context.CodeWriter
.Write(node.Content)
.Write(" ")
.WriteStartAssignment(TypeHelper)
.Write("default(")
.Write(node.Content)
.WriteLine(");");
using (context.CodeWriter.BuildLinePragma(node.Source))
{
context.AddSourceMappingFor(node);
context.CodeWriter
.Write(node.Content)
.Write(" ")
.WriteStartAssignment(TypeHelper)
.Write("default(")
.Write(node.Content)
.WriteLine(");");
}
break;
case DirectiveTokenKind.Member:
@ -86,16 +88,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
}
// global::System.Object {node.content} = null;
context.CodeWriter
using (context.CodeWriter.BuildLinePragma(node.Source))
{
context.CodeWriter
.Write("global::")
.Write(typeof(object).FullName)
.Write(" ");
context.AddSourceMappingFor(node);
context.CodeWriter
.Write(node.Content)
.WriteLine(" = null;");
context.AddSourceMappingFor(node);
context.CodeWriter
.Write(node.Content)
.WriteLine(" = null;");
}
break;
case DirectiveTokenKind.Namespace:
@ -108,46 +112,50 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
}
// global::System.Object __typeHelper = nameof({node.Content});
context.CodeWriter
using (context.CodeWriter.BuildLinePragma(node.Source))
{
context.CodeWriter
.Write("global::")
.Write(typeof(object).FullName)
.Write(" ")
.WriteStartAssignment(TypeHelper);
context.CodeWriter.Write("nameof(");
context.CodeWriter.Write("nameof(");
context.AddSourceMappingFor(node);
context.CodeWriter
.Write(node.Content)
.WriteLine(");");
context.AddSourceMappingFor(node);
context.CodeWriter
.Write(node.Content)
.WriteLine(");");
}
break;
case DirectiveTokenKind.String:
// global::System.Object __typeHelper = "{node.Content}";
context.CodeWriter
using (context.CodeWriter.BuildLinePragma(node.Source))
{
context.CodeWriter
.Write("global::")
.Write(typeof(object).FullName)
.Write(" ")
.WriteStartAssignment(TypeHelper);
if (node.Content.StartsWith("\"", StringComparison.Ordinal))
{
context.AddSourceMappingFor(node);
context.CodeWriter.Write(node.Content);
}
else
{
context.CodeWriter.Write("\"");
context.AddSourceMappingFor(node);
context.CodeWriter
.Write(node.Content)
.Write("\"");
}
if (node.Content.StartsWith("\"", StringComparison.Ordinal))
{
context.AddSourceMappingFor(node);
context.CodeWriter.Write(node.Content);
}
else
{
context.CodeWriter.Write("\"");
context.AddSourceMappingFor(node);
context.CodeWriter
.Write(node.Content)
.Write("\"");
}
context.CodeWriter.WriteLine(";");
context.CodeWriter.WriteLine(";");
}
break;
}
context.CodeWriter.CurrentIndent = originalIndent;

View File

@ -10,13 +10,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
None = 0,
NewLine = 1,
WhiteSpace = 2,
Whitespace = 2,
NonWhiteSpace = 4,
NonWhitespace = 4,
AllWhiteSpace = NewLine | WhiteSpace,
Any = AllWhiteSpace | NonWhiteSpace,
AllWhitespace = NewLine | Whitespace,
Any = AllWhitespace | NonWhitespace,
AnyExceptNewline = NonWhiteSpace | WhiteSpace
AnyExceptNewline = NonWhitespace | Whitespace
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
@ -11,18 +12,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
private static readonly int TypeHashCode = typeof(AutoCompleteEditHandler).GetHashCode();
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer)
public AutoCompleteEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer)
: base(tokenizer)
{
}
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer, bool autoCompleteAtEndOfSpan)
public AutoCompleteEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer, bool autoCompleteAtEndOfSpan)
: this(tokenizer)
{
AutoCompleteAtEndOfSpan = autoCompleteAtEndOfSpan;
}
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer, AcceptedCharactersInternal accepted)
public AutoCompleteEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer, AcceptedCharactersInternal accepted)
: base(tokenizer, accepted)
{
}

View File

@ -5,17 +5,18 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer, CSharpToken, CSharpTokenType>
internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
{
private static HashSet<char> InvalidNonWhitespaceNameCharacters = new HashSet<char>(new[]
{
'@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*'
});
private static readonly Func<CSharpToken, bool> IsValidStatementSpacingToken =
private static readonly Func<SyntaxToken, bool> IsValidStatementSpacingToken =
IsSpacingToken(includeNewLines: true, includeComments: true);
internal static readonly DirectiveDescriptor AddTagHelperDirectiveDescriptor = DirectiveDescriptor.CreateDirective(
@ -102,7 +103,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public bool IsNested { get; set; }
protected override bool TokenTypeEquals(CSharpTokenType x, CSharpTokenType y) => x == y;
protected override bool TokenKindEquals(SyntaxKind x, SyntaxKind y) => x == y;
protected void MapDirectives(Action handler, params string[] directives)
{
@ -161,16 +162,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
[Conditional("DEBUG")]
internal void Assert(CSharpKeyword expectedKeyword)
{
Debug.Assert(CurrentToken.Type == CSharpTokenType.Keyword &&
CurrentToken.Keyword.HasValue &&
CurrentToken.Keyword.Value == expectedKeyword);
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword &&
result.HasValue &&
result.Value == expectedKeyword);
}
protected internal bool At(CSharpKeyword keyword)
{
return At(CSharpTokenType.Keyword) &&
CurrentToken.Keyword.HasValue &&
CurrentToken.Keyword.Value == keyword;
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
return At(SyntaxKind.Keyword) &&
result.HasValue &&
result.Value == keyword;
}
protected internal bool AcceptIf(CSharpKeyword keyword)
@ -183,11 +186,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
protected static Func<CSharpToken, bool> IsSpacingToken(bool includeNewLines, bool includeComments)
protected static Func<SyntaxToken, bool> IsSpacingToken(bool includeNewLines, bool includeComments)
{
return token => token.Type == CSharpTokenType.WhiteSpace ||
(includeNewLines && token.Type == CSharpTokenType.NewLine) ||
(includeComments && token.Type == CSharpTokenType.Comment);
return token => token.Kind == SyntaxKind.Whitespace ||
(includeNewLines && token.Kind == SyntaxKind.NewLine) ||
(includeComments && token.Kind == SyntaxKind.CSharpComment);
}
public override void ParseBlock()
@ -209,24 +212,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
var current = CurrentToken;
if (At(CSharpTokenType.StringLiteral) &&
if (At(SyntaxKind.StringLiteral) &&
CurrentToken.Content.Length > 0 &&
CurrentToken.Content[0] == SyntaxConstants.TransitionCharacter)
{
var split = Language.SplitToken(CurrentToken, 1, CSharpTokenType.Transition);
var split = Language.SplitToken(CurrentToken, 1, SyntaxKind.Transition);
current = split.Item1;
// Back up to the end of the transition
Context.Source.Position -= split.Item2.Content.Length;
NextToken();
}
else if (At(CSharpTokenType.Transition))
else if (At(SyntaxKind.Transition))
{
NextToken();
}
// Accept "@" if we see it, but if we don't, that's OK. We assume we were started for a good reason
if (current.Type == CSharpTokenType.Transition)
if (current.Kind == SyntaxKind.Transition)
{
if (Span.Tokens.Count > 0)
{
@ -251,9 +254,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
span.ChunkGenerator = new StatementChunkGenerator();
}
private void AtTransition(CSharpToken current)
private void AtTransition(SyntaxToken current)
{
Debug.Assert(current.Type == CSharpTokenType.Transition);
Debug.Assert(current.Kind == SyntaxKind.Transition);
Accept(current);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
Span.ChunkGenerator = SpanChunkGenerator.Null;
@ -273,14 +276,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// What type of block is this?
if (!EndOfFile)
{
if (CurrentToken.Type == CSharpTokenType.LeftParenthesis)
if (CurrentToken.Kind == SyntaxKind.LeftParenthesis)
{
Context.Builder.CurrentBlock.Type = BlockKindInternal.Expression;
Context.Builder.CurrentBlock.ChunkGenerator = new ExpressionChunkGenerator();
ExplicitExpression();
return;
}
else if (CurrentToken.Type == CSharpTokenType.Identifier)
else if (CurrentToken.Kind == SyntaxKind.Identifier)
{
if (TryGetDirectiveHandler(CurrentToken.Content, out var handler))
{
@ -306,7 +309,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
}
else if (CurrentToken.Type == CSharpTokenType.Keyword)
else if (CurrentToken.Kind == SyntaxKind.Keyword)
{
if (TryGetDirectiveHandler(CurrentToken.Content, out var handler))
{
@ -320,7 +323,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
}
else if (CurrentToken.Type == CSharpTokenType.LeftBrace)
else if (CurrentToken.Kind == SyntaxKind.LeftBrace)
{
VerbatimBlock();
return;
@ -337,9 +340,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
CurrentKeywords,
acceptTrailingDot: IsNested)
{
AcceptedCharacters = AcceptedCharactersInternal.NonWhiteSpace
AcceptedCharacters = AcceptedCharactersInternal.NonWhitespace
};
if (At(CSharpTokenType.WhiteSpace) || At(CSharpTokenType.NewLine))
if (At(SyntaxKind.Whitespace) || At(SyntaxKind.NewLine))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnexpectedWhiteSpaceAtStartOfCodeBlock(
@ -369,7 +372,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void VerbatimBlock()
{
Assert(CSharpTokenType.LeftBrace);
Assert(SyntaxKind.LeftBrace);
var block = new Block(Resources.BlockName_Code, CurrentStart);
AcceptAndMoveNext();
@ -385,13 +388,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Span.ChunkGenerator = new StatementChunkGenerator();
AddMarkerTokenIfNecessary();
if (!At(CSharpTokenType.RightBrace))
if (!At(SyntaxKind.RightBrace))
{
editHandler.AutoCompleteString = "}";
}
Output(SpanKindInternal.Code);
if (Optional(CSharpTokenType.RightBrace))
if (Optional(SyntaxKind.RightBrace))
{
// Set up the "}" span
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
@ -401,8 +404,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (!IsNested)
{
EnsureCurrent();
if (At(CSharpTokenType.NewLine) ||
(At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.NewLine)))
if (At(SyntaxKind.NewLine) ||
(At(SyntaxKind.Whitespace) && NextIs(SyntaxKind.NewLine)))
{
Context.NullGenerateWhitespaceAndNewLine = true;
}
@ -413,7 +416,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void ImplicitExpression()
{
ImplicitExpression(AcceptedCharactersInternal.NonWhiteSpace);
ImplicitExpression(AcceptedCharactersInternal.NonWhitespace);
}
// Async implicit expressions include the "await" keyword and therefore need to allow spaces to
@ -456,13 +459,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
if (!EndOfFile)
{
if (CurrentToken.Type == CSharpTokenType.LeftParenthesis ||
CurrentToken.Type == CSharpTokenType.LeftBracket)
if (CurrentToken.Kind == SyntaxKind.LeftParenthesis ||
CurrentToken.Kind == SyntaxKind.LeftBracket)
{
// If we end within "(", whitespace is fine
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
CSharpTokenType right;
SyntaxKind right;
bool success;
using (PushSpanConfig((span, prev) =>
@ -471,13 +474,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
}))
{
right = Language.FlipBracket(CurrentToken.Type);
right = Language.FlipBracket(CurrentToken.Kind);
success = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates);
}
if (!success)
{
AcceptUntil(CSharpTokenType.LessThan);
AcceptUntil(SyntaxKind.LessThan);
}
if (At(right))
{
@ -488,22 +491,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
return MethodCallOrArrayIndex(acceptedCharacters);
}
if (At(CSharpTokenType.QuestionMark))
if (At(SyntaxKind.QuestionMark))
{
var next = Lookahead(count: 1);
if (next != null)
{
if (next.Type == CSharpTokenType.Dot)
if (next.Kind == SyntaxKind.Dot)
{
// Accept null conditional dot operator (?.).
AcceptAndMoveNext();
AcceptAndMoveNext();
// If the next piece after the ?. is a keyword or identifier then we want to continue.
return At(CSharpTokenType.Identifier) || At(CSharpTokenType.Keyword);
return At(SyntaxKind.Identifier) || At(SyntaxKind.Keyword);
}
else if (next.Type == CSharpTokenType.LeftBracket)
else if (next.Kind == SyntaxKind.LeftBracket)
{
// We're at the ? for a null conditional bracket operator (?[).
AcceptAndMoveNext();
@ -513,12 +516,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
}
else if (At(CSharpTokenType.Dot))
else if (At(SyntaxKind.Dot))
{
var dot = CurrentToken;
if (NextToken())
{
if (At(CSharpTokenType.Identifier) || At(CSharpTokenType.Keyword))
if (At(SyntaxKind.Identifier) || At(SyntaxKind.Keyword))
{
// Accept the dot and return to the start
Accept(dot);
@ -540,7 +543,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Accept(dot);
}
}
else if (!At(CSharpTokenType.WhiteSpace) && !At(CSharpTokenType.NewLine))
else if (!At(SyntaxKind.Whitespace) && !At(SyntaxKind.NewLine))
{
PutCurrentBack();
}
@ -587,8 +590,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void CaptureWhitespaceAtEndOfCodeOnlyLine()
{
var whitespace = ReadWhile(token => token.Type == CSharpTokenType.WhiteSpace);
if (At(CSharpTokenType.NewLine))
var whitespace = ReadWhile(token => token.Kind == SyntaxKind.Whitespace);
if (At(SyntaxKind.NewLine))
{
Accept(whitespace);
AcceptAndMoveNext();
@ -610,7 +613,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void ExplicitExpression()
{
var block = new Block(Resources.BlockName_ExplicitExpression, CurrentStart);
Assert(CSharpTokenType.LeftParenthesis);
Assert(SyntaxKind.LeftParenthesis);
AcceptAndMoveNext();
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
Span.ChunkGenerator = SpanChunkGenerator.Null;
@ -621,13 +624,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
BalancingModes.BacktrackOnFailure |
BalancingModes.NoErrorOnFailure |
BalancingModes.AllowCommentsAndTemplates,
CSharpTokenType.LeftParenthesis,
CSharpTokenType.RightParenthesis,
SyntaxKind.LeftParenthesis,
SyntaxKind.RightParenthesis,
block.Start);
if (!success)
{
AcceptUntil(CSharpTokenType.LessThan);
AcceptUntil(SyntaxKind.LessThan);
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF(
new SourceSpan(block.Start, contentLength: 1 /* ( */), block.Name, ")", "("));
@ -636,13 +639,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// If necessary, put an empty-content marker token here
if (Span.Tokens.Count == 0)
{
Accept(new CSharpToken(string.Empty, CSharpTokenType.Unknown));
Accept(SyntaxFactory.Token(SyntaxKind.Unknown, string.Empty));
}
// Output the content span and then capture the ")"
Output(SpanKindInternal.Code);
}
Optional(CSharpTokenType.RightParenthesis);
Optional(SyntaxKind.RightParenthesis);
if (!EndOfFile)
{
PutCurrentBack();
@ -702,7 +705,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// No embedded transitions in C#, so ignore that param
return allowTemplatesAndComments
&& ((Language.IsTransition(CurrentToken)
&& NextIs(CSharpTokenType.LessThan, CSharpTokenType.Colon, CSharpTokenType.DoubleColon))
&& NextIs(SyntaxKind.LessThan, SyntaxKind.Colon, SyntaxKind.DoubleColon))
|| Language.IsCommentStart(CurrentToken));
}
@ -783,12 +786,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void CaseStatement(bool topLevel)
{
Assert(CSharpTokenType.Keyword);
Debug.Assert(CurrentToken.Keyword != null &&
(CurrentToken.Keyword.Value == CSharpKeyword.Case ||
CurrentToken.Keyword.Value == CSharpKeyword.Default));
AcceptUntil(CSharpTokenType.Colon);
Optional(CSharpTokenType.Colon);
Assert(SyntaxKind.Keyword);
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
Debug.Assert(result.HasValue &&
(result.Value == CSharpKeyword.Case ||
result.Value == CSharpKeyword.Default));
AcceptUntil(SyntaxKind.Colon);
Optional(SyntaxKind.Colon);
}
private void DoStatement(bool topLevel)
@ -813,7 +817,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Assert(CSharpKeyword.While);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
if (AcceptCondition() && Optional(CSharpTokenType.Semicolon))
if (AcceptCondition() && Optional(SyntaxKind.Semicolon))
{
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
}
@ -832,12 +836,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
if (At(CSharpTokenType.LeftParenthesis))
if (At(SyntaxKind.LeftParenthesis))
{
// using ( ==> Using Statement
UsingStatement(block);
}
else if (At(CSharpTokenType.Identifier) || At(CSharpKeyword.Static))
else if (At(SyntaxKind.Identifier) || At(CSharpKeyword.Static))
{
// using Identifier ==> Using Declaration
if (!topLevel)
@ -865,16 +869,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Context.Builder.CurrentBlock.Type = BlockKindInternal.Directive;
var start = CurrentStart;
if (At(CSharpTokenType.Identifier))
if (At(SyntaxKind.Identifier))
{
// non-static using
NamespaceOrTypeName();
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
if (At(CSharpTokenType.Assign))
if (At(SyntaxKind.Assign))
{
// Alias
Accept(whitespace);
Assert(CSharpTokenType.Assign);
Assert(SyntaxKind.Assign);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
@ -904,7 +908,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Optional ";"
if (EnsureCurrent())
{
Optional(CSharpTokenType.Semicolon);
Optional(SyntaxKind.Semicolon);
}
}
@ -919,16 +923,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var expectingDot = false;
var tokens = ReadWhile(token =>
{
var type = token.Type;
if ((expectingDot && type == CSharpTokenType.Dot) ||
(!expectingDot && type == CSharpTokenType.Identifier))
var type = token.Kind;
if ((expectingDot && type == SyntaxKind.Dot) ||
(!expectingDot && type == SyntaxKind.Identifier))
{
expectingDot = !expectingDot;
return true;
}
if (type != CSharpTokenType.WhiteSpace &&
type != CSharpTokenType.NewLine)
if (type != SyntaxKind.Whitespace &&
type != SyntaxKind.NewLine)
{
expectingDot = false;
currentIdentifierLength += token.Content.Length;
@ -966,69 +970,69 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected bool NamespaceOrTypeName()
{
if (Optional(CSharpTokenType.LeftParenthesis))
if (Optional(SyntaxKind.LeftParenthesis))
{
while (!Optional(CSharpTokenType.RightParenthesis) && !EndOfFile)
while (!Optional(SyntaxKind.RightParenthesis) && !EndOfFile)
{
Optional(CSharpTokenType.WhiteSpace);
Optional(SyntaxKind.Whitespace);
if (!NamespaceOrTypeName())
{
return false;
}
Optional(CSharpTokenType.WhiteSpace);
Optional(CSharpTokenType.Identifier);
Optional(CSharpTokenType.WhiteSpace);
Optional(CSharpTokenType.Comma);
Optional(SyntaxKind.Whitespace);
Optional(SyntaxKind.Identifier);
Optional(SyntaxKind.Whitespace);
Optional(SyntaxKind.Comma);
}
if (At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.QuestionMark))
if (At(SyntaxKind.Whitespace) && NextIs(SyntaxKind.QuestionMark))
{
// Only accept the whitespace if we are going to consume the next token.
AcceptAndMoveNext();
}
Optional(CSharpTokenType.QuestionMark); // Nullable
Optional(SyntaxKind.QuestionMark); // Nullable
return true;
}
else if (Optional(CSharpTokenType.Identifier) || Optional(CSharpTokenType.Keyword))
else if (Optional(SyntaxKind.Identifier) || Optional(SyntaxKind.Keyword))
{
if (Optional(CSharpTokenType.DoubleColon))
if (Optional(SyntaxKind.DoubleColon))
{
if (!Optional(CSharpTokenType.Identifier))
if (!Optional(SyntaxKind.Identifier))
{
Optional(CSharpTokenType.Keyword);
Optional(SyntaxKind.Keyword);
}
}
if (At(CSharpTokenType.LessThan))
if (At(SyntaxKind.LessThan))
{
TypeArgumentList();
}
if (Optional(CSharpTokenType.Dot))
if (Optional(SyntaxKind.Dot))
{
NamespaceOrTypeName();
}
if (At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.QuestionMark))
if (At(SyntaxKind.Whitespace) && NextIs(SyntaxKind.QuestionMark))
{
// Only accept the whitespace if we are going to consume the next token.
AcceptAndMoveNext();
}
Optional(CSharpTokenType.QuestionMark); // Nullable
Optional(SyntaxKind.QuestionMark); // Nullable
if (At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.LeftBracket))
if (At(SyntaxKind.Whitespace) && NextIs(SyntaxKind.LeftBracket))
{
// Only accept the whitespace if we are going to consume the next token.
AcceptAndMoveNext();
}
while (At(CSharpTokenType.LeftBracket))
while (At(SyntaxKind.LeftBracket))
{
Balance(BalancingModes.None);
Optional(CSharpTokenType.RightBracket);
Optional(SyntaxKind.RightBracket);
}
return true;
}
@ -1040,14 +1044,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void TypeArgumentList()
{
Assert(CSharpTokenType.LessThan);
Assert(SyntaxKind.LessThan);
Balance(BalancingModes.None);
Optional(CSharpTokenType.GreaterThan);
Optional(SyntaxKind.GreaterThan);
}
private void UsingStatement(Block block)
{
Assert(CSharpTokenType.LeftParenthesis);
Assert(SyntaxKind.LeftParenthesis);
// Parse condition
if (AcceptCondition())
@ -1159,12 +1163,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (!EndOfFile)
{
// Check for "{" to make sure we're at a block
if (!At(CSharpTokenType.LeftBrace))
if (!At(SyntaxKind.LeftBrace))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_SingleLineControlFlowStatementsNotAllowed(
new SourceSpan(CurrentStart, CurrentToken.Content.Length),
Language.GetSample(CSharpTokenType.LeftBrace),
Language.GetSample(SyntaxKind.LeftBrace),
CurrentToken.Content));
}
@ -1175,7 +1179,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void UnconditionalBlock()
{
Assert(CSharpTokenType.Keyword);
Assert(SyntaxKind.Keyword);
var block = new Block(CurrentToken, CurrentStart);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
@ -1219,7 +1223,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void ConditionalBlock(bool topLevel)
{
Assert(CSharpTokenType.Keyword);
Assert(SyntaxKind.Keyword);
var block = new Block(CurrentToken, CurrentStart);
ConditionalBlock(block);
if (topLevel)
@ -1243,16 +1247,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private bool AcceptCondition()
{
if (At(CSharpTokenType.LeftParenthesis))
if (At(SyntaxKind.LeftParenthesis))
{
var complete = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates);
if (!complete)
{
AcceptUntil(CSharpTokenType.NewLine);
AcceptUntil(SyntaxKind.NewLine);
}
else
{
Optional(CSharpTokenType.RightParenthesis);
Optional(SyntaxKind.RightParenthesis);
}
return complete;
}
@ -1280,16 +1284,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
var type = CurrentToken.Type;
var type = CurrentToken.Kind;
var loc = CurrentStart;
// Both cases @: and @:: are triggered as markup, second colon in second case will be triggered as a plain text
var isSingleLineMarkup = type == CSharpTokenType.Transition &&
(NextIs(CSharpTokenType.Colon, CSharpTokenType.DoubleColon));
var isSingleLineMarkup = type == SyntaxKind.Transition &&
(NextIs(SyntaxKind.Colon, SyntaxKind.DoubleColon));
var isMarkup = isSingleLineMarkup ||
type == CSharpTokenType.LessThan ||
(type == CSharpTokenType.Transition && NextIs(CSharpTokenType.LessThan));
type == SyntaxKind.LessThan ||
(type == SyntaxKind.Transition && NextIs(SyntaxKind.LessThan));
if (Context.DesignTimeMode || !isMarkup)
{
@ -1321,7 +1325,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (isMarkup)
{
if (type == CSharpTokenType.Transition && !isSingleLineMarkup)
if (type == SyntaxKind.Transition && !isSingleLineMarkup)
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_AtInCodeMustBeFollowedByColonParenOrIdentifierStart(
@ -1331,7 +1335,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Markup block
Output(SpanKindInternal.Code);
if (Context.DesignTimeMode && CurrentToken != null &&
(CurrentToken.Type == CSharpTokenType.LessThan || CurrentToken.Type == CSharpTokenType.Transition))
(CurrentToken.Kind == SyntaxKind.LessThan || CurrentToken.Kind == SyntaxKind.Transition))
{
PutCurrentBack();
}
@ -1344,33 +1348,33 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
private void HandleStatement(Block block, CSharpTokenType type)
private void HandleStatement(Block block, SyntaxKind type)
{
switch (type)
{
case CSharpTokenType.RazorCommentTransition:
case SyntaxKind.RazorCommentTransition:
Output(SpanKindInternal.Code);
RazorComment();
Statement(block);
break;
case CSharpTokenType.LeftBrace:
case SyntaxKind.LeftBrace:
// Verbatim Block
block = block ?? new Block(Resources.BlockName_Code, CurrentStart);
AcceptAndMoveNext();
CodeBlock(block);
break;
case CSharpTokenType.Keyword:
case SyntaxKind.Keyword:
// Keyword block
HandleKeyword(false, StandardStatement);
break;
case CSharpTokenType.Transition:
case SyntaxKind.Transition:
// Embedded Expression block
EmbeddedExpression();
break;
case CSharpTokenType.RightBrace:
case SyntaxKind.RightBrace:
// Possible end of Code Block, just run the continuation
break;
case CSharpTokenType.Comment:
case SyntaxKind.CSharpComment:
AcceptAndMoveNext();
break;
default:
@ -1383,11 +1387,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void EmbeddedExpression()
{
// First, verify the type of the block
Assert(CSharpTokenType.Transition);
Assert(SyntaxKind.Transition);
var transition = CurrentToken;
NextToken();
if (At(CSharpTokenType.Transition))
if (At(SyntaxKind.Transition))
{
// Escaped "@"
Output(SpanKindInternal.Code);
@ -1397,14 +1401,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.Code);
Assert(CSharpTokenType.Transition);
Assert(SyntaxKind.Transition);
AcceptAndMoveNext();
StandardStatement();
}
else
{
// Throw errors as necessary, but continue parsing
if (At(CSharpTokenType.LeftBrace))
if (At(SyntaxKind.LeftBrace))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnexpectedNestedCodeBlock(
@ -1428,48 +1432,48 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
var bookmark = CurrentStart.AbsoluteIndex;
var read = ReadWhile(token =>
token.Type != CSharpTokenType.Semicolon &&
token.Type != CSharpTokenType.RazorCommentTransition &&
token.Type != CSharpTokenType.Transition &&
token.Type != CSharpTokenType.LeftBrace &&
token.Type != CSharpTokenType.LeftParenthesis &&
token.Type != CSharpTokenType.LeftBracket &&
token.Type != CSharpTokenType.RightBrace);
token.Kind != SyntaxKind.Semicolon &&
token.Kind != SyntaxKind.RazorCommentTransition &&
token.Kind != SyntaxKind.Transition &&
token.Kind != SyntaxKind.LeftBrace &&
token.Kind != SyntaxKind.LeftParenthesis &&
token.Kind != SyntaxKind.LeftBracket &&
token.Kind != SyntaxKind.RightBrace);
if (At(CSharpTokenType.LeftBrace) ||
At(CSharpTokenType.LeftParenthesis) ||
At(CSharpTokenType.LeftBracket))
if (At(SyntaxKind.LeftBrace) ||
At(SyntaxKind.LeftParenthesis) ||
At(SyntaxKind.LeftBracket))
{
Accept(read);
if (Balance(BalancingModes.AllowCommentsAndTemplates | BalancingModes.BacktrackOnFailure))
{
Optional(CSharpTokenType.RightBrace);
Optional(SyntaxKind.RightBrace);
}
else
{
// Recovery
AcceptUntil(CSharpTokenType.LessThan, CSharpTokenType.RightBrace);
AcceptUntil(SyntaxKind.LessThan, SyntaxKind.RightBrace);
return;
}
}
else if (At(CSharpTokenType.Transition) && (NextIs(CSharpTokenType.LessThan, CSharpTokenType.Colon)))
else if (At(SyntaxKind.Transition) && (NextIs(SyntaxKind.LessThan, SyntaxKind.Colon)))
{
Accept(read);
Output(SpanKindInternal.Code);
Template();
}
else if (At(CSharpTokenType.RazorCommentTransition))
else if (At(SyntaxKind.RazorCommentTransition))
{
Accept(read);
RazorComment();
}
else if (At(CSharpTokenType.Semicolon))
else if (At(SyntaxKind.Semicolon))
{
Accept(read);
AcceptAndMoveNext();
return;
}
else if (At(CSharpTokenType.RightBrace))
else if (At(SyntaxKind.RightBrace))
{
Accept(read);
return;
@ -1478,7 +1482,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Context.Source.Position = bookmark;
NextToken();
AcceptUntil(CSharpTokenType.LessThan, CSharpTokenType.LeftBrace, CSharpTokenType.RightBrace);
AcceptUntil(SyntaxKind.LessThan, SyntaxKind.LeftBrace, SyntaxKind.RightBrace);
return;
}
}
@ -1492,7 +1496,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void CodeBlock(bool acceptTerminatingBrace, Block block)
{
EnsureCurrent();
while (!EndOfFile && !At(CSharpTokenType.RightBrace))
while (!EndOfFile && !At(SyntaxKind.RightBrace))
{
// Parse a statement, then return here
Statement();
@ -1507,7 +1511,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else if (acceptTerminatingBrace)
{
Assert(CSharpTokenType.RightBrace);
Assert(SyntaxKind.RightBrace);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
AcceptAndMoveNext();
}
@ -1515,8 +1519,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void HandleKeyword(bool topLevel, Action fallback)
{
Debug.Assert(CurrentToken.Type == CSharpTokenType.Keyword && CurrentToken.Keyword != null);
if (_keywordParsers.TryGetValue(CurrentToken.Keyword.Value, out var handler))
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword && result.HasValue);
if (_keywordParsers.TryGetValue(result.Value, out var handler))
{
handler(topLevel);
}
@ -1526,12 +1531,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
private IEnumerable<CSharpToken> SkipToNextImportantToken()
private IEnumerable<SyntaxToken> SkipToNextImportantToken()
{
while (!EndOfFile)
{
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
if (At(CSharpTokenType.RazorCommentTransition))
if (At(SyntaxKind.RazorCommentTransition))
{
Accept(whitespace);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
@ -1542,7 +1547,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return whitespace;
}
}
return Enumerable.Empty<CSharpToken>();
return Enumerable.Empty<SyntaxToken>();
}
// Common code for Parsers, but FxCop REALLY doesn't like it in the base class.. moving it here for now.
@ -1644,8 +1649,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
for (var i = 0; i < descriptor.Tokens.Count; i++)
{
if (!At(CSharpTokenType.WhiteSpace) &&
!At(CSharpTokenType.NewLine) &&
if (!At(SyntaxKind.Whitespace) &&
!At(SyntaxKind.NewLine) &&
!EndOfFile)
{
Context.ErrorSink.OnError(
@ -1662,24 +1667,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
tokenDescriptor.Kind == DirectiveTokenKind.Type)
{
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.Code, AcceptedCharactersInternal.WhiteSpace);
Output(SpanKindInternal.Code, AcceptedCharactersInternal.Whitespace);
if (EndOfFile || At(CSharpTokenType.NewLine))
if (EndOfFile || At(SyntaxKind.NewLine))
{
// Add a marker token to provide CSharp intellisense when we start typing the directive token.
AddMarkerTokenIfNecessary();
Span.ChunkGenerator = new DirectiveTokenChunkGenerator(tokenDescriptor);
Span.EditHandler = new DirectiveTokenEditHandler(Language.TokenizeString);
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhiteSpace);
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhitespace);
}
}
else
{
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.WhiteSpace);
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.Whitespace);
}
if (tokenDescriptor.Optional && (EndOfFile || At(CSharpTokenType.NewLine)))
if (tokenDescriptor.Optional && (EndOfFile || At(SyntaxKind.NewLine)))
{
break;
}
@ -1718,7 +1723,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
break;
case DirectiveTokenKind.Member:
if (At(CSharpTokenType.Identifier))
if (At(SyntaxKind.Identifier))
{
AcceptAndMoveNext();
}
@ -1732,7 +1737,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
break;
case DirectiveTokenKind.String:
if (At(CSharpTokenType.StringLiteral) && CurrentToken.Errors.Count == 0)
if (At(SyntaxKind.StringLiteral) && !CurrentToken.ContainsDiagnostics)
{
AcceptAndMoveNext();
}
@ -1748,7 +1753,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Span.ChunkGenerator = new DirectiveTokenChunkGenerator(tokenDescriptor);
Span.EditHandler = new DirectiveTokenEditHandler(Language.TokenizeString);
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhiteSpace);
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhitespace);
}
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
@ -1757,15 +1762,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
switch (descriptor.Kind)
{
case DirectiveKind.SingleLine:
Output(SpanKindInternal.None, AcceptedCharactersInternal.WhiteSpace);
Output(SpanKindInternal.None, AcceptedCharactersInternal.Whitespace);
Optional(CSharpTokenType.Semicolon);
Optional(SyntaxKind.Semicolon);
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.MetaCode, AcceptedCharactersInternal.WhiteSpace);
Output(SpanKindInternal.MetaCode, AcceptedCharactersInternal.Whitespace);
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
if (At(CSharpTokenType.NewLine))
if (At(SyntaxKind.NewLine))
{
AcceptAndMoveNext();
}
@ -1782,11 +1787,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// This should contain the optional whitespace after the optional semicolon and the new line.
// Output as Markup as we want intellisense here.
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.WhiteSpace);
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.Whitespace);
break;
case DirectiveKind.RazorBlock:
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.AllWhiteSpace);
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.AllWhitespace);
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
{
@ -1811,12 +1816,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
break;
case DirectiveKind.CodeBlock:
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.AllWhiteSpace);
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.AllWhitespace);
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
{
NextToken();
Balance(BalancingModes.NoErrorOnFailure, CSharpTokenType.LeftBrace, CSharpTokenType.RightBrace, startingBraceLocation);
Balance(BalancingModes.NoErrorOnFailure, SyntaxKind.LeftBrace, SyntaxKind.RightBrace, startingBraceLocation);
Span.ChunkGenerator = new StatementChunkGenerator();
var existingEditHandler = Span.EditHandler;
Span.EditHandler = new CodeBlockEditHandler(Language.TokenizeString);
@ -1868,7 +1873,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
RazorDiagnosticFactory.CreateParsing_UnexpectedEOFAfterDirective(
new SourceSpan(CurrentStart, contentLength: 1 /* { */), descriptor.Directive, "{"));
}
else if (!At(CSharpTokenType.LeftBrace))
else if (!At(SyntaxKind.LeftBrace))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_UnexpectedDirectiveLiteral(
@ -1886,7 +1891,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
parseChildren(startingBraceLocation);
Span.ChunkGenerator = SpanChunkGenerator.Null;
if (!Optional(CSharpTokenType.RightBrace))
if (!Optional(SyntaxKind.RightBrace))
{
editHandler.AutoCompleteString = "}";
Context.ErrorSink.OnError(
@ -1982,7 +1987,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Ex: @addTagHelper "*, Microsoft.AspNetCore.CoolLibrary"
// ^ ^
// Start End
if (Span.Tokens.Count == 1 && (Span.Tokens[0] as CSharpToken)?.Type == CSharpTokenType.StringLiteral)
if (Span.Tokens.Count == 1 && (Span.Tokens[0] as SyntaxToken)?.Kind == SyntaxKind.StringLiteral)
{
offset += Span.Tokens[0].Content.IndexOf(directiveText, StringComparison.Ordinal);
@ -2081,7 +2086,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
[Conditional("DEBUG")]
protected void AssertDirective(string directive)
{
Debug.Assert(CurrentToken.Type == CSharpTokenType.Identifier || CurrentToken.Type == CSharpTokenType.Keyword);
Debug.Assert(CurrentToken.Kind == SyntaxKind.Identifier || CurrentToken.Kind == SyntaxKind.Keyword);
Debug.Assert(string.Equals(CurrentToken.Content, directive, StringComparison.Ordinal));
}
@ -2108,18 +2113,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var keywordLength = Span.End.AbsoluteIndex - Span.Start.AbsoluteIndex;
var foundWhitespace = At(CSharpTokenType.WhiteSpace);
var foundWhitespace = At(SyntaxKind.Whitespace);
// If we found whitespace then any content placed within the whitespace MAY cause a destructive change
// to the document. We can't accept it.
var acceptedCharacters = foundWhitespace ? AcceptedCharactersInternal.None : AcceptedCharactersInternal.AnyExceptNewline;
Output(SpanKindInternal.MetaCode, acceptedCharacters);
AcceptWhile(CSharpTokenType.WhiteSpace);
AcceptWhile(SyntaxKind.Whitespace);
Span.ChunkGenerator = SpanChunkGenerator.Null;
Output(SpanKindInternal.Markup, acceptedCharacters);
if (EndOfFile || At(CSharpTokenType.NewLine))
if (EndOfFile || At(SyntaxKind.NewLine))
{
Context.ErrorSink.OnError(
RazorDiagnosticFactory.CreateParsing_DirectiveMustHaveValue(
@ -2134,7 +2139,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Parse to the end of the line. Essentially accepts anything until end of line, comments, invalid code
// etc.
AcceptUntil(CSharpTokenType.NewLine);
AcceptUntil(SyntaxKind.NewLine);
// Pull out the value and remove whitespaces and optional quotes
var rawValue = string.Concat(Span.Tokens.Select(s => s.Content)).Trim();
@ -2170,7 +2175,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Start = start;
}
public Block(CSharpToken token, SourceLocation start)
public Block(SyntaxToken token, SourceLocation start)
: this(GetName(token), start)
{
}
@ -2178,11 +2183,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public string Name { get; set; }
public SourceLocation Start { get; set; }
private static string GetName(CSharpToken token)
private static string GetName(SyntaxToken token)
{
if (token.Type == CSharpTokenType.Keyword)
var result = CSharpTokenizer.GetTokenKeyword(token);
if (result.HasValue && token.Kind == SyntaxKind.Keyword)
{
return CSharpLanguageCharacteristics.GetKeyword(token.Keyword.Value);
return CSharpLanguageCharacteristics.GetKeyword(result.Value);
}
return token.Content;
}

View File

@ -3,64 +3,65 @@
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class CSharpLanguageCharacteristics : LanguageCharacteristics<CSharpTokenizer, CSharpToken, CSharpTokenType>
internal class CSharpLanguageCharacteristics : LanguageCharacteristics<CSharpTokenizer>
{
private static readonly CSharpLanguageCharacteristics _instance = new CSharpLanguageCharacteristics();
private static Dictionary<CSharpTokenType, string> _tokenSamples = new Dictionary<CSharpTokenType, string>()
private static Dictionary<SyntaxKind, string> _tokenSamples = new Dictionary<SyntaxKind, string>()
{
{ 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, "@" },
{ SyntaxKind.Arrow, "->" },
{ SyntaxKind.Minus, "-" },
{ SyntaxKind.Decrement, "--" },
{ SyntaxKind.MinusAssign, "-=" },
{ SyntaxKind.NotEqual, "!=" },
{ SyntaxKind.Not, "!" },
{ SyntaxKind.Modulo, "%" },
{ SyntaxKind.ModuloAssign, "%=" },
{ SyntaxKind.AndAssign, "&=" },
{ SyntaxKind.And, "&" },
{ SyntaxKind.DoubleAnd, "&&" },
{ SyntaxKind.LeftParenthesis, "(" },
{ SyntaxKind.RightParenthesis, ")" },
{ SyntaxKind.Star, "*" },
{ SyntaxKind.MultiplyAssign, "*=" },
{ SyntaxKind.Comma, "," },
{ SyntaxKind.Dot, "." },
{ SyntaxKind.Slash, "/" },
{ SyntaxKind.DivideAssign, "/=" },
{ SyntaxKind.DoubleColon, "::" },
{ SyntaxKind.Colon, ":" },
{ SyntaxKind.Semicolon, ";" },
{ SyntaxKind.QuestionMark, "?" },
{ SyntaxKind.NullCoalesce, "??" },
{ SyntaxKind.RightBracket, "]" },
{ SyntaxKind.LeftBracket, "[" },
{ SyntaxKind.XorAssign, "^=" },
{ SyntaxKind.Xor, "^" },
{ SyntaxKind.LeftBrace, "{" },
{ SyntaxKind.OrAssign, "|=" },
{ SyntaxKind.DoubleOr, "||" },
{ SyntaxKind.Or, "|" },
{ SyntaxKind.RightBrace, "}" },
{ SyntaxKind.Tilde, "~" },
{ SyntaxKind.Plus, "+" },
{ SyntaxKind.PlusAssign, "+=" },
{ SyntaxKind.Increment, "++" },
{ SyntaxKind.LessThan, "<" },
{ SyntaxKind.LessThanEqual, "<=" },
{ SyntaxKind.LeftShift, "<<" },
{ SyntaxKind.LeftShiftAssign, "<<=" },
{ SyntaxKind.Assign, "=" },
{ SyntaxKind.Equals, "==" },
{ SyntaxKind.GreaterThan, ">" },
{ SyntaxKind.GreaterThanEqual, ">=" },
{ SyntaxKind.RightShift, ">>" },
{ SyntaxKind.RightShiftAssign, ">>=" },
{ SyntaxKind.Hash, "#" },
{ SyntaxKind.Transition, "@" },
};
protected CSharpLanguageCharacteristics()
@ -74,35 +75,35 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return new CSharpTokenizer(source);
}
protected override CSharpToken CreateToken(string content, CSharpTokenType type, IReadOnlyList<RazorDiagnostic> errors)
protected override SyntaxToken CreateToken(string content, SyntaxKind kind, IReadOnlyList<RazorDiagnostic> errors)
{
return new CSharpToken(content, type, errors);
return SyntaxFactory.Token(kind, content, errors);
}
public override string GetSample(CSharpTokenType type)
public override string GetSample(SyntaxKind kind)
{
string sample;
if (!_tokenSamples.TryGetValue(type, out sample))
if (!_tokenSamples.TryGetValue(kind, out sample))
{
switch (type)
switch (kind)
{
case CSharpTokenType.Identifier:
case SyntaxKind.Identifier:
return Resources.CSharpToken_Identifier;
case CSharpTokenType.Keyword:
case SyntaxKind.Keyword:
return Resources.CSharpToken_Keyword;
case CSharpTokenType.IntegerLiteral:
case SyntaxKind.IntegerLiteral:
return Resources.CSharpToken_IntegerLiteral;
case CSharpTokenType.NewLine:
case SyntaxKind.NewLine:
return Resources.CSharpToken_Newline;
case CSharpTokenType.WhiteSpace:
case SyntaxKind.Whitespace:
return Resources.CSharpToken_Whitespace;
case CSharpTokenType.Comment:
case SyntaxKind.CSharpComment:
return Resources.CSharpToken_Comment;
case CSharpTokenType.RealLiteral:
case SyntaxKind.RealLiteral:
return Resources.CSharpToken_RealLiteral;
case CSharpTokenType.CharacterLiteral:
case SyntaxKind.CharacterLiteral:
return Resources.CSharpToken_CharacterLiteral;
case CSharpTokenType.StringLiteral:
case SyntaxKind.StringLiteral:
return Resources.CSharpToken_StringLiteral;
default:
return Resources.Token_Unknown;
@ -111,59 +112,59 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return sample;
}
public override CSharpToken CreateMarkerToken()
public override SyntaxToken CreateMarkerToken()
{
return new CSharpToken(string.Empty, CSharpTokenType.Unknown);
return SyntaxFactory.Token(SyntaxKind.Unknown, string.Empty);
}
public override CSharpTokenType GetKnownTokenType(KnownTokenType type)
public override SyntaxKind GetKnownTokenType(KnownTokenType type)
{
switch (type)
{
case KnownTokenType.Identifier:
return CSharpTokenType.Identifier;
return SyntaxKind.Identifier;
case KnownTokenType.Keyword:
return CSharpTokenType.Keyword;
return SyntaxKind.Keyword;
case KnownTokenType.NewLine:
return CSharpTokenType.NewLine;
return SyntaxKind.NewLine;
case KnownTokenType.WhiteSpace:
return CSharpTokenType.WhiteSpace;
return SyntaxKind.Whitespace;
case KnownTokenType.Transition:
return CSharpTokenType.Transition;
return SyntaxKind.Transition;
case KnownTokenType.CommentStart:
return CSharpTokenType.RazorCommentTransition;
return SyntaxKind.RazorCommentTransition;
case KnownTokenType.CommentStar:
return CSharpTokenType.RazorCommentStar;
return SyntaxKind.RazorCommentStar;
case KnownTokenType.CommentBody:
return CSharpTokenType.RazorComment;
return SyntaxKind.RazorCommentLiteral;
default:
return CSharpTokenType.Unknown;
return SyntaxKind.Unknown;
}
}
public override CSharpTokenType FlipBracket(CSharpTokenType bracket)
public override SyntaxKind FlipBracket(SyntaxKind bracket)
{
switch (bracket)
{
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;
case SyntaxKind.LeftBrace:
return SyntaxKind.RightBrace;
case SyntaxKind.LeftBracket:
return SyntaxKind.RightBracket;
case SyntaxKind.LeftParenthesis:
return SyntaxKind.RightParenthesis;
case SyntaxKind.LessThan:
return SyntaxKind.GreaterThan;
case SyntaxKind.RightBrace:
return SyntaxKind.LeftBrace;
case SyntaxKind.RightBracket:
return SyntaxKind.LeftBracket;
case SyntaxKind.RightParenthesis:
return SyntaxKind.LeftParenthesis;
case SyntaxKind.GreaterThan:
return SyntaxKind.LessThan;
default:
Debug.Fail("FlipBracket must be called with a bracket character");
return CSharpTokenType.Unknown;
return SyntaxKind.Unknown;
}
}

View File

@ -1,49 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class CSharpToken : TokenBase<CSharpTokenType>
{
public CSharpToken(
string content,
CSharpTokenType type)
: base(content, type, RazorDiagnostic.EmptyArray)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
}
public CSharpToken(
string content,
CSharpTokenType type,
IReadOnlyList<RazorDiagnostic> errors)
: base(content, type, errors)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
}
public CSharpKeyword? Keyword { get; set; }
public override bool Equals(object obj)
{
var other = obj as CSharpToken;
return base.Equals(other) &&
other.Keyword == Keyword;
}
public override int GetHashCode()
{
// Hash code should include only immutable properties.
return base.GetHashCode();
}
}
}

View File

@ -5,12 +5,13 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class CSharpTokenizer : Tokenizer<CSharpToken, CSharpTokenType>
internal class CSharpTokenizer : Tokenizer
{
private Dictionary<char, Func<CSharpTokenType>> _operatorHandlers;
private Dictionary<char, Func<SyntaxKind>> _operatorHandlers;
private static readonly Dictionary<string, CSharpKeyword> _keywords = new Dictionary<string, CSharpKeyword>(StringComparer.Ordinal)
{
@ -100,31 +101,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
base.CurrentState = StartState;
_operatorHandlers = new Dictionary<char, Func<CSharpTokenType>>()
_operatorHandlers = new Dictionary<char, Func<SyntaxKind>>()
{
{ '-', MinusOperator },
{ '<', LessThanOperator },
{ '>', GreaterThanOperator },
{ '&', 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 }
{ '&', CreateTwoCharOperatorHandler(SyntaxKind.And, '=', SyntaxKind.AndAssign, '&', SyntaxKind.DoubleAnd) },
{ '|', CreateTwoCharOperatorHandler(SyntaxKind.Or, '=', SyntaxKind.OrAssign, '|', SyntaxKind.DoubleOr) },
{ '+', CreateTwoCharOperatorHandler(SyntaxKind.Plus, '=', SyntaxKind.PlusAssign, '+', SyntaxKind.Increment) },
{ '=', CreateTwoCharOperatorHandler(SyntaxKind.Assign, '=', SyntaxKind.Equals, '>', SyntaxKind.GreaterThanEqual) },
{ '!', CreateTwoCharOperatorHandler(SyntaxKind.Not, '=', SyntaxKind.NotEqual) },
{ '%', CreateTwoCharOperatorHandler(SyntaxKind.Modulo, '=', SyntaxKind.ModuloAssign) },
{ '*', CreateTwoCharOperatorHandler(SyntaxKind.Star, '=', SyntaxKind.MultiplyAssign) },
{ ':', CreateTwoCharOperatorHandler(SyntaxKind.Colon, ':', SyntaxKind.DoubleColon) },
{ '?', CreateTwoCharOperatorHandler(SyntaxKind.QuestionMark, '?', SyntaxKind.NullCoalesce) },
{ '^', CreateTwoCharOperatorHandler(SyntaxKind.Xor, '=', SyntaxKind.XorAssign) },
{ '(', () => SyntaxKind.LeftParenthesis },
{ ')', () => SyntaxKind.RightParenthesis },
{ '{', () => SyntaxKind.LeftBrace },
{ '}', () => SyntaxKind.RightBrace },
{ '[', () => SyntaxKind.LeftBracket },
{ ']', () => SyntaxKind.RightBracket },
{ ',', () => SyntaxKind.Comma },
{ ';', () => SyntaxKind.Semicolon },
{ '~', () => SyntaxKind.Tilde },
{ '#', () => SyntaxKind.Hash }
};
}
@ -132,11 +133,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private new CSharpTokenizerState? CurrentState => (CSharpTokenizerState?)base.CurrentState;
public override CSharpTokenType RazorCommentType => CSharpTokenType.RazorComment;
public override SyntaxKind RazorCommentKind => SyntaxKind.RazorCommentLiteral;
public override CSharpTokenType RazorCommentTransitionType => CSharpTokenType.RazorCommentTransition;
public override SyntaxKind RazorCommentTransitionKind => SyntaxKind.RazorCommentTransition;
public override CSharpTokenType RazorCommentStarType => CSharpTokenType.RazorCommentStar;
public override SyntaxKind RazorCommentStarKind => SyntaxKind.RazorCommentStar;
protected override StateResult Dispatch()
{
@ -169,7 +170,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Optimize memory allocation by returning constants for the most frequent cases
protected override string GetTokenContent(CSharpTokenType type)
protected override string GetTokenContent(SyntaxKind type)
{
var tokenLength = Buffer.Length;
@ -177,7 +178,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
switch (type)
{
case CSharpTokenType.IntegerLiteral:
case SyntaxKind.IntegerLiteral:
switch (Buffer[0])
{
case '0':
@ -202,13 +203,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return "9";
}
break;
case CSharpTokenType.NewLine:
case SyntaxKind.NewLine:
if (Buffer[0] == '\n')
{
return "\n";
}
break;
case CSharpTokenType.WhiteSpace:
case SyntaxKind.Whitespace:
if (Buffer[0] == ' ')
{
return " ";
@ -218,57 +219,57 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return "\t";
}
break;
case CSharpTokenType.Minus:
case SyntaxKind.Minus:
return "-";
case CSharpTokenType.Not:
case SyntaxKind.Not:
return "!";
case CSharpTokenType.Modulo:
case SyntaxKind.Modulo:
return "%";
case CSharpTokenType.And:
case SyntaxKind.And:
return "&";
case CSharpTokenType.LeftParenthesis:
case SyntaxKind.LeftParenthesis:
return "(";
case CSharpTokenType.RightParenthesis:
case SyntaxKind.RightParenthesis:
return ")";
case CSharpTokenType.Star:
case SyntaxKind.Star:
return "*";
case CSharpTokenType.Comma:
case SyntaxKind.Comma:
return ",";
case CSharpTokenType.Dot:
case SyntaxKind.Dot:
return ".";
case CSharpTokenType.Slash:
case SyntaxKind.Slash:
return "/";
case CSharpTokenType.Colon:
case SyntaxKind.Colon:
return ":";
case CSharpTokenType.Semicolon:
case SyntaxKind.Semicolon:
return ";";
case CSharpTokenType.QuestionMark:
case SyntaxKind.QuestionMark:
return "?";
case CSharpTokenType.RightBracket:
case SyntaxKind.RightBracket:
return "]";
case CSharpTokenType.LeftBracket:
case SyntaxKind.LeftBracket:
return "[";
case CSharpTokenType.Xor:
case SyntaxKind.Xor:
return "^";
case CSharpTokenType.LeftBrace:
case SyntaxKind.LeftBrace:
return "{";
case CSharpTokenType.Or:
case SyntaxKind.Or:
return "|";
case CSharpTokenType.RightBrace:
case SyntaxKind.RightBrace:
return "}";
case CSharpTokenType.Tilde:
case SyntaxKind.Tilde:
return "~";
case CSharpTokenType.Plus:
case SyntaxKind.Plus:
return "+";
case CSharpTokenType.LessThan:
case SyntaxKind.LessThan:
return "<";
case CSharpTokenType.Assign:
case SyntaxKind.Assign:
return "=";
case CSharpTokenType.GreaterThan:
case SyntaxKind.GreaterThan:
return ">";
case CSharpTokenType.Hash:
case SyntaxKind.Hash:
return "#";
case CSharpTokenType.Transition:
case SyntaxKind.Transition:
return "@";
}
@ -277,53 +278,53 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
switch (type)
{
case CSharpTokenType.NewLine:
case SyntaxKind.NewLine:
return "\r\n";
case CSharpTokenType.Arrow:
case SyntaxKind.Arrow:
return "->";
case CSharpTokenType.Decrement:
case SyntaxKind.Decrement:
return "--";
case CSharpTokenType.MinusAssign:
case SyntaxKind.MinusAssign:
return "-=";
case CSharpTokenType.NotEqual:
case SyntaxKind.NotEqual:
return "!=";
case CSharpTokenType.ModuloAssign:
case SyntaxKind.ModuloAssign:
return "%=";
case CSharpTokenType.AndAssign:
case SyntaxKind.AndAssign:
return "&=";
case CSharpTokenType.DoubleAnd:
case SyntaxKind.DoubleAnd:
return "&&";
case CSharpTokenType.MultiplyAssign:
case SyntaxKind.MultiplyAssign:
return "*=";
case CSharpTokenType.DivideAssign:
case SyntaxKind.DivideAssign:
return "/=";
case CSharpTokenType.DoubleColon:
case SyntaxKind.DoubleColon:
return "::";
case CSharpTokenType.NullCoalesce:
case SyntaxKind.NullCoalesce:
return "??";
case CSharpTokenType.XorAssign:
case SyntaxKind.XorAssign:
return "^=";
case CSharpTokenType.OrAssign:
case SyntaxKind.OrAssign:
return "|=";
case CSharpTokenType.DoubleOr:
case SyntaxKind.DoubleOr:
return "||";
case CSharpTokenType.PlusAssign:
case SyntaxKind.PlusAssign:
return "+=";
case CSharpTokenType.Increment:
case SyntaxKind.Increment:
return "++";
case CSharpTokenType.LessThanEqual:
case SyntaxKind.LessThanEqual:
return "<=";
case CSharpTokenType.LeftShift:
case SyntaxKind.LeftShift:
return "<<";
case CSharpTokenType.Equals:
case SyntaxKind.Equals:
return "==";
case CSharpTokenType.GreaterThanEqual:
case SyntaxKind.GreaterThanEqual:
if (Buffer[0] == '=')
{
return "=>";
}
return ">=";
case CSharpTokenType.RightShift:
case SyntaxKind.RightShift:
return ">>";
@ -333,9 +334,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
switch (type)
{
case CSharpTokenType.LeftShiftAssign:
case SyntaxKind.LeftShiftAssign:
return "<<=";
case CSharpTokenType.RightShiftAssign:
case SyntaxKind.RightShiftAssign:
return ">>=";
}
}
@ -343,9 +344,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return base.GetTokenContent(type);
}
protected override CSharpToken CreateToken(string content, CSharpTokenType type, IReadOnlyList<RazorDiagnostic> errors)
protected override SyntaxToken CreateToken(string content, SyntaxKind kind, IReadOnlyList<RazorDiagnostic> errors)
{
return new CSharpToken(content, type, errors);
return SyntaxFactory.Token(kind, content, errors);
}
private StateResult Data()
@ -359,13 +360,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeCurrent();
}
return Stay(EndToken(CSharpTokenType.NewLine));
return Stay(EndToken(SyntaxKind.NewLine));
}
else if (ParserHelpers.IsWhitespace(CurrentCharacter))
{
// CSharp Spec §2.3.3
TakeUntil(c => !ParserHelpers.IsWhitespace(c));
return Stay(EndToken(CSharpTokenType.WhiteSpace));
return Stay(EndToken(SyntaxKind.Whitespace));
}
else if (IsIdentifierStart(CurrentCharacter))
{
@ -390,7 +391,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return RealLiteral();
}
return Stay(Single(CSharpTokenType.Dot));
return Stay(Single(SyntaxKind.Dot));
case '/':
TakeCurrent();
if (CurrentCharacter == '/')
@ -406,11 +407,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else if (CurrentCharacter == '=')
{
TakeCurrent();
return Stay(EndToken(CSharpTokenType.DivideAssign));
return Stay(EndToken(SyntaxKind.DivideAssign));
}
else
{
return Stay(EndToken(CSharpTokenType.Slash));
return Stay(EndToken(SyntaxKind.Slash));
}
default:
return Stay(EndToken(Operator()));
@ -429,78 +430,78 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return Transition(
CSharpTokenizerState.AfterRazorCommentTransition,
EndToken(CSharpTokenType.RazorCommentTransition));
EndToken(SyntaxKind.RazorCommentTransition));
}
else if (CurrentCharacter == '@')
{
// Could be escaped comment transition
return Transition(
CSharpTokenizerState.EscapedRazorCommentTransition,
EndToken(CSharpTokenType.Transition));
EndToken(SyntaxKind.Transition));
}
return Stay(EndToken(CSharpTokenType.Transition));
return Stay(EndToken(SyntaxKind.Transition));
}
private StateResult EscapedRazorCommentTransition()
{
TakeCurrent();
return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Transition));
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.Transition));
}
private CSharpTokenType Operator()
private SyntaxKind Operator()
{
var first = CurrentCharacter;
TakeCurrent();
Func<CSharpTokenType> handler;
Func<SyntaxKind> handler;
if (_operatorHandlers.TryGetValue(first, out handler))
{
return handler();
}
return CSharpTokenType.Unknown;
return SyntaxKind.Unknown;
}
private CSharpTokenType LessThanOperator()
private SyntaxKind LessThanOperator()
{
if (CurrentCharacter == '=')
{
TakeCurrent();
return CSharpTokenType.LessThanEqual;
return SyntaxKind.LessThanEqual;
}
return CSharpTokenType.LessThan;
return SyntaxKind.LessThan;
}
private CSharpTokenType GreaterThanOperator()
private SyntaxKind GreaterThanOperator()
{
if (CurrentCharacter == '=')
{
TakeCurrent();
return CSharpTokenType.GreaterThanEqual;
return SyntaxKind.GreaterThanEqual;
}
return CSharpTokenType.GreaterThan;
return SyntaxKind.GreaterThan;
}
private CSharpTokenType MinusOperator()
private SyntaxKind MinusOperator()
{
if (CurrentCharacter == '>')
{
TakeCurrent();
return CSharpTokenType.Arrow;
return SyntaxKind.Arrow;
}
else if (CurrentCharacter == '-')
{
TakeCurrent();
return CSharpTokenType.Decrement;
return SyntaxKind.Decrement;
}
else if (CurrentCharacter == '=')
{
TakeCurrent();
return CSharpTokenType.MinusAssign;
return SyntaxKind.MinusAssign;
}
return CSharpTokenType.Minus;
return SyntaxKind.Minus;
}
private Func<CSharpTokenType> CreateTwoCharOperatorHandler(CSharpTokenType typeIfOnlyFirst, char second, CSharpTokenType typeIfBoth)
private Func<SyntaxKind> CreateTwoCharOperatorHandler(SyntaxKind typeIfOnlyFirst, char second, SyntaxKind typeIfBoth)
{
return () =>
{
@ -513,7 +514,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
};
}
private Func<CSharpTokenType> CreateTwoCharOperatorHandler(CSharpTokenType typeIfOnlyFirst, char option1, CSharpTokenType typeIfOption1, char option2, CSharpTokenType typeIfOption2)
private Func<SyntaxKind> CreateTwoCharOperatorHandler(SyntaxKind typeIfOnlyFirst, char option1, SyntaxKind typeIfOption1, char option2, SyntaxKind typeIfOption2)
{
return () =>
{
@ -550,14 +551,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
RazorDiagnosticFactory.CreateParsing_UnterminatedStringLiteral(
new SourceSpan(CurrentStart, contentLength: 1 /* end of file */)));
}
return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.StringLiteral));
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.StringLiteral));
}
private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', CSharpTokenType.CharacterLiteral);
private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', SyntaxKind.CharacterLiteral);
private StateResult QuotedStringLiteral() => QuotedLiteral('\"', CSharpTokenType.StringLiteral);
private StateResult QuotedStringLiteral() => QuotedLiteral('\"', SyntaxKind.StringLiteral);
private StateResult QuotedLiteral(char quote, CSharpTokenType literalType)
private StateResult QuotedLiteral(char quote, SyntaxKind literalType)
{
TakeUntil(c => c == '\\' || c == quote || ParserHelpers.IsNewLine(c));
if (CurrentCharacter == '\\')
@ -594,7 +595,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
RazorDiagnosticFactory.CreateParsing_BlockCommentNotTerminated(
new SourceSpan(CurrentStart, contentLength: 1 /* end of file */)));
return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Comment));
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.CSharpComment));
}
if (CurrentCharacter == '*')
{
@ -602,7 +603,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (CurrentCharacter == '/')
{
TakeCurrent();
return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Comment));
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.CSharpComment));
}
}
return Stay();
@ -612,7 +613,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private StateResult SingleLineComment()
{
TakeUntil(c => ParserHelpers.IsNewLine(c));
return Stay(EndToken(CSharpTokenType.Comment));
return Stay(EndToken(SyntaxKind.CSharpComment));
}
// CSharp Spec §2.4.4
@ -632,7 +633,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeUntil(c => !IsHexDigit(c));
TakeIntegerSuffix();
return Stay(EndToken(CSharpTokenType.IntegerLiteral));
return Stay(EndToken(SyntaxKind.IntegerLiteral));
}
private StateResult DecimalLiteral()
@ -650,7 +651,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else
{
TakeIntegerSuffix();
return Stay(EndToken(CSharpTokenType.IntegerLiteral));
return Stay(EndToken(SyntaxKind.IntegerLiteral));
}
}
@ -669,7 +670,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeCurrent();
}
return Stay(EndToken(CSharpTokenType.RealLiteral));
return Stay(EndToken(SyntaxKind.RealLiteral));
}
// CSharp Spec §2.4.4.3
@ -708,21 +709,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Debug.Assert(IsIdentifierStart(CurrentCharacter));
TakeCurrent();
TakeUntil(c => !IsIdentifierPart(c));
CSharpToken token = null;
SyntaxToken token = null;
if (HaveContent)
{
CSharpKeyword keyword;
var type = CSharpTokenType.Identifier;
var type = SyntaxKind.Identifier;
var tokenContent = Buffer.ToString();
if (_keywords.TryGetValue(tokenContent, out keyword))
{
type = CSharpTokenType.Keyword;
type = SyntaxKind.Keyword;
}
token = new CSharpToken(tokenContent, type)
{
Keyword = type == CSharpTokenType.Keyword ? (CSharpKeyword?)keyword : null,
};
token = SyntaxFactory.Token(type, tokenContent);
Buffer.Clear();
CurrentErrors.Clear();
@ -736,7 +734,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Transition((int)state, result: null);
}
private StateResult Transition(CSharpTokenizerState state, CSharpToken result)
private StateResult Transition(CSharpTokenizerState state, SyntaxToken result)
{
return Transition((int)state, result);
}
@ -780,6 +778,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return (value >= '0' && value <= '9') || (value >= 'A' && value <= 'F') || (value >= 'a' && value <= 'f');
}
internal static CSharpKeyword? GetTokenKeyword(SyntaxToken token)
{
if (token != null && _keywords.TryGetValue(token.Content, out var keyword))
{
return keyword;
}
return null;
}
private enum CSharpTokenizerState
{
Data,

View File

@ -4,12 +4,13 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class CodeBlockEditHandler : SpanEditHandler
{
public CodeBlockEditHandler(Func<string, IEnumerable<IToken>> tokenizer) : base(tokenizer)
public CodeBlockEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer) : base(tokenizer)
{
}

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.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@ -17,11 +18,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected override StateResult Dispatch()
{
var result = base.Dispatch();
if (result.Result != null && !_visitedFirstTokenStart && IsValidTokenType(result.Result.Type))
if (result.Result != null && !_visitedFirstTokenStart && IsValidTokenType(result.Result.Kind))
{
_visitedFirstTokenStart = true;
}
else if (result.Result != null && _visitedFirstTokenStart && result.Result.Type == CSharpTokenType.NewLine)
else if (result.Result != null && _visitedFirstTokenStart && result.Result.Kind == SyntaxKind.NewLine)
{
_visitedFirstTokenLineEnd = true;
}
@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return result;
}
public override CSharpToken NextToken()
public override SyntaxToken NextToken()
{
// Post-Condition: Buffer should be empty at the start of Next()
Debug.Assert(Buffer.Length == 0);
@ -48,15 +49,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return token;
}
private bool IsValidTokenType(CSharpTokenType type)
private bool IsValidTokenType(SyntaxKind kind)
{
return type != CSharpTokenType.WhiteSpace &&
type != CSharpTokenType.NewLine &&
type != CSharpTokenType.Comment &&
type != CSharpTokenType.RazorComment &&
type != CSharpTokenType.RazorCommentStar &&
type != CSharpTokenType.RazorCommentTransition &&
type != CSharpTokenType.Transition;
return kind != SyntaxKind.Whitespace &&
kind != SyntaxKind.NewLine &&
kind != SyntaxKind.CSharpComment &&
kind != SyntaxKind.RazorCommentLiteral &&
kind != SyntaxKind.RazorCommentStar &&
kind != SyntaxKind.RazorCommentTransition &&
kind != SyntaxKind.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.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected override StateResult Dispatch()
{
var result = base.Dispatch();
if (result.Result != null && IsValidTokenType(result.Result.Type))
if (result.Result != null && IsValidTokenType(result.Result.Kind))
{
_visitedFirstTokenStart = true;
}
@ -24,7 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return result;
}
public override HtmlToken NextToken()
public override SyntaxToken NextToken()
{
// Post-Condition: Buffer should be empty at the start of Next()
Debug.Assert(Buffer.Length == 0);
@ -43,14 +44,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return token;
}
private bool IsValidTokenType(HtmlTokenType type)
private bool IsValidTokenType(SyntaxKind kind)
{
return type != HtmlTokenType.WhiteSpace &&
type != HtmlTokenType.NewLine &&
type != HtmlTokenType.RazorComment &&
type != HtmlTokenType.RazorCommentStar &&
type != HtmlTokenType.RazorCommentTransition &&
type != HtmlTokenType.Transition;
return kind != SyntaxKind.Whitespace &&
kind != SyntaxKind.NewLine &&
kind != SyntaxKind.RazorCommentLiteral &&
kind != SyntaxKind.RazorCommentStar &&
kind != SyntaxKind.RazorCommentTransition &&
kind != SyntaxKind.Transition;
}
}
}

View File

@ -3,10 +3,11 @@
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class HtmlLanguageCharacteristics : LanguageCharacteristics<HtmlTokenizer, HtmlToken, HtmlTokenType>
internal class HtmlLanguageCharacteristics : LanguageCharacteristics<HtmlTokenizer>
{
private static readonly HtmlLanguageCharacteristics _instance = new HtmlLanguageCharacteristics();
@ -19,47 +20,47 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
get { return _instance; }
}
public override string GetSample(HtmlTokenType type)
public override string GetSample(SyntaxKind type)
{
switch (type)
{
case HtmlTokenType.Text:
case SyntaxKind.Text:
return Resources.HtmlToken_Text;
case HtmlTokenType.WhiteSpace:
case SyntaxKind.Whitespace:
return Resources.HtmlToken_WhiteSpace;
case HtmlTokenType.NewLine:
case SyntaxKind.NewLine:
return Resources.HtmlToken_NewLine;
case HtmlTokenType.OpenAngle:
case SyntaxKind.OpenAngle:
return "<";
case HtmlTokenType.Bang:
case SyntaxKind.Bang:
return "!";
case HtmlTokenType.ForwardSlash:
case SyntaxKind.ForwardSlash:
return "/";
case HtmlTokenType.QuestionMark:
case SyntaxKind.QuestionMark:
return "?";
case HtmlTokenType.DoubleHyphen:
case SyntaxKind.DoubleHyphen:
return "--";
case HtmlTokenType.LeftBracket:
case SyntaxKind.LeftBracket:
return "[";
case HtmlTokenType.CloseAngle:
case SyntaxKind.CloseAngle:
return ">";
case HtmlTokenType.RightBracket:
case SyntaxKind.RightBracket:
return "]";
case HtmlTokenType.Equals:
case SyntaxKind.Equals:
return "=";
case HtmlTokenType.DoubleQuote:
case SyntaxKind.DoubleQuote:
return "\"";
case HtmlTokenType.SingleQuote:
case SyntaxKind.SingleQuote:
return "'";
case HtmlTokenType.Transition:
case SyntaxKind.Transition:
return "@";
case HtmlTokenType.Colon:
case SyntaxKind.Colon:
return ":";
case HtmlTokenType.RazorComment:
case SyntaxKind.RazorCommentLiteral:
return Resources.HtmlToken_RazorComment;
case HtmlTokenType.RazorCommentStar:
case SyntaxKind.RazorCommentStar:
return "*";
case HtmlTokenType.RazorCommentTransition:
case SyntaxKind.RazorCommentTransition:
return "@";
default:
return Resources.Token_Unknown;
@ -71,57 +72,57 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return new HtmlTokenizer(source);
}
public override HtmlTokenType FlipBracket(HtmlTokenType bracket)
public override SyntaxKind FlipBracket(SyntaxKind bracket)
{
switch (bracket)
{
case HtmlTokenType.LeftBracket:
return HtmlTokenType.RightBracket;
case HtmlTokenType.OpenAngle:
return HtmlTokenType.CloseAngle;
case HtmlTokenType.RightBracket:
return HtmlTokenType.LeftBracket;
case HtmlTokenType.CloseAngle:
return HtmlTokenType.OpenAngle;
case SyntaxKind.LeftBracket:
return SyntaxKind.RightBracket;
case SyntaxKind.OpenAngle:
return SyntaxKind.CloseAngle;
case SyntaxKind.RightBracket:
return SyntaxKind.LeftBracket;
case SyntaxKind.CloseAngle:
return SyntaxKind.OpenAngle;
default:
Debug.Fail("FlipBracket must be called with a bracket character");
return HtmlTokenType.Unknown;
return SyntaxKind.Unknown;
}
}
public override HtmlToken CreateMarkerToken()
public override SyntaxToken CreateMarkerToken()
{
return new HtmlToken(string.Empty, HtmlTokenType.Unknown);
return SyntaxFactory.Token(SyntaxKind.Unknown, string.Empty);
}
public override HtmlTokenType GetKnownTokenType(KnownTokenType type)
public override SyntaxKind GetKnownTokenType(KnownTokenType type)
{
switch (type)
{
case KnownTokenType.CommentStart:
return HtmlTokenType.RazorCommentTransition;
return SyntaxKind.RazorCommentTransition;
case KnownTokenType.CommentStar:
return HtmlTokenType.RazorCommentStar;
return SyntaxKind.RazorCommentStar;
case KnownTokenType.CommentBody:
return HtmlTokenType.RazorComment;
return SyntaxKind.RazorCommentLiteral;
case KnownTokenType.Identifier:
return HtmlTokenType.Text;
return SyntaxKind.Text;
case KnownTokenType.Keyword:
return HtmlTokenType.Text;
return SyntaxKind.Text;
case KnownTokenType.NewLine:
return HtmlTokenType.NewLine;
return SyntaxKind.NewLine;
case KnownTokenType.Transition:
return HtmlTokenType.Transition;
return SyntaxKind.Transition;
case KnownTokenType.WhiteSpace:
return HtmlTokenType.WhiteSpace;
return SyntaxKind.Whitespace;
default:
return HtmlTokenType.Unknown;
return SyntaxKind.Unknown;
}
}
protected override HtmlToken CreateToken(string content, HtmlTokenType type, IReadOnlyList<RazorDiagnostic> errors)
protected override SyntaxToken CreateToken(string content, SyntaxKind kind, IReadOnlyList<RazorDiagnostic> errors)
{
return new HtmlToken(content, type, errors);
return SyntaxFactory.Token(kind, content, errors);
}
}
}

View File

@ -1,34 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class HtmlToken : TokenBase<HtmlTokenType>
{
internal static readonly HtmlToken Hyphen = new HtmlToken("-", HtmlTokenType.Text);
public HtmlToken(string content, HtmlTokenType type)
: base(content, type, RazorDiagnostic.EmptyArray)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
}
public HtmlToken(
string content,
HtmlTokenType type,
IReadOnlyList<RazorDiagnostic> errors)
: base(content, type, errors)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
}
}
}

View File

@ -1,32 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
[Flags]
internal enum HtmlTokenType
{
Unknown,
Text, // Text which isn't one of the below
WhiteSpace, // Non-newline Whitespace
NewLine, // Newline
OpenAngle, // <
Bang, // !
ForwardSlash, // /
QuestionMark, // ?
DoubleHyphen, // --
LeftBracket, // [
CloseAngle, // >
RightBracket, // ]
Equals, // =
DoubleQuote, // "
SingleQuote, // '
Transition, // @
Colon,
RazorComment,
RazorCommentStar,
RazorCommentTransition
}
}

View File

@ -3,11 +3,12 @@
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// Tokenizer _loosely_ based on http://dev.w3.org/html5/spec/Overview.html#tokenization
internal class HtmlTokenizer : Tokenizer<HtmlToken, HtmlTokenType>
internal class HtmlTokenizer : Tokenizer
{
private const char TransitionChar = '@';
@ -21,24 +22,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private new HtmlTokenizerState? CurrentState => (HtmlTokenizerState?)base.CurrentState;
public override HtmlTokenType RazorCommentType
public override SyntaxKind RazorCommentKind
{
get { return HtmlTokenType.RazorComment; }
get { return SyntaxKind.RazorCommentLiteral; }
}
public override HtmlTokenType RazorCommentTransitionType
public override SyntaxKind RazorCommentTransitionKind
{
get { return HtmlTokenType.RazorCommentTransition; }
get { return SyntaxKind.RazorCommentTransition; }
}
public override HtmlTokenType RazorCommentStarType
public override SyntaxKind RazorCommentStarKind
{
get { return HtmlTokenType.RazorCommentStar; }
get { return SyntaxKind.RazorCommentStar; }
}
protected override HtmlToken CreateToken(string content, HtmlTokenType type, IReadOnlyList<RazorDiagnostic> errors)
protected override SyntaxToken CreateToken(string content, SyntaxKind type, IReadOnlyList<RazorDiagnostic> errors)
{
return new HtmlToken(content, type, errors);
return SyntaxFactory.Token(type, content, errors);
}
protected override StateResult Dispatch()
@ -66,7 +67,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Optimize memory allocation by returning constants for the most frequent cases
protected override string GetTokenContent(HtmlTokenType type)
protected override string GetTokenContent(SyntaxKind type)
{
var tokenLength = Buffer.Length;
@ -74,27 +75,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
switch (type)
{
case HtmlTokenType.OpenAngle:
case SyntaxKind.OpenAngle:
return "<";
case HtmlTokenType.Bang:
case SyntaxKind.Bang:
return "!";
case HtmlTokenType.ForwardSlash:
case SyntaxKind.ForwardSlash:
return "/";
case HtmlTokenType.QuestionMark:
case SyntaxKind.QuestionMark:
return "?";
case HtmlTokenType.LeftBracket:
case SyntaxKind.LeftBracket:
return "[";
case HtmlTokenType.CloseAngle:
case SyntaxKind.CloseAngle:
return ">";
case HtmlTokenType.RightBracket:
case SyntaxKind.RightBracket:
return "]";
case HtmlTokenType.Equals:
case SyntaxKind.Equals:
return "=";
case HtmlTokenType.DoubleQuote:
case SyntaxKind.DoubleQuote:
return "\"";
case HtmlTokenType.SingleQuote:
case SyntaxKind.SingleQuote:
return "'";
case HtmlTokenType.WhiteSpace:
case SyntaxKind.Whitespace:
if (Buffer[0] == ' ')
{
return " ";
@ -104,7 +105,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return "\t";
}
break;
case HtmlTokenType.NewLine:
case SyntaxKind.NewLine:
if (Buffer[0] == '\n')
{
return "\n";
@ -113,7 +114,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
if (tokenLength == 2 && type == HtmlTokenType.NewLine)
if (tokenLength == 2 && type == SyntaxKind.NewLine)
{
return "\r\n";
}
@ -139,17 +140,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return Transition(
HtmlTokenizerState.AfterRazorCommentTransition,
EndToken(HtmlTokenType.RazorCommentTransition));
EndToken(SyntaxKind.RazorCommentTransition));
}
else if (CurrentCharacter == '@')
{
// Could be escaped comment transition
return Transition(
HtmlTokenizerState.EscapedRazorCommentTransition,
EndToken(HtmlTokenType.Transition));
EndToken(SyntaxKind.Transition));
}
return Stay(EndToken(HtmlTokenType.Transition));
return Stay(EndToken(SyntaxKind.Transition));
}
else if (AtToken())
{
@ -164,7 +165,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private StateResult EscapedRazorCommentTransition()
{
TakeCurrent();
return Transition(HtmlTokenizerState.Data, EndToken(HtmlTokenType.Transition));
return Transition(HtmlTokenizerState.Data, EndToken(SyntaxKind.Transition));
}
private StateResult Text()
@ -190,10 +191,10 @@ 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, EndToken(HtmlTokenType.Text));
return Transition(HtmlTokenizerState.Data, EndToken(SyntaxKind.Text));
}
private HtmlToken Token()
private SyntaxToken Token()
{
Debug.Assert(AtToken());
var sym = CurrentCharacter;
@ -201,45 +202,45 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
switch (sym)
{
case '<':
return EndToken(HtmlTokenType.OpenAngle);
return EndToken(SyntaxKind.OpenAngle);
case '!':
return EndToken(HtmlTokenType.Bang);
return EndToken(SyntaxKind.Bang);
case '/':
return EndToken(HtmlTokenType.ForwardSlash);
return EndToken(SyntaxKind.ForwardSlash);
case '?':
return EndToken(HtmlTokenType.QuestionMark);
return EndToken(SyntaxKind.QuestionMark);
case '[':
return EndToken(HtmlTokenType.LeftBracket);
return EndToken(SyntaxKind.LeftBracket);
case '>':
return EndToken(HtmlTokenType.CloseAngle);
return EndToken(SyntaxKind.CloseAngle);
case ']':
return EndToken(HtmlTokenType.RightBracket);
return EndToken(SyntaxKind.RightBracket);
case '=':
return EndToken(HtmlTokenType.Equals);
return EndToken(SyntaxKind.Equals);
case '"':
return EndToken(HtmlTokenType.DoubleQuote);
return EndToken(SyntaxKind.DoubleQuote);
case '\'':
return EndToken(HtmlTokenType.SingleQuote);
return EndToken(SyntaxKind.SingleQuote);
case '-':
Debug.Assert(CurrentCharacter == '-');
TakeCurrent();
return EndToken(HtmlTokenType.DoubleHyphen);
return EndToken(SyntaxKind.DoubleHyphen);
default:
Debug.Fail("Unexpected token!");
return EndToken(HtmlTokenType.Unknown);
return EndToken(SyntaxKind.Unknown);
}
}
private HtmlToken Whitespace()
private SyntaxToken Whitespace()
{
while (ParserHelpers.IsWhitespace(CurrentCharacter))
{
TakeCurrent();
}
return EndToken(HtmlTokenType.WhiteSpace);
return EndToken(SyntaxKind.Whitespace);
}
private HtmlToken Newline()
private SyntaxToken Newline()
{
Debug.Assert(ParserHelpers.IsNewLine(CurrentCharacter));
// CSharp Spec §2.3.1
@ -249,7 +250,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TakeCurrent();
}
return EndToken(HtmlTokenType.NewLine);
return EndToken(SyntaxKind.NewLine);
}
private bool AtToken()
@ -274,7 +275,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Transition((int)state, result: null);
}
private StateResult Transition(HtmlTokenizerState state, HtmlToken result)
private StateResult Transition(HtmlTokenizerState state, SyntaxToken result)
{
return Transition((int)state, result);
}

View File

@ -1,14 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal interface IToken
{
Span Parent { get; set; }
string Content { get; }
SourceLocation Start { get; }
}
}

View File

@ -1,10 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal interface ITokenizer
{
IToken NextToken();
SyntaxToken NextToken();
}
}

View File

@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly ISet<string> _keywords;
private readonly IReadOnlyCollection<string> _readOnlyKeywords;
public ImplicitExpressionEditHandler(Func<string, IEnumerable<IToken>> tokenizer, ISet<string> keywords, bool acceptTrailingDot)
public ImplicitExpressionEditHandler(Func<string, IEnumerable<Syntax.InternalSyntax.SyntaxToken>> tokenizer, ISet<string> keywords, bool acceptTrailingDot)
: base(tokenizer)
{
_keywords = keywords ?? new HashSet<string>();
@ -172,20 +173,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
for (var i = 0; i < target.Tokens.Count; i++)
{
var token = target.Tokens[i] as CSharpToken;
var token = target.Tokens[i];
if (token == null)
{
break;
}
var tokenStartIndex = token.Start.AbsoluteIndex;
var tokenEndIndex = tokenStartIndex + token.Content.Length;
var tokenStartIndex = token.Position;
var tokenEndIndex = token.EndPosition;
// We're looking for the first token that contains the SourceChange.
if (tokenEndIndex > change.Span.AbsoluteIndex)
{
if (tokenEndIndex >= change.Span.AbsoluteIndex + change.Span.Length && token.Type == CSharpTokenType.Identifier)
if (tokenEndIndex >= change.Span.AbsoluteIndex + change.Span.Length && token.Kind == SyntaxKind.Identifier)
{
// 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;
@ -200,8 +201,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
break;
}
var newToken = (CSharpToken)newTokens.First();
if (newToken.Type == CSharpTokenType.Identifier)
var newToken = newTokens.First();
if (newToken.Kind == SyntaxKind.Identifier)
{
return true;
}
@ -241,8 +242,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
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))
if (!IsInsideParenthesis(changeStart, target.Tokens) || !IsInsideParenthesis(changeEnd, target.Tokens))
{
// Either the start or end of the delete does not fall inside of parenthesis, unacceptable inner deletion.
return false;
@ -274,8 +274,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
var tokens = target.Tokens.Cast<CSharpToken>().ToArray();
if (IsInsideParenthesis(change.Span.AbsoluteIndex, tokens))
if (IsInsideParenthesis(change.Span.AbsoluteIndex, target.Tokens))
{
return true;
}
@ -284,7 +283,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Internal for testing
internal static bool IsInsideParenthesis(int position, IReadOnlyList<CSharpToken> tokens)
internal static bool IsInsideParenthesis(int position, IReadOnlyList<SyntaxToken> tokens)
{
var balanceCount = 0;
var foundInsertionPoint = false;
@ -322,9 +321,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Internal for testing
internal static bool ContainsPosition(int position, CSharpToken currentToken)
internal static bool ContainsPosition(int position, SyntaxToken currentToken)
{
var tokenStart = currentToken.Start.AbsoluteIndex;
var tokenStart = currentToken.Position;
if (tokenStart == position)
{
// Token is exactly at the insertion point.
@ -342,14 +341,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// Internal for testing
internal static bool TryUpdateBalanceCount(CSharpToken token, ref int count)
internal static bool TryUpdateBalanceCount(SyntaxToken token, ref int count)
{
var updatedCount = count;
if (token.Type == CSharpTokenType.LeftParenthesis)
if (token.Kind == SyntaxKind.LeftParenthesis)
{
updatedCount++;
}
else if (token.Type == CSharpTokenType.RightParenthesis)
else if (token.Kind == SyntaxKind.RightParenthesis)
{
if (updatedCount == 0)
{
@ -358,7 +357,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
updatedCount--;
}
else if (token.Type == CSharpTokenType.StringLiteral)
else if (token.Kind == SyntaxKind.StringLiteral)
{
var content = token.Content;
if (content.Length > 0 && content[content.Length - 1] != '"')
@ -370,7 +369,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
}
else if (token.Type == CSharpTokenType.CharacterLiteral)
else if (token.Kind == SyntaxKind.CharacterLiteral)
{
var content = token.Content;
if (content.Length > 0 && content[content.Length - 1] != '\'')

View File

@ -3,30 +3,29 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal abstract class LanguageCharacteristics<TTokenizer, TToken, TTokenType>
where TTokenType : struct
where TTokenizer : Tokenizer<TToken, TTokenType>
where TToken : TokenBase<TTokenType>
internal abstract class LanguageCharacteristics<TTokenizer>
where TTokenizer : Tokenizer
{
public abstract string GetSample(TTokenType type);
public abstract string GetSample(SyntaxKind type);
public abstract TTokenizer CreateTokenizer(ITextDocument source);
public abstract TTokenType FlipBracket(TTokenType bracket);
public abstract TToken CreateMarkerToken();
public abstract SyntaxKind FlipBracket(SyntaxKind bracket);
public abstract SyntaxToken CreateMarkerToken();
public virtual IEnumerable<TToken> TokenizeString(string content)
public virtual IEnumerable<SyntaxToken> TokenizeString(string content)
{
return TokenizeString(SourceLocation.Zero, content);
}
public virtual IEnumerable<TToken> TokenizeString(SourceLocation start, string input)
public virtual IEnumerable<SyntaxToken> TokenizeString(SourceLocation start, string input)
{
using (var reader = new SeekableTextReader(input, start.FilePath))
{
var tok = CreateTokenizer(reader);
TToken token;
SyntaxToken token;
while ((token = tok.NextToken()) != null)
{
yield return token;
@ -34,76 +33,76 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
public virtual bool IsWhiteSpace(TToken token)
public virtual bool IsWhiteSpace(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.WhiteSpace);
}
public virtual bool IsNewLine(TToken token)
public virtual bool IsNewLine(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.NewLine);
}
public virtual bool IsIdentifier(TToken token)
public virtual bool IsIdentifier(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.Identifier);
}
public virtual bool IsKeyword(TToken token)
public virtual bool IsKeyword(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.Keyword);
}
public virtual bool IsTransition(TToken token)
public virtual bool IsTransition(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.Transition);
}
public virtual bool IsCommentStart(TToken token)
public virtual bool IsCommentStart(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.CommentStart);
}
public virtual bool IsCommentStar(TToken token)
public virtual bool IsCommentStar(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.CommentStar);
}
public virtual bool IsCommentBody(TToken token)
public virtual bool IsCommentBody(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.CommentBody);
}
public virtual bool IsUnknown(TToken token)
public virtual bool IsUnknown(SyntaxToken token)
{
return IsKnownTokenType(token, KnownTokenType.Unknown);
}
public virtual bool IsKnownTokenType(TToken token, KnownTokenType type)
public virtual bool IsKnownTokenType(SyntaxToken token, KnownTokenType type)
{
return token != null && Equals(token.Type, GetKnownTokenType(type));
return token != null && Equals(token.Kind, GetKnownTokenType(type));
}
public virtual Tuple<TToken, TToken> SplitToken(TToken token, int splitAt, TTokenType leftType)
public virtual Tuple<SyntaxToken, SyntaxToken> SplitToken(SyntaxToken token, int splitAt, SyntaxKind leftType)
{
var left = CreateToken(token.Content.Substring(0, splitAt), leftType, RazorDiagnostic.EmptyArray);
TToken right = null;
SyntaxToken right = null;
if (splitAt < token.Content.Length)
{
right = CreateToken(token.Content.Substring(splitAt), token.Type, token.Errors);
right = CreateToken(token.Content.Substring(splitAt), token.Kind, token.GetDiagnostics());
}
return Tuple.Create(left, right);
}
public abstract TTokenType GetKnownTokenType(KnownTokenType type);
public abstract SyntaxKind GetKnownTokenType(KnownTokenType type);
public virtual bool KnowsTokenType(KnownTokenType type)
{
return type == KnownTokenType.Unknown || !Equals(GetKnownTokenType(type), GetKnownTokenType(KnownTokenType.Unknown));
}
protected abstract TToken CreateToken(string content, TTokenType type, IReadOnlyList<RazorDiagnostic> errors);
protected abstract SyntaxToken CreateToken(string content, SyntaxKind type, IReadOnlyList<RazorDiagnostic> errors);
}
}

View File

@ -67,9 +67,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
builder.Kind = SpanKindInternal.Markup;
builder.ChunkGenerator = new MarkupChunkGenerator();
foreach (IToken sym in HtmlLanguageCharacteristics.Instance.TokenizeString(start, content))
foreach (var token in HtmlLanguageCharacteristics.Instance.TokenizeString(start, content))
{
builder.Accept(sym);
builder.Accept(token);
}
}
}

View File

@ -5,12 +5,16 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class Span : SyntaxTreeNode
{
private static readonly List<SyntaxToken> EmptyTokenList = new List<SyntaxToken>(0);
private static readonly int TypeHashCode = typeof(Span).GetHashCode();
private IReadOnlyList<Syntax.InternalSyntax.SyntaxToken> _greenTokens;
private string _content;
private int? _length;
private SourceLocation _start;
@ -23,7 +27,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public ISpanChunkGenerator ChunkGenerator { get; private set; }
public SpanKindInternal Kind { get; private set; }
public IReadOnlyList<IToken> Tokens { get; private set; }
public IReadOnlyList<SyntaxToken> Tokens { get; private set; }
// Allow test code to re-link spans
public Span Previous { get; internal set; }
@ -31,6 +36,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public SpanEditHandler EditHandler { get; private set; }
public SyntaxNode SyntaxNode { get; private set; }
public override bool IsBlock => false;
public override int Length
@ -93,19 +100,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public void ReplaceWith(SpanBuilder builder)
{
Kind = builder.Kind;
Tokens = builder.Tokens;
for (var i = 0; i <Tokens.Count; i++)
{
Tokens[i].Parent = this;
}
_greenTokens = builder.Tokens;
EditHandler = builder.EditHandler;
ChunkGenerator = builder.ChunkGenerator ?? SpanChunkGenerator.Null;
_start = builder.Start;
SyntaxNode = builder.SyntaxNode?.CreateRed(parent: null, position: _start.AbsoluteIndex);
_content = null;
_length = null;
var tokens = EmptyTokenList;
if (_greenTokens.Count > 0)
{
tokens = new List<SyntaxToken>();
var currentStart = _start.AbsoluteIndex;
for (var i = 0; i < _greenTokens.Count; i++)
{
var token = new SyntaxToken(_greenTokens[i], parent: SyntaxNode, parentSpan: this, position: currentStart);
tokens.Add(token);
currentStart += token.FullWidth;
}
}
Tokens = tokens;
Parent?.ChildChanged();
// Since we took references to the values in SpanBuilder, clear its references out
@ -145,8 +162,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// </summary>
public override bool EquivalentTo(SyntaxTreeNode node)
{
var other = node as Span;
return other != null &&
return node is Span other &&
Kind.Equals(other.Kind) &&
Start.Equals(other.Start) &&
EditHandler.Equals(other.EditHandler) &&
@ -161,12 +177,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public override bool Equals(object obj)
{
var other = obj as Span;
return other != null &&
return obj is Span other &&
Kind.Equals(other.Kind) &&
EditHandler.Equals(other.EditHandler) &&
ChunkGenerator.Equals(other.ChunkGenerator) &&
Tokens.SequenceEqual(other.Tokens);
Tokens.SequenceEqual(other.Tokens, SyntaxTokenComparer.Default);
}
public override int GetHashCode()
@ -185,5 +200,28 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var spanBuilder = new SpanBuilder(this);
return spanBuilder.Build();
}
private class SyntaxTokenComparer : IEqualityComparer<SyntaxToken>
{
public static readonly SyntaxTokenComparer Default = new SyntaxTokenComparer();
private SyntaxTokenComparer()
{
}
public bool Equals(SyntaxToken x, SyntaxToken y)
{
return x.IsEquivalentTo(y);
}
public int GetHashCode(SyntaxToken obj)
{
var hash = HashCodeCombiner.Start();
hash.Add(obj.Content, StringComparer.Ordinal);
hash.Add(obj.Kind);
return hash;
}
}
}
}

View File

@ -3,14 +3,16 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class SpanBuilder
{
private SourceLocation _start;
private List<IToken> _tokens;
private List<SyntaxToken> _tokens;
private SourceLocationTracker _tracker;
public SpanBuilder(Span original)
@ -20,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
_start = original.Start;
ChunkGenerator = original.ChunkGenerator;
_tokens = new List<IToken>(original.Tokens);
_tokens = new List<SyntaxToken>(original.Tokens.Select(t =>t.Green));
_tracker = new SourceLocationTracker(original.Start);
}
@ -33,6 +35,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Start = location;
}
public Syntax.GreenNode SyntaxNode { get; private set; }
public ISpanChunkGenerator ChunkGenerator { get; set; }
public SourceLocation Start
@ -49,13 +53,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public SpanKindInternal Kind { get; set; }
public IReadOnlyList<IToken> Tokens
public IReadOnlyList<SyntaxToken> Tokens
{
get
{
if (_tokens == null)
{
_tokens = new List<IToken>();
_tokens = new List<SyntaxToken>();
}
return _tokens;
@ -69,22 +73,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Need to potentially allocate a new list because Span.ReplaceWith takes ownership
// of the original list.
_tokens = null;
_tokens = new List<IToken>();
_tokens = new List<SyntaxToken>();
EditHandler = SpanEditHandler.CreateDefault((content) => Enumerable.Empty<IToken>());
EditHandler = SpanEditHandler.CreateDefault((content) => Enumerable.Empty<SyntaxToken>());
ChunkGenerator = SpanChunkGenerator.Null;
Start = SourceLocation.Undefined;
}
public Span Build()
public Span Build(SyntaxKind syntaxKind = SyntaxKind.Unknown)
{
SyntaxNode = GetSyntaxNode(syntaxKind);
var span = new Span(this);
for (var i = 0; i < span.Tokens.Count; i++)
{
var token = span.Tokens[i];
token.Parent = span;
}
return span;
}
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
_tokens?.Clear();
}
public void Accept(IToken token)
public void Accept(SyntaxToken token)
{
if (token == null)
{
@ -109,5 +109,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
_tokens.Add(token);
_tracker.UpdateLocation(token.Content);
}
private Syntax.GreenNode GetSyntaxNode(SyntaxKind syntaxKind)
{
if (syntaxKind == SyntaxKind.HtmlTextLiteral)
{
var textTokens = new SyntaxListBuilder<SyntaxToken>(SyntaxListBuilder.Create());
foreach (var token in Tokens)
{
if (token.Kind == SyntaxKind.Unknown)
{
Debug.Assert(false, $"Unexpected token {token.Kind}");
continue;
}
textTokens.Add(token);
}
var textResult = textTokens.ToList();
return SyntaxFactory.HtmlTextLiteral(new SyntaxList<SyntaxToken>(textResult.Node));
}
return null;
}
}
}

View File

@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@ -11,12 +11,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
private static readonly int TypeHashCode = typeof(SpanEditHandler).GetHashCode();
public SpanEditHandler(Func<string, IEnumerable<IToken>> tokenizer)
public SpanEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer)
: this(tokenizer, AcceptedCharactersInternal.Any)
{
}
public SpanEditHandler(Func<string, IEnumerable<IToken>> tokenizer, AcceptedCharactersInternal accepted)
public SpanEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer, AcceptedCharactersInternal accepted)
{
AcceptedCharacters = accepted;
Tokenizer = tokenizer;
@ -24,9 +24,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public AcceptedCharactersInternal AcceptedCharacters { get; set; }
public Func<string, IEnumerable<IToken>> Tokenizer { get; set; }
public Func<string, IEnumerable<SyntaxToken>> Tokenizer { get; set; }
public static SpanEditHandler CreateDefault(Func<string, IEnumerable<IToken>> tokenizer)
public static SpanEditHandler CreateDefault(Func<string, IEnumerable<SyntaxToken>> tokenizer)
{
return new SpanEditHandler(tokenizer);
}
@ -116,8 +116,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public override bool Equals(object obj)
{
var other = obj as SpanEditHandler;
return other != null &&
return obj is SpanEditHandler other &&
GetType() == other.GetType() &&
AcceptedCharacters == other.AcceptedCharacters;
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.Language.Syntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@ -150,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
};
// Will contain tokens that represent a single attribute value: <input| class="btn"| />
var htmlTokens = span.Tokens.OfType<HtmlToken>().ToArray();
var tokens = span.Tokens;
var capturedAttributeValueStart = false;
var attributeValueStartLocation = span.Start;
@ -165,9 +166,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Iterate down through the tokens to find the name and the start of the value.
// We subtract the tokenOffset so we don't accept an ending quote of a span.
for (var i = 0; i < htmlTokens.Length - tokenOffset; i++)
for (var i = 0; i < tokens.Count - tokenOffset; i++)
{
var token = htmlTokens[i];
var token = tokens[i];
if (afterEquals)
{
@ -186,9 +187,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
attributeValueStartLocation = token.Start;
}
builder.Accept(token);
builder.Accept(token.Green);
}
else if (name == null && HtmlMarkupParser.IsValidAttributeNameToken(token))
else if (name == null && HtmlMarkupParser.IsValidAttributeNameToken(token.Green))
{
// We've captured all leading whitespace prior to the attribute name.
// We're now at: " |asp-for='...'" or " |asp-for=..."
@ -196,10 +197,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var nameBuilder = new StringBuilder();
// Move the indexer past the attribute name tokens.
for (var j = i; j < htmlTokens.Length; j++)
for (var j = i; j < tokens.Count; j++)
{
var nameToken = htmlTokens[j];
if (!HtmlMarkupParser.IsValidAttributeNameToken(nameToken))
var nameToken = tokens[j];
if (!HtmlMarkupParser.IsValidAttributeNameToken(nameToken.Green))
{
break;
}
@ -213,7 +214,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
name = nameBuilder.ToString();
attributeValueStartLocation = SourceLocationTracker.Advance(attributeValueStartLocation, name);
}
else if (token.Type == HtmlTokenType.Equals)
else if (token.Kind == SyntaxKind.Equals)
{
// We've captured all leading whitespace and the attribute name.
// We're now at: " asp-for|='...'" or " asp-for|=..."
@ -227,19 +228,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
do
{
i++; // Start from the token after '='.
} while (i < htmlTokens.Length &&
(htmlTokens[i].Type == HtmlTokenType.WhiteSpace ||
htmlTokens[i].Type == HtmlTokenType.NewLine));
} while (i < tokens.Count &&
(tokens[i].Kind == SyntaxKind.Whitespace ||
tokens[i].Kind == SyntaxKind.NewLine));
// Check for attribute start values, aka single or double quote
if (i < htmlTokens.Length && IsQuote(htmlTokens[i]))
if (i < tokens.Count && IsQuote(tokens[i]))
{
if (htmlTokens[i].Type == HtmlTokenType.SingleQuote)
if (tokens[i].Kind == SyntaxKind.SingleQuote)
{
attributeValueStyle = AttributeStructure.SingleQuotes;
}
tokenStartLocation = htmlTokens[i].Start;
tokenStartLocation = tokens[i].Start;
// If there's a start quote then there must be an end quote to be valid, skip it.
tokenOffset = 1;
@ -260,7 +261,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
afterEquals = true;
}
else if (token.Type == HtmlTokenType.WhiteSpace)
else if (token.Kind == SyntaxKind.Whitespace)
{
// We're at the start of the attribute, this branch may be hit on the first iterations of
// the loop since the parser separates attributes with their spaces included as tokens.
@ -343,9 +344,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var nameTokens = childSpan
.Tokens
.OfType<HtmlToken>()
.SkipWhile(token => !HtmlMarkupParser.IsValidAttributeNameToken(token)) // Skip prefix
.TakeWhile(nameToken => HtmlMarkupParser.IsValidAttributeNameToken(nameToken))
.SkipWhile(token => !HtmlMarkupParser.IsValidAttributeNameToken(token.Green)) // Skip prefix
.TakeWhile(nameToken => HtmlMarkupParser.IsValidAttributeNameToken(nameToken.Green))
.Select(nameToken => nameToken.Content);
var name = string.Concat(nameTokens);
@ -362,12 +362,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var result = CreateTryParseResult(name, descriptors, processedBoundAttributeNames);
var firstChild = builder.Children[0] as Span;
if (firstChild != null && firstChild.Tokens[0] is HtmlToken)
if (firstChild != null)
{
var htmlToken = firstChild.Tokens[firstChild.Tokens.Count - 1] as HtmlToken;
switch (htmlToken.Type)
var token = firstChild.Tokens[firstChild.Tokens.Count - 1];
switch (token.Kind)
{
case HtmlTokenType.Equals:
case SyntaxKind.Equals:
if (builder.Children.Count == 2 &&
builder.Children[1] is Span value &&
value.Kind == SpanKindInternal.Markup)
@ -385,10 +385,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
result.AttributeStructure = AttributeStructure.DoubleQuotes;
}
break;
case HtmlTokenType.DoubleQuote:
case SyntaxKind.DoubleQuote:
result.AttributeStructure = AttributeStructure.DoubleQuotes;
break;
case HtmlTokenType.SingleQuote:
case SyntaxKind.SingleQuote:
result.AttributeStructure = AttributeStructure.SingleQuotes;
break;
default:
@ -408,8 +408,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// In some malformed cases e.g. <p bar="false', the last Span (false' in the ex.) may contain more
// than a single HTML token. Do not ignore those other tokens.
var tokenCount = endSpan.Tokens.Count();
var endToken = tokenCount == 1 ? (HtmlToken)endSpan.Tokens.First() : null;
var tokenCount = endSpan.Tokens.Count;
var endToken = tokenCount == 1 ? endSpan.Tokens.First() : null;
// Checking to see if it's a quoted attribute, if so we should remove end quote
if (endToken != null && IsQuote(endToken))
@ -614,8 +614,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// expression).
var firstNonWhitespaceToken = span
.Tokens
.OfType<HtmlToken>()
.First(token => token.Type != HtmlTokenType.WhiteSpace && token.Type != HtmlTokenType.NewLine);
.First(token => token.Kind != SyntaxKind.Whitespace && token.Kind != SyntaxKind.NewLine);
var location = new SourceSpan(firstNonWhitespaceToken.Start, attributeName.Length);
return location;
@ -716,10 +715,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return firstBoundAttribute;
}
private static bool IsQuote(HtmlToken htmlToken)
private static bool IsQuote(SyntaxToken token)
{
return htmlToken.Type == HtmlTokenType.DoubleQuote ||
htmlToken.Type == HtmlTokenType.SingleQuote;
return token.Kind == SyntaxKind.DoubleQuote ||
token.Kind == SyntaxKind.SingleQuote;
}
private static void ConfigureNonStringAttribute(SpanBuilder builder, bool isDuplicateAttribute)

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.Language.Syntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@ -370,10 +371,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var childOffset = 0;
if (childSpan.Tokens.Count > 0)
{
var potentialQuote = childSpan.Tokens[childSpan.Tokens.Count - 1] as HtmlToken;
var potentialQuote = childSpan.Tokens[childSpan.Tokens.Count - 1];
if (potentialQuote != null &&
(potentialQuote.Type == HtmlTokenType.DoubleQuote ||
potentialQuote.Type == HtmlTokenType.SingleQuote))
(potentialQuote.Kind == SyntaxKind.DoubleQuote ||
potentialQuote.Kind == SyntaxKind.SingleQuote))
{
childOffset = 1;
}
@ -407,23 +408,23 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Entire attribute is a string
for (var j = 0; j < endValueMarker; j++)
{
var htmlToken = (HtmlToken)childSpan.Tokens[j];
var token = childSpan.Tokens[j];
if (!afterEquals)
{
afterEquals = htmlToken.Type == HtmlTokenType.Equals;
afterEquals = token.Kind == SyntaxKind.Equals;
continue;
}
if (!atValue)
{
atValue = htmlToken.Type != HtmlTokenType.WhiteSpace &&
htmlToken.Type != HtmlTokenType.NewLine;
atValue = token.Kind != SyntaxKind.Whitespace &&
token.Kind != SyntaxKind.NewLine;
if (atValue)
{
if (htmlToken.Type == HtmlTokenType.DoubleQuote ||
htmlToken.Type == HtmlTokenType.SingleQuote)
if (token.Kind == SyntaxKind.DoubleQuote ||
token.Kind == SyntaxKind.SingleQuote)
{
endValueMarker--;
}
@ -431,14 +432,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// Current token is considered the value (unquoted). Add its content to the
// attribute value builder before we move past it.
_attributeValueBuilder.Append(htmlToken.Content);
_attributeValueBuilder.Append(token.Content);
}
}
continue;
}
_attributeValueBuilder.Append(htmlToken.Content);
_attributeValueBuilder.Append(token.Content);
}
}
@ -641,10 +642,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (tagEnd != null && tagEnd.Kind == SpanKindInternal.Markup)
{
var endToken = tagEnd.Tokens.Count > 0 ?
tagEnd.Tokens[tagEnd.Tokens.Count - 1] as HtmlToken :
tagEnd.Tokens[tagEnd.Tokens.Count - 1] :
null;
if (endToken != null && endToken.Type == HtmlTokenType.CloseAngle)
if (endToken != null && endToken.Kind == SyntaxKind.CloseAngle)
{
return false;
}
@ -791,13 +792,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
var childSpan = (Span)child;
HtmlToken textToken = null;
SyntaxToken textToken = null;
for (var i = 0; i < childSpan.Tokens.Count; i++)
{
var token = childSpan.Tokens[i] as HtmlToken;
var token = childSpan.Tokens[i];
if (token != null &&
(token.Type & (HtmlTokenType.WhiteSpace | HtmlTokenType.Text)) == token.Type)
(token.Kind == SyntaxKind.Whitespace || token.Kind == SyntaxKind.Text))
{
textToken = token;
break;
@ -809,7 +810,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return null;
}
return textToken.Type == HtmlTokenType.WhiteSpace ? null : textToken.Content;
return textToken.Kind == SyntaxKind.Whitespace ? null : textToken.Content;
}
private static bool IsEndTag(Block tagBlock)
@ -819,9 +820,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var childSpan = (Span)tagBlock.Children.First();
// We grab the token that could be forward slash
var relevantToken = (HtmlToken)childSpan.Tokens[childSpan.Tokens.Count == 1 ? 0 : 1];
var relevantToken = childSpan.Tokens[childSpan.Tokens.Count == 1 ? 0 : 1];
return relevantToken.Type == HtmlTokenType.ForwardSlash;
return relevantToken.Kind == SyntaxKind.ForwardSlash;
}
internal static bool IsComment(Span span)

View File

@ -1,84 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal abstract class TokenBase<TType> : IToken where TType : struct
{
protected TokenBase(
string content,
TType type,
IReadOnlyList<RazorDiagnostic> errors)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
Content = content;
Type = type;
Errors = errors;
}
public Span Parent { get; set; }
public IReadOnlyList<RazorDiagnostic> Errors { get; }
public string Content { get; }
public TType Type { get; }
public SourceLocation Start
{
get
{
if (Parent == null)
{
return SourceLocation.Undefined;
}
var tracker = new SourceLocationTracker(Parent.Start);
for (var i = 0; i < Parent.Tokens.Count; i++)
{
var token = Parent.Tokens[i];
if (object.ReferenceEquals(this, token))
{
break;
}
tracker.UpdateLocation(token.Content);
}
return tracker.CurrentLocation;
}
}
public override bool Equals(object obj)
{
var other = obj as TokenBase<TType>;
return other != null &&
string.Equals(Content, other.Content, StringComparison.Ordinal) &&
Type.Equals(other.Type);
}
public override int GetHashCode()
{
// Hash code should include only immutable properties.
var hash = HashCodeCombiner.Start();
hash.Add(Content, StringComparer.Ordinal);
hash.Add(Type);
return hash;
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0} [{1}]", Type, Content);
}
}
}

View File

@ -5,12 +5,11 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal abstract partial class Tokenizer<TToken, TTokenType> : ITokenizer
where TTokenType : struct
where TToken : TokenBase<TTokenType>
internal abstract class Tokenizer : ITokenizer
{
protected Tokenizer(ITextDocument source)
{
@ -31,7 +30,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected int? CurrentState { get; set; }
protected TToken CurrentToken { get; private set; }
protected SyntaxToken CurrenSyntaxToken { get; private set; }
public ITextDocument Source { get; private set; }
@ -42,9 +41,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
get { return Source.Peek() == -1; }
}
public abstract TTokenType RazorCommentStarType { get; }
public abstract TTokenType RazorCommentType { get; }
public abstract TTokenType RazorCommentTransitionType { get; }
public abstract SyntaxKind RazorCommentStarKind { get; }
public abstract SyntaxKind RazorCommentKind { get; }
public abstract SyntaxKind RazorCommentTransitionKind { get; }
protected bool HaveContent
{
@ -64,16 +63,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public SourceLocation CurrentStart { get; private set; }
protected abstract TToken CreateToken(string content, TTokenType type, IReadOnlyList<RazorDiagnostic> errors);
protected abstract SyntaxToken CreateToken(string content, SyntaxKind type, IReadOnlyList<RazorDiagnostic> errors);
protected abstract StateResult Dispatch();
IToken ITokenizer.NextToken()
SyntaxToken ITokenizer.NextToken()
{
return NextToken();
}
public virtual TToken NextToken()
public virtual SyntaxToken NextToken()
{
// Post-Condition: Buffer should be empty at the start of Next()
Debug.Assert(Buffer.Length == 0);
@ -95,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return token;
}
protected virtual TToken Turn()
protected virtual SyntaxToken Turn()
{
if (CurrentState != null)
{
@ -105,19 +104,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var next = Dispatch();
CurrentState = next.State;
CurrentToken = next.Result;
CurrenSyntaxToken = next.Result;
}
while (CurrentState != null && CurrentToken == null);
while (CurrentState != null && CurrenSyntaxToken == null);
if (CurrentState == null)
{
return default(TToken); // Terminated
return default(SyntaxToken); // Terminated
}
return CurrentToken;
return CurrenSyntaxToken;
}
return default(TToken);
return default(SyntaxToken);
}
public void Reset()
@ -149,7 +148,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// Returns a result containing the specified output and indicating that the next call to
/// <see cref="Turn"/> should invoke the provided state.
/// </summary>
protected StateResult Transition(int state, TToken result)
protected StateResult Transition(int state, SyntaxToken result)
{
return new StateResult(state, result);
}
@ -159,7 +158,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return new StateResult((int)state, result: null);
}
protected StateResult Transition(RazorCommentTokenizerState state, TToken result)
protected StateResult Transition(RazorCommentTokenizerState state, SyntaxToken result)
{
return new StateResult((int)state, result);
}
@ -180,12 +179,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// Returns a result containing the specified output and indicating that the next call to
/// <see cref="Turn"/> should re-invoke the current state.
/// </summary>
protected StateResult Stay(TToken result)
protected StateResult Stay(SyntaxToken result)
{
return new StateResult(CurrentState, result);
}
protected TToken Single(TTokenType type)
protected SyntaxToken Single(SyntaxKind type)
{
TakeCurrent();
return EndToken(type);
@ -199,9 +198,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
CurrentStart = CurrentLocation;
}
protected TToken EndToken(TTokenType type)
protected SyntaxToken EndToken(SyntaxKind type)
{
TToken token = null;
SyntaxToken token = null;
if (HaveContent)
{
// Perf: Don't allocate a new errors array unless necessary.
@ -222,7 +221,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return token;
}
protected virtual string GetTokenContent(TTokenType type)
protected virtual string GetTokenContent(SyntaxKind type)
{
return Buffer.ToString();
}
@ -278,7 +277,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AssertCurrent('*');
TakeCurrent();
return Transition(1002, EndToken(RazorCommentStarType));
return Transition(1002, EndToken(RazorCommentStarKind));
}
protected StateResult RazorCommentBody()
@ -292,7 +291,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
return Transition(
RazorCommentTokenizerState.StarAfterRazorCommentBody,
EndToken(RazorCommentType));
EndToken(RazorCommentKind));
}
else
{
@ -306,7 +305,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
return Transition(StartState, EndToken(RazorCommentType));
return Transition(StartState, EndToken(RazorCommentKind));
}
protected StateResult StarAfterRazorCommentBody()
@ -315,14 +314,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
TakeCurrent();
return Transition(
RazorCommentTokenizerState.AtTokenAfterRazorCommentBody,
EndToken(RazorCommentStarType));
EndToken(RazorCommentStarKind));
}
protected StateResult AtTokenAfterRazorCommentBody()
{
AssertCurrent('@');
TakeCurrent();
return Transition(StartState, EndToken(RazorCommentTransitionType));
return Transition(StartState, EndToken(RazorCommentTransitionKind));
}
/// <summary>
@ -397,7 +396,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected struct StateResult
{
public StateResult(int? state, TToken result)
public StateResult(int? state, SyntaxToken result)
{
State = state;
Result = result;
@ -405,7 +404,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public int? State { get; }
public TToken Result { get; }
public SyntaxToken Result { get; }
}
private static LookaheadToken BeginLookahead(ITextBuffer buffer)

View File

@ -5,36 +5,37 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal abstract partial class TokenizerBackedParser<TTokenizer, TToken, TTokenType> : ParserBase
where TTokenType : struct
where TTokenizer : Tokenizer<TToken, TTokenType>
where TToken : TokenBase<TTokenType>
internal abstract class TokenizerBackedParser<TTokenizer> : ParserBase
where TTokenizer : Tokenizer
{
private readonly TokenizerView<TTokenizer, TToken, TTokenType> _tokenizer;
private readonly TokenizerView<TTokenizer> _tokenizer;
protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer, TToken, TTokenType> language, ParserContext context)
protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer> language, ParserContext context)
: base(context)
{
Language = language;
var languageTokenizer = Language.CreateTokenizer(Context.Source);
_tokenizer = new TokenizerView<TTokenizer, TToken, TTokenType>(languageTokenizer);
_tokenizer = new TokenizerView<TTokenizer>(languageTokenizer);
Span = new SpanBuilder(CurrentLocation);
}
protected ParserState ParserState { get; set; }
protected SpanBuilder Span { get; private set; }
protected Action<SpanBuilder> SpanConfig { get; set; }
protected TToken CurrentToken
protected SyntaxToken CurrentToken
{
get { return _tokenizer.Current; }
}
protected TToken PreviousToken { get; private set; }
protected SyntaxToken PreviousToken { get; private set; }
protected SourceLocation CurrentLocation => _tokenizer.Tokenizer.CurrentLocation;
@ -45,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
get { return _tokenizer.EndOfFile; }
}
protected LanguageCharacteristics<TTokenizer, TToken, TTokenType> Language { get; }
protected LanguageCharacteristics<TTokenizer> Language { get; }
protected virtual void HandleEmbeddedTransition()
{
@ -58,21 +59,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public override void BuildSpan(SpanBuilder span, SourceLocation start, string content)
{
foreach (IToken sym in Language.TokenizeString(start, content))
foreach (var token in Language.TokenizeString(start, content))
{
span.Accept(sym);
span.Accept(token);
}
}
protected void Initialize(SpanBuilder span)
{
if (SpanConfig != null)
{
SpanConfig(span);
}
SpanConfig?.Invoke(span);
}
protected TToken Lookahead(int count)
protected SyntaxToken Lookahead(int count)
{
if (count < 0)
{
@ -84,7 +82,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
// We add 1 in order to store the current token.
var tokens = new TToken[count + 1];
var tokens = new SyntaxToken[count + 1];
var currentToken = CurrentToken;
tokens[0] = currentToken;
@ -115,7 +113,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// <param name="condition">A predicate accepting the token being evaluated and the list of tokens which have been looped through.</param>
/// <returns>true, if the condition was met. false - if the condition wasn't met and the last token has already been processed.</returns>
/// <remarks>The list of previous tokens is passed in the reverse order. So the last processed element will be the first one in the list.</remarks>
protected bool LookaheadUntil(Func<TToken, IEnumerable<TToken>, bool> condition)
protected bool LookaheadUntil(Func<SyntaxToken, IEnumerable<SyntaxToken>, bool> condition)
{
if (condition == null)
{
@ -124,7 +122,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var matchFound = false;
var tokens = new List<TToken>();
var tokens = new List<SyntaxToken>();
tokens.Add(CurrentToken);
while (true)
@ -163,14 +161,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Helpers
[Conditional("DEBUG")]
internal void Assert(TTokenType expectedType)
internal void Assert(SyntaxKind expectedType)
{
Debug.Assert(!EndOfFile && TokenTypeEquals(CurrentToken.Type, expectedType));
Debug.Assert(!EndOfFile && TokenKindEquals(CurrentToken.Kind, expectedType));
}
abstract protected bool TokenTypeEquals(TTokenType x, TTokenType y);
protected abstract bool TokenKindEquals(SyntaxKind x, SyntaxKind y);
protected internal void PutBack(TToken token)
protected internal void PutBack(SyntaxToken token)
{
if (token != null)
{
@ -180,7 +178,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// <summary>
/// Put the specified tokens back in the input stream. The provided list MUST be in the ORDER THE TOKENS WERE READ. The
/// list WILL be reversed and the Putback(TToken) will be called on each item.
/// list WILL be reversed and the Putback(SyntaxToken) will be called on each item.
/// </summary>
/// <remarks>
/// If a document contains tokens: a, b, c, d, e, f
@ -189,9 +187,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
/// that is the correct format for providing to this method. The caller of this method would,
/// in that case, want to put c, b and a back into the stream, so "a, b, c" is the CORRECT order
/// </remarks>
protected internal void PutBack(IEnumerable<TToken> tokens)
protected internal void PutBack(IEnumerable<SyntaxToken> tokens)
{
foreach (TToken token in tokens.Reverse())
foreach (var token in tokens.Reverse())
{
PutBack(token);
}
@ -207,7 +205,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal bool Balance(BalancingModes mode)
{
var left = CurrentToken.Type;
var left = CurrentToken.Kind;
var right = Language.FlipBracket(left);
var start = CurrentStart;
AcceptAndMoveNext();
@ -223,21 +221,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Balance(mode, left, right, start);
}
protected internal bool Balance(BalancingModes mode, TTokenType left, TTokenType right, SourceLocation start)
protected internal bool Balance(BalancingModes mode, SyntaxKind left, SyntaxKind right, SourceLocation start)
{
var startPosition = CurrentStart.AbsoluteIndex;
var nesting = 1;
if (!EndOfFile)
{
var syms = new List<TToken>();
var tokens = new List<SyntaxToken>();
do
{
if (IsAtEmbeddedTransition(
(mode & BalancingModes.AllowCommentsAndTemplates) == BalancingModes.AllowCommentsAndTemplates,
(mode & BalancingModes.AllowEmbeddedTransitions) == BalancingModes.AllowEmbeddedTransitions))
{
Accept(syms);
syms.Clear();
Accept(tokens);
tokens.Clear();
HandleEmbeddedTransition();
// Reset backtracking since we've already outputted some spans.
@ -253,7 +251,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
if (nesting > 0)
{
syms.Add(CurrentToken);
tokens.Add(CurrentToken);
}
}
while (nesting > 0 && NextToken());
@ -275,29 +273,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
else
{
Accept(syms);
Accept(tokens);
}
}
else
{
// Accept all the tokens we saw
Accept(syms);
Accept(tokens);
}
}
return nesting == 0;
}
protected internal bool NextIs(TTokenType type)
protected internal bool NextIs(SyntaxKind type)
{
return NextIs(sym => sym != null && TokenTypeEquals(type, sym.Type));
return NextIs(token => token != null && TokenKindEquals(type, token.Kind));
}
protected internal bool NextIs(params TTokenType[] types)
protected internal bool NextIs(params SyntaxKind[] types)
{
return NextIs(sym => sym != null && types.Any(t => TokenTypeEquals(t, sym.Type)));
return NextIs(token => token != null && types.Any(t => TokenKindEquals(t, token.Kind)));
}
protected internal bool NextIs(Func<TToken, bool> condition)
protected internal bool NextIs(Func<SyntaxToken, bool> condition)
{
var cur = CurrentToken;
if (NextToken())
@ -317,14 +315,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
protected internal bool Was(TTokenType type)
protected internal bool Was(SyntaxKind type)
{
return PreviousToken != null && TokenTypeEquals(PreviousToken.Type, type);
return PreviousToken != null && TokenKindEquals(PreviousToken.Kind, type);
}
protected internal bool At(TTokenType type)
protected internal bool At(SyntaxKind type)
{
return !EndOfFile && CurrentToken != null && TokenTypeEquals(CurrentToken.Type, type);
return !EndOfFile && CurrentToken != null && TokenKindEquals(CurrentToken.Kind, type);
}
protected internal bool AcceptAndMoveNext()
@ -333,11 +331,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return NextToken();
}
protected TToken AcceptSingleWhiteSpaceCharacter()
protected SyntaxToken AcceptSingleWhiteSpaceCharacter()
{
if (Language.IsWhiteSpace(CurrentToken))
{
Tuple<TToken, TToken> pair = Language.SplitToken(CurrentToken, 1, Language.GetKnownTokenType(KnownTokenType.WhiteSpace));
var pair = Language.SplitToken(CurrentToken, 1, Language.GetKnownTokenType(KnownTokenType.WhiteSpace));
Accept(pair.Item1);
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
NextToken();
@ -346,19 +344,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return null;
}
protected internal void Accept(IEnumerable<TToken> tokens)
protected internal void Accept(IEnumerable<SyntaxToken> tokens)
{
foreach (TToken token in tokens)
foreach (var token in tokens)
{
Accept(token);
}
}
protected internal void Accept(TToken token)
protected internal void Accept(SyntaxToken token)
{
if (token != null)
{
foreach (var error in token.Errors)
foreach (var error in token.GetDiagnostics())
{
Context.ErrorSink.OnError(error);
}
@ -367,11 +365,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
protected internal bool AcceptAll(params TTokenType[] types)
protected internal bool AcceptAll(params SyntaxKind[] kinds)
{
foreach (TTokenType type in types)
foreach (var kind in kinds)
{
if (CurrentToken == null || !TokenTypeEquals(CurrentToken.Type, type))
if (CurrentToken == null || !TokenKindEquals(CurrentToken.Kind, kind))
{
return false;
}
@ -388,31 +386,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
protected internal void Output(SpanKindInternal kind)
protected internal void Output(SpanKindInternal kind, SyntaxKind syntaxKind = SyntaxKind.Unknown)
{
Configure(kind, null);
Output();
Output(syntaxKind);
}
protected internal void Output(SpanKindInternal kind, AcceptedCharactersInternal accepts)
protected internal void Output(SpanKindInternal kind, AcceptedCharactersInternal accepts, SyntaxKind syntaxKind = SyntaxKind.Unknown)
{
Configure(kind, accepts);
Output();
Output(syntaxKind);
}
protected internal void Output(AcceptedCharactersInternal accepts)
protected internal void Output(AcceptedCharactersInternal accepts, SyntaxKind syntaxKind = SyntaxKind.Unknown)
{
Configure(null, accepts);
Output();
Output(syntaxKind);
}
private void Output()
private void Output(SyntaxKind syntaxKind)
{
if (Span.Tokens.Count > 0)
{
var nextStart = Span.End;
var builtSpan = Span.Build();
var builtSpan = Span.Build(syntaxKind);
Context.Builder.Add(builtSpan);
Initialize(Span);
@ -437,7 +435,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected IDisposable PushSpanConfig(Action<SpanBuilder, Action<SpanBuilder>> newConfig)
{
Action<SpanBuilder> old = SpanConfig;
var old = SpanConfig;
ConfigureSpan(newConfig);
return new DisposableAction(() => SpanConfig = old);
}
@ -450,7 +448,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected void ConfigureSpan(Action<SpanBuilder, Action<SpanBuilder>> config)
{
Action<SpanBuilder> prev = SpanConfig;
var prev = SpanConfig;
if (config == null)
{
SpanConfig = null;
@ -467,9 +465,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Expected(Language.GetKnownTokenType(type));
}
protected internal void Expected(params TTokenType[] types)
protected internal void Expected(params SyntaxKind[] types)
{
Debug.Assert(!EndOfFile && CurrentToken != null && types.Contains(CurrentToken.Type));
Debug.Assert(!EndOfFile && CurrentToken != null && types.Contains(CurrentToken.Kind));
AcceptAndMoveNext();
}
@ -478,7 +476,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Optional(Language.GetKnownTokenType(type));
}
protected internal bool Optional(TTokenType type)
protected internal bool Optional(SyntaxKind type)
{
if (At(type))
{
@ -498,61 +496,61 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return true;
}
protected internal void AcceptWhile(TTokenType type)
protected internal void AcceptWhile(SyntaxKind type)
{
AcceptWhile(sym => TokenTypeEquals(type, sym.Type));
AcceptWhile(token => TokenKindEquals(type, token.Kind));
}
// We want to avoid array allocations and enumeration where possible, so we use the same technique as string.Format
protected internal void AcceptWhile(TTokenType type1, TTokenType type2)
protected internal void AcceptWhile(SyntaxKind type1, SyntaxKind type2)
{
AcceptWhile(sym => TokenTypeEquals(type1, sym.Type) || TokenTypeEquals(type2, sym.Type));
AcceptWhile(token => TokenKindEquals(type1, token.Kind) || TokenKindEquals(type2, token.Kind));
}
protected internal void AcceptWhile(TTokenType type1, TTokenType type2, TTokenType type3)
protected internal void AcceptWhile(SyntaxKind type1, SyntaxKind type2, SyntaxKind type3)
{
AcceptWhile(sym => TokenTypeEquals(type1, sym.Type) || TokenTypeEquals(type2, sym.Type) || TokenTypeEquals(type3, sym.Type));
AcceptWhile(token => TokenKindEquals(type1, token.Kind) || TokenKindEquals(type2, token.Kind) || TokenKindEquals(type3, token.Kind));
}
protected internal void AcceptWhile(params TTokenType[] types)
protected internal void AcceptWhile(params SyntaxKind[] types)
{
AcceptWhile(sym => types.Any(expected => TokenTypeEquals(expected, sym.Type)));
AcceptWhile(token => types.Any(expected => TokenKindEquals(expected, token.Kind)));
}
protected internal void AcceptUntil(TTokenType type)
protected internal void AcceptUntil(SyntaxKind type)
{
AcceptWhile(sym => !TokenTypeEquals(type, sym.Type));
AcceptWhile(token => !TokenKindEquals(type, token.Kind));
}
// We want to avoid array allocations and enumeration where possible, so we use the same technique as string.Format
protected internal void AcceptUntil(TTokenType type1, TTokenType type2)
protected internal void AcceptUntil(SyntaxKind type1, SyntaxKind type2)
{
AcceptWhile(sym => !TokenTypeEquals(type1, sym.Type) && !TokenTypeEquals(type2, sym.Type));
AcceptWhile(token => !TokenKindEquals(type1, token.Kind) && !TokenKindEquals(type2, token.Kind));
}
protected internal void AcceptUntil(TTokenType type1, TTokenType type2, TTokenType type3)
protected internal void AcceptUntil(SyntaxKind type1, SyntaxKind type2, SyntaxKind type3)
{
AcceptWhile(sym => !TokenTypeEquals(type1, sym.Type) && !TokenTypeEquals(type2, sym.Type) && !TokenTypeEquals(type3, sym.Type));
AcceptWhile(token => !TokenKindEquals(type1, token.Kind) && !TokenKindEquals(type2, token.Kind) && !TokenKindEquals(type3, token.Kind));
}
protected internal void AcceptUntil(params TTokenType[] types)
protected internal void AcceptUntil(params SyntaxKind[] types)
{
AcceptWhile(sym => types.All(expected => !TokenTypeEquals(expected, sym.Type)));
AcceptWhile(token => types.All(expected => !TokenKindEquals(expected, token.Kind)));
}
protected internal void AcceptWhile(Func<TToken, bool> condition)
protected internal void AcceptWhile(Func<SyntaxToken, bool> condition)
{
Accept(ReadWhileLazy(condition));
}
protected internal IEnumerable<TToken> ReadWhile(Func<TToken, bool> condition)
protected internal IEnumerable<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition)
{
return ReadWhileLazy(condition).ToList();
}
protected TToken AcceptWhiteSpaceInLines()
protected SyntaxToken AcceptWhiteSpaceInLines()
{
TToken lastWs = null;
SyntaxToken lastWs = null;
while (Language.IsWhiteSpace(CurrentToken) || Language.IsNewLine(CurrentToken))
{
// Capture the previous whitespace node
@ -586,7 +584,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Don't open this to sub classes because it's lazy but it looks eager.
// You have to advance the Enumerable to read the next characters.
internal IEnumerable<TToken> ReadWhileLazy(Func<TToken, bool> condition)
internal IEnumerable<SyntaxToken> ReadWhileLazy(Func<SyntaxToken, bool> condition)
{
while (EnsureCurrent() && condition(CurrentToken))
{

View File

@ -1,12 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal class TokenizerView<TTokenizer, TToken, TTokenType>
where TTokenType : struct
where TTokenizer : Tokenizer<TToken, TTokenType>
where TToken : TokenBase<TTokenType>
internal class TokenizerView<TTokenizer>
where TTokenizer : Tokenizer
{
public TokenizerView(TTokenizer tokenizer)
{
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public TTokenizer Tokenizer { get; private set; }
public bool EndOfFile { get; private set; }
public TToken Current { get; private set; }
public SyntaxToken Current { get; private set; }
public ITextDocument Source
{
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return !EndOfFile;
}
public void PutBack(TToken token)
public void PutBack(SyntaxToken token)
{
Source.Position -= token.Content.Length;
Current = null;

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("rzls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -0,0 +1,58 @@
// 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.Syntax
{
internal struct ArrayElement<T>
{
public T Value;
public static implicit operator T(ArrayElement<T> element)
{
return element.Value;
}
//NOTE: there is no opposite conversion operator T -> ArrayElement<T>
//
// that is because it is preferred to update array elements in-place
// "elements[i].Value = v" results in much better code than "elements[i] = (ArrayElement<T>)v"
//
// The reason is that x86 ABI requires that structs must be returned in
// a return buffer even if they can fit in a register like this one.
// Also since struct contains a reference, the write to the buffer is done with a checked GC barrier
// as JIT does not know if the write goes to a stack or a heap location.
// Assigning to Value directly easily avoids all this redundancy.
public static ArrayElement<T>[] MakeElementArray(T[] items)
{
if (items == null)
{
return null;
}
var array = new ArrayElement<T>[items.Length];
for (var i = 0; i < items.Length; i++)
{
array[i].Value = items[i];
}
return array;
}
public static T[] MakeArray(ArrayElement<T>[] items)
{
if (items == null)
{
return null;
}
var array = new T[items.Length];
for (var i = 0; i < items.Length; i++)
{
array[i] = items[i].Value;
}
return array;
}
}
}

View File

@ -0,0 +1,375 @@
// <auto-generated />
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal partial class SyntaxVisitor<TResult>
{
/// <summary>Called when the visitor visits a RazorCommentBlockSyntax node.</summary>
public virtual TResult VisitRazorCommentBlock(RazorCommentBlockSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a HtmlTextLiteralSyntax node.</summary>
public virtual TResult VisitHtmlTextLiteral(HtmlTextLiteralSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpTransitionSyntax node.</summary>
public virtual TResult VisitCSharpTransition(CSharpTransitionSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpMetaCodeSyntax node.</summary>
public virtual TResult VisitCSharpMetaCode(CSharpMetaCodeSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpCodeLiteralSyntax node.</summary>
public virtual TResult VisitCSharpCodeLiteral(CSharpCodeLiteralSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpCodeBlockSyntax node.</summary>
public virtual TResult VisitCSharpCodeBlock(CSharpCodeBlockSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpStatement node.</summary>
public virtual TResult VisitCSharpStatement(CSharpStatement node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpStatementBodySyntax node.</summary>
public virtual TResult VisitCSharpStatementBody(CSharpStatementBodySyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpExpression node.</summary>
public virtual TResult VisitCSharpExpression(CSharpExpression node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpExpressionBodySyntax node.</summary>
public virtual TResult VisitCSharpExpressionBody(CSharpExpressionBodySyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpDirectiveSyntax node.</summary>
public virtual TResult VisitCSharpDirective(CSharpDirectiveSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpDirectiveBodySyntax node.</summary>
public virtual TResult VisitCSharpDirectiveBody(CSharpDirectiveBodySyntax node)
{
return DefaultVisit(node);
}
}
internal partial class SyntaxVisitor
{
/// <summary>Called when the visitor visits a RazorCommentBlockSyntax node.</summary>
public virtual void VisitRazorCommentBlock(RazorCommentBlockSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a HtmlTextLiteralSyntax node.</summary>
public virtual void VisitHtmlTextLiteral(HtmlTextLiteralSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpTransitionSyntax node.</summary>
public virtual void VisitCSharpTransition(CSharpTransitionSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpMetaCodeSyntax node.</summary>
public virtual void VisitCSharpMetaCode(CSharpMetaCodeSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpCodeLiteralSyntax node.</summary>
public virtual void VisitCSharpCodeLiteral(CSharpCodeLiteralSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpCodeBlockSyntax node.</summary>
public virtual void VisitCSharpCodeBlock(CSharpCodeBlockSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpStatement node.</summary>
public virtual void VisitCSharpStatement(CSharpStatement node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpStatementBodySyntax node.</summary>
public virtual void VisitCSharpStatementBody(CSharpStatementBodySyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpExpression node.</summary>
public virtual void VisitCSharpExpression(CSharpExpression node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpExpressionBodySyntax node.</summary>
public virtual void VisitCSharpExpressionBody(CSharpExpressionBodySyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpDirectiveSyntax node.</summary>
public virtual void VisitCSharpDirective(CSharpDirectiveSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a CSharpDirectiveBodySyntax node.</summary>
public virtual void VisitCSharpDirectiveBody(CSharpDirectiveBodySyntax node)
{
DefaultVisit(node);
}
}
internal static partial class SyntaxFactory
{
/// <summary>Creates a new RazorCommentBlockSyntax instance.</summary>
public static RazorCommentBlockSyntax RazorCommentBlock(SyntaxToken startCommentTransition, SyntaxToken startCommentStar, SyntaxToken comment, SyntaxToken endCommentStar, SyntaxToken endCommentTransition)
{
switch (startCommentTransition.Kind)
{
case SyntaxKind.RazorCommentTransition:
break;
default:
throw new ArgumentException("startCommentTransition");
}
switch (startCommentStar.Kind)
{
case SyntaxKind.RazorCommentStar:
break;
default:
throw new ArgumentException("startCommentStar");
}
switch (comment.Kind)
{
case SyntaxKind.RazorComment:
case SyntaxKind.Unknown:
break;
default:
throw new ArgumentException("comment");
}
switch (endCommentStar.Kind)
{
case SyntaxKind.RazorCommentStar:
break;
default:
throw new ArgumentException("endCommentStar");
}
switch (endCommentTransition.Kind)
{
case SyntaxKind.RazorCommentTransition:
break;
default:
throw new ArgumentException("endCommentTransition");
}
return (RazorCommentBlockSyntax)InternalSyntax.SyntaxFactory.RazorCommentBlock((Syntax.InternalSyntax.SyntaxToken)startCommentTransition.Green, (Syntax.InternalSyntax.SyntaxToken)startCommentStar.Green, (Syntax.InternalSyntax.SyntaxToken)comment.Green, (Syntax.InternalSyntax.SyntaxToken)endCommentStar.Green, (Syntax.InternalSyntax.SyntaxToken)endCommentTransition.Green).CreateRed();
}
/// <summary>Creates a new RazorCommentBlockSyntax instance.</summary>
public static RazorCommentBlockSyntax RazorCommentBlock()
{
return SyntaxFactory.RazorCommentBlock(SyntaxFactory.Token(SyntaxKind.RazorCommentTransition), SyntaxFactory.Token(SyntaxKind.RazorCommentStar), default(SyntaxToken), SyntaxFactory.Token(SyntaxKind.RazorCommentStar), SyntaxFactory.Token(SyntaxKind.RazorCommentTransition));
}
/// <summary>Creates a new HtmlTextLiteralSyntax instance.</summary>
public static HtmlTextLiteralSyntax HtmlTextLiteral(SyntaxList<SyntaxToken> textTokens)
{
return (HtmlTextLiteralSyntax)InternalSyntax.SyntaxFactory.HtmlTextLiteral(textTokens.Node.ToGreenList<InternalSyntax.SyntaxToken>()).CreateRed();
}
/// <summary>Creates a new HtmlTextLiteralSyntax instance.</summary>
public static HtmlTextLiteralSyntax HtmlTextLiteral()
{
return SyntaxFactory.HtmlTextLiteral(default(SyntaxList<SyntaxToken>));
}
/// <summary>Creates a new CSharpTransitionSyntax instance.</summary>
public static CSharpTransitionSyntax CSharpTransition(SyntaxToken transition)
{
switch (transition.Kind)
{
case SyntaxKind.Transition:
break;
default:
throw new ArgumentException("transition");
}
return (CSharpTransitionSyntax)InternalSyntax.SyntaxFactory.CSharpTransition((Syntax.InternalSyntax.SyntaxToken)transition.Green).CreateRed();
}
/// <summary>Creates a new CSharpTransitionSyntax instance.</summary>
public static CSharpTransitionSyntax CSharpTransition()
{
return SyntaxFactory.CSharpTransition(SyntaxFactory.Token(SyntaxKind.Transition));
}
/// <summary>Creates a new CSharpMetaCodeSyntax instance.</summary>
public static CSharpMetaCodeSyntax CSharpMetaCode(SyntaxList<SyntaxToken> metaCode)
{
return (CSharpMetaCodeSyntax)InternalSyntax.SyntaxFactory.CSharpMetaCode(metaCode.Node.ToGreenList<InternalSyntax.SyntaxToken>()).CreateRed();
}
/// <summary>Creates a new CSharpMetaCodeSyntax instance.</summary>
public static CSharpMetaCodeSyntax CSharpMetaCode()
{
return SyntaxFactory.CSharpMetaCode(default(SyntaxList<SyntaxToken>));
}
/// <summary>Creates a new CSharpCodeLiteralSyntax instance.</summary>
public static CSharpCodeLiteralSyntax CSharpCodeLiteral(SyntaxList<SyntaxToken> cSharpTokens)
{
return (CSharpCodeLiteralSyntax)InternalSyntax.SyntaxFactory.CSharpCodeLiteral(cSharpTokens.Node.ToGreenList<InternalSyntax.SyntaxToken>()).CreateRed();
}
/// <summary>Creates a new CSharpCodeLiteralSyntax instance.</summary>
public static CSharpCodeLiteralSyntax CSharpCodeLiteral()
{
return SyntaxFactory.CSharpCodeLiteral(default(SyntaxList<SyntaxToken>));
}
/// <summary>Creates a new CSharpCodeBlockSyntax instance.</summary>
public static CSharpCodeBlockSyntax CSharpCodeBlock(SyntaxList<RazorSyntaxNode> children)
{
return (CSharpCodeBlockSyntax)InternalSyntax.SyntaxFactory.CSharpCodeBlock(children.Node.ToGreenList<InternalSyntax.RazorSyntaxNode>()).CreateRed();
}
/// <summary>Creates a new CSharpCodeBlockSyntax instance.</summary>
public static CSharpCodeBlockSyntax CSharpCodeBlock()
{
return SyntaxFactory.CSharpCodeBlock(default(SyntaxList<RazorSyntaxNode>));
}
/// <summary>Creates a new CSharpStatement instance.</summary>
public static CSharpStatement CSharpStatement(CSharpTransitionSyntax transition, CSharpSyntaxNode body)
{
if (transition == null)
throw new ArgumentNullException(nameof(transition));
if (body == null)
throw new ArgumentNullException(nameof(body));
return (CSharpStatement)InternalSyntax.SyntaxFactory.CSharpStatement(transition == null ? null : (InternalSyntax.CSharpTransitionSyntax)transition.Green, body == null ? null : (InternalSyntax.CSharpSyntaxNode)body.Green).CreateRed();
}
/// <summary>Creates a new CSharpStatement instance.</summary>
public static CSharpStatement CSharpStatement(CSharpSyntaxNode body)
{
return SyntaxFactory.CSharpStatement(SyntaxFactory.CSharpTransition(), body);
}
/// <summary>Creates a new CSharpStatementBodySyntax instance.</summary>
public static CSharpStatementBodySyntax CSharpStatementBody(CSharpMetaCodeSyntax openBrace, CSharpCodeBlockSyntax cSharpCode, CSharpMetaCodeSyntax closeBrace)
{
if (openBrace == null)
throw new ArgumentNullException(nameof(openBrace));
if (cSharpCode == null)
throw new ArgumentNullException(nameof(cSharpCode));
if (closeBrace == null)
throw new ArgumentNullException(nameof(closeBrace));
return (CSharpStatementBodySyntax)InternalSyntax.SyntaxFactory.CSharpStatementBody(openBrace == null ? null : (InternalSyntax.CSharpMetaCodeSyntax)openBrace.Green, cSharpCode == null ? null : (InternalSyntax.CSharpCodeBlockSyntax)cSharpCode.Green, closeBrace == null ? null : (InternalSyntax.CSharpMetaCodeSyntax)closeBrace.Green).CreateRed();
}
/// <summary>Creates a new CSharpStatementBodySyntax instance.</summary>
public static CSharpStatementBodySyntax CSharpStatementBody()
{
return SyntaxFactory.CSharpStatementBody(SyntaxFactory.CSharpMetaCode(), SyntaxFactory.CSharpCodeBlock(), SyntaxFactory.CSharpMetaCode());
}
/// <summary>Creates a new CSharpExpression instance.</summary>
public static CSharpExpression CSharpExpression(CSharpTransitionSyntax transition, CSharpSyntaxNode body)
{
if (transition == null)
throw new ArgumentNullException(nameof(transition));
if (body == null)
throw new ArgumentNullException(nameof(body));
return (CSharpExpression)InternalSyntax.SyntaxFactory.CSharpExpression(transition == null ? null : (InternalSyntax.CSharpTransitionSyntax)transition.Green, body == null ? null : (InternalSyntax.CSharpSyntaxNode)body.Green).CreateRed();
}
/// <summary>Creates a new CSharpExpression instance.</summary>
public static CSharpExpression CSharpExpression(CSharpSyntaxNode body)
{
return SyntaxFactory.CSharpExpression(SyntaxFactory.CSharpTransition(), body);
}
/// <summary>Creates a new CSharpExpressionBodySyntax instance.</summary>
public static CSharpExpressionBodySyntax CSharpExpressionBody(CSharpMetaCodeSyntax openParen, CSharpCodeBlockSyntax cSharpCode, CSharpMetaCodeSyntax closeParen)
{
if (cSharpCode == null)
throw new ArgumentNullException(nameof(cSharpCode));
return (CSharpExpressionBodySyntax)InternalSyntax.SyntaxFactory.CSharpExpressionBody(openParen == null ? null : (InternalSyntax.CSharpMetaCodeSyntax)openParen.Green, cSharpCode == null ? null : (InternalSyntax.CSharpCodeBlockSyntax)cSharpCode.Green, closeParen == null ? null : (InternalSyntax.CSharpMetaCodeSyntax)closeParen.Green).CreateRed();
}
/// <summary>Creates a new CSharpExpressionBodySyntax instance.</summary>
public static CSharpExpressionBodySyntax CSharpExpressionBody()
{
return SyntaxFactory.CSharpExpressionBody(default(CSharpMetaCodeSyntax), SyntaxFactory.CSharpCodeBlock(), default(CSharpMetaCodeSyntax));
}
/// <summary>Creates a new CSharpDirectiveSyntax instance.</summary>
public static CSharpDirectiveSyntax CSharpDirective(CSharpTransitionSyntax transition, CSharpSyntaxNode body)
{
if (transition == null)
throw new ArgumentNullException(nameof(transition));
if (body == null)
throw new ArgumentNullException(nameof(body));
return (CSharpDirectiveSyntax)InternalSyntax.SyntaxFactory.CSharpDirective(transition == null ? null : (InternalSyntax.CSharpTransitionSyntax)transition.Green, body == null ? null : (InternalSyntax.CSharpSyntaxNode)body.Green).CreateRed();
}
/// <summary>Creates a new CSharpDirectiveSyntax instance.</summary>
public static CSharpDirectiveSyntax CSharpDirective(CSharpSyntaxNode body)
{
return SyntaxFactory.CSharpDirective(SyntaxFactory.CSharpTransition(), body);
}
/// <summary>Creates a new CSharpDirectiveBodySyntax instance.</summary>
public static CSharpDirectiveBodySyntax CSharpDirectiveBody(CSharpMetaCodeSyntax keyword, CSharpCodeBlockSyntax cSharpCode)
{
if (keyword == null)
throw new ArgumentNullException(nameof(keyword));
if (cSharpCode == null)
throw new ArgumentNullException(nameof(cSharpCode));
return (CSharpDirectiveBodySyntax)InternalSyntax.SyntaxFactory.CSharpDirectiveBody(keyword == null ? null : (InternalSyntax.CSharpMetaCodeSyntax)keyword.Green, cSharpCode == null ? null : (InternalSyntax.CSharpCodeBlockSyntax)cSharpCode.Green).CreateRed();
}
/// <summary>Creates a new CSharpDirectiveBodySyntax instance.</summary>
public static CSharpDirectiveBodySyntax CSharpDirectiveBody()
{
return SyntaxFactory.CSharpDirectiveBody(SyntaxFactory.CSharpMetaCode(), SyntaxFactory.CSharpCodeBlock());
}
}
}

View File

@ -0,0 +1,577 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal abstract class GreenNode
{
private static readonly RazorDiagnostic[] EmptyDiagnostics = Array.Empty<RazorDiagnostic>();
private static readonly SyntaxAnnotation[] EmptyAnnotations = Array.Empty<SyntaxAnnotation>();
private static readonly ConditionalWeakTable<GreenNode, RazorDiagnostic[]> DiagnosticsTable =
new ConditionalWeakTable<GreenNode, RazorDiagnostic[]>();
private static readonly ConditionalWeakTable<GreenNode, SyntaxAnnotation[]> AnnotationsTable =
new ConditionalWeakTable<GreenNode, SyntaxAnnotation[]>();
private NodeFlags _flags;
private byte _slotCount;
protected GreenNode(SyntaxKind kind)
{
Kind = kind;
}
protected GreenNode(SyntaxKind kind, int fullWidth)
: this(kind)
{
FullWidth = fullWidth;
}
protected GreenNode(SyntaxKind kind, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
: this(kind, 0, diagnostics, annotations)
{
}
protected GreenNode(SyntaxKind kind, int fullWidth, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
: this(kind, fullWidth)
{
if (diagnostics?.Length > 0)
{
_flags |= NodeFlags.ContainsDiagnostics;
DiagnosticsTable.Add(this, diagnostics);
}
if (annotations?.Length > 0)
{
foreach (var annotation in annotations)
{
if (annotation == null)
{
throw new ArgumentException(nameof(annotations), "Annotation cannot be null");
}
}
_flags |= NodeFlags.ContainsAnnotations;
AnnotationsTable.Add(this, annotations);
}
}
protected void AdjustFlagsAndWidth(GreenNode node)
{
if (node == null)
{
return;
}
_flags |= (node.Flags & NodeFlags.InheritMask);
FullWidth += node.FullWidth;
}
#region Kind
internal SyntaxKind Kind { get; }
internal virtual bool IsList => false;
internal virtual bool IsToken => false;
internal virtual bool IsTrivia => false;
#endregion
#region Slots
public int SlotCount
{
get
{
int count = _slotCount;
if (count == byte.MaxValue)
{
count = GetSlotCount();
}
return count;
}
protected set
{
_slotCount = (byte)value;
}
}
internal abstract GreenNode GetSlot(int index);
// for slot counts >= byte.MaxValue
protected virtual int GetSlotCount()
{
return _slotCount;
}
public virtual int GetSlotOffset(int index)
{
var offset = 0;
for (var i = 0; i < index; i++)
{
var child = GetSlot(i);
if (child != null)
offset += child.FullWidth;
}
return offset;
}
public virtual int FindSlotIndexContainingOffset(int offset)
{
Debug.Assert(0 <= offset && offset < FullWidth);
int i;
var accumulatedWidth = 0;
for (i = 0; ; i++)
{
Debug.Assert(i < SlotCount);
var child = GetSlot(i);
if (child != null)
{
accumulatedWidth += child.FullWidth;
if (offset < accumulatedWidth)
{
break;
}
}
}
return i;
}
#endregion
#region Flags
internal NodeFlags Flags => _flags;
internal void SetFlags(NodeFlags flags)
{
_flags |= flags;
}
internal void ClearFlags(NodeFlags flags)
{
_flags &= ~flags;
}
internal virtual bool IsMissing => (_flags & NodeFlags.IsMissing) != 0;
public bool ContainsDiagnostics
{
get
{
return (_flags & NodeFlags.ContainsDiagnostics) != 0;
}
}
public bool ContainsAnnotations
{
get
{
return (_flags & NodeFlags.ContainsAnnotations) != 0;
}
}
#endregion
#region Spans
internal int FullWidth { get; private set; }
public virtual int Width
{
get
{
return FullWidth - GetLeadingTriviaWidth() - GetTrailingTriviaWidth();
}
}
public virtual int GetLeadingTriviaWidth()
{
return FullWidth != 0 ? GetFirstTerminal().GetLeadingTriviaWidth() : 0;
}
public virtual int GetTrailingTriviaWidth()
{
return FullWidth != 0 ? GetLastTerminal().GetTrailingTriviaWidth() : 0;
}
public bool HasLeadingTrivia
{
get
{
return GetLeadingTriviaWidth() != 0;
}
}
public bool HasTrailingTrivia
{
get
{
return GetTrailingTriviaWidth() != 0;
}
}
#endregion
#region Diagnostics
internal abstract GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics);
internal RazorDiagnostic[] GetDiagnostics()
{
if (ContainsDiagnostics)
{
if (DiagnosticsTable.TryGetValue(this, out var diagnostics))
{
return diagnostics;
}
}
return EmptyDiagnostics;
}
#endregion
#region Annotations
internal abstract GreenNode SetAnnotations(SyntaxAnnotation[] annotations);
internal SyntaxAnnotation[] GetAnnotations()
{
if (ContainsAnnotations)
{
if (AnnotationsTable.TryGetValue(this, out var annotations))
{
Debug.Assert(annotations.Length != 0, "There cannot be an empty annotation entry.");
return annotations;
}
}
return EmptyAnnotations;
}
#endregion
#region Text
public virtual string ToFullString()
{
var builder = new StringBuilder();
var writer = new StringWriter(builder, System.Globalization.CultureInfo.InvariantCulture);
WriteTo(writer);
return builder.ToString();
}
public virtual void WriteTo(TextWriter writer)
{
WriteTo(writer, leading: true, trailing: true);
}
protected internal void WriteTo(TextWriter writer, bool leading, bool trailing)
{
// Use an actual Stack so we can write out deeply recursive structures without overflowing.
var stack = new Stack<StackEntry>();
stack.Push(new StackEntry(this, leading, trailing));
// Separated out stack processing logic so that it does not unintentionally refer to
// "this", "leading" or "trailing.
ProcessStack(writer, stack);
}
protected virtual void WriteTriviaTo(TextWriter writer)
{
throw new NotImplementedException();
}
protected virtual void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{
throw new NotImplementedException();
}
#endregion
#region Tokens
public virtual object GetValue()
{
return null;
}
public virtual string GetValueText()
{
return string.Empty;
}
public virtual GreenNode GetLeadingTrivia()
{
return null;
}
public virtual GreenNode GetTrailingTrivia()
{
return null;
}
public virtual GreenNode WithLeadingTrivia(GreenNode trivia)
{
return this;
}
public virtual GreenNode WithTrailingTrivia(GreenNode trivia)
{
return this;
}
public InternalSyntax.SyntaxToken GetFirstToken()
{
return (InternalSyntax.SyntaxToken)GetFirstTerminal();
}
public InternalSyntax.SyntaxToken GetLastToken()
{
return (InternalSyntax.SyntaxToken)GetLastTerminal();
}
internal GreenNode GetFirstTerminal()
{
var node = this;
do
{
GreenNode firstChild = null;
for (int i = 0, n = node.SlotCount; i < n; i++)
{
var child = node.GetSlot(i);
if (child != null)
{
firstChild = child;
break;
}
}
node = firstChild;
} while (node?._slotCount > 0);
return node;
}
internal GreenNode GetLastTerminal()
{
var node = this;
do
{
GreenNode lastChild = null;
for (var i = node.SlotCount - 1; i >= 0; i--)
{
var child = node.GetSlot(i);
if (child != null)
{
lastChild = child;
break;
}
}
node = lastChild;
} while (node?._slotCount > 0);
return node;
}
#endregion
#region Equivalence
public virtual bool IsEquivalentTo(GreenNode other)
{
if (this == other)
{
return true;
}
if (other == null)
{
return false;
}
return EquivalentToInternal(this, other);
}
private static bool EquivalentToInternal(GreenNode node1, GreenNode node2)
{
if (node1.Kind != node2.Kind)
{
// A single-element list is usually represented as just a single node,
// but can be represented as a List node with one child. Move to that
// child if necessary.
if (node1.IsList && node1.SlotCount == 1)
{
node1 = node1.GetSlot(0);
}
if (node2.IsList && node2.SlotCount == 1)
{
node2 = node2.GetSlot(0);
}
if (node1.Kind != node2.Kind)
{
return false;
}
}
if (node1.FullWidth != node2.FullWidth)
{
return false;
}
var n = node1.SlotCount;
if (n != node2.SlotCount)
{
return false;
}
for (var i = 0; i < n; i++)
{
var node1Child = node1.GetSlot(i);
var node2Child = node2.GetSlot(i);
if (node1Child != null && node2Child != null && !node1Child.IsEquivalentTo(node2Child))
{
return false;
}
}
return true;
}
#endregion
#region Factories
public virtual GreenNode CreateList(IEnumerable<GreenNode> nodes, bool alwaysCreateListNode = false)
{
if (nodes == null)
{
return null;
}
var list = nodes.ToArray();
switch (list.Length)
{
case 0:
return null;
case 1:
if (alwaysCreateListNode)
{
goto default;
}
else
{
return list[0];
}
case 2:
return InternalSyntax.SyntaxList.List(list[0], list[1]);
case 3:
return InternalSyntax.SyntaxList.List(list[0], list[1], list[2]);
default:
return InternalSyntax.SyntaxList.List(list);
}
}
public SyntaxNode CreateRed()
{
return CreateRed(null, 0);
}
internal abstract SyntaxNode CreateRed(SyntaxNode parent, int position);
#endregion
public abstract TResult Accept<TResult>(InternalSyntax.SyntaxVisitor<TResult> visitor);
public abstract void Accept(InternalSyntax.SyntaxVisitor visitor);
#region StaticMethods
private static void ProcessStack(TextWriter writer,
Stack<StackEntry> stack)
{
while (stack.Count > 0)
{
var current = stack.Pop();
var currentNode = current.Node;
var currentLeading = current.Leading;
var currentTrailing = current.Trailing;
if (currentNode.IsToken)
{
currentNode.WriteTokenTo(writer, currentLeading, currentTrailing);
continue;
}
if (currentNode.IsTrivia)
{
currentNode.WriteTriviaTo(writer);
continue;
}
var firstIndex = GetFirstNonNullChildIndex(currentNode);
var lastIndex = GetLastNonNullChildIndex(currentNode);
for (var i = lastIndex; i >= firstIndex; i--)
{
var child = currentNode.GetSlot(i);
if (child != null)
{
var first = i == firstIndex;
var last = i == lastIndex;
stack.Push(new StackEntry(child, currentLeading | !first, currentTrailing | !last));
}
}
}
}
private static int GetFirstNonNullChildIndex(GreenNode node)
{
int n = node.SlotCount;
int firstIndex = 0;
for (; firstIndex < n; firstIndex++)
{
var child = node.GetSlot(firstIndex);
if (child != null)
{
break;
}
}
return firstIndex;
}
private static int GetLastNonNullChildIndex(GreenNode node)
{
int n = node.SlotCount;
int lastIndex = n - 1;
for (; lastIndex >= 0; lastIndex--)
{
var child = node.GetSlot(lastIndex);
if (child != null)
{
break;
}
}
return lastIndex;
}
private struct StackEntry
{
public StackEntry(GreenNode node, bool leading, bool trailing)
{
Node = node;
Leading = leading;
Trailing = trailing;
}
public GreenNode Node { get; }
public bool Leading { get; }
public bool Trailing { get; }
}
#endregion
}
}

View File

@ -0,0 +1,56 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal static class GreenNodeExtensions
{
internal static InternalSyntax.SyntaxList<T> ToGreenList<T>(this SyntaxNode node) where T : GreenNode
{
return node != null ?
ToGreenList<T>(node.Green) :
default(InternalSyntax.SyntaxList<T>);
}
internal static InternalSyntax.SyntaxList<T> ToGreenList<T>(this GreenNode node) where T : GreenNode
{
return new InternalSyntax.SyntaxList<T>(node);
}
public static TNode WithAnnotationsGreen<TNode>(this TNode node, IEnumerable<SyntaxAnnotation> annotations) where TNode : GreenNode
{
var newAnnotations = new List<SyntaxAnnotation>();
foreach (var candidate in annotations)
{
if (!newAnnotations.Contains(candidate))
{
newAnnotations.Add(candidate);
}
}
if (newAnnotations.Count == 0)
{
var existingAnnotations = node.GetAnnotations();
if (existingAnnotations == null || existingAnnotations.Length == 0)
{
return node;
}
else
{
return (TNode)node.SetAnnotations(null);
}
}
else
{
return (TNode)node.SetAnnotations(newAnnotations.ToArray());
}
}
public static TNode WithDiagnosticsGreen<TNode>(this TNode node, RazorDiagnostic[] diagnostics) where TNode : GreenNode
{
return (TNode)node.SetDiagnostics(diagnostics);
}
}
}

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.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
{
internal static partial class SyntaxFactory
{
internal static SyntaxToken Token(SyntaxKind kind, string content, IEnumerable<RazorDiagnostic> diagnostics)
{
return Token(kind, content, diagnostics.ToArray());
}
internal static SyntaxToken Token(SyntaxKind kind, string content, params RazorDiagnostic[] diagnostics)
{
return new SyntaxToken(kind, content, diagnostics);
}
}
}

View File

@ -0,0 +1,428 @@
// 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;
namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
{
internal abstract class SyntaxList : GreenNode
{
internal SyntaxList()
: base(SyntaxKind.List)
{
}
internal SyntaxList(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
: base(SyntaxKind.List, diagnostics, annotations)
{
}
internal override bool IsList => true;
internal static GreenNode List(GreenNode child)
{
return child;
}
internal static WithTwoChildren List(GreenNode child0, GreenNode child1)
{
Debug.Assert(child0 != null);
Debug.Assert(child1 != null);
var result = new WithTwoChildren(child0, child1);
return result;
}
internal static WithThreeChildren List(GreenNode child0, GreenNode child1, GreenNode child2)
{
Debug.Assert(child0 != null);
Debug.Assert(child1 != null);
Debug.Assert(child2 != null);
var result = new WithThreeChildren(child0, child1, child2);
return result;
}
internal static GreenNode List(GreenNode[] nodes)
{
return List(nodes, nodes.Length);
}
internal static GreenNode List(GreenNode[] nodes, int count)
{
var array = new ArrayElement<GreenNode>[count];
for (int i = 0; i < count; i++)
{
Debug.Assert(nodes[i] != null);
array[i].Value = nodes[i];
}
return List(array);
}
internal static SyntaxList List(ArrayElement<GreenNode>[] children)
{
// "WithLotsOfChildren" list will allocate a separate array to hold
// precomputed node offsets. It may not be worth it for smallish lists.
if (children.Length < 10)
{
return new WithManyChildren(children);
}
else
{
return new WithLotsOfChildren(children);
}
}
internal abstract void CopyTo(ArrayElement<GreenNode>[] array, int offset);
internal static GreenNode Concat(GreenNode left, GreenNode right)
{
if (left == null)
{
return right;
}
if (right == null)
{
return left;
}
var leftList = left as SyntaxList;
var rightList = right as SyntaxList;
if (leftList != null)
{
if (rightList != null)
{
var tmp = new ArrayElement<GreenNode>[left.SlotCount + right.SlotCount];
leftList.CopyTo(tmp, 0);
rightList.CopyTo(tmp, left.SlotCount);
return List(tmp);
}
else
{
var tmp = new ArrayElement<GreenNode>[left.SlotCount + 1];
leftList.CopyTo(tmp, 0);
tmp[left.SlotCount].Value = right;
return List(tmp);
}
}
else if (rightList != null)
{
var tmp = new ArrayElement<GreenNode>[rightList.SlotCount + 1];
tmp[0].Value = left;
rightList.CopyTo(tmp, 1);
return List(tmp);
}
else
{
return List(left, right);
}
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.Visit(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.Visit(this);
}
internal class WithTwoChildren : SyntaxList
{
private readonly GreenNode _child0;
private readonly GreenNode _child1;
internal WithTwoChildren(GreenNode child0, GreenNode child1)
{
SlotCount = 2;
AdjustFlagsAndWidth(child0);
_child0 = child0;
AdjustFlagsAndWidth(child1);
_child1 = child1;
}
internal WithTwoChildren(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations, GreenNode child0, GreenNode child1)
{
SlotCount = 2;
AdjustFlagsAndWidth(child0);
_child0 = child0;
AdjustFlagsAndWidth(child1);
_child1 = child1;
}
internal override GreenNode GetSlot(int index)
{
switch (index)
{
case 0:
return _child0;
case 1:
return _child1;
default:
return null;
}
}
internal override void CopyTo(ArrayElement<GreenNode>[] array, int offset)
{
array[offset].Value = _child0;
array[offset + 1].Value = _child1;
}
internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
{
return new Syntax.SyntaxList.WithTwoChildren(this, parent, position);
}
internal override GreenNode SetDiagnostics(RazorDiagnostic[] errors)
{
return new WithTwoChildren(errors, this.GetAnnotations(), _child0, _child1);
}
internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
{
return new WithTwoChildren(GetDiagnostics(), annotations, _child0, _child1);
}
}
internal class WithThreeChildren : SyntaxList
{
private readonly GreenNode _child0;
private readonly GreenNode _child1;
private readonly GreenNode _child2;
internal WithThreeChildren(GreenNode child0, GreenNode child1, GreenNode child2)
{
SlotCount = 3;
AdjustFlagsAndWidth(child0);
_child0 = child0;
AdjustFlagsAndWidth(child1);
_child1 = child1;
AdjustFlagsAndWidth(child2);
_child2 = child2;
}
internal WithThreeChildren(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations, GreenNode child0, GreenNode child1, GreenNode child2)
: base(diagnostics, annotations)
{
SlotCount = 3;
AdjustFlagsAndWidth(child0);
_child0 = child0;
AdjustFlagsAndWidth(child1);
_child1 = child1;
AdjustFlagsAndWidth(child2);
_child2 = child2;
}
internal override GreenNode GetSlot(int index)
{
switch (index)
{
case 0:
return _child0;
case 1:
return _child1;
case 2:
return _child2;
default:
return null;
}
}
internal override void CopyTo(ArrayElement<GreenNode>[] array, int offset)
{
array[offset].Value = _child0;
array[offset + 1].Value = _child1;
array[offset + 2].Value = _child2;
}
internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
{
return new Syntax.SyntaxList.WithThreeChildren(this, parent, position);
}
internal override GreenNode SetDiagnostics(RazorDiagnostic[] errors)
{
return new WithThreeChildren(errors, GetAnnotations(), _child0, _child1, _child2);
}
internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
{
return new WithThreeChildren(GetDiagnostics(), annotations, _child0, _child1, _child2);
}
}
internal abstract class WithManyChildrenBase : SyntaxList
{
internal readonly ArrayElement<GreenNode>[] children;
internal WithManyChildrenBase(ArrayElement<GreenNode>[] children)
{
this.children = children;
this.InitializeChildren();
}
internal WithManyChildrenBase(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations, ArrayElement<GreenNode>[] children)
: base(diagnostics, annotations)
{
this.children = children;
this.InitializeChildren();
}
private void InitializeChildren()
{
var n = children.Length;
if (n < byte.MaxValue)
{
SlotCount = (byte)n;
}
else
{
SlotCount = byte.MaxValue;
}
for (var i = 0; i < children.Length; i++)
{
AdjustFlagsAndWidth(children[i]);
}
}
protected override int GetSlotCount()
{
return children.Length;
}
internal override GreenNode GetSlot(int index)
{
return children[index];
}
internal override void CopyTo(ArrayElement<GreenNode>[] array, int offset)
{
Array.Copy(children, 0, array, offset, children.Length);
}
internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
{
return new Syntax.SyntaxList.WithManyChildren(this, parent, position);
}
}
internal sealed class WithManyChildren : WithManyChildrenBase
{
internal WithManyChildren(ArrayElement<GreenNode>[] children)
: base(children)
{
}
internal WithManyChildren(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations, ArrayElement<GreenNode>[] children)
: base(diagnostics, annotations, children)
{
}
internal override GreenNode SetDiagnostics(RazorDiagnostic[] errors)
{
return new WithManyChildren(errors, GetAnnotations(), children);
}
internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
{
return new WithManyChildren(GetDiagnostics(), annotations, children);
}
}
internal sealed class WithLotsOfChildren : WithManyChildrenBase
{
private readonly int[] _childOffsets;
internal WithLotsOfChildren(ArrayElement<GreenNode>[] children)
: base(children)
{
_childOffsets = CalculateOffsets(children);
}
internal WithLotsOfChildren(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations, ArrayElement<GreenNode>[] children, int[] childOffsets)
: base(diagnostics, annotations, children)
{
_childOffsets = childOffsets;
}
public override int GetSlotOffset(int index)
{
return _childOffsets[index];
}
/// <summary>
/// Find the slot that contains the given offset.
/// </summary>
/// <param name="offset">The target offset. Must be between 0 and <see cref="GreenNode.FullWidth"/>.</param>
/// <returns>The slot index of the slot containing the given offset.</returns>
/// <remarks>
/// This implementation uses a binary search to find the first slot that contains
/// the given offset.
/// </remarks>
public override int FindSlotIndexContainingOffset(int offset)
{
Debug.Assert(offset >= 0 && offset < FullWidth);
return BinarySearchUpperBound(_childOffsets, offset) - 1;
}
private static int[] CalculateOffsets(ArrayElement<GreenNode>[] children)
{
var n = children.Length;
var childOffsets = new int[n];
var offset = 0;
for (var i = 0; i < n; i++)
{
childOffsets[i] = offset;
offset += children[i].Value.FullWidth;
}
return childOffsets;
}
internal override GreenNode SetDiagnostics(RazorDiagnostic[] errors)
{
return new WithLotsOfChildren(errors, this.GetAnnotations(), children, _childOffsets);
}
internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
{
return new WithLotsOfChildren(GetDiagnostics(), annotations, children, _childOffsets);
}
/// <summary>
/// Search a sorted integer array for the target value in O(log N) time.
/// </summary>
/// <param name="array">The array of integers which must be sorted in ascending order.</param>
/// <param name="value">The target value.</param>
/// <returns>An index in the array pointing to the position where <paramref name="value"/> should be
/// inserted in order to maintain the sorted order. All values to the right of this position will be
/// strictly greater than <paramref name="value"/>. Note that this may return a position off the end
/// of the array if all elements are less than or equal to <paramref name="value"/>.</returns>
private static int BinarySearchUpperBound(int[] array, int value)
{
var low = 0;
var high = array.Length - 1;
while (low <= high)
{
var middle = low + ((high - low) >> 1);
if (array[middle] > value)
{
high = middle - 1;
}
else
{
low = middle + 1;
}
}
return low;
}
}
}
}

View File

@ -0,0 +1,201 @@
// 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;
namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
{
internal class SyntaxListBuilder
{
private ArrayElement<GreenNode>[] _nodes;
public int Count { get; private set; }
public SyntaxListBuilder(int size)
{
_nodes = new ArrayElement<GreenNode>[size];
}
public static SyntaxListBuilder Create()
{
return new SyntaxListBuilder(8);
}
public void Clear()
{
Count = 0;
}
public GreenNode this[int index]
{
get
{
return _nodes[index];
}
set
{
_nodes[index].Value = value;
}
}
public void Add(GreenNode item)
{
if (item == null) return;
if (item.IsList)
{
var slotCount = item.SlotCount;
// Necessary, but not sufficient (e.g. for nested lists).
EnsureAdditionalCapacity(slotCount);
for (var i = 0; i < slotCount; i++)
{
Add(item.GetSlot(i));
}
}
else
{
EnsureAdditionalCapacity(1);
_nodes[Count++].Value = item;
}
}
public void AddRange(GreenNode[] items)
{
AddRange(items, 0, items.Length);
}
public void AddRange(GreenNode[] items, int offset, int length)
{
// Necessary, but not sufficient (e.g. for nested lists).
EnsureAdditionalCapacity(length - offset);
var oldCount = Count;
for (var i = offset; i < length; i++)
{
Add(items[i]);
}
Validate(oldCount, Count);
}
[Conditional("DEBUG")]
private void Validate(int start, int end)
{
for (var i = start; i < end; i++)
{
Debug.Assert(_nodes[i].Value != null);
}
}
public void AddRange(SyntaxList<GreenNode> list)
{
this.AddRange(list, 0, list.Count);
}
public void AddRange(SyntaxList<GreenNode> list, int offset, int length)
{
// Necessary, but not sufficient (e.g. for nested lists).
EnsureAdditionalCapacity(length - offset);
var oldCount = Count;
for (var i = offset; i < length; i++)
{
Add(list[i]);
}
Validate(oldCount, Count);
}
public void AddRange<TNode>(SyntaxList<TNode> list) where TNode : GreenNode
{
this.AddRange(list, 0, list.Count);
}
public void AddRange<TNode>(SyntaxList<TNode> list, int offset, int length) where TNode : GreenNode
{
AddRange(new SyntaxList<GreenNode>(list.Node), offset, length);
}
public void RemoveLast()
{
Count--;
_nodes[Count].Value = null;
}
private void EnsureAdditionalCapacity(int additionalCount)
{
var currentSize = _nodes.Length;
var requiredSize = Count + additionalCount;
if (requiredSize <= currentSize) return;
var newSize =
requiredSize < 8 ? 8 :
requiredSize >= (int.MaxValue / 2) ? int.MaxValue :
Math.Max(requiredSize, currentSize * 2); // NB: Size will *at least* double.
Debug.Assert(newSize >= requiredSize);
Array.Resize(ref _nodes, newSize);
}
public bool Any(SyntaxKind kind)
{
for (var i = 0; i < Count; i++)
{
if (_nodes[i].Value.Kind == kind)
{
return true;
}
}
return false;
}
public GreenNode[] ToArray()
{
var array = new GreenNode[Count];
for (var i = 0; i < array.Length; i++)
{
array[i] = _nodes[i];
}
return array;
}
internal GreenNode ToListNode()
{
switch (Count)
{
case 0:
return null;
case 1:
return _nodes[0];
case 2:
return SyntaxList.List(_nodes[0], _nodes[1]);
case 3:
return SyntaxList.List(_nodes[0], _nodes[1], _nodes[2]);
default:
var tmp = new ArrayElement<GreenNode>[Count];
Array.Copy(_nodes, tmp, Count);
return SyntaxList.List(tmp);
}
}
public SyntaxList<GreenNode> ToList()
{
return new SyntaxList<GreenNode>(ToListNode());
}
public SyntaxList<TNode> ToList<TNode>() where TNode : GreenNode
{
return new SyntaxList<TNode>(ToListNode());
}
}
}

View File

@ -0,0 +1,115 @@
// 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.Syntax.InternalSyntax
{
internal readonly struct SyntaxListBuilder<TNode> where TNode : GreenNode
{
private readonly SyntaxListBuilder _builder;
public SyntaxListBuilder(int size)
: this(new SyntaxListBuilder(size))
{
}
public static SyntaxListBuilder<TNode> Create()
{
return new SyntaxListBuilder<TNode>(8);
}
internal SyntaxListBuilder(SyntaxListBuilder builder)
{
_builder = builder;
}
public bool IsNull
{
get
{
return _builder == null;
}
}
public int Count
{
get
{
return _builder.Count;
}
}
public TNode this[int index]
{
get
{
return (TNode)_builder[index];
}
set
{
_builder[index] = value;
}
}
public void Clear()
{
_builder.Clear();
}
public SyntaxListBuilder<TNode> Add(TNode node)
{
_builder.Add(node);
return this;
}
public void AddRange(TNode[] items, int offset, int length)
{
_builder.AddRange(items, offset, length);
}
public void AddRange(SyntaxList<TNode> nodes)
{
_builder.AddRange(nodes);
}
public void AddRange(SyntaxList<TNode> nodes, int offset, int length)
{
_builder.AddRange(nodes, offset, length);
}
public bool Any(SyntaxKind kind)
{
return _builder.Any(kind);
}
public SyntaxList<TNode> ToList()
{
return _builder.ToList<TNode>();
}
public GreenNode ToListNode()
{
return _builder.ToListNode();
}
public static implicit operator SyntaxListBuilder(SyntaxListBuilder<TNode> builder)
{
return builder._builder;
}
public static implicit operator SyntaxList<TNode>(SyntaxListBuilder<TNode> builder)
{
if (builder._builder != null)
{
return builder.ToList();
}
return default(SyntaxList<TNode>);
}
public SyntaxList<TDerived> ToList<TDerived>() where TDerived : GreenNode
{
return new SyntaxList<TDerived>(ToListNode());
}
}
}

View File

@ -0,0 +1,139 @@
// 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.Syntax.InternalSyntax
{
internal readonly struct SyntaxList<TNode>
where TNode : GreenNode
{
private readonly GreenNode _node;
public SyntaxList(GreenNode node)
{
_node = node;
}
public GreenNode Node
{
get
{
return ((GreenNode)_node);
}
}
public int Count
{
get
{
return (_node == null) ? 0 : _node.IsList ? _node.SlotCount : 1;
}
}
public TNode Last
{
get
{
var node = _node;
if (node.IsList)
{
return ((TNode)node.GetSlot(node.SlotCount - 1));
}
return ((TNode)node);
}
}
/* Not Implemented: Default */
public TNode this[int index]
{
get
{
var node = _node;
if (node.IsList)
{
return ((TNode)node.GetSlot(index));
}
Debug.Assert(index == 0);
return ((TNode)node);
}
}
public GreenNode ItemUntyped(int index)
{
var node = _node;
if (node.IsList)
{
return node.GetSlot(index);
}
Debug.Assert(index == 0);
return node;
}
public bool Any()
{
return _node != null;
}
public bool Any(SyntaxKind kind)
{
for (var i = 0; i < Count; i++)
{
var element = ItemUntyped(i);
if ((element.Kind == kind))
{
return true;
}
}
return false;
}
public TNode[] Nodes
{
get
{
var arr = new TNode[Count];
for (var i = 0; i < Count; i++)
{
arr[i] = this[i];
}
return arr;
}
}
public static bool operator ==(SyntaxList<TNode> left, SyntaxList<TNode> right)
{
return (left._node == right._node);
}
public static bool operator !=(SyntaxList<TNode> left, SyntaxList<TNode> right)
{
return !(left._node == right._node);
}
public override bool Equals(object obj)
{
return (obj is SyntaxList<TNode> && (_node == ((SyntaxList<TNode>)obj)._node));
}
public override int GetHashCode()
{
return _node != null ? _node.GetHashCode() : 0;
}
public static implicit operator SyntaxList<TNode>(TNode node)
{
return new SyntaxList<TNode>(node);
}
public static implicit operator SyntaxList<GreenNode>(SyntaxList<TNode> nodes)
{
return new SyntaxList<GreenNode>(nodes._node);
}
}
}

View File

@ -0,0 +1,199 @@
// 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 Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
{
internal class SyntaxToken : GreenNode
{
internal SyntaxToken(SyntaxKind kind, string content, RazorDiagnostic[] diagnostics)
: base(kind, content.Length, diagnostics, annotations: null)
{
Content = content;
}
internal SyntaxToken(SyntaxKind kind, string content, GreenNode leadingTrivia, GreenNode trailingTrivia)
: base(kind, content.Length)
{
Content = content;
LeadingTrivia = leadingTrivia;
AdjustFlagsAndWidth(leadingTrivia);
TrailingTrivia = trailingTrivia;
AdjustFlagsAndWidth(trailingTrivia);
}
internal SyntaxToken(SyntaxKind kind, string content, GreenNode leadingTrivia, GreenNode trailingTrivia, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
: base(kind, content.Length, diagnostics, annotations)
{
Content = content;
LeadingTrivia = leadingTrivia;
AdjustFlagsAndWidth(leadingTrivia);
TrailingTrivia = trailingTrivia;
AdjustFlagsAndWidth(trailingTrivia);
}
public string Content { get; }
public GreenNode LeadingTrivia { get; }
public GreenNode TrailingTrivia { get; }
internal override bool IsToken => true;
public override int Width => Content.Length;
internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
{
return new Syntax.SyntaxToken(this, parent, position);
}
protected override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{
if (leading)
{
var trivia = GetLeadingTrivia();
if (trivia != null)
{
trivia.WriteTo(writer, true, true);
}
}
writer.Write(Content);
if (trailing)
{
var trivia = GetTrailingTrivia();
if (trivia != null)
{
trivia.WriteTo(writer, true, true);
}
}
}
public override sealed GreenNode GetLeadingTrivia()
{
return LeadingTrivia;
}
public override int GetLeadingTriviaWidth()
{
return LeadingTrivia == null ? 0 : LeadingTrivia.FullWidth;
}
public override sealed GreenNode GetTrailingTrivia()
{
return TrailingTrivia;
}
public override int GetTrailingTriviaWidth()
{
return TrailingTrivia == null ? 0 : TrailingTrivia.FullWidth;
}
public sealed override GreenNode WithLeadingTrivia(GreenNode trivia)
{
return TokenWithLeadingTrivia(trivia);
}
public virtual SyntaxToken TokenWithLeadingTrivia(GreenNode trivia)
{
return new SyntaxToken(Kind, Content, trivia, TrailingTrivia, GetDiagnostics(), GetAnnotations());
}
public sealed override GreenNode WithTrailingTrivia(GreenNode trivia)
{
return TokenWithTrailingTrivia(trivia);
}
public virtual SyntaxToken TokenWithTrailingTrivia(GreenNode trivia)
{
return new SyntaxToken(Kind, Content, LeadingTrivia, trivia, GetDiagnostics(), GetAnnotations());
}
internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
{
return new SyntaxToken(Kind, Content, LeadingTrivia, TrailingTrivia, diagnostics, GetAnnotations());
}
internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
{
return new SyntaxToken(Kind, Content, LeadingTrivia, TrailingTrivia, GetDiagnostics(), annotations);
}
protected override sealed int GetSlotCount()
{
return 0;
}
internal override sealed GreenNode GetSlot(int index)
{
throw new InvalidOperationException("Tokens don't have slots.");
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.VisitToken(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitToken(this);
}
public override bool IsEquivalentTo(GreenNode other)
{
if (!base.IsEquivalentTo(other))
{
return false;
}
var otherToken = (SyntaxToken)other;
if (Content != otherToken.Content)
{
return false;
}
var thisLeading = GetLeadingTrivia();
var otherLeading = otherToken.GetLeadingTrivia();
if (thisLeading != otherLeading)
{
if (thisLeading == null || otherLeading == null)
{
return false;
}
if (!thisLeading.IsEquivalentTo(otherLeading))
{
return false;
}
}
var thisTrailing = GetTrailingTrivia();
var otherTrailing = otherToken.GetTrailingTrivia();
if (thisTrailing != otherTrailing)
{
if (thisTrailing == null || otherTrailing == null)
{
return false;
}
if (!thisTrailing.IsEquivalentTo(otherTrailing))
{
return false;
}
}
return true;
}
public override string ToString()
{
return Content;
}
}
}

View File

@ -0,0 +1,100 @@
// 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;
namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
{
internal class SyntaxTrivia : GreenNode
{
internal SyntaxTrivia(SyntaxKind kind, string text)
: base(kind, text.Length)
{
Text = text;
}
internal SyntaxTrivia(SyntaxKind kind, string text, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
: base(kind, text.Length, diagnostics, annotations)
{
Text = text;
}
public string Text { get; }
internal override bool IsTrivia => true;
public override int Width => Text.Length;
protected override void WriteTriviaTo(TextWriter writer)
{
writer.Write(Text);
}
public sealed override string ToFullString()
{
return Text;
}
public sealed override int GetLeadingTriviaWidth()
{
return 0;
}
public sealed override int GetTrailingTriviaWidth()
{
return 0;
}
protected override sealed int GetSlotCount()
{
return 0;
}
internal override sealed GreenNode GetSlot(int index)
{
throw new InvalidOperationException();
}
internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
{
return new Syntax.SyntaxTrivia(this, parent, position);
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.VisitTrivia(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitTrivia(this);
}
internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
{
return new SyntaxTrivia(Kind, Text, diagnostics, GetAnnotations());
}
internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
{
return new SyntaxTrivia(Kind, Text, GetDiagnostics(), annotations);
}
public override bool IsEquivalentTo(GreenNode other)
{
if (!base.IsEquivalentTo(other))
{
return false;
}
if (Text != ((SyntaxTrivia)other).Text)
{
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,60 @@
// 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.Syntax.InternalSyntax
{
internal abstract partial class SyntaxVisitor<TResult>
{
public virtual TResult Visit(GreenNode node)
{
if (node == null)
{
return default(TResult);
}
return node.Accept(this);
}
public virtual TResult VisitToken(SyntaxToken token)
{
return DefaultVisit(token);
}
public virtual TResult VisitTrivia(SyntaxTrivia trivia)
{
return DefaultVisit(trivia);
}
protected virtual TResult DefaultVisit(GreenNode node)
{
return default(TResult);
}
}
internal abstract partial class SyntaxVisitor
{
public virtual GreenNode Visit(GreenNode node)
{
if (node != null)
{
node.Accept(this);
}
return null;
}
public virtual void VisitToken(SyntaxToken token)
{
DefaultVisit(token);
}
public virtual void VisitTrivia(SyntaxTrivia trivia)
{
DefaultVisit(trivia);
}
protected virtual void DefaultVisit(GreenNode node)
{
}
}
}

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;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
[Flags]
internal enum NodeFlags : byte
{
None = 0,
ContainsDiagnostics = 1 << 0,
ContainsStructuredTrivia = 1 << 1,
ContainsDirectives = 1 << 2,
ContainsSkippedText = 1 << 3,
ContainsAnnotations = 1 << 4,
IsMissing = 1 << 5,
InheritMask = ContainsDiagnostics | ContainsStructuredTrivia | ContainsDirectives | ContainsSkippedText | ContainsAnnotations | IsMissing,
}
}

View File

@ -0,0 +1,248 @@
// 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.Threading;
#if DETECT_LEAKS
using System.Runtime.CompilerServices;
#endif
// define TRACE_LEAKS to get additional diagnostics that can lead to the leak sources. note: it will
// make everything about 2-3x slower
//
// #define TRACE_LEAKS
// define DETECT_LEAKS to detect possible leaks
// #if DEBUG
// #define DETECT_LEAKS //for now always enable DETECT_LEAKS in debug.
// #endif
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
/// <summary>
/// Generic implementation of object pooling pattern with predefined pool size limit. The main
/// purpose is that limited number of frequently used objects can be kept in the pool for
/// further recycling.
///
/// Notes:
/// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there
/// is no space in the pool, extra returned objects will be dropped.
///
/// 2) it is implied that if object was obtained from a pool, the caller will return it back in
/// a relatively short time. Keeping checked out objects for long durations is ok, but
/// reduces usefulness of pooling. Just new up your own.
///
/// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice.
/// Rationale:
/// If there is no intent for reusing the object, do not use pool - just use "new".
/// </summary>
internal class ObjectPool<T> where T : class
{
private struct Element
{
internal T Value;
}
/// <remarks>
/// Not using <see cref="T:System.Func{T}"/> because this file is linked into the (debugger) Formatter,
/// which does not have that type (since it compiles against .NET 2.0).
/// </remarks>
internal delegate T Factory();
// storage for the pool objects.
private readonly Element[] _items;
// factory is stored for the lifetime of the pool. We will call this only when pool needs to
// expand. compared to "new T()", Func gives more flexibility to implementers and faster
// than "new T()".
private readonly Factory _factory;
#if DETECT_LEAKS
private static readonly ConditionalWeakTable<T, LeakTracker> leakTrackers = new ConditionalWeakTable<T, LeakTracker>();
private class LeakTracker : IDisposable
{
private volatile bool disposed;
#if TRACE_LEAKS
internal volatile System.Diagnostics.StackTrace Trace = null;
#endif
public void Dispose()
{
disposed = true;
GC.SuppressFinalize(this);
}
private string GetTrace()
{
#if TRACE_LEAKS
return Trace == null? "": Trace.ToString();
#else
return "Leak tracing information is disabled. Define TRACE_LEAKS on ObjectPool`1.cs to get more info \n";
#endif
}
~LeakTracker()
{
if (!this.disposed &&
!Environment.HasShutdownStarted &&
!AppDomain.CurrentDomain.IsFinalizingForUnload())
{
string report = string.Format("Pool detected potential leaking of {0}. \n Location of the leak: \n {1} ",
typeof(T).ToString(),
GetTrace());
// If you are seeing this message it means that object has been allocated from the pool
// and has not been returned back. This is not critical, but turns pool into rather
// inefficient kind of "new".
Debug.WriteLine("TRACEOBJECTPOOLLEAKS_BEGIN\n" + report + "TRACEOBJECTPOOLLEAKS_END");
}
}
}
#endif
internal ObjectPool(Factory factory)
: this(factory, Environment.ProcessorCount * 2)
{ }
internal ObjectPool(Factory factory, int size)
{
_factory = factory;
_items = new Element[size];
}
private T CreateInstance()
{
var inst = _factory();
return inst;
}
/// <summary>
/// Produces an instance.
/// </summary>
/// <remarks>
/// Search strategy is a simple linear probing which is chosen for it cache-friendliness.
/// Note that Free will try to store recycled objects close to the start thus statistically
/// reducing how far we will typically search.
/// </remarks>
internal T Allocate()
{
var items = _items;
T inst;
for (var i = 0; i < items.Length; i++)
{
// Note that the read is optimistically not synchronized. That is intentional.
// We will interlock only when we have a candidate. in a worst case we may miss some
// recently returned objects. Not a big deal.
inst = items[i].Value;
if (inst != null)
{
if (inst == Interlocked.CompareExchange(ref items[i].Value, null, inst))
{
goto gotInstance;
}
}
}
inst = CreateInstance();
gotInstance:
#if DETECT_LEAKS
var tracker = new LeakTracker();
leakTrackers.Add(inst, tracker);
#if TRACE_LEAKS
var frame = new System.Diagnostics.StackTrace(false);
tracker.Trace = frame;
#endif
#endif
return inst;
}
/// <summary>
/// Returns objects to the pool.
/// </summary>
/// <remarks>
/// Search strategy is a simple linear probing which is chosen for it cache-friendliness.
/// Note that Free will try to store recycled objects close to the start thus statistically
/// reducing how far we will typically search in Allocate.
/// </remarks>
internal void Free(T obj)
{
Validate(obj);
ForgetTrackedObject(obj);
var items = _items;
for (var i = 0; i < items.Length; i++)
{
if (items[i].Value == null)
{
// Intentionally not using interlocked here.
// In a worst case scenario two objects may be stored into same slot.
// It is very unlikely to happen and will only mean that one of the objects will get collected.
items[i].Value = obj;
break;
}
}
}
/// <summary>
/// Removes an object from leak tracking.
///
/// This is called when an object is returned to the pool. It may also be explicitly
/// called if an object allocated from the pool is intentionally not being returned
/// to the pool. This can be of use with pooled arrays if the consumer wants to
/// return a larger array to the pool than was originally allocated.
/// </summary>
[Conditional("DEBUG")]
internal void ForgetTrackedObject(T old, T replacement = null)
{
#if DETECT_LEAKS
LeakTracker tracker;
if (leakTrackers.TryGetValue(old, out tracker))
{
tracker.Dispose();
leakTrackers.Remove(old);
}
else
{
string report = string.Format("Object of type {0} was freed, but was not from pool. \n Callstack: \n {1} ",
typeof(T).ToString(),
new System.Diagnostics.StackTrace(false));
Debug.WriteLine("TRACEOBJECTPOOLLEAKS_BEGIN\n" + report + "TRACEOBJECTPOOLLEAKS_END");
}
if (replacement != null)
{
tracker = new LeakTracker();
leakTrackers.Add(replacement, tracker);
}
#endif
}
[Conditional("DEBUG")]
private void Validate(object obj)
{
Debug.Assert(obj != null, "freeing null?");
var items = _items;
for (var i = 0; i < items.Length; i++)
{
var value = items[i].Value;
if (value == null)
{
return;
}
Debug.Assert(value != obj, "freeing twice?");
}
}
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Language
{
internal enum ParserState
{
Unknown,
Misc,
Content,
StartTag,
EndTag,
}
}

View File

@ -0,0 +1,169 @@
// 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;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal static class SpecializedCollections
{
public static IEnumerator<T> EmptyEnumerator<T>()
{
return Empty.Enumerator<T>.Instance;
}
public static IEnumerable<T> EmptyEnumerable<T>()
{
return Empty.List<T>.Instance;
}
public static ICollection<T> EmptyCollection<T>()
{
return Empty.List<T>.Instance;
}
public static IList<T> EmptyList<T>()
{
return Empty.List<T>.Instance;
}
public static IReadOnlyList<T> EmptyReadOnlyList<T>()
{
return Empty.List<T>.Instance;
}
private class Empty
{
internal class Enumerator<T> : Enumerator, IEnumerator<T>
{
public static new readonly IEnumerator<T> Instance = new Enumerator<T>();
protected Enumerator()
{
}
public new T Current => throw new InvalidOperationException();
public void Dispose()
{
}
}
internal class Enumerator : IEnumerator
{
public static readonly IEnumerator Instance = new Enumerator();
protected Enumerator()
{
}
public object Current => throw new InvalidOperationException();
public bool MoveNext()
{
return false;
}
public void Reset()
{
throw new InvalidOperationException();
}
}
internal class Enumerable<T> : IEnumerable<T>
{
// PERF: cache the instance of enumerator.
// accessing a generic static field is kinda slow from here,
// but since empty enumerables are singletons, there is no harm in having
// one extra instance field
private readonly IEnumerator<T> _enumerator = Enumerator<T>.Instance;
public IEnumerator<T> GetEnumerator()
{
return _enumerator;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
internal class Collection<T> : Enumerable<T>, ICollection<T>
{
public static readonly ICollection<T> Instance = new Collection<T>();
protected Collection()
{
}
public void Add(T item)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public bool Contains(T item)
{
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
}
public int Count => 0;
public bool IsReadOnly => true;
public bool Remove(T item)
{
throw new NotSupportedException();
}
}
internal class List<T> : Collection<T>, IList<T>, IReadOnlyList<T>
{
public static readonly new List<T> Instance = new List<T>();
protected List()
{
}
public int IndexOf(T item)
{
return -1;
}
public void Insert(int index, T item)
{
throw new NotSupportedException();
}
public void RemoveAt(int index)
{
throw new NotSupportedException();
}
public T this[int index]
{
get
{
throw new ArgumentOutOfRangeException(nameof(index));
}
set
{
throw new NotSupportedException();
}
}
}
}
}
}

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8" ?>
<Tree Root="SyntaxNode">
<PredefinedNode Name="SyntaxToken" Base="SyntaxNode" />
<!-- Common -->
<AbstractNode Name="RazorSyntaxNode" Base="SyntaxNode" />
<Node Name="RazorCommentBlockSyntax" Base="RazorSyntaxNode">
<Kind Name="RazorComment" />
<Field Name="StartCommentTransition" Type="SyntaxToken">
<Kind Name="RazorCommentTransition" />
</Field>
<Field Name="StartCommentStar" Type="SyntaxToken">
<Kind Name="RazorCommentStar" />
</Field>
<Field Name="Comment" Type="SyntaxToken" Optional="true">
<Kind Name="RazorComment" />
</Field>
<Field Name="EndCommentStar" Type="SyntaxToken">
<Kind Name="RazorCommentStar" />
</Field>
<Field Name="EndCommentTransition" Type="SyntaxToken">
<Kind Name="RazorCommentTransition" />
</Field>
</Node>
<!-- HTML -->
<AbstractNode Name="HtmlSyntaxNode" Base="RazorSyntaxNode" />
<Node Name="HtmlTextLiteralSyntax" Base="HtmlSyntaxNode">
<Kind Name="HtmlTextLiteral" />
<Field Name="TextTokens" Type="SyntaxList&lt;SyntaxToken&gt;" />
</Node>
<!-- CSharp -->
<AbstractNode Name="CSharpSyntaxNode" Base="RazorSyntaxNode" />
<Node Name="CSharpTransitionSyntax" Base="CSharpSyntaxNode">
<Kind Name="CSharpTransition" />
<Field Name="Transition" Type="SyntaxToken">
<Kind Name="Transition" />
</Field>
</Node>
<Node Name="CSharpMetaCodeSyntax" Base="CSharpSyntaxNode">
<Kind Name="CSharpMetaCode" />
<Field Name="MetaCode" Type="SyntaxList&lt;SyntaxToken&gt;" />
</Node>
<Node Name="CSharpCodeLiteralSyntax" Base="CSharpSyntaxNode">
<Kind Name="CSharpCodeLiteral" />
<Field Name="CSharpTokens" Type="SyntaxList&lt;SyntaxToken&gt;" />
</Node>
<Node Name="CSharpCodeBlockSyntax" Base="CSharpSyntaxNode">
<Kind Name="CSharpCodeBlock" />
<Field Name="Children" Type="SyntaxList&lt;RazorSyntaxNode&gt;" />
</Node>
<AbstractNode Name="CSharpBlockSyntax" Base="CSharpSyntaxNode">
<Field Name="Transition" Type="CSharpTransitionSyntax" />
<Field Name="Body" Type="CSharpSyntaxNode" />
</AbstractNode>
<Node Name="CSharpStatement" Base="CSharpBlockSyntax">
<Kind Name="CSharpStatement" />
<Field Name="Transition" Type="CSharpTransitionSyntax" Override="true">
<Kind Name="CSharpTransition" />
</Field>
<Field Name="Body" Type="CSharpSyntaxNode" Override="true">
<Kind Name="CSharpStatementBody" />
</Field>
</Node>
<Node Name="CSharpStatementBodySyntax" Base="CSharpSyntaxNode">
<Kind Name="CSharpStatementBody" />
<Field Name="OpenBrace" Type="CSharpMetaCodeSyntax" />
<Field Name="CSharpCode" Type="CSharpCodeBlockSyntax" />
<Field Name="CloseBrace" Type="CSharpMetaCodeSyntax" />
</Node>
<Node Name="CSharpExpression" Base="CSharpBlockSyntax">
<Kind Name="CSharpExpression" />
<Field Name="Transition" Type="CSharpTransitionSyntax" Override="true">
<Kind Name="CSharpTransition" />
</Field>
<Field Name="Body" Type="CSharpSyntaxNode" Override="true">
<Kind Name="CSharpExpressionBody" />
</Field>
</Node>
<Node Name="CSharpExpressionBodySyntax" Base="CSharpSyntaxNode">
<Kind Name="CSharpExpressionBody" />
<Field Name="OpenParen" Type="CSharpMetaCodeSyntax" Optional="true" />
<Field Name="CSharpCode" Type="CSharpCodeBlockSyntax" />
<Field Name="CloseParen" Type="CSharpMetaCodeSyntax" Optional="true" />
</Node>
<Node Name="CSharpDirectiveSyntax" Base="CSharpBlockSyntax">
<Kind Name="CSharpDirective" />
<Field Name="Transition" Type="CSharpTransitionSyntax" Override="true">
<Kind Name="CSharpTransition" />
</Field>
<Field Name="Body" Type="CSharpSyntaxNode" Override="true">
<Kind Name="CSharpDirectiveBody" />
</Field>
</Node>
<Node Name="CSharpDirectiveBodySyntax" Base="CSharpSyntaxNode">
<Kind Name="CSharpDirectiveBody" />
<Field Name="Keyword" Type="CSharpMetaCodeSyntax" />
<Field Name="CSharpCode" Type="CSharpCodeBlockSyntax" />
</Node>
</Tree>

View File

@ -0,0 +1,93 @@
// 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;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
/// <summary>
/// A SyntaxAnnotation is used to annotate syntax elements with additional information.
///
/// Since syntax elements are immutable, annotating them requires creating new instances of them
/// with the annotations attached.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal sealed class SyntaxAnnotation : IEquatable<SyntaxAnnotation>
{
// use a value identity instead of object identity so a deserialized instance matches the original instance.
private readonly long _id;
private static long s_nextId;
// use a value identity instead of object identity so a deserialized instance matches the original instance.
public string Kind { get; }
public string Data { get; }
public SyntaxAnnotation()
{
_id = System.Threading.Interlocked.Increment(ref s_nextId);
}
public SyntaxAnnotation(string kind)
: this()
{
Kind = kind;
}
public SyntaxAnnotation(string kind, string data)
: this(kind)
{
Data = data;
}
private string GetDebuggerDisplay()
{
return string.Format("Annotation: Kind='{0}' Data='{1}'", this.Kind ?? "", this.Data ?? "");
}
public bool Equals(SyntaxAnnotation other)
{
return (object)other != null && _id == other._id;
}
public static bool operator ==(SyntaxAnnotation left, SyntaxAnnotation right)
{
if ((object)left == (object)right)
{
return true;
}
if ((object)left == null || (object)right == null)
{
return false;
}
return left.Equals(right);
}
public static bool operator !=(SyntaxAnnotation left, SyntaxAnnotation right)
{
if ((object)left == (object)right)
{
return false;
}
if ((object)left == null || (object)right == null)
{
return true;
}
return !left.Equals(right);
}
public override bool Equals(object obj)
{
return Equals(obj as SyntaxAnnotation);
}
public override int GetHashCode()
{
return _id.GetHashCode();
}
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal static partial class SyntaxFactory
{
public static SyntaxToken Token(SyntaxKind kind, params RazorDiagnostic[] diagnostics)
{
return Token(kind, content: string.Empty, diagnostics: diagnostics);
}
public static SyntaxToken Token(SyntaxKind kind, string content, params RazorDiagnostic[] diagnostics)
{
return new SyntaxToken(InternalSyntax.SyntaxFactory.Token(kind, content), parent: null, position: 0);
}
}
}

View File

@ -1,22 +1,66 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Language.Legacy
namespace Microsoft.AspNetCore.Razor.Language
{
internal enum CSharpTokenType
internal enum SyntaxKind : byte
{
#region Nodes
// HTML
HtmlTextLiteral,
HtmlDocument,
HtmlDeclaration,
// CSharp
CSharpBlock,
CSharpStatement,
CSharpStatementBody,
CSharpExpression,
CSharpExpressionBody,
CSharpDirective,
CSharpDirectiveBody,
CSharpCodeBlock,
CSharpCodeLiteral,
CSharpMetaCode,
CSharpTransition,
// Common
RazorComment,
#endregion
#region Tokens
// Common
Unknown,
List,
Whitespace,
NewLine,
Colon,
QuestionMark,
RightBracket,
LeftBracket,
Equals,
Transition,
// HTML
Text,
OpenAngle,
Bang,
ForwardSlash,
DoubleHyphen,
CloseAngle,
DoubleQuote,
SingleQuote,
// CSharp literals
Identifier,
Keyword,
IntegerLiteral,
NewLine,
WhiteSpace,
Comment,
CSharpComment,
RealLiteral,
CharacterLiteral,
StringLiteral,
// Operators
// CSharp operators
Arrow,
Minus,
Decrement,
@ -37,12 +81,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Slash,
DivideAssign,
DoubleColon,
Colon,
Semicolon,
QuestionMark,
NullCoalesce,
RightBracket,
LeftBracket,
XorAssign,
Xor,
LeftBrace,
@ -59,17 +99,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
LeftShift,
LeftShiftAssign,
Assign,
Equals,
GreaterThan,
GreaterThanEqual,
RightShift,
RightShiftAssign,
Hash,
Transition,
// Razor specific
RazorCommentTransition,
RazorCommentLiteral,
RazorCommentStar,
RazorComment
RazorCommentTransition,
#endregion
}
}

View File

@ -0,0 +1,123 @@
// 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.Syntax
{
internal abstract class SyntaxList : SyntaxNode
{
internal SyntaxList(InternalSyntax.SyntaxList green, SyntaxNode parent, int position)
: base(green, parent, position)
{
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.Visit(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.Visit(this);
}
internal class WithTwoChildren : SyntaxList
{
private SyntaxNode _child0;
private SyntaxNode _child1;
internal WithTwoChildren(InternalSyntax.SyntaxList green, SyntaxNode parent, int position)
: base(green, parent, position)
{
}
internal override SyntaxNode GetNodeSlot(int index)
{
switch (index)
{
case 0:
return GetRedElement(ref _child0, 0);
case 1:
return GetRedElement(ref _child1, 1);
default:
return null;
}
}
internal override SyntaxNode GetCachedSlot(int index)
{
switch (index)
{
case 0:
return _child0;
case 1:
return _child1;
default:
return null;
}
}
}
internal class WithThreeChildren : SyntaxList
{
private SyntaxNode _child0;
private SyntaxNode _child1;
private SyntaxNode _child2;
internal WithThreeChildren(InternalSyntax.SyntaxList green, SyntaxNode parent, int position)
: base(green, parent, position)
{
}
internal override SyntaxNode GetNodeSlot(int index)
{
switch (index)
{
case 0:
return GetRedElement(ref _child0, 0);
case 1:
return GetRedElement(ref _child1, 1);
case 2:
return GetRedElement(ref _child2, 2);
default:
return null;
}
}
internal override SyntaxNode GetCachedSlot(int index)
{
switch (index)
{
case 0:
return _child0;
case 1:
return _child1;
case 2:
return _child2;
default:
return null;
}
}
}
internal class WithManyChildren : SyntaxList
{
private readonly ArrayElement<SyntaxNode>[] _children;
internal WithManyChildren(InternalSyntax.SyntaxList green, SyntaxNode parent, int position)
: base(green, parent, position)
{
_children = new ArrayElement<SyntaxNode>[green.SlotCount];
}
internal override SyntaxNode GetNodeSlot(int index)
{
return this.GetRedElement(ref _children[index].Value, index);
}
internal override SyntaxNode GetCachedSlot(int index)
{
return _children[index];
}
}
}
}

View File

@ -0,0 +1,172 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal class SyntaxListBuilder
{
private ArrayElement<GreenNode>[] _nodes;
public int Count { get; private set; }
public SyntaxListBuilder(int size)
{
_nodes = new ArrayElement<GreenNode>[size];
}
public void Clear()
{
Count = 0;
}
public void Add(SyntaxNode item)
{
AddInternal(item.Green);
}
internal void AddInternal(GreenNode item)
{
if (item == null)
{
throw new ArgumentNullException();
}
if (_nodes == null || Count >= _nodes.Length)
{
Grow(Count == 0 ? 8 : _nodes.Length * 2);
}
_nodes[Count++].Value = item;
}
public void AddRange(SyntaxNode[] items)
{
AddRange(items, 0, items.Length);
}
public void AddRange(SyntaxNode[] items, int offset, int length)
{
if (_nodes == null || Count + length > _nodes.Length)
{
Grow(Count + length);
}
for (int i = offset, j = Count; i < offset + length; ++i, ++j)
{
_nodes[j].Value = items[i].Green;
}
var start = Count;
Count += length;
Validate(start, Count);
}
[Conditional("DEBUG")]
private void Validate(int start, int end)
{
for (var i = start; i < end; i++)
{
if (_nodes[i].Value == null)
{
throw new ArgumentException("Cannot add a null node.");
}
}
}
public void AddRange(SyntaxList<SyntaxNode> list)
{
AddRange(list, 0, list.Count);
}
public void AddRange(SyntaxList<SyntaxNode> list, int offset, int count)
{
if (_nodes == null || Count + count > _nodes.Length)
{
Grow(Count + count);
}
var dst = Count;
for (int i = offset, limit = offset + count; i < limit; i++)
{
_nodes[dst].Value = list.ItemInternal(i).Green;
dst++;
}
var start = Count;
Count += count;
Validate(start, Count);
}
public void AddRange<TNode>(SyntaxList<TNode> list) where TNode : SyntaxNode
{
AddRange(list, 0, list.Count);
}
public void AddRange<TNode>(SyntaxList<TNode> list, int offset, int count) where TNode : SyntaxNode
{
AddRange(new SyntaxList<SyntaxNode>(list.Node), offset, count);
}
private void Grow(int size)
{
var tmp = new ArrayElement<GreenNode>[size];
Array.Copy(_nodes, tmp, _nodes.Length);
_nodes = tmp;
}
public bool Any(SyntaxKind kind)
{
for (var i = 0; i < Count; i++)
{
if (_nodes[i].Value.Kind == kind)
{
return true;
}
}
return false;
}
internal GreenNode ToListNode()
{
switch (Count)
{
case 0:
return null;
case 1:
return _nodes[0].Value;
case 2:
return InternalSyntax.SyntaxList.List(_nodes[0].Value, _nodes[1].Value);
case 3:
return InternalSyntax.SyntaxList.List(_nodes[0].Value, _nodes[1].Value, _nodes[2].Value);
default:
var tmp = new ArrayElement<GreenNode>[Count];
for (var i = 0; i < Count; i++)
{
tmp[i].Value = _nodes[i].Value;
}
return InternalSyntax.SyntaxList.List(tmp);
}
}
public static implicit operator SyntaxList<SyntaxNode>(SyntaxListBuilder builder)
{
if (builder == null)
{
return default(SyntaxList<SyntaxNode>);
}
return builder.ToList();
}
internal void RemoveLast()
{
Count -= 1;
_nodes[Count] = default(ArrayElement<GreenNode>);
}
}
}

View File

@ -0,0 +1,29 @@
// 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.Syntax
{
internal static class SyntaxListBuilderExtensions
{
public static SyntaxList<SyntaxNode> ToList(this SyntaxListBuilder builder)
{
if (builder == null || builder.Count == 0)
{
return default(SyntaxList<SyntaxNode>);
}
return new SyntaxList<SyntaxNode>(builder.ToListNode().CreateRed());
}
public static SyntaxList<TNode> ToList<TNode>(this SyntaxListBuilder builder)
where TNode : SyntaxNode
{
if (builder == null || builder.Count == 0)
{
return new SyntaxList<TNode>();
}
return new SyntaxList<TNode>(builder.ToListNode().CreateRed());
}
}
}

View File

@ -0,0 +1,93 @@
// 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.Syntax
{
internal readonly struct SyntaxListBuilder<TNode>
where TNode : SyntaxNode
{
private readonly SyntaxListBuilder _builder;
public SyntaxListBuilder(int size)
: this(new SyntaxListBuilder(size))
{
}
public static SyntaxListBuilder<TNode> Create()
{
return new SyntaxListBuilder<TNode>(8);
}
internal SyntaxListBuilder(SyntaxListBuilder builder)
{
_builder = builder;
}
public bool IsNull
{
get
{
return _builder == null;
}
}
public int Count
{
get
{
return _builder.Count;
}
}
public void Clear()
{
_builder.Clear();
}
public SyntaxListBuilder<TNode> Add(TNode node)
{
_builder.Add(node);
return this;
}
public void AddRange(TNode[] items, int offset, int length)
{
_builder.AddRange(items, offset, length);
}
public void AddRange(SyntaxList<TNode> nodes)
{
_builder.AddRange(nodes);
}
public void AddRange(SyntaxList<TNode> nodes, int offset, int length)
{
_builder.AddRange(nodes, offset, length);
}
public bool Any(SyntaxKind kind)
{
return _builder.Any(kind);
}
public SyntaxList<TNode> ToList()
{
return _builder.ToList();
}
public static implicit operator SyntaxListBuilder(SyntaxListBuilder<TNode> builder)
{
return builder._builder;
}
public static implicit operator SyntaxList<TNode>(SyntaxListBuilder<TNode> builder)
{
if (builder._builder != null)
{
return builder.ToList();
}
return default(SyntaxList<TNode>);
}
}
}

View File

@ -0,0 +1,606 @@
// 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.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal readonly struct SyntaxList<TNode> : IReadOnlyList<TNode>, IEquatable<SyntaxList<TNode>>
where TNode : SyntaxNode
{
public SyntaxList(SyntaxNode node)
{
Node = node;
}
/// <summary>
/// Creates a singleton list of syntax nodes.
/// </summary>
/// <param name="node">The single element node.</param>
public SyntaxList(TNode node)
: this((SyntaxNode)node)
{
}
/// <summary>
/// Creates a list of syntax nodes.
/// </summary>
/// <param name="nodes">A sequence of element nodes.</param>
public SyntaxList(IEnumerable<TNode> nodes)
: this(CreateNode(nodes))
{
}
private static SyntaxNode CreateNode(IEnumerable<TNode> nodes)
{
if (nodes == null)
{
return null;
}
var builder = (nodes is ICollection<TNode> collection) ? new SyntaxListBuilder<TNode>(collection.Count) : SyntaxListBuilder<TNode>.Create();
foreach (var node in nodes)
{
builder.Add(node);
}
return builder.ToList().Node;
}
internal SyntaxNode Node { get; }
/// <summary>
/// The number of nodes in the list.
/// </summary>
public int Count
{
get
{
return Node == null ? 0 : (Node.IsList ? Node.SlotCount : 1);
}
}
/// <summary>
/// Gets the node at the specified index.
/// </summary>
/// <param name="index">The zero-based index of the node to get or set.</param>
/// <returns>The node at the specified index.</returns>
public TNode this[int index]
{
get
{
if (Node != null)
{
if (Node.IsList)
{
if (unchecked((uint)index < (uint)Node.SlotCount))
{
return (TNode)Node.GetNodeSlot(index);
}
}
else if (index == 0)
{
return (TNode)Node;
}
}
throw new ArgumentOutOfRangeException();
}
}
internal SyntaxNode ItemInternal(int index)
{
if (Node.IsList)
{
return Node.GetNodeSlot(index);
}
Debug.Assert(index == 0);
return Node;
}
/// <summary>
/// The absolute span of the list elements in characters, including the leading and trailing trivia of the first and last elements.
/// </summary>
public TextSpan FullSpan
{
get
{
if (Count == 0)
{
return default(TextSpan);
}
else
{
return TextSpan.FromBounds(this[0].FullSpan.Start, this[Count - 1].FullSpan.End);
}
}
}
/// <summary>
/// The absolute span of the list elements in characters, not including the leading and trailing trivia of the first and last elements.
/// </summary>
public TextSpan Span
{
get
{
if (Count == 0)
{
return default(TextSpan);
}
else
{
return TextSpan.FromBounds(this[0].Span.Start, this[Count - 1].Span.End);
}
}
}
/// <summary>
/// Returns the string representation of the nodes in this list, not including
/// the first node's leading trivia and the last node's trailing trivia.
/// </summary>
/// <returns>
/// The string representation of the nodes in this list, not including
/// the first node's leading trivia and the last node's trailing trivia.
/// </returns>
public override string ToString()
{
return Node != null ? Node.ToString() : string.Empty;
}
/// <summary>
/// Returns the full string representation of the nodes in this list including
/// the first node's leading trivia and the last node's trailing trivia.
/// </summary>
/// <returns>
/// The full string representation of the nodes in this list including
/// the first node's leading trivia and the last node's trailing trivia.
/// </returns>
public string ToFullString()
{
return Node != null ? Node.ToFullString() : string.Empty;
}
/// <summary>
/// Creates a new list with the specified node added at the end.
/// </summary>
/// <param name="node">The node to add.</param>
public SyntaxList<TNode> Add(TNode node)
{
return Insert(Count, node);
}
/// <summary>
/// Creates a new list with the specified nodes added at the end.
/// </summary>
/// <param name="nodes">The nodes to add.</param>
public SyntaxList<TNode> AddRange(IEnumerable<TNode> nodes)
{
return InsertRange(Count, nodes);
}
/// <summary>
/// Creates a new list with the specified node inserted at the index.
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="node">The node to insert.</param>
public SyntaxList<TNode> Insert(int index, TNode node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
return InsertRange(index, new[] { node });
}
/// <summary>
/// Creates a new list with the specified nodes inserted at the index.
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="nodes">The nodes to insert.</param>
public SyntaxList<TNode> InsertRange(int index, IEnumerable<TNode> nodes)
{
if (index < 0 || index > Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
if (nodes == null)
{
throw new ArgumentNullException(nameof(nodes));
}
var list = this.ToList();
list.InsertRange(index, nodes);
if (list.Count == 0)
{
return this;
}
else
{
return CreateList(list[0].Green, list);
}
}
/// <summary>
/// Creates a new list with the element at specified index removed.
/// </summary>
/// <param name="index">The index of the element to remove.</param>
public SyntaxList<TNode> RemoveAt(int index)
{
if (index < 0 || index > Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return Remove(this[index]);
}
/// <summary>
/// Creates a new list with the element removed.
/// </summary>
/// <param name="node">The element to remove.</param>
public SyntaxList<TNode> Remove(TNode node)
{
return CreateList(this.Where(x => x != node).ToList());
}
/// <summary>
/// Creates a new list with the specified element replaced with the new node.
/// </summary>
/// <param name="nodeInList">The element to replace.</param>
/// <param name="newNode">The new node.</param>
public SyntaxList<TNode> Replace(TNode nodeInList, TNode newNode)
{
return ReplaceRange(nodeInList, new[] { newNode });
}
/// <summary>
/// Creates a new list with the specified element replaced with new nodes.
/// </summary>
/// <param name="nodeInList">The element to replace.</param>
/// <param name="newNodes">The new nodes.</param>
public SyntaxList<TNode> ReplaceRange(TNode nodeInList, IEnumerable<TNode> newNodes)
{
if (nodeInList == null)
{
throw new ArgumentNullException(nameof(nodeInList));
}
if (newNodes == null)
{
throw new ArgumentNullException(nameof(newNodes));
}
var index = IndexOf(nodeInList);
if (index >= 0 && index < Count)
{
var list = this.ToList();
list.RemoveAt(index);
list.InsertRange(index, newNodes);
return CreateList(list);
}
else
{
throw new ArgumentException(nameof(nodeInList));
}
}
static SyntaxList<TNode> CreateList(List<TNode> items)
{
if (items.Count == 0)
{
return default(SyntaxList<TNode>);
}
else
{
return CreateList(items[0].Green, items);
}
}
static SyntaxList<TNode> CreateList(GreenNode creator, List<TNode> items)
{
if (items.Count == 0)
{
return default(SyntaxList<TNode>);
}
var newGreen = creator.CreateList(items.Select(n => n.Green));
return new SyntaxList<TNode>(newGreen.CreateRed());
}
/// <summary>
/// The first node in the list.
/// </summary>
public TNode First()
{
return this[0];
}
/// <summary>
/// The first node in the list or default if the list is empty.
/// </summary>
public TNode FirstOrDefault()
{
if (this.Any())
{
return this[0];
}
else
{
return null;
}
}
/// <summary>
/// The last node in the list.
/// </summary>
public TNode Last()
{
return this[Count - 1];
}
/// <summary>
/// The last node in the list or default if the list is empty.
/// </summary>
public TNode LastOrDefault()
{
if (Any())
{
return this[Count - 1];
}
else
{
return null;
}
}
/// <summary>
/// True if the list has at least one node.
/// </summary>
public bool Any()
{
Debug.Assert(Node == null || Count != 0);
return Node != null;
}
// for debugging
private TNode[] Nodes
{
get { return this.ToArray(); }
}
/// <summary>
/// Get's the enumerator for this list.
/// </summary>
public Enumerator GetEnumerator()
{
return new Enumerator(in this);
}
IEnumerator<TNode> IEnumerable<TNode>.GetEnumerator()
{
if (this.Any())
{
return new EnumeratorImpl(this);
}
return SpecializedCollections.EmptyEnumerator<TNode>();
}
IEnumerator IEnumerable.GetEnumerator()
{
if (this.Any())
{
return new EnumeratorImpl(this);
}
return SpecializedCollections.EmptyEnumerator<TNode>();
}
public static bool operator ==(SyntaxList<TNode> left, SyntaxList<TNode> right)
{
return left.Node == right.Node;
}
public static bool operator !=(SyntaxList<TNode> left, SyntaxList<TNode> right)
{
return left.Node != right.Node;
}
public bool Equals(SyntaxList<TNode> other)
{
return Node == other.Node;
}
public override bool Equals(object obj)
{
return obj is SyntaxList<TNode> && Equals((SyntaxList<TNode>)obj);
}
public override int GetHashCode()
{
return Node?.GetHashCode() ?? 0;
}
public static implicit operator SyntaxList<TNode>(SyntaxList<SyntaxNode> nodes)
{
return new SyntaxList<TNode>(nodes.Node);
}
public static implicit operator SyntaxList<SyntaxNode>(SyntaxList<TNode> nodes)
{
return new SyntaxList<SyntaxNode>(nodes.Node);
}
/// <summary>
/// The index of the node in this list, or -1 if the node is not in the list.
/// </summary>
public int IndexOf(TNode node)
{
var index = 0;
foreach (var child in this)
{
if (object.Equals(child, node))
{
return index;
}
index++;
}
return -1;
}
public int IndexOf(Func<TNode, bool> predicate)
{
var index = 0;
foreach (var child in this)
{
if (predicate(child))
{
return index;
}
index++;
}
return -1;
}
internal int IndexOf(SyntaxKind kind)
{
var index = 0;
foreach (var child in this)
{
if (child.Kind == kind)
{
return index;
}
index++;
}
return -1;
}
public int LastIndexOf(TNode node)
{
for (var i = Count - 1; i >= 0; i--)
{
if (object.Equals(this[i], node))
{
return i;
}
}
return -1;
}
public int LastIndexOf(Func<TNode, bool> predicate)
{
for (var i = Count - 1; i >= 0; i--)
{
if (predicate(this[i]))
{
return i;
}
}
return -1;
}
public struct Enumerator
{
private readonly SyntaxList<TNode> _list;
private int _index;
internal Enumerator(in SyntaxList<TNode> list)
{
_list = list;
_index = -1;
}
public bool MoveNext()
{
var newIndex = _index + 1;
if (newIndex < _list.Count)
{
_index = newIndex;
return true;
}
return false;
}
public TNode Current
{
get
{
return (TNode)_list.ItemInternal(_index);
}
}
public void Reset()
{
_index = -1;
}
public override bool Equals(object obj)
{
throw new NotSupportedException();
}
public override int GetHashCode()
{
throw new NotSupportedException();
}
}
private class EnumeratorImpl : IEnumerator<TNode>
{
private Enumerator _e;
internal EnumeratorImpl(in SyntaxList<TNode> list)
{
_e = new Enumerator(in list);
}
public bool MoveNext()
{
return _e.MoveNext();
}
public TNode Current
{
get
{
return _e.Current;
}
}
void IDisposable.Dispose()
{
}
object IEnumerator.Current
{
get
{
return _e.Current;
}
}
void IEnumerator.Reset()
{
_e.Reset();
}
}
}
}

View File

@ -0,0 +1,305 @@
// 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;
using System.Threading;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal abstract class SyntaxNode
{
public SyntaxNode(GreenNode green, SyntaxNode parent, int position)
{
Green = green;
Parent = parent;
Position = position;
}
internal GreenNode Green { get; }
public SyntaxNode Parent { get; }
public int Position { get; }
public int EndPosition => Position + FullWidth;
public SyntaxKind Kind => Green.Kind;
public int Width => Green.Width;
public int FullWidth => Green.FullWidth;
public int SpanStart => Position + Green.GetLeadingTriviaWidth();
public TextSpan FullSpan => new TextSpan(Position, Green.FullWidth);
public TextSpan Span
{
get
{
// Start with the full span.
var start = Position;
var width = Green.FullWidth;
// adjust for preceding trivia (avoid calling this twice, do not call Green.Width)
var precedingWidth = Green.GetLeadingTriviaWidth();
start += precedingWidth;
width -= precedingWidth;
// adjust for following trivia width
width -= Green.GetTrailingTriviaWidth();
Debug.Assert(width >= 0);
return new TextSpan(start, width);
}
}
internal int SlotCount => Green.SlotCount;
public bool IsList => Green.IsList;
public bool IsMissing => Green.IsMissing;
public bool HasLeadingTrivia
{
get
{
return GetLeadingTrivia().Count > 0;
}
}
public bool HasTrailingTrivia
{
get
{
return GetTrailingTrivia().Count > 0;
}
}
public bool ContainsDiagnostics => Green.ContainsDiagnostics;
public bool ContainsAnnotations => Green.ContainsAnnotations;
public abstract TResult Accept<TResult>(SyntaxVisitor<TResult> visitor);
public abstract void Accept(SyntaxVisitor visitor);
internal abstract SyntaxNode GetNodeSlot(int index);
internal abstract SyntaxNode GetCachedSlot(int index);
internal SyntaxNode GetRed(ref SyntaxNode field, int slot)
{
var result = field;
if (result == null)
{
var green = Green.GetSlot(slot);
if (green != null)
{
Interlocked.CompareExchange(ref field, green.CreateRed(this, GetChildPosition(slot)), null);
result = field;
}
}
return result;
}
// Special case of above function where slot = 0, does not need GetChildPosition
internal SyntaxNode GetRedAtZero(ref SyntaxNode field)
{
var result = field;
if (result == null)
{
var green = Green.GetSlot(0);
if (green != null)
{
Interlocked.CompareExchange(ref field, green.CreateRed(this, Position), null);
result = field;
}
}
return result;
}
protected T GetRed<T>(ref T field, int slot) where T : SyntaxNode
{
var result = field;
if (result == null)
{
var green = Green.GetSlot(slot);
if (green != null)
{
Interlocked.CompareExchange(ref field, (T)green.CreateRed(this, this.GetChildPosition(slot)), null);
result = field;
}
}
return result;
}
// special case of above function where slot = 0, does not need GetChildPosition
protected T GetRedAtZero<T>(ref T field) where T : SyntaxNode
{
var result = field;
if (result == null)
{
var green = Green.GetSlot(0);
if (green != null)
{
Interlocked.CompareExchange(ref field, (T)green.CreateRed(this, Position), null);
result = field;
}
}
return result;
}
internal SyntaxNode GetRedElement(ref SyntaxNode element, int slot)
{
Debug.Assert(IsList);
var result = element;
if (result == null)
{
var green = Green.GetSlot(slot);
// passing list's parent
Interlocked.CompareExchange(ref element, green.CreateRed(Parent, GetChildPosition(slot)), null);
result = element;
}
return result;
}
internal virtual int GetChildPosition(int index)
{
var offset = 0;
var green = Green;
while (index > 0)
{
index--;
var prevSibling = GetCachedSlot(index);
if (prevSibling != null)
{
return prevSibling.EndPosition + offset;
}
var greenChild = green.GetSlot(index);
if (greenChild != null)
{
offset += greenChild.FullWidth;
}
}
return Position + offset;
}
public virtual SyntaxTriviaList GetLeadingTrivia()
{
var firstToken = GetFirstToken();
return firstToken != null ? firstToken.GetLeadingTrivia() : default(SyntaxTriviaList);
}
public virtual SyntaxTriviaList GetTrailingTrivia()
{
var lastToken = GetLastToken();
return lastToken != null ? lastToken.GetTrailingTrivia() : default(SyntaxTriviaList);
}
internal SyntaxToken GetFirstToken()
{
return ((SyntaxToken)GetFirstTerminal());
}
internal SyntaxToken GetLastToken()
{
return ((SyntaxToken)GetLastTerminal());
}
public SyntaxNode GetFirstTerminal()
{
var node = this;
do
{
var foundChild = false;
for (int i = 0, n = node.SlotCount; i < n; i++)
{
var child = node.GetNodeSlot(i);
if (child != null)
{
node = child;
foundChild = true;
break;
}
}
if (!foundChild)
{
return null;
}
}
while (node.SlotCount != 0);
return node == this ? this : node;
}
public SyntaxNode GetLastTerminal()
{
var node = this;
do
{
for (var i = node.SlotCount - 1; i >= 0; i--)
{
var child = node.GetNodeSlot(i);
if (child != null)
{
node = child;
break;
}
}
} while (node.SlotCount != 0);
return node == this ? this : node;
}
public RazorDiagnostic[] GetDiagnostics()
{
return Green.GetDiagnostics();
}
public SyntaxAnnotation[] GetAnnotations()
{
return Green.GetAnnotations();
}
public bool IsEquivalentTo(SyntaxNode other)
{
if (this == other)
{
return true;
}
if (other == null)
{
return false;
}
return Green.IsEquivalentTo(other.Green);
}
public override string ToString()
{
return Green.ToString();
}
public virtual string ToFullString()
{
return Green.ToFullString();
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal static class SyntaxNodeExtensions
{
public static TNode WithAnnotations<TNode>(this TNode node, params SyntaxAnnotation[] annotations) where TNode : SyntaxNode
{
return (TNode)node.Green.SetAnnotations(annotations).CreateRed();
}
}
}

View File

@ -0,0 +1,143 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal class SyntaxToken : SyntaxNode
{
internal SyntaxToken(GreenNode green, SyntaxNode parent, int position)
: this(green, parent, null, position)
{
}
// Temporary plumbing
internal SyntaxToken(GreenNode green, SyntaxNode parent, Span parentSpan, int position)
: base(green, parent, position)
{
Debug.Assert(parent == null || !parent.Green.IsList, "list cannot be a parent");
Debug.Assert(green == null || green.IsToken, "green must be a token");
ParentSpan = parentSpan;
}
// Temporary plumbing
internal Span ParentSpan { get; }
// Temporary plumbing
internal SourceLocation Start
{
get
{
if (ParentSpan == null)
{
return SourceLocation.Undefined;
}
var tracker = new SourceLocationTracker(ParentSpan.Start);
for (var i = 0; i < ParentSpan.Tokens.Count; i++)
{
var token = ParentSpan.Tokens[i];
if (object.ReferenceEquals(this, token))
{
break;
}
tracker.UpdateLocation(token.Content);
}
return tracker.CurrentLocation;
}
}
internal new InternalSyntax.SyntaxToken Green => (InternalSyntax.SyntaxToken)base.Green;
public string Content => Green.Content;
internal override sealed SyntaxNode GetCachedSlot(int index)
{
throw new InvalidOperationException("Tokens can't have slots.");
}
internal override sealed SyntaxNode GetNodeSlot(int slot)
{
throw new InvalidOperationException("Tokens can't have slots.");
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.VisitToken(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitToken(this);
}
public SyntaxToken WithLeadingTrivia(SyntaxNode trivia)
{
return Green != null
? new SyntaxToken(Green.WithLeadingTrivia(trivia.Green), parent: null, position: 0)
: default(SyntaxToken);
}
public SyntaxToken WithTrailingTrivia(SyntaxNode trivia)
{
return Green != null
? new SyntaxToken(Green.WithTrailingTrivia(trivia.Green), parent: null, position: 0)
: default(SyntaxToken);
}
public SyntaxToken WithLeadingTrivia(IEnumerable<SyntaxTrivia> trivia)
{
var greenList = trivia?.Select(t => t.Green);
return WithLeadingTrivia(Green.CreateList(greenList)?.CreateRed());
}
public SyntaxToken WithTrailingTrivia(IEnumerable<SyntaxTrivia> trivia)
{
var greenList = trivia?.Select(t => t.Green);
return WithTrailingTrivia(Green.CreateList(greenList)?.CreateRed());
}
public override SyntaxTriviaList GetLeadingTrivia()
{
if (Green.LeadingTrivia == null)
{
return default(SyntaxTriviaList);
}
return new SyntaxTriviaList(Green.LeadingTrivia.CreateRed(this, Position), Position);
}
public override SyntaxTriviaList GetTrailingTrivia()
{
var trailingGreen = Green.TrailingTrivia;
if (trailingGreen == null)
{
return default(SyntaxTriviaList);
}
var leading = Green.LeadingTrivia;
int index = 0;
if (leading != null)
{
index = leading.IsList ? leading.SlotCount : 1;
}
int trailingPosition = Position + FullWidth;
trailingPosition -= trailingGreen.FullWidth;
return new SyntaxTriviaList(trailingGreen.CreateRed(this, trailingPosition), trailingPosition, index);
}
public override string ToString()
{
return Content;
}
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal class SyntaxTrivia : SyntaxNode
{
internal SyntaxTrivia(GreenNode green, SyntaxNode parent, int position)
: base(green, parent, position)
{
}
internal new InternalSyntax.SyntaxTrivia Green => (InternalSyntax.SyntaxTrivia)base.Green;
public string Text => Green.Text;
internal override sealed SyntaxNode GetCachedSlot(int index)
{
throw new InvalidOperationException();
}
internal override sealed SyntaxNode GetNodeSlot(int slot)
{
throw new InvalidOperationException();
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.VisitTrivia(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitTrivia(this);
}
public sealed override SyntaxTriviaList GetTrailingTrivia()
{
return default(SyntaxTriviaList);
}
public sealed override SyntaxTriviaList GetLeadingTrivia()
{
return default(SyntaxTriviaList);
}
public override string ToString()
{
return Text;
}
public sealed override string ToFullString()
{
return Text;
}
}
}

View File

@ -0,0 +1,785 @@
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
[StructLayout(LayoutKind.Auto)]
internal readonly struct SyntaxTriviaList : IEquatable<SyntaxTriviaList>, IReadOnlyList<SyntaxTrivia>
{
public static SyntaxTriviaList Empty => default(SyntaxTriviaList);
internal SyntaxTriviaList(SyntaxNode node, int position, int index = 0)
{
Node = node;
Position = position;
Index = index;
}
internal SyntaxTriviaList(SyntaxNode node)
{
Node = node;
Position = node.Position;
Index = 0;
}
public SyntaxTriviaList(SyntaxTrivia trivia)
{
Node = trivia;
Position = 0;
Index = 0;
}
/// <summary>
/// Creates a list of trivia.
/// </summary>
/// <param name="trivias">An array of trivia.</param>
public SyntaxTriviaList(params SyntaxTrivia[] trivias)
: this(CreateNode(trivias), 0, 0)
{
}
/// <summary>
/// Creates a list of trivia.
/// </summary>
/// <param name="trivias">A sequence of trivia.</param>
public SyntaxTriviaList(IEnumerable<SyntaxTrivia> trivias)
: this(SyntaxTriviaListBuilder.Create(trivias).Node, 0, 0)
{
}
private static SyntaxNode CreateNode(SyntaxTrivia[] trivias)
{
if (trivias == null)
{
return null;
}
var builder = new SyntaxTriviaListBuilder(trivias.Length);
builder.Add(trivias);
return builder.ToList().Node;
}
internal SyntaxNode Node { get; }
internal int Position { get; }
internal int Index { get; }
public int Count
{
get { return Node == null ? 0 : (Node.IsList ? Node.SlotCount : 1); }
}
public SyntaxTrivia ElementAt(int index)
{
return this[index];
}
/// <summary>
/// Gets the trivia at the specified index.
/// </summary>
/// <param name="index">The zero-based index of the trivia to get.</param>
/// <returns>The token at the specified index.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="index" /> is less than 0.-or-<paramref name="index" /> is equal to or greater than <see cref="Count" />. </exception>
public SyntaxTrivia this[int index]
{
get
{
if (Node != null)
{
if (Node.IsList)
{
if (unchecked((uint)index < (uint)Node.SlotCount))
{
return Node.GetNodeSlot(index) as SyntaxTrivia;
}
}
else if (index == 0)
{
return Node as SyntaxTrivia;
}
}
throw new ArgumentOutOfRangeException(nameof(index));
}
}
/// <summary>
/// The absolute span of the list elements in characters, including the leading and trailing trivia of the first and last elements.
/// </summary>
public TextSpan FullSpan
{
get
{
if (Node == null)
{
return default(TextSpan);
}
return new TextSpan(Position, Node.FullWidth);
}
}
/// <summary>
/// The absolute span of the list elements in characters, not including the leading and trailing trivia of the first and last elements.
/// </summary>
public TextSpan Span
{
get
{
if (Node == null)
{
return default(TextSpan);
}
return TextSpan.FromBounds(Position + Node.Green.GetLeadingTriviaWidth(),
Position + Node.FullWidth - Node.Green.GetTrailingTriviaWidth());
}
}
/// <summary>
/// Returns the first trivia in the list.
/// </summary>
/// <returns>The first trivia in the list.</returns>
/// <exception cref="InvalidOperationException">The list is empty.</exception>
public SyntaxTrivia First()
{
if (Any())
{
return this[0];
}
throw new InvalidOperationException();
}
/// <summary>
/// Returns the last trivia in the list.
/// </summary>
/// <returns>The last trivia in the list.</returns>
/// <exception cref="InvalidOperationException">The list is empty.</exception>
public SyntaxTrivia Last()
{
if (Any())
{
return this[Count - 1];
}
throw new InvalidOperationException();
}
/// <summary>
/// Does this list have any items.
/// </summary>
public bool Any()
{
return Node != null;
}
/// <summary>
/// Returns a list which contains all elements of <see cref="SyntaxTriviaList"/> in reversed order.
/// </summary>
/// <returns><see cref="Reversed"/> which contains all elements of <see cref="SyntaxTriviaList"/> in reversed order</returns>
public Reversed Reverse()
{
return new Reversed(this);
}
public Enumerator GetEnumerator()
{
return new Enumerator(this);
}
public int IndexOf(SyntaxTrivia triviaInList)
{
for (int i = 0, n = Count; i < n; i++)
{
var trivia = this[i];
if (trivia == triviaInList)
{
return i;
}
}
return -1;
}
internal int IndexOf(SyntaxKind kind)
{
for (int i = 0, n = Count; i < n; i++)
{
if (this[i].Kind == kind)
{
return i;
}
}
return -1;
}
/// <summary>
/// Creates a new <see cref="SyntaxTriviaList"/> with the specified trivia added to the end.
/// </summary>
/// <param name="trivia">The trivia to add.</param>
public SyntaxTriviaList Add(SyntaxTrivia trivia)
{
return Insert(Count, trivia);
}
/// <summary>
/// Creates a new <see cref="SyntaxTriviaList"/> with the specified trivia added to the end.
/// </summary>
/// <param name="trivia">The trivia to add.</param>
public SyntaxTriviaList AddRange(IEnumerable<SyntaxTrivia> trivia)
{
return InsertRange(Count, trivia);
}
/// <summary>
/// Creates a new <see cref="SyntaxTriviaList"/> with the specified trivia inserted at the index.
/// </summary>
/// <param name="index">The index in the list to insert the trivia at.</param>
/// <param name="trivia">The trivia to insert.</param>
public SyntaxTriviaList Insert(int index, SyntaxTrivia trivia)
{
if (trivia == default(SyntaxTrivia))
{
throw new ArgumentOutOfRangeException(nameof(trivia));
}
return InsertRange(index, new[] { trivia });
}
private static readonly ObjectPool<SyntaxTriviaListBuilder> s_builderPool =
new ObjectPool<SyntaxTriviaListBuilder>(() => SyntaxTriviaListBuilder.Create());
private static SyntaxTriviaListBuilder GetBuilder()
=> s_builderPool.Allocate();
private static void ClearAndFreeBuilder(SyntaxTriviaListBuilder builder)
{
// It's possible someone might create a list with a huge amount of trivia
// in it. We don't want to hold onto such items forever. So only cache
// reasonably sized lists. In IDE testing, around 99% of all trivia lists
// were 16 or less elements.
const int MaxBuilderCount = 16;
if (builder.Count <= MaxBuilderCount)
{
builder.Clear();
s_builderPool.Free(builder);
}
}
/// <summary>
/// Creates a new <see cref="SyntaxTriviaList"/> with the specified trivia inserted at the index.
/// </summary>
/// <param name="index">The index in the list to insert the trivia at.</param>
/// <param name="trivia">The trivia to insert.</param>
public SyntaxTriviaList InsertRange(int index, IEnumerable<SyntaxTrivia> trivia)
{
var thisCount = Count;
if (index < 0 || index > thisCount)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
if (trivia == null)
{
throw new ArgumentNullException(nameof(trivia));
}
// Just return ourselves if we're not being asked to add anything.
if (trivia is ICollection<SyntaxTrivia> triviaCollection && triviaCollection.Count == 0)
{
return this;
}
var builder = GetBuilder();
try
{
for (var i = 0; i < index; i++)
{
builder.Add(this[i]);
}
builder.AddRange(trivia);
for (var i = index; i < thisCount; i++)
{
builder.Add(this[i]);
}
return builder.Count == thisCount ? this : builder.ToList();
}
finally
{
ClearAndFreeBuilder(builder);
}
}
/// <summary>
/// Creates a new <see cref="SyntaxTriviaList"/> with the element at the specified index removed.
/// </summary>
/// <param name="index">The index identifying the element to remove.</param>
public SyntaxTriviaList RemoveAt(int index)
{
if (index < 0 || index >= Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
var list = this.ToList();
list.RemoveAt(index);
return new SyntaxTriviaList(list);
}
/// <summary>
/// Creates a new <see cref="SyntaxTriviaList"/> with the specified element removed.
/// </summary>
/// <param name="triviaInList">The trivia element to remove.</param>
public SyntaxTriviaList Remove(SyntaxTrivia triviaInList)
{
var index = IndexOf(triviaInList);
if (index >= 0 && index < Count)
{
return RemoveAt(index);
}
return this;
}
/// <summary>
/// Creates a new <see cref="SyntaxTriviaList"/> with the specified element replaced with new trivia.
/// </summary>
/// <param name="triviaInList">The trivia element to replace.</param>
/// <param name="newTrivia">The trivia to replace the element with.</param>
public SyntaxTriviaList Replace(SyntaxTrivia triviaInList, SyntaxTrivia newTrivia)
{
if (newTrivia == default(SyntaxTrivia))
{
throw new ArgumentOutOfRangeException(nameof(newTrivia));
}
return ReplaceRange(triviaInList, new[] { newTrivia });
}
/// <summary>
/// Creates a new <see cref="SyntaxTriviaList"/> with the specified element replaced with new trivia.
/// </summary>
/// <param name="triviaInList">The trivia element to replace.</param>
/// <param name="newTrivia">The trivia to replace the element with.</param>
public SyntaxTriviaList ReplaceRange(SyntaxTrivia triviaInList, IEnumerable<SyntaxTrivia> newTrivia)
{
var index = IndexOf(triviaInList);
if (index >= 0 && index < Count)
{
var list = this.ToList();
list.RemoveAt(index);
list.InsertRange(index, newTrivia);
return new SyntaxTriviaList(list);
}
throw new ArgumentOutOfRangeException(nameof(triviaInList));
}
// for debugging
private SyntaxTrivia[] Nodes => this.ToArray();
IEnumerator<SyntaxTrivia> IEnumerable<SyntaxTrivia>.GetEnumerator()
{
if (Node == null)
{
return SpecializedCollections.EmptyEnumerator<SyntaxTrivia>();
}
return new EnumeratorImpl(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
if (Node == null)
{
return SpecializedCollections.EmptyEnumerator<SyntaxTrivia>();
}
return new EnumeratorImpl(this);
}
/// <summary>
/// get the green node at the specific slot
/// </summary>
private SyntaxNode GetNodeAt(int i)
{
return GetNodeAt(Node, i);
}
private static SyntaxNode GetNodeAt(SyntaxNode node, int i)
{
Debug.Assert(node.IsList || (i == 0 && !node.IsList));
return node.IsList ? node.GetNodeSlot(i) : node;
}
public bool Equals(SyntaxTriviaList other)
{
return Node == other.Node && Index == other.Index;
}
public static bool operator ==(SyntaxTriviaList left, SyntaxTriviaList right)
{
return left.Equals(right);
}
public static bool operator !=(SyntaxTriviaList left, SyntaxTriviaList right)
{
return !left.Equals(right);
}
public override bool Equals(object obj)
{
return (obj is SyntaxTriviaList) && Equals((SyntaxTriviaList)obj);
}
public override int GetHashCode()
{
var hash = HashCodeCombiner.Start();
hash.Add(Node);
hash.Add(Index);
return hash.CombinedHash;
}
/// <summary>
/// Copy <paramref name="count"/> number of items starting at <paramref name="offset"/> from this list into <paramref name="array"/> starting at <paramref name="arrayOffset"/>.
/// </summary>
internal void CopyTo(int offset, SyntaxTrivia[] array, int arrayOffset, int count)
{
if (offset < 0 || count < 0 || Count < offset + count)
{
throw new IndexOutOfRangeException();
}
if (count == 0)
{
return;
}
// get first one without creating any red node
var first = this[offset];
array[arrayOffset] = first;
// calculate trivia position from the first ourselves from now on
var position = first.Position;
var current = first;
for (var i = 1; i < count; i++)
{
position += current.FullWidth;
current = GetNodeAt(offset + i) as SyntaxTrivia;
array[arrayOffset + i] = current;
}
}
public override string ToString()
{
return Node != null ? Node.ToString() : string.Empty;
}
public string ToFullString()
{
return Node != null ? Node.ToFullString() : string.Empty;
}
public static SyntaxTriviaList Create(SyntaxTrivia trivia)
{
return new SyntaxTriviaList(trivia);
}
/// <summary>
/// Reversed enumerable.
/// </summary>
public struct Reversed : IEnumerable<SyntaxTrivia>, IEquatable<Reversed>
{
private SyntaxTriviaList _list;
public Reversed(in SyntaxTriviaList list)
{
_list = list;
}
public Enumerator GetEnumerator()
{
return new Enumerator(in _list);
}
IEnumerator<SyntaxTrivia> IEnumerable<SyntaxTrivia>.GetEnumerator()
{
if (_list.Count == 0)
{
return SpecializedCollections.EmptyEnumerator<SyntaxTrivia>();
}
return new ReversedEnumeratorImpl(in _list);
}
IEnumerator
IEnumerable.GetEnumerator()
{
if (_list.Count == 0)
{
return SpecializedCollections.EmptyEnumerator<SyntaxTrivia>();
}
return new ReversedEnumeratorImpl(in _list);
}
public override int GetHashCode()
{
return _list.GetHashCode();
}
public override bool Equals(object obj)
{
return obj is Reversed && Equals((Reversed)obj);
}
public bool Equals(Reversed other)
{
return _list.Equals(other._list);
}
[StructLayout(LayoutKind.Auto)]
public struct Enumerator
{
private readonly SyntaxNode _singleNodeOrList;
private readonly int _baseIndex;
private readonly int _count;
private int _index;
private SyntaxNode _current;
private int _position;
public Enumerator(in SyntaxTriviaList list)
: this()
{
if (list.Any())
{
_singleNodeOrList = list.Node;
_baseIndex = list.Index;
_count = list.Count;
_index = _count;
_current = null;
var last = list.Last();
_position = last.Position + last.FullWidth;
}
}
public bool MoveNext()
{
if (_count == 0 || _index <= 0)
{
_current = null;
return false;
}
_index--;
_current = GetNodeAt(_singleNodeOrList, _index);
_position -= _current.FullWidth;
return true;
}
public SyntaxTrivia Current
{
get
{
if (_current == null)
{
throw new InvalidOperationException();
}
return (SyntaxTrivia)_current;
}
}
}
private class ReversedEnumeratorImpl : IEnumerator<SyntaxTrivia>
{
private Enumerator _enumerator;
// SyntaxTriviaList is a relatively big struct so is passed as ref
internal ReversedEnumeratorImpl(in SyntaxTriviaList list)
{
_enumerator = new Enumerator(in list);
}
public SyntaxTrivia Current => _enumerator.Current;
object IEnumerator.Current => _enumerator.Current;
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public void Reset()
{
throw new NotSupportedException();
}
public void Dispose()
{
}
}
}
[StructLayout(LayoutKind.Auto)]
public struct Enumerator
{
private SyntaxNode _singleNodeOrList;
private int _baseIndex;
private int _count;
private int _index;
private SyntaxNode _current;
private int _position;
internal Enumerator(in SyntaxTriviaList list)
{
_singleNodeOrList = list.Node;
_baseIndex = list.Index;
_count = list.Count;
_index = -1;
_current = null;
_position = list.Position;
}
private void InitializeFrom(SyntaxNode node, int index, int position)
{
_singleNodeOrList = node;
_baseIndex = index;
_count = node.IsList ? node.SlotCount : 1;
_index = -1;
_current = null;
_position = position;
}
// PERF: Used to initialize an enumerator for leading trivia directly from a token.
// This saves constructing an intermediate SyntaxTriviaList. Also, passing token
// by ref since it's a non-trivial struct
internal void InitializeFromLeadingTrivia(in SyntaxToken token)
{
InitializeFrom(token.GetLeadingTrivia().Node, 0, token.Position);
}
// PERF: Used to initialize an enumerator for trailing trivia directly from a token.
// This saves constructing an intermediate SyntaxTriviaList. Also, passing token
// by ref since it's a non-trivial struct
internal void InitializeFromTrailingTrivia(in SyntaxToken token)
{
var leading = token.GetLeadingTrivia().Node;
var index = 0;
if (leading != null)
{
index = leading.IsList ? leading.SlotCount : 1;
}
var trailing = token.GetTrailingTrivia().Node;
var trailingPosition = token.Position + token.FullWidth;
if (trailing != null)
{
trailingPosition -= trailing.FullWidth;
}
InitializeFrom(trailing, index, trailingPosition);
}
public bool MoveNext()
{
var newIndex = _index + 1;
if (newIndex >= _count)
{
// invalidate iterator
_current = null;
return false;
}
_index = newIndex;
if (_current != null)
{
_position += _current.FullWidth;
}
_current = GetNodeAt(_singleNodeOrList, newIndex);
return true;
}
public SyntaxTrivia Current
{
get
{
if (_current == null)
{
throw new InvalidOperationException();
}
return _current as SyntaxTrivia;
}
}
internal bool TryMoveNextAndGetCurrent(out SyntaxTrivia current)
{
if (!MoveNext())
{
current = default;
return false;
}
current = _current as SyntaxTrivia;
return true;
}
}
private class EnumeratorImpl : IEnumerator<SyntaxTrivia>
{
private Enumerator _enumerator;
// SyntaxTriviaList is a relatively big struct so is passed as ref
internal EnumeratorImpl(in SyntaxTriviaList list)
{
_enumerator = new Enumerator(list);
}
public SyntaxTrivia Current => _enumerator.Current;
object IEnumerator.Current => _enumerator.Current;
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public void Reset()
{
throw new NotSupportedException();
}
public void Dispose()
{
}
}
}
}

View File

@ -0,0 +1,138 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
internal class SyntaxTriviaListBuilder
{
private SyntaxTrivia[] _nodes;
public SyntaxTriviaListBuilder(int size)
{
_nodes = new SyntaxTrivia[size];
}
public static SyntaxTriviaListBuilder Create()
{
return new SyntaxTriviaListBuilder(4);
}
public static SyntaxTriviaList Create(IEnumerable<SyntaxTrivia> trivia)
{
if (trivia == null)
{
return new SyntaxTriviaList();
}
var builder = Create();
builder.AddRange(trivia);
return builder.ToList();
}
public int Count { get; private set; }
public void Clear()
{
Count = 0;
}
public SyntaxTrivia this[int index]
{
get
{
if (index < 0 || index > Count)
{
throw new IndexOutOfRangeException();
}
return _nodes[index];
}
}
public void AddRange(IEnumerable<SyntaxTrivia> items)
{
if (items != null)
{
foreach (var item in items)
{
Add(item);
}
}
}
public SyntaxTriviaListBuilder Add(SyntaxTrivia item)
{
if (_nodes == null || Count >= _nodes.Length)
{
Grow(Count == 0 ? 8 : _nodes.Length * 2);
}
_nodes[Count++] = item;
return this;
}
public void Add(SyntaxTrivia[] items)
{
Add(items, 0, items.Length);
}
public void Add(SyntaxTrivia[] items, int offset, int length)
{
if (_nodes == null || Count + length > _nodes.Length)
{
Grow(Count + length);
}
Array.Copy(items, offset, _nodes, Count, length);
Count += length;
}
public void Add(in SyntaxTriviaList list)
{
Add(list, 0, list.Count);
}
public void Add(in SyntaxTriviaList list, int offset, int length)
{
if (_nodes == null || Count + length > _nodes.Length)
{
Grow(Count + length);
}
list.CopyTo(offset, _nodes, Count, length);
Count += length;
}
private void Grow(int size)
{
var tmp = new SyntaxTrivia[size];
Array.Copy(_nodes, tmp, _nodes.Length);
_nodes = tmp;
}
public static implicit operator SyntaxTriviaList(SyntaxTriviaListBuilder builder)
{
return builder.ToList();
}
public SyntaxTriviaList ToList()
{
if (Count > 0)
{
var tmp = new ArrayElement<GreenNode>[Count];
for (var i = 0; i < Count; i++)
{
tmp[i].Value = _nodes[i].Green;
}
return new SyntaxTriviaList(InternalSyntax.SyntaxList.List(tmp).CreateRed(), position: 0, index: 0);
}
else
{
return default(SyntaxTriviaList);
}
}
}
}

View File

@ -0,0 +1,58 @@
// 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.Syntax
{
internal abstract partial class SyntaxVisitor<TResult>
{
public virtual TResult Visit(SyntaxNode node)
{
if (node != null)
{
return node.Accept(this);
}
return default(TResult);
}
public virtual TResult VisitToken(SyntaxToken token)
{
return DefaultVisit(token);
}
public virtual TResult VisitTrivia(SyntaxTrivia trivia)
{
return DefaultVisit(trivia);
}
protected virtual TResult DefaultVisit(SyntaxNode node)
{
return default(TResult);
}
}
internal abstract partial class SyntaxVisitor
{
public virtual void Visit(SyntaxNode node)
{
if (node != null)
{
node.Accept(this);
}
}
public virtual void VisitToken(SyntaxToken token)
{
DefaultVisit(token);
}
public virtual void VisitTrivia(SyntaxTrivia trivia)
{
DefaultVisit(trivia);
}
public virtual void DefaultVisit(SyntaxNode node)
{
}
}
}

View File

@ -0,0 +1,261 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Syntax
{
/// <summary>
/// Immutable abstract representation of a span of text. For example, in an error diagnostic that reports a
/// location, it could come from a parsed string, text from a tool editor buffer, etc.
/// </summary>
internal readonly struct TextSpan : IEquatable<TextSpan>, IComparable<TextSpan>
{
/// <summary>
/// Creates a TextSpan instance beginning with the position Start and having the Length
/// specified with <paramref name="length" />.
/// </summary>
public TextSpan(int start, int length)
{
if (start < 0)
{
throw new ArgumentOutOfRangeException(nameof(start));
}
if (start + length < start)
{
throw new ArgumentOutOfRangeException(nameof(length));
}
Start = start;
Length = length;
}
/// <summary>
/// Start point of the span.
/// </summary>
public int Start { get; }
/// <summary>
/// End of the span.
/// </summary>
public int End => Start + Length;
/// <summary>
/// Length of the span.
/// </summary>
public int Length { get; }
/// <summary>
/// Determines whether or not the span is empty.
/// </summary>
public bool IsEmpty => Length == 0;
/// <summary>
/// Determines whether the position lies within the span.
/// </summary>
/// <param name="position">
/// The position to check.
/// </param>
/// <returns>
/// <c>true</c> if the position is greater than or equal to Start and strictly less
/// than End, otherwise <c>false</c>.
/// </returns>
public bool Contains(int position)
{
return unchecked((uint)(position - Start) < (uint)Length);
}
/// <summary>
/// Determines whether <paramref name="span"/> falls completely within this span.
/// </summary>
/// <param name="span">
/// The span to check.
/// </param>
/// <returns>
/// <c>true</c> if the specified span falls completely within this span, otherwise <c>false</c>.
/// </returns>
public bool Contains(TextSpan span)
{
return span.Start >= Start && span.End <= End;
}
/// <summary>
/// Determines whether <paramref name="span"/> overlaps this span. Two spans are considered to overlap
/// if they have positions in common and neither is empty. Empty spans do not overlap with any
/// other span.
/// </summary>
/// <param name="span">
/// The span to check.
/// </param>
/// <returns>
/// <c>true</c> if the spans overlap, otherwise <c>false</c>.
/// </returns>
public bool OverlapsWith(TextSpan span)
{
var overlapStart = Math.Max(Start, span.Start);
var overlapEnd = Math.Min(End, span.End);
return overlapStart < overlapEnd;
}
/// <summary>
/// Returns the overlap with the given span, or null if there is no overlap.
/// </summary>
/// <param name="span">
/// The span to check.
/// </param>
/// <returns>
/// The overlap of the spans, or null if the overlap is empty.
/// </returns>
public TextSpan? Overlap(TextSpan span)
{
var overlapStart = Math.Max(Start, span.Start);
var overlapEnd = Math.Min(End, span.End);
return overlapStart < overlapEnd
? FromBounds(overlapStart, overlapEnd)
: (TextSpan?)null;
}
/// <summary>
/// Determines whether <paramref name="span"/> intersects this span. Two spans are considered to
/// intersect if they have positions in common or the end of one span
/// coincides with the start of the other span.
/// </summary>
/// <param name="span">
/// The span to check.
/// </param>
/// <returns>
/// <c>true</c> if the spans intersect, otherwise <c>false</c>.
/// </returns>
public bool IntersectsWith(TextSpan span)
{
return span.Start <= End && span.End >= Start;
}
/// <summary>
/// Determines whether <paramref name="position"/> intersects this span.
/// A position is considered to intersect if it is between the start and
/// end positions (inclusive) of this span.
/// </summary>
/// <param name="position">
/// The position to check.
/// </param>
/// <returns>
/// <c>true</c> if the position intersects, otherwise <c>false</c>.
/// </returns>
public bool IntersectsWith(int position)
{
return unchecked((uint)(position - Start) <= (uint)Length);
}
/// <summary>
/// Returns the intersection with the given span, or null if there is no intersection.
/// </summary>
/// <param name="span">
/// The span to check.
/// </param>
/// <returns>
/// The intersection of the spans, or null if the intersection is empty.
/// </returns>
public TextSpan? Intersection(TextSpan span)
{
var intersectStart = Math.Max(Start, span.Start);
var intersectEnd = Math.Min(End, span.End);
return intersectStart <= intersectEnd
? FromBounds(intersectStart, intersectEnd)
: (TextSpan?)null;
}
/// <summary>
/// Creates a new <see cref="TextSpan"/> from <paramref name="start" /> and <paramref
/// name="end"/> positions as opposed to a position and length.
///
/// The returned TextSpan contains the range with <paramref name="start"/> inclusive,
/// and <paramref name="end"/> exclusive.
/// </summary>
public static TextSpan FromBounds(int start, int end)
{
if (start < 0)
{
throw new ArgumentOutOfRangeException(nameof(start), "start must not be negative");
}
if (end < start)
{
throw new ArgumentOutOfRangeException(nameof(end), "end must not be less than start");
}
return new TextSpan(start, end - start);
}
/// <summary>
/// Determines if two instances of <see cref="TextSpan"/> are the same.
/// </summary>
public static bool operator ==(TextSpan left, TextSpan right)
{
return left.Equals(right);
}
/// <summary>
/// Determines if two instances of <see cref="TextSpan"/> are different.
/// </summary>
public static bool operator !=(TextSpan left, TextSpan right)
{
return !left.Equals(right);
}
/// <summary>
/// Determines if current instance of <see cref="TextSpan"/> is equal to another.
/// </summary>
public bool Equals(TextSpan other)
{
return Start == other.Start && Length == other.Length;
}
/// <summary>
/// Determines if current instance of <see cref="TextSpan"/> is equal to another.
/// </summary>
public override bool Equals(object obj)
{
return obj is TextSpan && Equals((TextSpan)obj);
}
/// <summary>
/// Produces a hash code for <see cref="TextSpan"/>.
/// </summary>
public override int GetHashCode()
{
var combiner = new HashCodeCombiner();
combiner.Add(Start);
combiner.Add(Length);
return combiner.CombinedHash;
}
/// <summary>
/// Provides a string representation for <see cref="TextSpan"/>.
/// </summary>
public override string ToString()
{
return $"[{Start}..{End})";
}
/// <summary>
/// Compares current instance of <see cref="TextSpan"/> with another.
/// </summary>
public int CompareTo(TextSpan other)
{
var diff = Start - other.Start;
if (diff != 0)
{
return diff;
}
return Length - other.Length;
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Composition;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.CodeAnalysis.Razor
{
[Export(typeof(DocumentServiceProviderFactory))]
internal class DefaultDocumentServiceProviderFactory : DocumentServiceProviderFactory
{
public override IDocumentServiceProvider Create(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return new RazorDocumentServiceProvider(document);
}
public override IDocumentServiceProvider CreateEmpty()
{
return new RazorDocumentServiceProvider();
}
}
}

View File

@ -32,13 +32,8 @@ namespace Microsoft.CodeAnalysis.Razor
_factories = factories;
}
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
public override RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
if (project == null)
{
throw new ArgumentNullException(nameof(project));
}
if (fileSystem == null)
{
throw new ArgumentNullException(nameof(fileSystem));
@ -55,7 +50,7 @@ namespace Microsoft.CodeAnalysis.Razor
//
// We typically want this because the language adds features over time - we don't want to a bunch of errors
// to show up when a document is first opened, and then go away when the configuration loads, we'd prefer the opposite.
var configuration = project.Configuration ?? DefaultConfiguration;
configuration = configuration ?? DefaultConfiguration;
// If there's no factory to handle the configuration then fall back to a very basic configuration.
//

View File

@ -0,0 +1,92 @@
// 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.Composition;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.CodeAnalysis.Razor
{
[Shared]
[Export(typeof(RazorCompletionFactsService))]
internal class DefaultRazorCompletionFactsService : RazorCompletionFactsService
{
private static readonly IEnumerable<DirectiveDescriptor> DefaultDirectives = new[]
{
CSharpCodeParser.AddTagHelperDirectiveDescriptor,
CSharpCodeParser.RemoveTagHelperDirectiveDescriptor,
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
};
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree, SourceSpan location)
{
var completionItems = new List<RazorCompletionItem>();
if (AtDirectiveCompletionPoint(syntaxTree, location))
{
var directiveCompletions = GetDirectiveCompletionItems(syntaxTree);
completionItems.AddRange(directiveCompletions);
}
return completionItems;
}
// Internal for testing
internal static List<RazorCompletionItem> GetDirectiveCompletionItems(RazorSyntaxTree syntaxTree)
{
var directives = syntaxTree.Options.Directives.Concat(DefaultDirectives);
var completionItems = new List<RazorCompletionItem>();
foreach (var directive in directives)
{
var completionDisplayText = directive.DisplayName ?? directive.Directive;
var completionItem = new RazorCompletionItem(
completionDisplayText,
directive.Directive,
directive.Description,
RazorCompletionItemKind.Directive);
completionItems.Add(completionItem);
}
return completionItems;
}
// Internal for testing
internal static bool AtDirectiveCompletionPoint(RazorSyntaxTree syntaxTree, SourceSpan location)
{
if (syntaxTree == null)
{
return false;
}
var change = new SourceChange(location, string.Empty);
var owner = syntaxTree.Root.LocateOwner(change);
if (owner == null)
{
return false;
}
if (owner.ChunkGenerator is ExpressionChunkGenerator &&
owner.Tokens.All(IsDirectiveCompletableToken) &&
// Do not provide IntelliSense for explicit expressions. Explicit expressions will usually look like:
// [@] [(] [DateTime.Now] [)]
owner.Parent?.Children.Count > 1 &&
owner.Parent.Children[1] == owner)
{
return true;
}
return false;
}
// Internal for testing
internal static bool IsDirectiveCompletableToken(AspNetCore.Razor.Language.Syntax.SyntaxToken token)
{
return token.Kind == SyntaxKind.Identifier ||
// Marker symbol
token.Kind == SyntaxKind.Unknown;
}
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.CodeAnalysis.Razor
{
internal abstract class DocumentServiceProviderFactory
{
public abstract IDocumentServiceProvider CreateEmpty();
public abstract IDocumentServiceProvider Create(DocumentSnapshot document);
}
}

View File

@ -0,0 +1,79 @@
// Copyright (c) Microsoft. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DOCUMENT_SERVICE_FACTORY
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Host
{
/// <summary>
/// excerpt some part of <see cref="Document"/>
/// </summary>
internal interface IDocumentExcerptService : IDocumentService
{
/// <summary>
/// return <see cref="ExcerptResult"/> of given <see cref="Document"/> and <see cref="TextSpan"/>
///
/// the result might not be an exact copy of the given source or contains more then given span
/// </summary>
Task<ExcerptResult?> TryExcerptAsync(Document document, TextSpan span, ExcerptMode mode, CancellationToken cancellationToken);
}
/// <summary>
/// this mode shows intention not actual behavior. it is up to implementation how to interpret the intention.
/// </summary>
internal enum ExcerptMode
{
SingleLine,
Tooltip
}
/// <summary>
/// Result of excerpt
/// </summary>
internal struct ExcerptResult
{
/// <summary>
/// excerpt content
/// </summary>
public readonly SourceText Content;
/// <summary>
/// span on <see cref="Content"/> that given <see cref="Span"/> got mapped to
/// </summary>
public readonly TextSpan MappedSpan;
/// <summary>
/// classification information on the <see cref="Content"/>
/// </summary>
public readonly ImmutableArray<ClassifiedSpan> ClassifiedSpans;
/// <summary>
/// <see cref="Document"/> this excerpt is from
/// </summary>
public readonly Document Document;
/// <summary>
/// span on <see cref="Document"/> this excerpt is from
/// </summary>
public readonly TextSpan Span;
public ExcerptResult(SourceText content, TextSpan mappedSpan, ImmutableArray<ClassifiedSpan> classifiedSpans, Document document, TextSpan span)
{
Content = content;
MappedSpan = mappedSpan;
ClassifiedSpans = classifiedSpans;
// these 2 might not actually needed
Document = document;
Span = span;
}
}
}
#endif

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DOCUMENT_SERVICE_FACTORY
namespace Microsoft.CodeAnalysis.Host
{
/// <summary>
/// provide various operations for this document
///
/// I followed name from EditorOperation for now.
/// </summary>
internal interface IDocumentOperationService : IDocumentService
{
/// <summary>
/// document version of <see cref="Workspace.CanApplyChange(ApplyChangesKind)"/>
/// </summary>
bool CanApplyChange { get; }
/// <summary>
/// indicates whether this document supports diagnostics or not
/// </summary>
bool SupportDiagnostics { get; }
}
}
#endif

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DOCUMENT_SERVICE_FACTORY
namespace Microsoft.CodeAnalysis.Host
{
/// <summary>
/// Empty interface just to mark document services.
/// </summary>
internal interface IDocumentService
{
}
}
#endif

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DOCUMENT_SERVICE_FACTORY
namespace Microsoft.CodeAnalysis.Host
{
internal interface IDocumentServiceProvider
{
/// <summary>
/// Gets a document specific service provided by the host identified by the service type.
/// If the host does not provide the service, this method returns null.
/// </summary>
TService GetService<TService>() where TService : class, IDocumentService;
}
}
#endif

View File

@ -0,0 +1,70 @@
// Copyright (c) Microsoft. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DOCUMENT_SERVICE_FACTORY
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Host
{
/// <summary>
/// Map spans in a document to other spans even in other document
///
/// this will be used by various features if provided to convert span in one document to other spans.
///
/// for example, it is used to show spans users expect in a razor file rather than spans in
/// auto generated file that is implementation detail or navigate to the right place rather
/// than the generated file and etc.
/// </summary>
internal interface ISpanMappingService : IDocumentService
{
/// <summary>
/// Map spans in the document to more appropriate locations
///
/// in current design, this can NOT map a span to a span that is not backed by a file.
/// for example, roslyn supports someone to have a document that is not backed by a file. and current design doesn't allow
/// such document to be returned from this API
/// for example, span on razor secondary buffer document in roslyn solution mapped to a span on razor cshtml file is possible but
/// a span on razor cshtml file to a span on secondary buffer document is not possible since secondary buffer document is not backed by a file
/// </summary>
/// <param name="document">Document given spans belong to</param>
/// <param name="spans">Spans in the document</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Return mapped span. order of result should be same as the given span</returns>
Task<ImmutableArray<MappedSpanResult>> MapSpansAsync(Document document, IEnumerable<TextSpan> spans, CancellationToken cancellationToken);
}
/// <summary>
/// Result of span mapping
/// </summary>
internal struct MappedSpanResult
{
/// <summary>
/// Path to mapped file
/// </summary>
public readonly string FilePath;
/// <summary>
/// LinePosition representation of the Span
/// </summary>
public readonly LinePositionSpan LinePositionSpan;
/// <summary>
/// Mapped span
/// </summary>
public readonly TextSpan Span;
public MappedSpanResult(string filePath, LinePositionSpan linePositionSpan, TextSpan span)
{
FilePath = filePath;
LinePositionSpan = linePositionSpan;
Span = span;
}
}
}
#endif

View File

@ -4,6 +4,7 @@
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains the Razor design-time infrastructure.</Description>
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
<EnableApiCheck>false</EnableApiCheck>
<DefineConstants>$(DefineConstants);DOCUMENT_SERVICE_FACTORY</DefineConstants>
</PropertyGroup>
<ItemGroup>

View File

@ -45,7 +45,36 @@ namespace Microsoft.CodeAnalysis.Razor
return Create(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), configure);
}
public abstract RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure);
public RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
if (project == null)
{
throw new ArgumentNullException(nameof(project));
}
if (fileSystem == null)
{
throw new ArgumentNullException(nameof(fileSystem));
}
return Create(project.Configuration, fileSystem, configure);
}
public RazorProjectEngine Create(RazorConfiguration configuration, string directoryPath, Action<RazorProjectEngineBuilder> configure)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (directoryPath == null)
{
throw new ArgumentNullException(nameof(directoryPath));
}
return Create(configuration, RazorProjectFileSystem.Create(directoryPath), configure);
}
public abstract RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure);
}
}

View File

@ -23,11 +23,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
throw new ArgumentNullException(nameof(state));
}
Project = project;
ProjectInternal = project;
State = state;
}
public DefaultProjectSnapshot Project { get; }
public DefaultProjectSnapshot ProjectInternal { get; }
public DocumentState State { get; }
@ -35,9 +35,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
public override string TargetPath => State.HostDocument.TargetPath;
public override ProjectSnapshot Project => ProjectInternal;
public override bool SupportsOutput => true;
public override IReadOnlyList<DocumentSnapshot> GetImports()
{
return State.Imports.GetImports(Project, this);
return State.GetImports(ProjectInternal);
}
public override Task<SourceText> GetTextAsync()
@ -50,10 +54,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return State.GetTextVersionAsync();
}
public override Task<RazorCodeDocument> GetGeneratedOutputAsync()
public override async Task<RazorCodeDocument> GetGeneratedOutputAsync()
{
// IMPORTANT: Don't put more code here. We want this to return a cached task.
return State.GeneratedOutput.GetGeneratedOutputInitializationTask(Project, this);
var (output, _, _) = await State.GetGeneratedOutputAndVersionAsync(ProjectInternal, this).ConfigureAwait(false);
return output;
}
public override bool TryGetText(out SourceText result)
@ -68,9 +72,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
public override bool TryGetGeneratedOutput(out RazorCodeDocument result)
{
if (State.GeneratedOutput.IsResultAvailable)
if (State.IsGeneratedOutputResultAvailable)
{
result = State.GeneratedOutput.GetGeneratedOutputInitializationTask(Project, this).Result;
result = State.GetGeneratedOutputAndVersionAsync(ProjectInternal, this).Result.output;
return true;
}

View File

@ -0,0 +1,85 @@
// 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.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
internal class DefaultImportDocumentSnapshot : DocumentSnapshot
{
private ProjectSnapshot _project;
private RazorProjectItem _importItem;
private SourceText _sourceText;
private VersionStamp _version;
public DefaultImportDocumentSnapshot(ProjectSnapshot project, RazorProjectItem item)
{
_project = project;
_importItem = item;
_version = VersionStamp.Default;
}
public override string FilePath => null;
public override string TargetPath => null;
public override bool SupportsOutput => false;
public override ProjectSnapshot Project => _project;
public override Task<RazorCodeDocument> GetGeneratedOutputAsync()
{
throw new NotSupportedException();
}
public override IReadOnlyList<DocumentSnapshot> GetImports()
{
return Array.Empty<DocumentSnapshot>();
}
public async override Task<SourceText> GetTextAsync()
{
using (var stream = _importItem.Read())
using (var reader = new StreamReader(stream))
{
var content = await reader.ReadToEndAsync();
_sourceText = SourceText.From(content);
}
return _sourceText;
}
public override Task<VersionStamp> GetTextVersionAsync()
{
return Task.FromResult(_version);
}
public override bool TryGetText(out SourceText result)
{
if (_sourceText != null)
{
result = _sourceText;
return true;
}
result = null;
return false;
}
public override bool TryGetTextVersion(out VersionStamp result)
{
result = _version;
return true;
}
public override bool TryGetGeneratedOutput(out RazorCodeDocument result)
{
throw new NotSupportedException();
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
@ -58,22 +59,50 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
}
}
public override bool IsImportDocument(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return State.ImportsToRelatedDocuments.ContainsKey(document.TargetPath);
}
public override IEnumerable<DocumentSnapshot> GetRelatedDocuments(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (State.ImportsToRelatedDocuments.TryGetValue(document.TargetPath, out var relatedDocuments))
{
lock (_lock)
{
return relatedDocuments.Select(GetDocument).ToArray();
}
}
return Array.Empty<DocumentSnapshot>();
}
public override RazorProjectEngine GetProjectEngine()
{
return State.ProjectEngine.GetProjectEngine(this);
return State.ProjectEngine;
}
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()
{
// IMPORTANT: Don't put more code here. We want this to return a cached task.
return State.TagHelpers.GetTagHelperInitializationTask(this);
return State.GetTagHelpersAsync(this);
}
public override bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> result)
{
if (State.TagHelpers.IsResultAvailable)
if (State.IsTagHelperResultAvailable)
{
result = State.TagHelpers.GetTagHelperInitializationTask(this).Result;
result = State.GetTagHelpersAsync(this).Result;
return true;
}

View File

@ -90,12 +90,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
var projects = new ProjectSnapshot[_projects.Count];
foreach (var entry in _projects)
{
if (entry.Value.Snapshot == null)
{
entry.Value.Snapshot = new DefaultProjectSnapshot(entry.Value.State);
}
projects[i++] = entry.Value.Snapshot;
projects[i++] = entry.Value.GetSnapshot();
}
return projects;
@ -115,12 +110,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
if (_projects.TryGetValue(filePath, out var entry))
{
if (entry.Snapshot == null)
{
entry.Snapshot = new DefaultProjectSnapshot(entry.State);
}
return entry.Snapshot;
return entry.GetSnapshot();
}
return null;
@ -175,8 +165,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[hostProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentAdded));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[hostProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), document.FilePath, ProjectChangeKind.DocumentAdded));
}
}
}
@ -201,8 +193,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[hostProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentRemoved));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[hostProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), document.FilePath, ProjectChangeKind.DocumentRemoved));
}
}
}
@ -229,12 +223,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
entry.State.Documents.TryGetValue(documentFilePath, out var older))
{
ProjectState state;
SourceText olderText;
VersionStamp olderVersion;
var currentText = sourceText;
if (older.TryGetText(out olderText) &&
older.TryGetTextVersion(out olderVersion))
if (older.TryGetText(out var olderText) &&
older.TryGetTextVersion(out var olderVersion))
{
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
@ -256,8 +248,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[projectFilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[projectFilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@ -293,8 +287,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[projectFilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[projectFilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@ -321,12 +317,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
entry.State.Documents.TryGetValue(documentFilePath, out var older))
{
ProjectState state;
SourceText olderText;
VersionStamp olderVersion;
var currentText = sourceText;
if (older.TryGetText(out olderText) &&
older.TryGetTextVersion(out olderVersion))
if (older.TryGetText(out var olderText) &&
older.TryGetTextVersion(out var olderVersion))
{
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
@ -346,8 +340,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[projectFilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[projectFilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@ -381,8 +377,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[projectFilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[projectFilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@ -407,10 +405,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
var state = ProjectState.Create(Workspace.Services, hostProject, workspaceProject);
_projects[hostProject.FilePath] = new Entry(state);
var entry = new Entry(state);
_projects[hostProject.FilePath] = entry;
// We need to notify listeners about every project add.
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectAdded));
NotifyListeners(new ProjectChangeEventArgs(null, entry.GetSnapshot(), ProjectChangeKind.ProjectAdded));
}
public override void HostProjectChanged(HostProject hostProject)
@ -429,9 +428,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// HostProject updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[hostProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[hostProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
}
}
@ -445,12 +445,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
_foregroundDispatcher.AssertForegroundThread();
if (_projects.TryGetValue(hostProject.FilePath, out var snapshot))
if (_projects.TryGetValue(hostProject.FilePath, out var entry))
{
_projects.Remove(hostProject.FilePath);
// We need to notify listeners about every project removal.
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectRemoved));
var oldSnapshot = entry.GetSnapshot();
_projects.Remove(hostProject.FilePath);
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, null, ProjectChangeKind.ProjectRemoved));
}
}
@ -477,9 +477,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
if (entry.State.WorkspaceProject == null)
{
var state = entry.State.WithWorkspaceProject(workspaceProject);
_projects[workspaceProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[workspaceProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
}
}
@ -505,14 +507,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
(entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Version.GetNewerVersion(workspaceProject.Version) == workspaceProject.Version))
{
var state = entry.State.WithWorkspaceProject(workspaceProject);
// WorkspaceProject updates can no-op. This can be the case if a build is triggered, but we've
// already seen the update.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[workspaceProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[workspaceProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
}
}
@ -549,17 +552,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
// OK there's another WorkspaceProject, use that.
state = entry.State.WithWorkspaceProject(otherWorkspaceProject);
_projects[otherWorkspaceProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(otherWorkspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[otherWorkspaceProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
else
{
state = entry.State.WithWorkspaceProject(null);
_projects[workspaceProject.FilePath] = new Entry(state);
// Notify listeners of a change because we've removed computed state.
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
state = entry.State.WithWorkspaceProject(null);
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[workspaceProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
}
}
@ -601,7 +608,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
throw new ArgumentNullException(nameof(exception));
}
_errorReporter.ReportError(exception, workspaceProject);
}
@ -644,13 +651,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private class Entry
{
public ProjectSnapshot Snapshot;
public ProjectSnapshot SnapshotUnsafe;
public readonly ProjectState State;
public Entry(ProjectState state)
{
State = state;
}
public ProjectSnapshot GetSnapshot()
{
return SnapshotUnsafe ?? (SnapshotUnsafe = new DefaultProjectSnapshot(State));
}
}
}
}

View File

@ -1,172 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Internal;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
internal class DocumentGeneratedOutputTracker
{
private readonly object _lock;
private DocumentGeneratedOutputTracker _older;
private Task<RazorCodeDocument> _task;
private IReadOnlyList<TagHelperDescriptor> _tagHelpers;
private IReadOnlyList<ImportItem> _imports;
public DocumentGeneratedOutputTracker(DocumentGeneratedOutputTracker older)
{
_older = older;
_lock = new object();
}
public bool IsResultAvailable => _task?.IsCompleted == true;
public DocumentGeneratedOutputTracker Older => _older;
public Task<RazorCodeDocument> GetGeneratedOutputInitializationTask(ProjectSnapshot project, DocumentSnapshot document)
{
if (project == null)
{
throw new ArgumentNullException(nameof(project));
}
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (_task == null)
{
lock (_lock)
{
if (_task == null)
{
_task = GetGeneratedOutputInitializationTaskCore(project, document);
}
}
}
return _task;
}
public DocumentGeneratedOutputTracker Fork()
{
return new DocumentGeneratedOutputTracker(this);
}
private async Task<RazorCodeDocument> GetGeneratedOutputInitializationTaskCore(ProjectSnapshot project, DocumentSnapshot document)
{
var tagHelpers = await project.GetTagHelpersAsync().ConfigureAwait(false);
var imports = await GetImportsAsync(project, document);
if (_older != null && _older.IsResultAvailable)
{
var tagHelperDifference = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
tagHelperDifference.UnionWith(_older._tagHelpers);
tagHelperDifference.SymmetricExceptWith(tagHelpers);
var importDifference = new HashSet<ImportItem>();
importDifference.UnionWith(_older._imports);
importDifference.SymmetricExceptWith(imports);
if (tagHelperDifference.Count == 0 && importDifference.Count == 0)
{
// We can use the cached result.
var result = _older._task.Result;
// Drop reference so it can be GC'ed
_older = null;
// Cache the tag helpers and imports so the next version can use them
_tagHelpers = tagHelpers;
_imports = imports;
return result;
}
}
// Drop reference so it can be GC'ed
_older = null;
// Cache the tag helpers and imports so the next version can use them
_tagHelpers = tagHelpers;
_imports = imports;
var importSources = new List<RazorSourceDocument>();
foreach (var item in imports)
{
var sourceDocument = await GetRazorSourceDocumentAsync(item.Import);
importSources.Add(sourceDocument);
}
var documentSource = await GetRazorSourceDocumentAsync(document);
var projectEngine = project.GetProjectEngine();
return projectEngine.ProcessDesignTime(documentSource, importSources, tagHelpers);
}
private async Task<RazorSourceDocument> GetRazorSourceDocumentAsync(DocumentSnapshot document)
{
var sourceText = await document.GetTextAsync();
return sourceText.GetRazorSourceDocument(document.FilePath);
}
private async Task<IReadOnlyList<ImportItem>> GetImportsAsync(ProjectSnapshot project, DocumentSnapshot document)
{
var imports = new List<ImportItem>();
foreach (var snapshot in document.GetImports())
{
var versionStamp = await snapshot.GetTextVersionAsync();
imports.Add(new ImportItem(snapshot.FilePath, versionStamp, snapshot));
}
return imports;
}
private struct ImportItem : IEquatable<ImportItem>
{
public ImportItem(string filePath, VersionStamp versionStamp, DocumentSnapshot import)
{
FilePath = filePath;
VersionStamp = versionStamp;
Import = import;
}
public string FilePath { get; }
public VersionStamp VersionStamp { get; }
public DocumentSnapshot Import { get; }
public bool Equals(ImportItem other)
{
return
FilePathComparer.Instance.Equals(FilePath, other.FilePath) &&
VersionStamp == other.VersionStamp;
}
public override bool Equals(object obj)
{
return obj is ImportItem item ? Equals(item) : false;
}
public override int GetHashCode()
{
var hash = new HashCodeCombiner();
hash.Add(FilePath, FilePathComparer.Instance);
hash.Add(VersionStamp);
return hash;
}
}
}
}

View File

@ -1,165 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
internal class DocumentImportsTracker
{
private readonly object _lock;
private IReadOnlyList<DocumentSnapshot> _imports;
public DocumentImportsTracker()
{
_lock = new object();
}
public IReadOnlyList<DocumentSnapshot> GetImports(ProjectSnapshot project, DocumentSnapshot document)
{
if (project == null)
{
throw new ArgumentNullException(nameof(project));
}
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (_imports == null)
{
lock (_lock)
{
if (_imports == null)
{
_imports = GetImportsCore(project, document);
}
}
}
return _imports;
}
private IReadOnlyList<DocumentSnapshot> GetImportsCore(ProjectSnapshot project, DocumentSnapshot document)
{
var projectEngine = project.GetProjectEngine();
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
var projectItem = projectEngine.FileSystem.GetItem(document.FilePath);
var importItems = importFeature?.GetImports(projectItem).Where(i => i.Exists);
if (importItems == null)
{
return Array.Empty<DocumentSnapshot>();
}
var imports = new List<DocumentSnapshot>();
foreach (var item in importItems)
{
if (item.PhysicalPath == null)
{
// This is a default import.
var defaultImport = new DefaultImportDocumentSnapshot(project, item);
imports.Add(defaultImport);
}
else
{
var import = project.GetDocument(item.PhysicalPath);
if (import == null)
{
// We are not tracking this document in this project. So do nothing.
continue;
}
imports.Add(import);
}
}
return imports;
}
private class DefaultImportDocumentSnapshot : DocumentSnapshot
{
private ProjectSnapshot _project;
private RazorProjectItem _importItem;
private SourceText _sourceText;
private VersionStamp _version;
private DocumentGeneratedOutputTracker _generatedOutput;
public DefaultImportDocumentSnapshot(ProjectSnapshot project, RazorProjectItem item)
{
_project = project;
_importItem = item;
_version = VersionStamp.Default;
_generatedOutput = new DocumentGeneratedOutputTracker(null);
}
public override string FilePath => null;
public override string TargetPath => null;
public override Task<RazorCodeDocument> GetGeneratedOutputAsync()
{
return _generatedOutput.GetGeneratedOutputInitializationTask(_project, this);
}
public override IReadOnlyList<DocumentSnapshot> GetImports()
{
return Array.Empty<DocumentSnapshot>();
}
public async override Task<SourceText> GetTextAsync()
{
using (var stream = _importItem.Read())
using (var reader = new StreamReader(stream))
{
var content = await reader.ReadToEndAsync();
_sourceText = SourceText.From(content);
}
return _sourceText;
}
public override Task<VersionStamp> GetTextVersionAsync()
{
return Task.FromResult(_version);
}
public override bool TryGetText(out SourceText result)
{
if (_sourceText != null)
{
result = _sourceText;
return true;
}
result = null;
return false;
}
public override bool TryGetTextVersion(out VersionStamp result)
{
result = _version;
return true;
}
public override bool TryGetGeneratedOutput(out RazorCodeDocument result)
{
if (_generatedOutput.IsResultAvailable)
{
result = GetGeneratedOutputAsync().Result;
return true;
}
result = null;
return false;
}
}
}
}

View File

@ -14,6 +14,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
public abstract string TargetPath { get; }
public abstract ProjectSnapshot Project { get; }
public abstract bool SupportsOutput { get; }
public abstract IReadOnlyList<DocumentSnapshot> GetImports();
public abstract Task<SourceText> GetTextAsync();

View File

@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;
@ -18,14 +21,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private readonly object _lock;
private ComputedStateTracker _computedState;
private Func<Task<TextAndVersion>> _loader;
private Task<TextAndVersion> _loaderTask;
private SourceText _sourceText;
private VersionStamp? _version;
private DocumentGeneratedOutputTracker _generatedOutput;
private DocumentImportsTracker _imports;
public static DocumentState Create(
HostWorkspaceServices services,
HostDocument hostDocument,
@ -65,42 +67,37 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
public HostWorkspaceServices Services { get; }
public DocumentGeneratedOutputTracker GeneratedOutput
public GeneratedCodeContainer GeneratedCodeContainer => HostDocument.GeneratedCodeContainer;
public bool IsGeneratedOutputResultAvailable => ComputedState.IsResultAvailable == true;
private ComputedStateTracker ComputedState
{
get
{
if (_generatedOutput == null)
if (_computedState == null)
{
lock (_lock)
{
if (_generatedOutput == null)
if (_computedState == null)
{
_generatedOutput = new DocumentGeneratedOutputTracker(null);
_computedState = new ComputedStateTracker(this);
}
}
}
return _generatedOutput;
return _computedState;
}
}
public DocumentImportsTracker Imports
public Task<(RazorCodeDocument output, VersionStamp inputVersion, VersionStamp outputVersion)> GetGeneratedOutputAndVersionAsync(DefaultProjectSnapshot project, DefaultDocumentSnapshot document)
{
get
{
if (_imports == null)
{
lock (_lock)
{
if (_imports == null)
{
_imports = new DocumentImportsTracker();
}
}
}
return ComputedState.GetGeneratedOutputAndVersionAsync(project, document);
}
return _imports;
}
public IReadOnlyList<DocumentSnapshot> GetImports(DefaultProjectSnapshot project)
{
return GetImportsCore(project);
}
public async Task<SourceText> GetTextAsync()
@ -178,6 +175,23 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
state._version = _version;
state._loaderTask = _loaderTask;
// Do not cache computed state
return state;
}
public virtual DocumentState WithImportsChange()
{
var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);
// The source could not have possibly changed.
state._sourceText = _sourceText;
state._version = _version;
state._loaderTask = _loaderTask;
// Optimisically cache the computed state
state._computedState = new ComputedStateTracker(state, _computedState);
return state;
}
@ -190,8 +204,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
state._version = _version;
state._loaderTask = _loaderTask;
// Opportunistically cache the generated code
state._generatedOutput = _generatedOutput?.Fork();
// Optimisically cache the computed state
state._computedState = new ComputedStateTracker(state, _computedState);
return state;
}
@ -203,6 +217,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
throw new ArgumentNullException(nameof(sourceText));
}
// Do not cache the computed state
return new DocumentState(Services, HostDocument, sourceText, version, null);
}
@ -213,7 +229,239 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
throw new ArgumentNullException(nameof(loader));
}
// Do not cache the computed state
return new DocumentState(Services, HostDocument, null, null, loader);
}
private IReadOnlyList<DocumentSnapshot> GetImportsCore(DefaultProjectSnapshot project)
{
var projectEngine = project.GetProjectEngine();
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
var projectItem = projectEngine.FileSystem.GetItem(HostDocument.FilePath);
var importItems = importFeature?.GetImports(projectItem);
if (importItems == null)
{
return Array.Empty<DocumentSnapshot>();
}
var imports = new List<DocumentSnapshot>();
foreach (var item in importItems)
{
if (item.PhysicalPath == null)
{
// This is a default import.
var defaultImport = new DefaultImportDocumentSnapshot(project, item);
imports.Add(defaultImport);
}
else
{
var import = project.GetDocument(item.PhysicalPath);
if (import == null)
{
// We are not tracking this document in this project. So do nothing.
continue;
}
imports.Add(import);
}
}
return imports;
}
// See design notes on ProjectState.ComputedStateTracker.
private class ComputedStateTracker
{
private readonly object _lock;
private ComputedStateTracker _older;
public Task<(RazorCodeDocument, VersionStamp, VersionStamp)> TaskUnsafe;
public ComputedStateTracker(DocumentState state, ComputedStateTracker older = null)
{
_lock = state._lock;
_older = older;
}
public bool IsResultAvailable => TaskUnsafe?.IsCompleted == true;
public Task<(RazorCodeDocument, VersionStamp, VersionStamp)> GetGeneratedOutputAndVersionAsync(DefaultProjectSnapshot project, DocumentSnapshot document)
{
if (project == null)
{
throw new ArgumentNullException(nameof(project));
}
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (TaskUnsafe == null)
{
lock (_lock)
{
if (TaskUnsafe == null)
{
TaskUnsafe = GetGeneratedOutputAndVersionCoreAsync(project, document);
}
}
}
return TaskUnsafe;
}
private async Task<(RazorCodeDocument, VersionStamp, VersionStamp)> GetGeneratedOutputAndVersionCoreAsync(DefaultProjectSnapshot project, DocumentSnapshot document)
{
// We only need to produce the generated code if any of our inputs is newer than the
// previously cached output.
//
// First find the versions that are the inputs:
// - The project + computed state
// - The imports
// - This document
//
// All of these things are cached, so no work is wasted if we do need to generate the code.
var computedStateVersion = await project.State.GetComputedStateVersionAsync(project).ConfigureAwait(false);
var documentCollectionVersion = project.State.DocumentCollectionVersion;
var imports = await GetImportsAsync(project, document).ConfigureAwait(false);
var documentVersion = await document.GetTextVersionAsync().ConfigureAwait(false);
// OK now that have the previous output and all of the versions, we can see if anything
// has changed that would require regenerating the code.
var inputVersion = documentVersion;
if (inputVersion.GetNewerVersion(computedStateVersion) == computedStateVersion)
{
inputVersion = computedStateVersion;
}
if (inputVersion.GetNewerVersion(documentCollectionVersion) == documentCollectionVersion)
{
inputVersion = documentCollectionVersion;
}
for (var i = 0; i < imports.Count; i++)
{
var importVersion = imports[i].Version;
if (inputVersion.GetNewerVersion(importVersion) == importVersion)
{
inputVersion = importVersion;
}
}
RazorCodeDocument olderOutput = null;
var olderInputVersion = default(VersionStamp);
var olderOutputVersion = default(VersionStamp);
if (_older?.TaskUnsafe != null)
{
(olderOutput, olderInputVersion, olderOutputVersion) = await _older.TaskUnsafe.ConfigureAwait(false);
if (inputVersion.GetNewerVersion(olderInputVersion) == olderInputVersion)
{
// Nothing has changed, we can use the cached result.
lock (_lock)
{
TaskUnsafe = _older.TaskUnsafe;
_older = null;
return (olderOutput, olderInputVersion, olderOutputVersion);
}
}
}
// OK we have to generate the code.
var tagHelpers = await project.GetTagHelpersAsync().ConfigureAwait(false);
var importSources = new List<RazorSourceDocument>();
foreach (var item in imports)
{
var sourceDocument = await GetRazorSourceDocumentAsync(item.Document).ConfigureAwait(false);
importSources.Add(sourceDocument);
}
var documentSource = await GetRazorSourceDocumentAsync(document).ConfigureAwait(false);
var projectEngine = project.GetProjectEngine();
var codeDocument = projectEngine.ProcessDesignTime(documentSource, importSources, tagHelpers);
var csharpDocument = codeDocument.GetCSharpDocument();
// OK now we've generated the code. Let's check if the output is actually different. This is
// a valuable optimization for our use cases because lots of changes you could make require
// us to run code generation, but don't change the result.
//
// Note that we're talking about the effect on the generated C# code here (not the other artifacts).
// This is the reason why we have two versions associated with the output.
//
// The INPUT version is related the .cshtml files and tag helpers
// The OUTPUT version is related to the generated C#.
//
// Examples:
//
// A change to a tag helper not used by this document - updates the INPUT version, but not
// the OUTPUT version.
//
// A change in the HTML - updates the INPUT version, but not the OUTPUT version.
//
//
// Razor IDE features should always retrieve the output and party on it regardless. Depending
// on the use cases we may or may not need to synchronize the output.
var outputVersion = inputVersion;
if (olderOutput != null)
{
if (string.Equals(
olderOutput.GetCSharpDocument().GeneratedCode,
csharpDocument.GeneratedCode,
StringComparison.Ordinal))
{
outputVersion = olderOutputVersion;
}
}
if (document is DefaultDocumentSnapshot defaultDocument)
{
defaultDocument.State.HostDocument.GeneratedCodeContainer.SetOutput(
defaultDocument,
csharpDocument,
inputVersion,
outputVersion);
}
return (codeDocument, inputVersion, outputVersion);
}
private async Task<RazorSourceDocument> GetRazorSourceDocumentAsync(DocumentSnapshot document)
{
var sourceText = await document.GetTextAsync();
return sourceText.GetRazorSourceDocument(document.FilePath);
}
private async Task<IReadOnlyList<ImportItem>> GetImportsAsync(ProjectSnapshot project, DocumentSnapshot document)
{
var imports = new List<ImportItem>();
foreach (var snapshot in document.GetImports())
{
var versionStamp = await snapshot.GetTextVersionAsync();
imports.Add(new ImportItem(snapshot.FilePath, versionStamp, snapshot));
}
return imports;
}
private readonly struct ImportItem
{
public ImportItem(string filePath, VersionStamp version, DocumentSnapshot document)
{
FilePath = filePath;
Version = version;
Document = document;
}
public string FilePath { get; }
public VersionStamp Version { get; }
public DocumentSnapshot Document { get; }
}
}
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
internal class EmptyTextLoader : TextLoader
{
private readonly string _filePath;
private readonly VersionStamp _version;
public EmptyTextLoader(string filePath)
{
_filePath = filePath;
_version = VersionStamp.Create(); // Version will never change so this can be reused.
}
public override Task<TextAndVersion> LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
{
return Task.FromResult(TextAndVersion.Create(SourceText.From(""), _version, _filePath));
}
}
}

View File

@ -56,6 +56,26 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return null;
}
public override bool IsImportDocument(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return false;
}
public override IEnumerable<DocumentSnapshot> GetRelatedDocuments(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return Array.Empty<DocumentSnapshot>();
}
public override RazorProjectEngine GetProjectEngine()
{
return _projectEngine.Value;

View File

@ -0,0 +1,184 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
internal class GeneratedCodeContainer
{
public event EventHandler<TextChangeEventArgs> GeneratedCodeChanged;
private SourceText _source;
private VersionStamp? _inputVersion;
private VersionStamp? _outputVersion;
private RazorCSharpDocument _output;
private DocumentSnapshot _latestDocument;
private readonly object _setOutputLock = new object();
private readonly TextContainer _textContainer;
public GeneratedCodeContainer()
{
_textContainer = new TextContainer(_setOutputLock);
_textContainer.TextChanged += TextContainer_TextChanged;
}
public SourceText Source
{
get
{
lock (_setOutputLock)
{
return _source;
}
}
}
public VersionStamp InputVersion
{
get
{
lock (_setOutputLock)
{
return _inputVersion.Value;
}
}
}
public VersionStamp OutputVersion
{
get
{
lock (_setOutputLock)
{
return _outputVersion.Value;
}
}
}
public RazorCSharpDocument Output
{
get
{
lock (_setOutputLock)
{
return _output;
}
}
}
public DocumentSnapshot LatestDocument
{
get
{
lock (_setOutputLock)
{
return _latestDocument;
}
}
}
public SourceTextContainer SourceTextContainer
{
get
{
lock (_setOutputLock)
{
return _textContainer;
}
}
}
public void SetOutput(
DefaultDocumentSnapshot document,
RazorCSharpDocument output,
VersionStamp inputVersion,
VersionStamp outputVersion)
{
lock (_setOutputLock)
{
if (_inputVersion.HasValue &&
_inputVersion != inputVersion &&
_inputVersion == _inputVersion.Value.GetNewerVersion(inputVersion))
{
// Latest document is newer than the provided document.
return;
}
if (!document.TryGetText(out var source))
{
Debug.Fail("The text should have already been evaluated.");
return;
}
_source = source;
_inputVersion = inputVersion;
_outputVersion = outputVersion;
_output = output;
_latestDocument = document;
_textContainer.SetText(SourceText.From(_output.GeneratedCode));
}
}
private void TextContainer_TextChanged(object sender, TextChangeEventArgs args)
{
GeneratedCodeChanged?.Invoke(this, args);
}
private class TextContainer : SourceTextContainer
{
public override event EventHandler<TextChangeEventArgs> TextChanged;
private readonly object _outerLock;
private SourceText _currentText;
public TextContainer(object outerLock)
: this(SourceText.From(string.Empty))
{
_outerLock = outerLock;
}
public TextContainer(SourceText sourceText)
{
if (sourceText == null)
{
throw new ArgumentNullException(nameof(sourceText));
}
_currentText = sourceText;
}
public override SourceText CurrentText
{
get
{
lock (_outerLock)
{
return _currentText;
}
}
}
public void SetText(SourceText sourceText)
{
if (sourceText == null)
{
throw new ArgumentNullException(nameof(sourceText));
}
lock (_outerLock)
{
var e = new TextChangeEventArgs(_currentText, sourceText);
_currentText = sourceText;
TextChanged?.Invoke(this, e);
}
}
}
}
}

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