Merge branch 'master' into merge/release/2.2-to-master
This commit is contained in:
commit
70aad7cb70
|
|
@ -12,7 +12,7 @@ os:
|
|||
osx_image: xcode8.2
|
||||
branches:
|
||||
only:
|
||||
- dev
|
||||
- master
|
||||
- /^release\/.*$/
|
||||
- /^(.*\/)?ci-.*$/
|
||||
before_install:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
207
LICENSE.txt
207
LICENSE.txt
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
version:2.2.0-preview2-20181019.2
|
||||
commithash:72ba316025aca165c09cb97b407965d944bad93f
|
||||
version:3.0.0-alpha1-20181017.7
|
||||
commithash:f4082c290c6c7610e7cb9d787072004453fc175f
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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, '/');
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] != '\'')
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<SyntaxToken>" />
|
||||
</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<SyntaxToken>" />
|
||||
</Node>
|
||||
<Node Name="CSharpCodeLiteralSyntax" Base="CSharpSyntaxNode">
|
||||
<Kind Name="CSharpCodeLiteral" />
|
||||
<Field Name="CSharpTokens" Type="SyntaxList<SyntaxToken>" />
|
||||
</Node>
|
||||
<Node Name="CSharpCodeBlockSyntax" Base="CSharpSyntaxNode">
|
||||
<Kind Name="CSharpCodeBlock" />
|
||||
<Field Name="Children" Type="SyntaxList<RazorSyntaxNode>" />
|
||||
</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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue