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
|
osx_image: xcode8.2
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- dev
|
- master
|
||||||
- /^release\/.*$/
|
- /^release\/.*$/
|
||||||
- /^(.*\/)?ci-.*$/
|
- /^(.*\/)?ci-.*$/
|
||||||
before_install:
|
before_install:
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ resources:
|
||||||
- repository: buildtools
|
- repository: buildtools
|
||||||
type: git
|
type: git
|
||||||
name: aspnet-BuildTools
|
name: aspnet-BuildTools
|
||||||
ref: refs/heads/release/2.2
|
ref: refs/heads/master
|
||||||
|
|
||||||
phases:
|
phases:
|
||||||
- template: .vsts-pipelines/templates/project-ci.yml@buildtools
|
- template: .vsts-pipelines/templates/project-ci.yml@buildtools
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Contributing
|
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
|
1. Definitions.
|
||||||
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
|
"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
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
the copyright owner that is granting the License.
|
||||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations under 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
|
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)
|
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|
@ -149,6 +141,14 @@ namespace Microsoft.AspNetCore.Razor.Performance
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
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:Configuration=$(Configuration);
|
||||||
/p:FeatureBranchVersionSuffix=$(FeatureBranchVersionSuffix);
|
/p:FeatureBranchVersionSuffix=$(FeatureBranchVersionSuffix);
|
||||||
/p:BuildNumber=$(BuildNumber);
|
/p:BuildNumber=$(BuildNumber);
|
||||||
/p:LangVersion=7.1" />
|
/p:LangVersion=7.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<MakeDir Directories="$(LogOutputDir)" />
|
<MakeDir Directories="$(LogOutputDir)" />
|
||||||
|
|
|
||||||
|
|
@ -4,49 +4,52 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Package Versions">
|
<PropertyGroup Label="Package Versions">
|
||||||
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
||||||
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview2-20181019.2</InternalAspNetCoreSdkPackageVersion>
|
<InternalAspNetCoreSdkPackageVersion>3.0.0-alpha1-20181017.7</InternalAspNetCoreSdkPackageVersion>
|
||||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>3.0.0-alpha1-10549</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreTestingPackageVersion>
|
<MicrosoftAspNetCoreTestingPackageVersion>3.0.0-alpha1-10549</MicrosoftAspNetCoreTestingPackageVersion>
|
||||||
<MicrosoftBuildFrameworkPackageVersion>15.6.82</MicrosoftBuildFrameworkPackageVersion>
|
<MicrosoftBuildFrameworkPackageVersion>15.8.166</MicrosoftBuildFrameworkPackageVersion>
|
||||||
<MicrosoftBuildPackageVersion>15.6.82</MicrosoftBuildPackageVersion>
|
<MicrosoftBuildPackageVersion>15.8.166</MicrosoftBuildPackageVersion>
|
||||||
<MicrosoftBuildUtilitiesCorePackageVersion>15.6.82</MicrosoftBuildUtilitiesCorePackageVersion>
|
<MicrosoftBuildUtilitiesCorePackageVersion>15.8.166</MicrosoftBuildUtilitiesCorePackageVersion>
|
||||||
<MicrosoftCodeAnalysisCommonPackageVersion>2.8.0</MicrosoftCodeAnalysisCommonPackageVersion>
|
<MicrosoftCodeAnalysisCommonPackageVersion>2.8.0</MicrosoftCodeAnalysisCommonPackageVersion>
|
||||||
<MicrosoftCodeAnalysisCSharpPackageVersion>2.8.0</MicrosoftCodeAnalysisCSharpPackageVersion>
|
<MicrosoftCodeAnalysisCSharpPackageVersion>2.8.0</MicrosoftCodeAnalysisCSharpPackageVersion>
|
||||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0</MicrosoftExtensionsDependencyModelPackageVersion>
|
<MicrosoftExtensionsDependencyModelPackageVersion>3.0.0-preview1-26907-05</MicrosoftExtensionsDependencyModelPackageVersion>
|
||||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>
|
<MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsNonCapturingTimerSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsWebEncodersPackageVersion>
|
<MicrosoftExtensionsWebEncodersPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
|
||||||
<MicrosoftNETCoreApp21PackageVersion>2.1.3</MicrosoftNETCoreApp21PackageVersion>
|
<MicrosoftNETCoreApp21PackageVersion>2.1.3</MicrosoftNETCoreApp21PackageVersion>
|
||||||
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview3-27014-02</MicrosoftNETCoreApp22PackageVersion>
|
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview3-27014-02</MicrosoftNETCoreApp22PackageVersion>
|
||||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||||
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.0.26606</MicrosoftVisualStudioComponentModelHostPackageVersion>
|
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.8.525</MicrosoftVisualStudioComponentModelHostPackageVersion>
|
||||||
<MicrosoftVisualStudioEditorPackageVersion>15.6.161-preview</MicrosoftVisualStudioEditorPackageVersion>
|
<MicrosoftVisualStudioImageCatalogPackageVersion>15.8.28010</MicrosoftVisualStudioImageCatalogPackageVersion>
|
||||||
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>15.6.161-preview</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
|
<MicrosoftVisualStudioEditorPackageVersion>16.0.142-g25b7188c54</MicrosoftVisualStudioEditorPackageVersion>
|
||||||
<MicrosoftVisualStudioOLEInteropPackageVersion>7.10.6070</MicrosoftVisualStudioOLEInteropPackageVersion>
|
<MicrosoftVisualStudioLanguagePackageVersion>16.0.142-g25b7188c54</MicrosoftVisualStudioLanguagePackageVersion>
|
||||||
<MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>15.3.224</MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>
|
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>16.0.142-g25b7188c54</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
|
||||||
|
<MicrosoftVisualStudioOLEInteropPackageVersion>7.10.6071</MicrosoftVisualStudioOLEInteropPackageVersion>
|
||||||
|
<MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>15.8.243</MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>
|
||||||
<MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>2.0.6142705</MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>
|
<MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>2.0.6142705</MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>
|
||||||
<MicrosoftVisualStudioProjectSystemSDKPackageVersion>15.3.224</MicrosoftVisualStudioProjectSystemSDKPackageVersion>
|
<MicrosoftVisualStudioProjectSystemSDKPackageVersion>15.8.243</MicrosoftVisualStudioProjectSystemSDKPackageVersion>
|
||||||
<MicrosoftVisualStudioShell150PackageVersion>15.0.26606</MicrosoftVisualStudioShell150PackageVersion>
|
<MicrosoftVisualStudioShell150PackageVersion>15.8.28010</MicrosoftVisualStudioShell150PackageVersion>
|
||||||
<MicrosoftVisualStudioShellInterop100PackageVersion>10.0.30319</MicrosoftVisualStudioShellInterop100PackageVersion>
|
<MicrosoftVisualStudioShellInterop100PackageVersion>10.0.30320</MicrosoftVisualStudioShellInterop100PackageVersion>
|
||||||
<MicrosoftVisualStudioShellInterop110PackageVersion>11.0.61030</MicrosoftVisualStudioShellInterop110PackageVersion>
|
<MicrosoftVisualStudioShellInterop110PackageVersion>11.0.61031</MicrosoftVisualStudioShellInterop110PackageVersion>
|
||||||
<MicrosoftVisualStudioShellInterop120PackageVersion>12.0.30110</MicrosoftVisualStudioShellInterop120PackageVersion>
|
<MicrosoftVisualStudioShellInterop120PackageVersion>12.0.30111</MicrosoftVisualStudioShellInterop120PackageVersion>
|
||||||
<MicrosoftVisualStudioShellInterop80PackageVersion>8.0.50727</MicrosoftVisualStudioShellInterop80PackageVersion>
|
<MicrosoftVisualStudioShellInterop80PackageVersion>8.0.50728</MicrosoftVisualStudioShellInterop80PackageVersion>
|
||||||
<MicrosoftVisualStudioShellInterop90PackageVersion>9.0.30729</MicrosoftVisualStudioShellInterop90PackageVersion>
|
<MicrosoftVisualStudioShellInterop90PackageVersion>9.0.30730</MicrosoftVisualStudioShellInterop90PackageVersion>
|
||||||
<MicrosoftVisualStudioShellInteropPackageVersion>7.10.6071</MicrosoftVisualStudioShellInteropPackageVersion>
|
<MicrosoftVisualStudioShellInteropPackageVersion>7.10.6072</MicrosoftVisualStudioShellInteropPackageVersion>
|
||||||
<MicrosoftVisualStudioTextUIPackageVersion>15.6.161-preview</MicrosoftVisualStudioTextUIPackageVersion>
|
<MicrosoftVisualStudioTextUIPackageVersion>16.0.142-g25b7188c54</MicrosoftVisualStudioTextUIPackageVersion>
|
||||||
|
<MicrosoftVisualStudioThreadingPackageVersion>15.8.168</MicrosoftVisualStudioThreadingPackageVersion>
|
||||||
<MonoAddinsPackageVersion>1.3.8</MonoAddinsPackageVersion>
|
<MonoAddinsPackageVersion>1.3.8</MonoAddinsPackageVersion>
|
||||||
<MonoDevelopSdkPackageVersion>1.0.1</MonoDevelopSdkPackageVersion>
|
<MonoDevelopSdkPackageVersion>1.0.1</MonoDevelopSdkPackageVersion>
|
||||||
<MoqPackageVersion>4.10.0</MoqPackageVersion>
|
<MoqPackageVersion>4.10.0</MoqPackageVersion>
|
||||||
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
||||||
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
|
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
|
||||||
<StreamJsonRpcPackageVersion>1.1.92</StreamJsonRpcPackageVersion>
|
<StreamJsonRpcPackageVersion>1.3.23</StreamJsonRpcPackageVersion>
|
||||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.6.0-preview1-26907-04</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||||
<SystemRuntimeInteropServicesRuntimeInformationPackageVersion>4.3.0</SystemRuntimeInteropServicesRuntimeInformationPackageVersion>
|
<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>
|
<VisualStudio_NewtonsoftJsonPackageVersion>9.0.1</VisualStudio_NewtonsoftJsonPackageVersion>
|
||||||
<VSIX_MicrosoftCodeAnalysisCommonPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCommonPackageVersion>
|
<VSIX_MicrosoftCodeAnalysisCommonPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCommonPackageVersion>
|
||||||
<VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
|
<VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>2.9.0-beta4-62911-02</VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- These properties are use by the automation that updates dependencies.props -->
|
<!-- These properties are use by the automation that updates dependencies.props -->
|
||||||
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
|
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
|
||||||
<LineupPackageVersion>2.2.0-*</LineupPackageVersion>
|
|
||||||
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json</LineupPackageRestoreSource>
|
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json</LineupPackageRestoreSource>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
version:2.2.0-preview2-20181019.2
|
version:3.0.0-alpha1-20181017.7
|
||||||
commithash:72ba316025aca165c09cb97b407965d944bad93f
|
commithash:f4082c290c6c7610e7cb9d787072004453fc175f
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/release/2.2/tools/korebuild.schema.json",
|
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json",
|
||||||
"channel": "release/2.2",
|
"channel": "master",
|
||||||
"toolsets": {
|
"toolsets": {
|
||||||
"visualstudio": {
|
"visualstudio": {
|
||||||
"required": false,
|
"required": false,
|
||||||
|
|
|
||||||
|
|
@ -595,9 +595,9 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
if (span.Tokens.Count == 1)
|
if (span.Tokens.Count == 1)
|
||||||
{
|
{
|
||||||
var token = span.Tokens[0] as HtmlToken;
|
var token = span.Tokens[0];
|
||||||
if (token != null &&
|
if (token != null &&
|
||||||
token.Type == HtmlTokenType.Unknown &&
|
token.Kind == SyntaxKind.Unknown &&
|
||||||
token.Content.Length == 0)
|
token.Content.Length == 0)
|
||||||
{
|
{
|
||||||
// We don't want to create IR nodes for marker tokens.
|
// 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 absolutePath = NormalizeAndEnsureValidPath(path);
|
||||||
|
|
||||||
var file = new FileInfo(absolutePath);
|
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 relativePhysicalPath = file.FullName.Substring(absoluteBasePath.Length + 1); // Include leading separator
|
||||||
var filePath = "/" + relativePhysicalPath.Replace(Path.DirectorySeparatorChar, '/');
|
var filePath = "/" + relativePhysicalPath.Replace(Path.DirectorySeparatorChar, '/');
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,19 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
internal class DirectiveTokenEditHandler : SpanEditHandler
|
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)
|
protected override PartialParseResultInternal CanAcceptChange(Span target, SourceChange change)
|
||||||
{
|
{
|
||||||
if (AcceptedCharacters == AcceptedCharactersInternal.NonWhiteSpace)
|
if (AcceptedCharacters == AcceptedCharactersInternal.NonWhitespace)
|
||||||
{
|
{
|
||||||
var originalText = change.GetOriginalText(target);
|
var originalText = change.GetOriginalText(target);
|
||||||
var editedContent = change.GetEditedContent(target);
|
var editedContent = change.GetEditedContent(target);
|
||||||
|
|
|
||||||
|
|
@ -65,15 +65,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
// {node.Content} __typeHelper = default({node.Content});
|
// {node.Content} __typeHelper = default({node.Content});
|
||||||
|
using (context.CodeWriter.BuildLinePragma(node.Source))
|
||||||
context.AddSourceMappingFor(node);
|
{
|
||||||
context.CodeWriter
|
context.AddSourceMappingFor(node);
|
||||||
.Write(node.Content)
|
context.CodeWriter
|
||||||
.Write(" ")
|
.Write(node.Content)
|
||||||
.WriteStartAssignment(TypeHelper)
|
.Write(" ")
|
||||||
.Write("default(")
|
.WriteStartAssignment(TypeHelper)
|
||||||
.Write(node.Content)
|
.Write("default(")
|
||||||
.WriteLine(");");
|
.Write(node.Content)
|
||||||
|
.WriteLine(");");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DirectiveTokenKind.Member:
|
case DirectiveTokenKind.Member:
|
||||||
|
|
@ -86,16 +88,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
// global::System.Object {node.content} = null;
|
// global::System.Object {node.content} = null;
|
||||||
|
using (context.CodeWriter.BuildLinePragma(node.Source))
|
||||||
context.CodeWriter
|
{
|
||||||
|
context.CodeWriter
|
||||||
.Write("global::")
|
.Write("global::")
|
||||||
.Write(typeof(object).FullName)
|
.Write(typeof(object).FullName)
|
||||||
.Write(" ");
|
.Write(" ");
|
||||||
|
|
||||||
context.AddSourceMappingFor(node);
|
context.AddSourceMappingFor(node);
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
.Write(node.Content)
|
.Write(node.Content)
|
||||||
.WriteLine(" = null;");
|
.WriteLine(" = null;");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DirectiveTokenKind.Namespace:
|
case DirectiveTokenKind.Namespace:
|
||||||
|
|
@ -108,46 +112,50 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
// global::System.Object __typeHelper = nameof({node.Content});
|
// global::System.Object __typeHelper = nameof({node.Content});
|
||||||
|
using (context.CodeWriter.BuildLinePragma(node.Source))
|
||||||
context.CodeWriter
|
{
|
||||||
|
context.CodeWriter
|
||||||
.Write("global::")
|
.Write("global::")
|
||||||
.Write(typeof(object).FullName)
|
.Write(typeof(object).FullName)
|
||||||
.Write(" ")
|
.Write(" ")
|
||||||
.WriteStartAssignment(TypeHelper);
|
.WriteStartAssignment(TypeHelper);
|
||||||
|
|
||||||
context.CodeWriter.Write("nameof(");
|
context.CodeWriter.Write("nameof(");
|
||||||
|
|
||||||
context.AddSourceMappingFor(node);
|
context.AddSourceMappingFor(node);
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
.Write(node.Content)
|
.Write(node.Content)
|
||||||
.WriteLine(");");
|
.WriteLine(");");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DirectiveTokenKind.String:
|
case DirectiveTokenKind.String:
|
||||||
|
|
||||||
// global::System.Object __typeHelper = "{node.Content}";
|
// global::System.Object __typeHelper = "{node.Content}";
|
||||||
|
using (context.CodeWriter.BuildLinePragma(node.Source))
|
||||||
context.CodeWriter
|
{
|
||||||
|
context.CodeWriter
|
||||||
.Write("global::")
|
.Write("global::")
|
||||||
.Write(typeof(object).FullName)
|
.Write(typeof(object).FullName)
|
||||||
.Write(" ")
|
.Write(" ")
|
||||||
.WriteStartAssignment(TypeHelper);
|
.WriteStartAssignment(TypeHelper);
|
||||||
|
|
||||||
if (node.Content.StartsWith("\"", StringComparison.Ordinal))
|
if (node.Content.StartsWith("\"", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
context.AddSourceMappingFor(node);
|
context.AddSourceMappingFor(node);
|
||||||
context.CodeWriter.Write(node.Content);
|
context.CodeWriter.Write(node.Content);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.CodeWriter.Write("\"");
|
context.CodeWriter.Write("\"");
|
||||||
context.AddSourceMappingFor(node);
|
context.AddSourceMappingFor(node);
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
.Write(node.Content)
|
.Write(node.Content)
|
||||||
.Write("\"");
|
.Write("\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.CodeWriter.WriteLine(";");
|
context.CodeWriter.WriteLine(";");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
context.CodeWriter.CurrentIndent = originalIndent;
|
context.CodeWriter.CurrentIndent = originalIndent;
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
NewLine = 1,
|
NewLine = 1,
|
||||||
WhiteSpace = 2,
|
Whitespace = 2,
|
||||||
|
|
||||||
NonWhiteSpace = 4,
|
NonWhitespace = 4,
|
||||||
|
|
||||||
AllWhiteSpace = NewLine | WhiteSpace,
|
AllWhitespace = NewLine | Whitespace,
|
||||||
Any = AllWhiteSpace | NonWhiteSpace,
|
Any = AllWhitespace | NonWhitespace,
|
||||||
|
|
||||||
AnyExceptNewline = NonWhiteSpace | WhiteSpace
|
AnyExceptNewline = NonWhitespace | Whitespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
@ -11,18 +12,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
private static readonly int TypeHashCode = typeof(AutoCompleteEditHandler).GetHashCode();
|
private static readonly int TypeHashCode = typeof(AutoCompleteEditHandler).GetHashCode();
|
||||||
|
|
||||||
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer)
|
public AutoCompleteEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer)
|
||||||
: base(tokenizer)
|
: base(tokenizer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer, bool autoCompleteAtEndOfSpan)
|
public AutoCompleteEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer, bool autoCompleteAtEndOfSpan)
|
||||||
: this(tokenizer)
|
: this(tokenizer)
|
||||||
{
|
{
|
||||||
AutoCompleteAtEndOfSpan = autoCompleteAtEndOfSpan;
|
AutoCompleteAtEndOfSpan = autoCompleteAtEndOfSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AutoCompleteEditHandler(Func<string, IEnumerable<IToken>> tokenizer, AcceptedCharactersInternal accepted)
|
public AutoCompleteEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer, AcceptedCharactersInternal accepted)
|
||||||
: base(tokenizer, accepted)
|
: base(tokenizer, accepted)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,18 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
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 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);
|
IsSpacingToken(includeNewLines: true, includeComments: true);
|
||||||
|
|
||||||
internal static readonly DirectiveDescriptor AddTagHelperDirectiveDescriptor = DirectiveDescriptor.CreateDirective(
|
internal static readonly DirectiveDescriptor AddTagHelperDirectiveDescriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
|
@ -102,7 +103,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public bool IsNested { get; set; }
|
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)
|
protected void MapDirectives(Action handler, params string[] directives)
|
||||||
{
|
{
|
||||||
|
|
@ -161,16 +162,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
[Conditional("DEBUG")]
|
[Conditional("DEBUG")]
|
||||||
internal void Assert(CSharpKeyword expectedKeyword)
|
internal void Assert(CSharpKeyword expectedKeyword)
|
||||||
{
|
{
|
||||||
Debug.Assert(CurrentToken.Type == CSharpTokenType.Keyword &&
|
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
|
||||||
CurrentToken.Keyword.HasValue &&
|
Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword &&
|
||||||
CurrentToken.Keyword.Value == expectedKeyword);
|
result.HasValue &&
|
||||||
|
result.Value == expectedKeyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected internal bool At(CSharpKeyword keyword)
|
protected internal bool At(CSharpKeyword keyword)
|
||||||
{
|
{
|
||||||
return At(CSharpTokenType.Keyword) &&
|
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
|
||||||
CurrentToken.Keyword.HasValue &&
|
return At(SyntaxKind.Keyword) &&
|
||||||
CurrentToken.Keyword.Value == keyword;
|
result.HasValue &&
|
||||||
|
result.Value == keyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected internal bool AcceptIf(CSharpKeyword keyword)
|
protected internal bool AcceptIf(CSharpKeyword keyword)
|
||||||
|
|
@ -183,11 +186,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return false;
|
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 ||
|
return token => token.Kind == SyntaxKind.Whitespace ||
|
||||||
(includeNewLines && token.Type == CSharpTokenType.NewLine) ||
|
(includeNewLines && token.Kind == SyntaxKind.NewLine) ||
|
||||||
(includeComments && token.Type == CSharpTokenType.Comment);
|
(includeComments && token.Kind == SyntaxKind.CSharpComment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ParseBlock()
|
public override void ParseBlock()
|
||||||
|
|
@ -209,24 +212,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
|
|
||||||
var current = CurrentToken;
|
var current = CurrentToken;
|
||||||
if (At(CSharpTokenType.StringLiteral) &&
|
if (At(SyntaxKind.StringLiteral) &&
|
||||||
CurrentToken.Content.Length > 0 &&
|
CurrentToken.Content.Length > 0 &&
|
||||||
CurrentToken.Content[0] == SyntaxConstants.TransitionCharacter)
|
CurrentToken.Content[0] == SyntaxConstants.TransitionCharacter)
|
||||||
{
|
{
|
||||||
var split = Language.SplitToken(CurrentToken, 1, CSharpTokenType.Transition);
|
var split = Language.SplitToken(CurrentToken, 1, SyntaxKind.Transition);
|
||||||
current = split.Item1;
|
current = split.Item1;
|
||||||
|
|
||||||
// Back up to the end of the transition
|
// Back up to the end of the transition
|
||||||
Context.Source.Position -= split.Item2.Content.Length;
|
Context.Source.Position -= split.Item2.Content.Length;
|
||||||
NextToken();
|
NextToken();
|
||||||
}
|
}
|
||||||
else if (At(CSharpTokenType.Transition))
|
else if (At(SyntaxKind.Transition))
|
||||||
{
|
{
|
||||||
NextToken();
|
NextToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept "@" if we see it, but if we don't, that's OK. We assume we were started for a good reason
|
// 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)
|
if (Span.Tokens.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -251,9 +254,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
span.ChunkGenerator = new StatementChunkGenerator();
|
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);
|
Accept(current);
|
||||||
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
||||||
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
|
|
@ -273,14 +276,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
// What type of block is this?
|
// What type of block is this?
|
||||||
if (!EndOfFile)
|
if (!EndOfFile)
|
||||||
{
|
{
|
||||||
if (CurrentToken.Type == CSharpTokenType.LeftParenthesis)
|
if (CurrentToken.Kind == SyntaxKind.LeftParenthesis)
|
||||||
{
|
{
|
||||||
Context.Builder.CurrentBlock.Type = BlockKindInternal.Expression;
|
Context.Builder.CurrentBlock.Type = BlockKindInternal.Expression;
|
||||||
Context.Builder.CurrentBlock.ChunkGenerator = new ExpressionChunkGenerator();
|
Context.Builder.CurrentBlock.ChunkGenerator = new ExpressionChunkGenerator();
|
||||||
ExplicitExpression();
|
ExplicitExpression();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (CurrentToken.Type == CSharpTokenType.Identifier)
|
else if (CurrentToken.Kind == SyntaxKind.Identifier)
|
||||||
{
|
{
|
||||||
if (TryGetDirectiveHandler(CurrentToken.Content, out var handler))
|
if (TryGetDirectiveHandler(CurrentToken.Content, out var handler))
|
||||||
{
|
{
|
||||||
|
|
@ -306,7 +309,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (CurrentToken.Type == CSharpTokenType.Keyword)
|
else if (CurrentToken.Kind == SyntaxKind.Keyword)
|
||||||
{
|
{
|
||||||
if (TryGetDirectiveHandler(CurrentToken.Content, out var handler))
|
if (TryGetDirectiveHandler(CurrentToken.Content, out var handler))
|
||||||
{
|
{
|
||||||
|
|
@ -320,7 +323,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (CurrentToken.Type == CSharpTokenType.LeftBrace)
|
else if (CurrentToken.Kind == SyntaxKind.LeftBrace)
|
||||||
{
|
{
|
||||||
VerbatimBlock();
|
VerbatimBlock();
|
||||||
return;
|
return;
|
||||||
|
|
@ -337,9 +340,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
CurrentKeywords,
|
CurrentKeywords,
|
||||||
acceptTrailingDot: IsNested)
|
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(
|
Context.ErrorSink.OnError(
|
||||||
RazorDiagnosticFactory.CreateParsing_UnexpectedWhiteSpaceAtStartOfCodeBlock(
|
RazorDiagnosticFactory.CreateParsing_UnexpectedWhiteSpaceAtStartOfCodeBlock(
|
||||||
|
|
@ -369,7 +372,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private void VerbatimBlock()
|
private void VerbatimBlock()
|
||||||
{
|
{
|
||||||
Assert(CSharpTokenType.LeftBrace);
|
Assert(SyntaxKind.LeftBrace);
|
||||||
var block = new Block(Resources.BlockName_Code, CurrentStart);
|
var block = new Block(Resources.BlockName_Code, CurrentStart);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
|
|
||||||
|
|
@ -385,13 +388,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
Span.ChunkGenerator = new StatementChunkGenerator();
|
Span.ChunkGenerator = new StatementChunkGenerator();
|
||||||
AddMarkerTokenIfNecessary();
|
AddMarkerTokenIfNecessary();
|
||||||
if (!At(CSharpTokenType.RightBrace))
|
if (!At(SyntaxKind.RightBrace))
|
||||||
{
|
{
|
||||||
editHandler.AutoCompleteString = "}";
|
editHandler.AutoCompleteString = "}";
|
||||||
}
|
}
|
||||||
Output(SpanKindInternal.Code);
|
Output(SpanKindInternal.Code);
|
||||||
|
|
||||||
if (Optional(CSharpTokenType.RightBrace))
|
if (Optional(SyntaxKind.RightBrace))
|
||||||
{
|
{
|
||||||
// Set up the "}" span
|
// Set up the "}" span
|
||||||
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
||||||
|
|
@ -401,8 +404,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
if (!IsNested)
|
if (!IsNested)
|
||||||
{
|
{
|
||||||
EnsureCurrent();
|
EnsureCurrent();
|
||||||
if (At(CSharpTokenType.NewLine) ||
|
if (At(SyntaxKind.NewLine) ||
|
||||||
(At(CSharpTokenType.WhiteSpace) && NextIs(CSharpTokenType.NewLine)))
|
(At(SyntaxKind.Whitespace) && NextIs(SyntaxKind.NewLine)))
|
||||||
{
|
{
|
||||||
Context.NullGenerateWhitespaceAndNewLine = true;
|
Context.NullGenerateWhitespaceAndNewLine = true;
|
||||||
}
|
}
|
||||||
|
|
@ -413,7 +416,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private void ImplicitExpression()
|
private void ImplicitExpression()
|
||||||
{
|
{
|
||||||
ImplicitExpression(AcceptedCharactersInternal.NonWhiteSpace);
|
ImplicitExpression(AcceptedCharactersInternal.NonWhitespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Async implicit expressions include the "await" keyword and therefore need to allow spaces to
|
// 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 (!EndOfFile)
|
||||||
{
|
{
|
||||||
if (CurrentToken.Type == CSharpTokenType.LeftParenthesis ||
|
if (CurrentToken.Kind == SyntaxKind.LeftParenthesis ||
|
||||||
CurrentToken.Type == CSharpTokenType.LeftBracket)
|
CurrentToken.Kind == SyntaxKind.LeftBracket)
|
||||||
{
|
{
|
||||||
// If we end within "(", whitespace is fine
|
// If we end within "(", whitespace is fine
|
||||||
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
|
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
|
||||||
|
|
||||||
CSharpTokenType right;
|
SyntaxKind right;
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
using (PushSpanConfig((span, prev) =>
|
using (PushSpanConfig((span, prev) =>
|
||||||
|
|
@ -471,13 +474,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
|
span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
right = Language.FlipBracket(CurrentToken.Type);
|
right = Language.FlipBracket(CurrentToken.Kind);
|
||||||
success = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates);
|
success = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
AcceptUntil(CSharpTokenType.LessThan);
|
AcceptUntil(SyntaxKind.LessThan);
|
||||||
}
|
}
|
||||||
if (At(right))
|
if (At(right))
|
||||||
{
|
{
|
||||||
|
|
@ -488,22 +491,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
return MethodCallOrArrayIndex(acceptedCharacters);
|
return MethodCallOrArrayIndex(acceptedCharacters);
|
||||||
}
|
}
|
||||||
if (At(CSharpTokenType.QuestionMark))
|
if (At(SyntaxKind.QuestionMark))
|
||||||
{
|
{
|
||||||
var next = Lookahead(count: 1);
|
var next = Lookahead(count: 1);
|
||||||
|
|
||||||
if (next != null)
|
if (next != null)
|
||||||
{
|
{
|
||||||
if (next.Type == CSharpTokenType.Dot)
|
if (next.Kind == SyntaxKind.Dot)
|
||||||
{
|
{
|
||||||
// Accept null conditional dot operator (?.).
|
// Accept null conditional dot operator (?.).
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
|
|
||||||
// If the next piece after the ?. is a keyword or identifier then we want to continue.
|
// 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 (?[).
|
// We're at the ? for a null conditional bracket operator (?[).
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
|
|
@ -513,12 +516,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (At(CSharpTokenType.Dot))
|
else if (At(SyntaxKind.Dot))
|
||||||
{
|
{
|
||||||
var dot = CurrentToken;
|
var dot = CurrentToken;
|
||||||
if (NextToken())
|
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 the dot and return to the start
|
||||||
Accept(dot);
|
Accept(dot);
|
||||||
|
|
@ -540,7 +543,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Accept(dot);
|
Accept(dot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!At(CSharpTokenType.WhiteSpace) && !At(CSharpTokenType.NewLine))
|
else if (!At(SyntaxKind.Whitespace) && !At(SyntaxKind.NewLine))
|
||||||
{
|
{
|
||||||
PutCurrentBack();
|
PutCurrentBack();
|
||||||
}
|
}
|
||||||
|
|
@ -587,8 +590,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private void CaptureWhitespaceAtEndOfCodeOnlyLine()
|
private void CaptureWhitespaceAtEndOfCodeOnlyLine()
|
||||||
{
|
{
|
||||||
var whitespace = ReadWhile(token => token.Type == CSharpTokenType.WhiteSpace);
|
var whitespace = ReadWhile(token => token.Kind == SyntaxKind.Whitespace);
|
||||||
if (At(CSharpTokenType.NewLine))
|
if (At(SyntaxKind.NewLine))
|
||||||
{
|
{
|
||||||
Accept(whitespace);
|
Accept(whitespace);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
|
|
@ -610,7 +613,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
private void ExplicitExpression()
|
private void ExplicitExpression()
|
||||||
{
|
{
|
||||||
var block = new Block(Resources.BlockName_ExplicitExpression, CurrentStart);
|
var block = new Block(Resources.BlockName_ExplicitExpression, CurrentStart);
|
||||||
Assert(CSharpTokenType.LeftParenthesis);
|
Assert(SyntaxKind.LeftParenthesis);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
||||||
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
|
|
@ -621,13 +624,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
BalancingModes.BacktrackOnFailure |
|
BalancingModes.BacktrackOnFailure |
|
||||||
BalancingModes.NoErrorOnFailure |
|
BalancingModes.NoErrorOnFailure |
|
||||||
BalancingModes.AllowCommentsAndTemplates,
|
BalancingModes.AllowCommentsAndTemplates,
|
||||||
CSharpTokenType.LeftParenthesis,
|
SyntaxKind.LeftParenthesis,
|
||||||
CSharpTokenType.RightParenthesis,
|
SyntaxKind.RightParenthesis,
|
||||||
block.Start);
|
block.Start);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
AcceptUntil(CSharpTokenType.LessThan);
|
AcceptUntil(SyntaxKind.LessThan);
|
||||||
Context.ErrorSink.OnError(
|
Context.ErrorSink.OnError(
|
||||||
RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF(
|
RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF(
|
||||||
new SourceSpan(block.Start, contentLength: 1 /* ( */), block.Name, ")", "("));
|
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 necessary, put an empty-content marker token here
|
||||||
if (Span.Tokens.Count == 0)
|
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 the content span and then capture the ")"
|
||||||
Output(SpanKindInternal.Code);
|
Output(SpanKindInternal.Code);
|
||||||
}
|
}
|
||||||
Optional(CSharpTokenType.RightParenthesis);
|
Optional(SyntaxKind.RightParenthesis);
|
||||||
if (!EndOfFile)
|
if (!EndOfFile)
|
||||||
{
|
{
|
||||||
PutCurrentBack();
|
PutCurrentBack();
|
||||||
|
|
@ -702,7 +705,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
// No embedded transitions in C#, so ignore that param
|
// No embedded transitions in C#, so ignore that param
|
||||||
return allowTemplatesAndComments
|
return allowTemplatesAndComments
|
||||||
&& ((Language.IsTransition(CurrentToken)
|
&& ((Language.IsTransition(CurrentToken)
|
||||||
&& NextIs(CSharpTokenType.LessThan, CSharpTokenType.Colon, CSharpTokenType.DoubleColon))
|
&& NextIs(SyntaxKind.LessThan, SyntaxKind.Colon, SyntaxKind.DoubleColon))
|
||||||
|| Language.IsCommentStart(CurrentToken));
|
|| Language.IsCommentStart(CurrentToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -783,12 +786,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private void CaseStatement(bool topLevel)
|
private void CaseStatement(bool topLevel)
|
||||||
{
|
{
|
||||||
Assert(CSharpTokenType.Keyword);
|
Assert(SyntaxKind.Keyword);
|
||||||
Debug.Assert(CurrentToken.Keyword != null &&
|
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
|
||||||
(CurrentToken.Keyword.Value == CSharpKeyword.Case ||
|
Debug.Assert(result.HasValue &&
|
||||||
CurrentToken.Keyword.Value == CSharpKeyword.Default));
|
(result.Value == CSharpKeyword.Case ||
|
||||||
AcceptUntil(CSharpTokenType.Colon);
|
result.Value == CSharpKeyword.Default));
|
||||||
Optional(CSharpTokenType.Colon);
|
AcceptUntil(SyntaxKind.Colon);
|
||||||
|
Optional(SyntaxKind.Colon);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoStatement(bool topLevel)
|
private void DoStatement(bool topLevel)
|
||||||
|
|
@ -813,7 +817,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Assert(CSharpKeyword.While);
|
Assert(CSharpKeyword.While);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
if (AcceptCondition() && Optional(CSharpTokenType.Semicolon))
|
if (AcceptCondition() && Optional(SyntaxKind.Semicolon))
|
||||||
{
|
{
|
||||||
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
||||||
}
|
}
|
||||||
|
|
@ -832,12 +836,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||||
|
|
||||||
if (At(CSharpTokenType.LeftParenthesis))
|
if (At(SyntaxKind.LeftParenthesis))
|
||||||
{
|
{
|
||||||
// using ( ==> Using Statement
|
// using ( ==> Using Statement
|
||||||
UsingStatement(block);
|
UsingStatement(block);
|
||||||
}
|
}
|
||||||
else if (At(CSharpTokenType.Identifier) || At(CSharpKeyword.Static))
|
else if (At(SyntaxKind.Identifier) || At(CSharpKeyword.Static))
|
||||||
{
|
{
|
||||||
// using Identifier ==> Using Declaration
|
// using Identifier ==> Using Declaration
|
||||||
if (!topLevel)
|
if (!topLevel)
|
||||||
|
|
@ -865,16 +869,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Context.Builder.CurrentBlock.Type = BlockKindInternal.Directive;
|
Context.Builder.CurrentBlock.Type = BlockKindInternal.Directive;
|
||||||
|
|
||||||
var start = CurrentStart;
|
var start = CurrentStart;
|
||||||
if (At(CSharpTokenType.Identifier))
|
if (At(SyntaxKind.Identifier))
|
||||||
{
|
{
|
||||||
// non-static using
|
// non-static using
|
||||||
NamespaceOrTypeName();
|
NamespaceOrTypeName();
|
||||||
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
if (At(CSharpTokenType.Assign))
|
if (At(SyntaxKind.Assign))
|
||||||
{
|
{
|
||||||
// Alias
|
// Alias
|
||||||
Accept(whitespace);
|
Accept(whitespace);
|
||||||
Assert(CSharpTokenType.Assign);
|
Assert(SyntaxKind.Assign);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
|
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
|
|
@ -904,7 +908,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
// Optional ";"
|
// Optional ";"
|
||||||
if (EnsureCurrent())
|
if (EnsureCurrent())
|
||||||
{
|
{
|
||||||
Optional(CSharpTokenType.Semicolon);
|
Optional(SyntaxKind.Semicolon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -919,16 +923,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
var expectingDot = false;
|
var expectingDot = false;
|
||||||
var tokens = ReadWhile(token =>
|
var tokens = ReadWhile(token =>
|
||||||
{
|
{
|
||||||
var type = token.Type;
|
var type = token.Kind;
|
||||||
if ((expectingDot && type == CSharpTokenType.Dot) ||
|
if ((expectingDot && type == SyntaxKind.Dot) ||
|
||||||
(!expectingDot && type == CSharpTokenType.Identifier))
|
(!expectingDot && type == SyntaxKind.Identifier))
|
||||||
{
|
{
|
||||||
expectingDot = !expectingDot;
|
expectingDot = !expectingDot;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != CSharpTokenType.WhiteSpace &&
|
if (type != SyntaxKind.Whitespace &&
|
||||||
type != CSharpTokenType.NewLine)
|
type != SyntaxKind.NewLine)
|
||||||
{
|
{
|
||||||
expectingDot = false;
|
expectingDot = false;
|
||||||
currentIdentifierLength += token.Content.Length;
|
currentIdentifierLength += token.Content.Length;
|
||||||
|
|
@ -966,69 +970,69 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
protected bool NamespaceOrTypeName()
|
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())
|
if (!NamespaceOrTypeName())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional(CSharpTokenType.WhiteSpace);
|
Optional(SyntaxKind.Whitespace);
|
||||||
Optional(CSharpTokenType.Identifier);
|
Optional(SyntaxKind.Identifier);
|
||||||
Optional(CSharpTokenType.WhiteSpace);
|
Optional(SyntaxKind.Whitespace);
|
||||||
Optional(CSharpTokenType.Comma);
|
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.
|
// Only accept the whitespace if we are going to consume the next token.
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional(CSharpTokenType.QuestionMark); // Nullable
|
Optional(SyntaxKind.QuestionMark); // Nullable
|
||||||
|
|
||||||
return true;
|
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();
|
TypeArgumentList();
|
||||||
}
|
}
|
||||||
if (Optional(CSharpTokenType.Dot))
|
if (Optional(SyntaxKind.Dot))
|
||||||
{
|
{
|
||||||
NamespaceOrTypeName();
|
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.
|
// Only accept the whitespace if we are going to consume the next token.
|
||||||
AcceptAndMoveNext();
|
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.
|
// Only accept the whitespace if we are going to consume the next token.
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (At(CSharpTokenType.LeftBracket))
|
while (At(SyntaxKind.LeftBracket))
|
||||||
{
|
{
|
||||||
Balance(BalancingModes.None);
|
Balance(BalancingModes.None);
|
||||||
Optional(CSharpTokenType.RightBracket);
|
Optional(SyntaxKind.RightBracket);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1040,14 +1044,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private void TypeArgumentList()
|
private void TypeArgumentList()
|
||||||
{
|
{
|
||||||
Assert(CSharpTokenType.LessThan);
|
Assert(SyntaxKind.LessThan);
|
||||||
Balance(BalancingModes.None);
|
Balance(BalancingModes.None);
|
||||||
Optional(CSharpTokenType.GreaterThan);
|
Optional(SyntaxKind.GreaterThan);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UsingStatement(Block block)
|
private void UsingStatement(Block block)
|
||||||
{
|
{
|
||||||
Assert(CSharpTokenType.LeftParenthesis);
|
Assert(SyntaxKind.LeftParenthesis);
|
||||||
|
|
||||||
// Parse condition
|
// Parse condition
|
||||||
if (AcceptCondition())
|
if (AcceptCondition())
|
||||||
|
|
@ -1159,12 +1163,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
if (!EndOfFile)
|
if (!EndOfFile)
|
||||||
{
|
{
|
||||||
// Check for "{" to make sure we're at a block
|
// Check for "{" to make sure we're at a block
|
||||||
if (!At(CSharpTokenType.LeftBrace))
|
if (!At(SyntaxKind.LeftBrace))
|
||||||
{
|
{
|
||||||
Context.ErrorSink.OnError(
|
Context.ErrorSink.OnError(
|
||||||
RazorDiagnosticFactory.CreateParsing_SingleLineControlFlowStatementsNotAllowed(
|
RazorDiagnosticFactory.CreateParsing_SingleLineControlFlowStatementsNotAllowed(
|
||||||
new SourceSpan(CurrentStart, CurrentToken.Content.Length),
|
new SourceSpan(CurrentStart, CurrentToken.Content.Length),
|
||||||
Language.GetSample(CSharpTokenType.LeftBrace),
|
Language.GetSample(SyntaxKind.LeftBrace),
|
||||||
CurrentToken.Content));
|
CurrentToken.Content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1175,7 +1179,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private void UnconditionalBlock()
|
private void UnconditionalBlock()
|
||||||
{
|
{
|
||||||
Assert(CSharpTokenType.Keyword);
|
Assert(SyntaxKind.Keyword);
|
||||||
var block = new Block(CurrentToken, CurrentStart);
|
var block = new Block(CurrentToken, CurrentStart);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
|
|
@ -1219,7 +1223,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private void ConditionalBlock(bool topLevel)
|
private void ConditionalBlock(bool topLevel)
|
||||||
{
|
{
|
||||||
Assert(CSharpTokenType.Keyword);
|
Assert(SyntaxKind.Keyword);
|
||||||
var block = new Block(CurrentToken, CurrentStart);
|
var block = new Block(CurrentToken, CurrentStart);
|
||||||
ConditionalBlock(block);
|
ConditionalBlock(block);
|
||||||
if (topLevel)
|
if (topLevel)
|
||||||
|
|
@ -1243,16 +1247,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private bool AcceptCondition()
|
private bool AcceptCondition()
|
||||||
{
|
{
|
||||||
if (At(CSharpTokenType.LeftParenthesis))
|
if (At(SyntaxKind.LeftParenthesis))
|
||||||
{
|
{
|
||||||
var complete = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates);
|
var complete = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates);
|
||||||
if (!complete)
|
if (!complete)
|
||||||
{
|
{
|
||||||
AcceptUntil(CSharpTokenType.NewLine);
|
AcceptUntil(SyntaxKind.NewLine);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Optional(CSharpTokenType.RightParenthesis);
|
Optional(SyntaxKind.RightParenthesis);
|
||||||
}
|
}
|
||||||
return complete;
|
return complete;
|
||||||
}
|
}
|
||||||
|
|
@ -1280,16 +1284,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = CurrentToken.Type;
|
var type = CurrentToken.Kind;
|
||||||
var loc = CurrentStart;
|
var loc = CurrentStart;
|
||||||
|
|
||||||
// Both cases @: and @:: are triggered as markup, second colon in second case will be triggered as a plain text
|
// Both cases @: and @:: are triggered as markup, second colon in second case will be triggered as a plain text
|
||||||
var isSingleLineMarkup = type == CSharpTokenType.Transition &&
|
var isSingleLineMarkup = type == SyntaxKind.Transition &&
|
||||||
(NextIs(CSharpTokenType.Colon, CSharpTokenType.DoubleColon));
|
(NextIs(SyntaxKind.Colon, SyntaxKind.DoubleColon));
|
||||||
|
|
||||||
var isMarkup = isSingleLineMarkup ||
|
var isMarkup = isSingleLineMarkup ||
|
||||||
type == CSharpTokenType.LessThan ||
|
type == SyntaxKind.LessThan ||
|
||||||
(type == CSharpTokenType.Transition && NextIs(CSharpTokenType.LessThan));
|
(type == SyntaxKind.Transition && NextIs(SyntaxKind.LessThan));
|
||||||
|
|
||||||
if (Context.DesignTimeMode || !isMarkup)
|
if (Context.DesignTimeMode || !isMarkup)
|
||||||
{
|
{
|
||||||
|
|
@ -1321,7 +1325,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
if (isMarkup)
|
if (isMarkup)
|
||||||
{
|
{
|
||||||
if (type == CSharpTokenType.Transition && !isSingleLineMarkup)
|
if (type == SyntaxKind.Transition && !isSingleLineMarkup)
|
||||||
{
|
{
|
||||||
Context.ErrorSink.OnError(
|
Context.ErrorSink.OnError(
|
||||||
RazorDiagnosticFactory.CreateParsing_AtInCodeMustBeFollowedByColonParenOrIdentifierStart(
|
RazorDiagnosticFactory.CreateParsing_AtInCodeMustBeFollowedByColonParenOrIdentifierStart(
|
||||||
|
|
@ -1331,7 +1335,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
// Markup block
|
// Markup block
|
||||||
Output(SpanKindInternal.Code);
|
Output(SpanKindInternal.Code);
|
||||||
if (Context.DesignTimeMode && CurrentToken != null &&
|
if (Context.DesignTimeMode && CurrentToken != null &&
|
||||||
(CurrentToken.Type == CSharpTokenType.LessThan || CurrentToken.Type == CSharpTokenType.Transition))
|
(CurrentToken.Kind == SyntaxKind.LessThan || CurrentToken.Kind == SyntaxKind.Transition))
|
||||||
{
|
{
|
||||||
PutCurrentBack();
|
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)
|
switch (type)
|
||||||
{
|
{
|
||||||
case CSharpTokenType.RazorCommentTransition:
|
case SyntaxKind.RazorCommentTransition:
|
||||||
Output(SpanKindInternal.Code);
|
Output(SpanKindInternal.Code);
|
||||||
RazorComment();
|
RazorComment();
|
||||||
Statement(block);
|
Statement(block);
|
||||||
break;
|
break;
|
||||||
case CSharpTokenType.LeftBrace:
|
case SyntaxKind.LeftBrace:
|
||||||
// Verbatim Block
|
// Verbatim Block
|
||||||
block = block ?? new Block(Resources.BlockName_Code, CurrentStart);
|
block = block ?? new Block(Resources.BlockName_Code, CurrentStart);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
CodeBlock(block);
|
CodeBlock(block);
|
||||||
break;
|
break;
|
||||||
case CSharpTokenType.Keyword:
|
case SyntaxKind.Keyword:
|
||||||
// Keyword block
|
// Keyword block
|
||||||
HandleKeyword(false, StandardStatement);
|
HandleKeyword(false, StandardStatement);
|
||||||
break;
|
break;
|
||||||
case CSharpTokenType.Transition:
|
case SyntaxKind.Transition:
|
||||||
// Embedded Expression block
|
// Embedded Expression block
|
||||||
EmbeddedExpression();
|
EmbeddedExpression();
|
||||||
break;
|
break;
|
||||||
case CSharpTokenType.RightBrace:
|
case SyntaxKind.RightBrace:
|
||||||
// Possible end of Code Block, just run the continuation
|
// Possible end of Code Block, just run the continuation
|
||||||
break;
|
break;
|
||||||
case CSharpTokenType.Comment:
|
case SyntaxKind.CSharpComment:
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -1383,11 +1387,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
private void EmbeddedExpression()
|
private void EmbeddedExpression()
|
||||||
{
|
{
|
||||||
// First, verify the type of the block
|
// First, verify the type of the block
|
||||||
Assert(CSharpTokenType.Transition);
|
Assert(SyntaxKind.Transition);
|
||||||
var transition = CurrentToken;
|
var transition = CurrentToken;
|
||||||
NextToken();
|
NextToken();
|
||||||
|
|
||||||
if (At(CSharpTokenType.Transition))
|
if (At(SyntaxKind.Transition))
|
||||||
{
|
{
|
||||||
// Escaped "@"
|
// Escaped "@"
|
||||||
Output(SpanKindInternal.Code);
|
Output(SpanKindInternal.Code);
|
||||||
|
|
@ -1397,14 +1401,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
Output(SpanKindInternal.Code);
|
Output(SpanKindInternal.Code);
|
||||||
|
|
||||||
Assert(CSharpTokenType.Transition);
|
Assert(SyntaxKind.Transition);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
StandardStatement();
|
StandardStatement();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Throw errors as necessary, but continue parsing
|
// Throw errors as necessary, but continue parsing
|
||||||
if (At(CSharpTokenType.LeftBrace))
|
if (At(SyntaxKind.LeftBrace))
|
||||||
{
|
{
|
||||||
Context.ErrorSink.OnError(
|
Context.ErrorSink.OnError(
|
||||||
RazorDiagnosticFactory.CreateParsing_UnexpectedNestedCodeBlock(
|
RazorDiagnosticFactory.CreateParsing_UnexpectedNestedCodeBlock(
|
||||||
|
|
@ -1428,48 +1432,48 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
var bookmark = CurrentStart.AbsoluteIndex;
|
var bookmark = CurrentStart.AbsoluteIndex;
|
||||||
var read = ReadWhile(token =>
|
var read = ReadWhile(token =>
|
||||||
token.Type != CSharpTokenType.Semicolon &&
|
token.Kind != SyntaxKind.Semicolon &&
|
||||||
token.Type != CSharpTokenType.RazorCommentTransition &&
|
token.Kind != SyntaxKind.RazorCommentTransition &&
|
||||||
token.Type != CSharpTokenType.Transition &&
|
token.Kind != SyntaxKind.Transition &&
|
||||||
token.Type != CSharpTokenType.LeftBrace &&
|
token.Kind != SyntaxKind.LeftBrace &&
|
||||||
token.Type != CSharpTokenType.LeftParenthesis &&
|
token.Kind != SyntaxKind.LeftParenthesis &&
|
||||||
token.Type != CSharpTokenType.LeftBracket &&
|
token.Kind != SyntaxKind.LeftBracket &&
|
||||||
token.Type != CSharpTokenType.RightBrace);
|
token.Kind != SyntaxKind.RightBrace);
|
||||||
|
|
||||||
if (At(CSharpTokenType.LeftBrace) ||
|
if (At(SyntaxKind.LeftBrace) ||
|
||||||
At(CSharpTokenType.LeftParenthesis) ||
|
At(SyntaxKind.LeftParenthesis) ||
|
||||||
At(CSharpTokenType.LeftBracket))
|
At(SyntaxKind.LeftBracket))
|
||||||
{
|
{
|
||||||
Accept(read);
|
Accept(read);
|
||||||
if (Balance(BalancingModes.AllowCommentsAndTemplates | BalancingModes.BacktrackOnFailure))
|
if (Balance(BalancingModes.AllowCommentsAndTemplates | BalancingModes.BacktrackOnFailure))
|
||||||
{
|
{
|
||||||
Optional(CSharpTokenType.RightBrace);
|
Optional(SyntaxKind.RightBrace);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Recovery
|
// Recovery
|
||||||
AcceptUntil(CSharpTokenType.LessThan, CSharpTokenType.RightBrace);
|
AcceptUntil(SyntaxKind.LessThan, SyntaxKind.RightBrace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (At(CSharpTokenType.Transition) && (NextIs(CSharpTokenType.LessThan, CSharpTokenType.Colon)))
|
else if (At(SyntaxKind.Transition) && (NextIs(SyntaxKind.LessThan, SyntaxKind.Colon)))
|
||||||
{
|
{
|
||||||
Accept(read);
|
Accept(read);
|
||||||
Output(SpanKindInternal.Code);
|
Output(SpanKindInternal.Code);
|
||||||
Template();
|
Template();
|
||||||
}
|
}
|
||||||
else if (At(CSharpTokenType.RazorCommentTransition))
|
else if (At(SyntaxKind.RazorCommentTransition))
|
||||||
{
|
{
|
||||||
Accept(read);
|
Accept(read);
|
||||||
RazorComment();
|
RazorComment();
|
||||||
}
|
}
|
||||||
else if (At(CSharpTokenType.Semicolon))
|
else if (At(SyntaxKind.Semicolon))
|
||||||
{
|
{
|
||||||
Accept(read);
|
Accept(read);
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (At(CSharpTokenType.RightBrace))
|
else if (At(SyntaxKind.RightBrace))
|
||||||
{
|
{
|
||||||
Accept(read);
|
Accept(read);
|
||||||
return;
|
return;
|
||||||
|
|
@ -1478,7 +1482,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
Context.Source.Position = bookmark;
|
Context.Source.Position = bookmark;
|
||||||
NextToken();
|
NextToken();
|
||||||
AcceptUntil(CSharpTokenType.LessThan, CSharpTokenType.LeftBrace, CSharpTokenType.RightBrace);
|
AcceptUntil(SyntaxKind.LessThan, SyntaxKind.LeftBrace, SyntaxKind.RightBrace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1492,7 +1496,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
private void CodeBlock(bool acceptTerminatingBrace, Block block)
|
private void CodeBlock(bool acceptTerminatingBrace, Block block)
|
||||||
{
|
{
|
||||||
EnsureCurrent();
|
EnsureCurrent();
|
||||||
while (!EndOfFile && !At(CSharpTokenType.RightBrace))
|
while (!EndOfFile && !At(SyntaxKind.RightBrace))
|
||||||
{
|
{
|
||||||
// Parse a statement, then return here
|
// Parse a statement, then return here
|
||||||
Statement();
|
Statement();
|
||||||
|
|
@ -1507,7 +1511,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
else if (acceptTerminatingBrace)
|
else if (acceptTerminatingBrace)
|
||||||
{
|
{
|
||||||
Assert(CSharpTokenType.RightBrace);
|
Assert(SyntaxKind.RightBrace);
|
||||||
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
}
|
}
|
||||||
|
|
@ -1515,8 +1519,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private void HandleKeyword(bool topLevel, Action fallback)
|
private void HandleKeyword(bool topLevel, Action fallback)
|
||||||
{
|
{
|
||||||
Debug.Assert(CurrentToken.Type == CSharpTokenType.Keyword && CurrentToken.Keyword != null);
|
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
|
||||||
if (_keywordParsers.TryGetValue(CurrentToken.Keyword.Value, out var handler))
|
Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword && result.HasValue);
|
||||||
|
if (_keywordParsers.TryGetValue(result.Value, out var handler))
|
||||||
{
|
{
|
||||||
handler(topLevel);
|
handler(topLevel);
|
||||||
}
|
}
|
||||||
|
|
@ -1526,12 +1531,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<CSharpToken> SkipToNextImportantToken()
|
private IEnumerable<SyntaxToken> SkipToNextImportantToken()
|
||||||
{
|
{
|
||||||
while (!EndOfFile)
|
while (!EndOfFile)
|
||||||
{
|
{
|
||||||
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
if (At(CSharpTokenType.RazorCommentTransition))
|
if (At(SyntaxKind.RazorCommentTransition))
|
||||||
{
|
{
|
||||||
Accept(whitespace);
|
Accept(whitespace);
|
||||||
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
|
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
|
||||||
|
|
@ -1542,7 +1547,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return whitespace;
|
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.
|
// 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++)
|
for (var i = 0; i < descriptor.Tokens.Count; i++)
|
||||||
{
|
{
|
||||||
if (!At(CSharpTokenType.WhiteSpace) &&
|
if (!At(SyntaxKind.Whitespace) &&
|
||||||
!At(CSharpTokenType.NewLine) &&
|
!At(SyntaxKind.NewLine) &&
|
||||||
!EndOfFile)
|
!EndOfFile)
|
||||||
{
|
{
|
||||||
Context.ErrorSink.OnError(
|
Context.ErrorSink.OnError(
|
||||||
|
|
@ -1662,24 +1667,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
tokenDescriptor.Kind == DirectiveTokenKind.Type)
|
tokenDescriptor.Kind == DirectiveTokenKind.Type)
|
||||||
{
|
{
|
||||||
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
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.
|
// Add a marker token to provide CSharp intellisense when we start typing the directive token.
|
||||||
AddMarkerTokenIfNecessary();
|
AddMarkerTokenIfNecessary();
|
||||||
Span.ChunkGenerator = new DirectiveTokenChunkGenerator(tokenDescriptor);
|
Span.ChunkGenerator = new DirectiveTokenChunkGenerator(tokenDescriptor);
|
||||||
Span.EditHandler = new DirectiveTokenEditHandler(Language.TokenizeString);
|
Span.EditHandler = new DirectiveTokenEditHandler(Language.TokenizeString);
|
||||||
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhiteSpace);
|
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhitespace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1718,7 +1723,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DirectiveTokenKind.Member:
|
case DirectiveTokenKind.Member:
|
||||||
if (At(CSharpTokenType.Identifier))
|
if (At(SyntaxKind.Identifier))
|
||||||
{
|
{
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
}
|
}
|
||||||
|
|
@ -1732,7 +1737,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DirectiveTokenKind.String:
|
case DirectiveTokenKind.String:
|
||||||
if (At(CSharpTokenType.StringLiteral) && CurrentToken.Errors.Count == 0)
|
if (At(SyntaxKind.StringLiteral) && !CurrentToken.ContainsDiagnostics)
|
||||||
{
|
{
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
}
|
}
|
||||||
|
|
@ -1748,7 +1753,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
Span.ChunkGenerator = new DirectiveTokenChunkGenerator(tokenDescriptor);
|
Span.ChunkGenerator = new DirectiveTokenChunkGenerator(tokenDescriptor);
|
||||||
Span.EditHandler = new DirectiveTokenEditHandler(Language.TokenizeString);
|
Span.EditHandler = new DirectiveTokenEditHandler(Language.TokenizeString);
|
||||||
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhiteSpace);
|
Output(SpanKindInternal.Code, AcceptedCharactersInternal.NonWhitespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||||
|
|
@ -1757,15 +1762,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
switch (descriptor.Kind)
|
switch (descriptor.Kind)
|
||||||
{
|
{
|
||||||
case DirectiveKind.SingleLine:
|
case DirectiveKind.SingleLine:
|
||||||
Output(SpanKindInternal.None, AcceptedCharactersInternal.WhiteSpace);
|
Output(SpanKindInternal.None, AcceptedCharactersInternal.Whitespace);
|
||||||
|
|
||||||
Optional(CSharpTokenType.Semicolon);
|
Optional(SyntaxKind.Semicolon);
|
||||||
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
Output(SpanKindInternal.MetaCode, AcceptedCharactersInternal.WhiteSpace);
|
Output(SpanKindInternal.MetaCode, AcceptedCharactersInternal.Whitespace);
|
||||||
|
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||||
|
|
||||||
if (At(CSharpTokenType.NewLine))
|
if (At(SyntaxKind.NewLine))
|
||||||
{
|
{
|
||||||
AcceptAndMoveNext();
|
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.
|
// This should contain the optional whitespace after the optional semicolon and the new line.
|
||||||
// Output as Markup as we want intellisense here.
|
// Output as Markup as we want intellisense here.
|
||||||
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.WhiteSpace);
|
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.Whitespace);
|
||||||
break;
|
break;
|
||||||
case DirectiveKind.RazorBlock:
|
case DirectiveKind.RazorBlock:
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.AllWhiteSpace);
|
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.AllWhitespace);
|
||||||
|
|
||||||
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
|
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
|
||||||
{
|
{
|
||||||
|
|
@ -1811,12 +1816,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
break;
|
break;
|
||||||
case DirectiveKind.CodeBlock:
|
case DirectiveKind.CodeBlock:
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.AllWhiteSpace);
|
Output(SpanKindInternal.Markup, AcceptedCharactersInternal.AllWhitespace);
|
||||||
|
|
||||||
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
|
ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) =>
|
||||||
{
|
{
|
||||||
NextToken();
|
NextToken();
|
||||||
Balance(BalancingModes.NoErrorOnFailure, CSharpTokenType.LeftBrace, CSharpTokenType.RightBrace, startingBraceLocation);
|
Balance(BalancingModes.NoErrorOnFailure, SyntaxKind.LeftBrace, SyntaxKind.RightBrace, startingBraceLocation);
|
||||||
Span.ChunkGenerator = new StatementChunkGenerator();
|
Span.ChunkGenerator = new StatementChunkGenerator();
|
||||||
var existingEditHandler = Span.EditHandler;
|
var existingEditHandler = Span.EditHandler;
|
||||||
Span.EditHandler = new CodeBlockEditHandler(Language.TokenizeString);
|
Span.EditHandler = new CodeBlockEditHandler(Language.TokenizeString);
|
||||||
|
|
@ -1868,7 +1873,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
RazorDiagnosticFactory.CreateParsing_UnexpectedEOFAfterDirective(
|
RazorDiagnosticFactory.CreateParsing_UnexpectedEOFAfterDirective(
|
||||||
new SourceSpan(CurrentStart, contentLength: 1 /* { */), descriptor.Directive, "{"));
|
new SourceSpan(CurrentStart, contentLength: 1 /* { */), descriptor.Directive, "{"));
|
||||||
}
|
}
|
||||||
else if (!At(CSharpTokenType.LeftBrace))
|
else if (!At(SyntaxKind.LeftBrace))
|
||||||
{
|
{
|
||||||
Context.ErrorSink.OnError(
|
Context.ErrorSink.OnError(
|
||||||
RazorDiagnosticFactory.CreateParsing_UnexpectedDirectiveLiteral(
|
RazorDiagnosticFactory.CreateParsing_UnexpectedDirectiveLiteral(
|
||||||
|
|
@ -1886,7 +1891,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
parseChildren(startingBraceLocation);
|
parseChildren(startingBraceLocation);
|
||||||
|
|
||||||
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
if (!Optional(CSharpTokenType.RightBrace))
|
if (!Optional(SyntaxKind.RightBrace))
|
||||||
{
|
{
|
||||||
editHandler.AutoCompleteString = "}";
|
editHandler.AutoCompleteString = "}";
|
||||||
Context.ErrorSink.OnError(
|
Context.ErrorSink.OnError(
|
||||||
|
|
@ -1982,7 +1987,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
// Ex: @addTagHelper "*, Microsoft.AspNetCore.CoolLibrary"
|
// Ex: @addTagHelper "*, Microsoft.AspNetCore.CoolLibrary"
|
||||||
// ^ ^
|
// ^ ^
|
||||||
// Start End
|
// 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);
|
offset += Span.Tokens[0].Content.IndexOf(directiveText, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
|
@ -2081,7 +2086,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
[Conditional("DEBUG")]
|
[Conditional("DEBUG")]
|
||||||
protected void AssertDirective(string directive)
|
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));
|
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 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
|
// If we found whitespace then any content placed within the whitespace MAY cause a destructive change
|
||||||
// to the document. We can't accept it.
|
// to the document. We can't accept it.
|
||||||
var acceptedCharacters = foundWhitespace ? AcceptedCharactersInternal.None : AcceptedCharactersInternal.AnyExceptNewline;
|
var acceptedCharacters = foundWhitespace ? AcceptedCharactersInternal.None : AcceptedCharactersInternal.AnyExceptNewline;
|
||||||
Output(SpanKindInternal.MetaCode, acceptedCharacters);
|
Output(SpanKindInternal.MetaCode, acceptedCharacters);
|
||||||
|
|
||||||
AcceptWhile(CSharpTokenType.WhiteSpace);
|
AcceptWhile(SyntaxKind.Whitespace);
|
||||||
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
Span.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
Output(SpanKindInternal.Markup, acceptedCharacters);
|
Output(SpanKindInternal.Markup, acceptedCharacters);
|
||||||
|
|
||||||
if (EndOfFile || At(CSharpTokenType.NewLine))
|
if (EndOfFile || At(SyntaxKind.NewLine))
|
||||||
{
|
{
|
||||||
Context.ErrorSink.OnError(
|
Context.ErrorSink.OnError(
|
||||||
RazorDiagnosticFactory.CreateParsing_DirectiveMustHaveValue(
|
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
|
// Parse to the end of the line. Essentially accepts anything until end of line, comments, invalid code
|
||||||
// etc.
|
// etc.
|
||||||
AcceptUntil(CSharpTokenType.NewLine);
|
AcceptUntil(SyntaxKind.NewLine);
|
||||||
|
|
||||||
// Pull out the value and remove whitespaces and optional quotes
|
// Pull out the value and remove whitespaces and optional quotes
|
||||||
var rawValue = string.Concat(Span.Tokens.Select(s => s.Content)).Trim();
|
var rawValue = string.Concat(Span.Tokens.Select(s => s.Content)).Trim();
|
||||||
|
|
@ -2170,7 +2175,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Start = start;
|
Start = start;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block(CSharpToken token, SourceLocation start)
|
public Block(SyntaxToken token, SourceLocation start)
|
||||||
: this(GetName(token), start)
|
: this(GetName(token), start)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -2178,11 +2183,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public SourceLocation Start { 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;
|
return token.Content;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,64 +3,65 @@
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
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 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, "->" },
|
{ SyntaxKind.Arrow, "->" },
|
||||||
{ CSharpTokenType.Minus, "-" },
|
{ SyntaxKind.Minus, "-" },
|
||||||
{ CSharpTokenType.Decrement, "--" },
|
{ SyntaxKind.Decrement, "--" },
|
||||||
{ CSharpTokenType.MinusAssign, "-=" },
|
{ SyntaxKind.MinusAssign, "-=" },
|
||||||
{ CSharpTokenType.NotEqual, "!=" },
|
{ SyntaxKind.NotEqual, "!=" },
|
||||||
{ CSharpTokenType.Not, "!" },
|
{ SyntaxKind.Not, "!" },
|
||||||
{ CSharpTokenType.Modulo, "%" },
|
{ SyntaxKind.Modulo, "%" },
|
||||||
{ CSharpTokenType.ModuloAssign, "%=" },
|
{ SyntaxKind.ModuloAssign, "%=" },
|
||||||
{ CSharpTokenType.AndAssign, "&=" },
|
{ SyntaxKind.AndAssign, "&=" },
|
||||||
{ CSharpTokenType.And, "&" },
|
{ SyntaxKind.And, "&" },
|
||||||
{ CSharpTokenType.DoubleAnd, "&&" },
|
{ SyntaxKind.DoubleAnd, "&&" },
|
||||||
{ CSharpTokenType.LeftParenthesis, "(" },
|
{ SyntaxKind.LeftParenthesis, "(" },
|
||||||
{ CSharpTokenType.RightParenthesis, ")" },
|
{ SyntaxKind.RightParenthesis, ")" },
|
||||||
{ CSharpTokenType.Star, "*" },
|
{ SyntaxKind.Star, "*" },
|
||||||
{ CSharpTokenType.MultiplyAssign, "*=" },
|
{ SyntaxKind.MultiplyAssign, "*=" },
|
||||||
{ CSharpTokenType.Comma, "," },
|
{ SyntaxKind.Comma, "," },
|
||||||
{ CSharpTokenType.Dot, "." },
|
{ SyntaxKind.Dot, "." },
|
||||||
{ CSharpTokenType.Slash, "/" },
|
{ SyntaxKind.Slash, "/" },
|
||||||
{ CSharpTokenType.DivideAssign, "/=" },
|
{ SyntaxKind.DivideAssign, "/=" },
|
||||||
{ CSharpTokenType.DoubleColon, "::" },
|
{ SyntaxKind.DoubleColon, "::" },
|
||||||
{ CSharpTokenType.Colon, ":" },
|
{ SyntaxKind.Colon, ":" },
|
||||||
{ CSharpTokenType.Semicolon, ";" },
|
{ SyntaxKind.Semicolon, ";" },
|
||||||
{ CSharpTokenType.QuestionMark, "?" },
|
{ SyntaxKind.QuestionMark, "?" },
|
||||||
{ CSharpTokenType.NullCoalesce, "??" },
|
{ SyntaxKind.NullCoalesce, "??" },
|
||||||
{ CSharpTokenType.RightBracket, "]" },
|
{ SyntaxKind.RightBracket, "]" },
|
||||||
{ CSharpTokenType.LeftBracket, "[" },
|
{ SyntaxKind.LeftBracket, "[" },
|
||||||
{ CSharpTokenType.XorAssign, "^=" },
|
{ SyntaxKind.XorAssign, "^=" },
|
||||||
{ CSharpTokenType.Xor, "^" },
|
{ SyntaxKind.Xor, "^" },
|
||||||
{ CSharpTokenType.LeftBrace, "{" },
|
{ SyntaxKind.LeftBrace, "{" },
|
||||||
{ CSharpTokenType.OrAssign, "|=" },
|
{ SyntaxKind.OrAssign, "|=" },
|
||||||
{ CSharpTokenType.DoubleOr, "||" },
|
{ SyntaxKind.DoubleOr, "||" },
|
||||||
{ CSharpTokenType.Or, "|" },
|
{ SyntaxKind.Or, "|" },
|
||||||
{ CSharpTokenType.RightBrace, "}" },
|
{ SyntaxKind.RightBrace, "}" },
|
||||||
{ CSharpTokenType.Tilde, "~" },
|
{ SyntaxKind.Tilde, "~" },
|
||||||
{ CSharpTokenType.Plus, "+" },
|
{ SyntaxKind.Plus, "+" },
|
||||||
{ CSharpTokenType.PlusAssign, "+=" },
|
{ SyntaxKind.PlusAssign, "+=" },
|
||||||
{ CSharpTokenType.Increment, "++" },
|
{ SyntaxKind.Increment, "++" },
|
||||||
{ CSharpTokenType.LessThan, "<" },
|
{ SyntaxKind.LessThan, "<" },
|
||||||
{ CSharpTokenType.LessThanEqual, "<=" },
|
{ SyntaxKind.LessThanEqual, "<=" },
|
||||||
{ CSharpTokenType.LeftShift, "<<" },
|
{ SyntaxKind.LeftShift, "<<" },
|
||||||
{ CSharpTokenType.LeftShiftAssign, "<<=" },
|
{ SyntaxKind.LeftShiftAssign, "<<=" },
|
||||||
{ CSharpTokenType.Assign, "=" },
|
{ SyntaxKind.Assign, "=" },
|
||||||
{ CSharpTokenType.Equals, "==" },
|
{ SyntaxKind.Equals, "==" },
|
||||||
{ CSharpTokenType.GreaterThan, ">" },
|
{ SyntaxKind.GreaterThan, ">" },
|
||||||
{ CSharpTokenType.GreaterThanEqual, ">=" },
|
{ SyntaxKind.GreaterThanEqual, ">=" },
|
||||||
{ CSharpTokenType.RightShift, ">>" },
|
{ SyntaxKind.RightShift, ">>" },
|
||||||
{ CSharpTokenType.RightShiftAssign, ">>=" },
|
{ SyntaxKind.RightShiftAssign, ">>=" },
|
||||||
{ CSharpTokenType.Hash, "#" },
|
{ SyntaxKind.Hash, "#" },
|
||||||
{ CSharpTokenType.Transition, "@" },
|
{ SyntaxKind.Transition, "@" },
|
||||||
};
|
};
|
||||||
|
|
||||||
protected CSharpLanguageCharacteristics()
|
protected CSharpLanguageCharacteristics()
|
||||||
|
|
@ -74,35 +75,35 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return new CSharpTokenizer(source);
|
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;
|
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;
|
return Resources.CSharpToken_Identifier;
|
||||||
case CSharpTokenType.Keyword:
|
case SyntaxKind.Keyword:
|
||||||
return Resources.CSharpToken_Keyword;
|
return Resources.CSharpToken_Keyword;
|
||||||
case CSharpTokenType.IntegerLiteral:
|
case SyntaxKind.IntegerLiteral:
|
||||||
return Resources.CSharpToken_IntegerLiteral;
|
return Resources.CSharpToken_IntegerLiteral;
|
||||||
case CSharpTokenType.NewLine:
|
case SyntaxKind.NewLine:
|
||||||
return Resources.CSharpToken_Newline;
|
return Resources.CSharpToken_Newline;
|
||||||
case CSharpTokenType.WhiteSpace:
|
case SyntaxKind.Whitespace:
|
||||||
return Resources.CSharpToken_Whitespace;
|
return Resources.CSharpToken_Whitespace;
|
||||||
case CSharpTokenType.Comment:
|
case SyntaxKind.CSharpComment:
|
||||||
return Resources.CSharpToken_Comment;
|
return Resources.CSharpToken_Comment;
|
||||||
case CSharpTokenType.RealLiteral:
|
case SyntaxKind.RealLiteral:
|
||||||
return Resources.CSharpToken_RealLiteral;
|
return Resources.CSharpToken_RealLiteral;
|
||||||
case CSharpTokenType.CharacterLiteral:
|
case SyntaxKind.CharacterLiteral:
|
||||||
return Resources.CSharpToken_CharacterLiteral;
|
return Resources.CSharpToken_CharacterLiteral;
|
||||||
case CSharpTokenType.StringLiteral:
|
case SyntaxKind.StringLiteral:
|
||||||
return Resources.CSharpToken_StringLiteral;
|
return Resources.CSharpToken_StringLiteral;
|
||||||
default:
|
default:
|
||||||
return Resources.Token_Unknown;
|
return Resources.Token_Unknown;
|
||||||
|
|
@ -111,59 +112,59 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return sample;
|
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)
|
switch (type)
|
||||||
{
|
{
|
||||||
case KnownTokenType.Identifier:
|
case KnownTokenType.Identifier:
|
||||||
return CSharpTokenType.Identifier;
|
return SyntaxKind.Identifier;
|
||||||
case KnownTokenType.Keyword:
|
case KnownTokenType.Keyword:
|
||||||
return CSharpTokenType.Keyword;
|
return SyntaxKind.Keyword;
|
||||||
case KnownTokenType.NewLine:
|
case KnownTokenType.NewLine:
|
||||||
return CSharpTokenType.NewLine;
|
return SyntaxKind.NewLine;
|
||||||
case KnownTokenType.WhiteSpace:
|
case KnownTokenType.WhiteSpace:
|
||||||
return CSharpTokenType.WhiteSpace;
|
return SyntaxKind.Whitespace;
|
||||||
case KnownTokenType.Transition:
|
case KnownTokenType.Transition:
|
||||||
return CSharpTokenType.Transition;
|
return SyntaxKind.Transition;
|
||||||
case KnownTokenType.CommentStart:
|
case KnownTokenType.CommentStart:
|
||||||
return CSharpTokenType.RazorCommentTransition;
|
return SyntaxKind.RazorCommentTransition;
|
||||||
case KnownTokenType.CommentStar:
|
case KnownTokenType.CommentStar:
|
||||||
return CSharpTokenType.RazorCommentStar;
|
return SyntaxKind.RazorCommentStar;
|
||||||
case KnownTokenType.CommentBody:
|
case KnownTokenType.CommentBody:
|
||||||
return CSharpTokenType.RazorComment;
|
return SyntaxKind.RazorCommentLiteral;
|
||||||
default:
|
default:
|
||||||
return CSharpTokenType.Unknown;
|
return SyntaxKind.Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override CSharpTokenType FlipBracket(CSharpTokenType bracket)
|
public override SyntaxKind FlipBracket(SyntaxKind bracket)
|
||||||
{
|
{
|
||||||
switch (bracket)
|
switch (bracket)
|
||||||
{
|
{
|
||||||
case CSharpTokenType.LeftBrace:
|
case SyntaxKind.LeftBrace:
|
||||||
return CSharpTokenType.RightBrace;
|
return SyntaxKind.RightBrace;
|
||||||
case CSharpTokenType.LeftBracket:
|
case SyntaxKind.LeftBracket:
|
||||||
return CSharpTokenType.RightBracket;
|
return SyntaxKind.RightBracket;
|
||||||
case CSharpTokenType.LeftParenthesis:
|
case SyntaxKind.LeftParenthesis:
|
||||||
return CSharpTokenType.RightParenthesis;
|
return SyntaxKind.RightParenthesis;
|
||||||
case CSharpTokenType.LessThan:
|
case SyntaxKind.LessThan:
|
||||||
return CSharpTokenType.GreaterThan;
|
return SyntaxKind.GreaterThan;
|
||||||
case CSharpTokenType.RightBrace:
|
case SyntaxKind.RightBrace:
|
||||||
return CSharpTokenType.LeftBrace;
|
return SyntaxKind.LeftBrace;
|
||||||
case CSharpTokenType.RightBracket:
|
case SyntaxKind.RightBracket:
|
||||||
return CSharpTokenType.LeftBracket;
|
return SyntaxKind.LeftBracket;
|
||||||
case CSharpTokenType.RightParenthesis:
|
case SyntaxKind.RightParenthesis:
|
||||||
return CSharpTokenType.LeftParenthesis;
|
return SyntaxKind.LeftParenthesis;
|
||||||
case CSharpTokenType.GreaterThan:
|
case SyntaxKind.GreaterThan:
|
||||||
return CSharpTokenType.LessThan;
|
return SyntaxKind.LessThan;
|
||||||
default:
|
default:
|
||||||
Debug.Fail("FlipBracket must be called with a bracket character");
|
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.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
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)
|
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;
|
base.CurrentState = StartState;
|
||||||
|
|
||||||
_operatorHandlers = new Dictionary<char, Func<CSharpTokenType>>()
|
_operatorHandlers = new Dictionary<char, Func<SyntaxKind>>()
|
||||||
{
|
{
|
||||||
{ '-', MinusOperator },
|
{ '-', MinusOperator },
|
||||||
{ '<', LessThanOperator },
|
{ '<', LessThanOperator },
|
||||||
{ '>', GreaterThanOperator },
|
{ '>', GreaterThanOperator },
|
||||||
{ '&', CreateTwoCharOperatorHandler(CSharpTokenType.And, '=', CSharpTokenType.AndAssign, '&', CSharpTokenType.DoubleAnd) },
|
{ '&', CreateTwoCharOperatorHandler(SyntaxKind.And, '=', SyntaxKind.AndAssign, '&', SyntaxKind.DoubleAnd) },
|
||||||
{ '|', CreateTwoCharOperatorHandler(CSharpTokenType.Or, '=', CSharpTokenType.OrAssign, '|', CSharpTokenType.DoubleOr) },
|
{ '|', CreateTwoCharOperatorHandler(SyntaxKind.Or, '=', SyntaxKind.OrAssign, '|', SyntaxKind.DoubleOr) },
|
||||||
{ '+', CreateTwoCharOperatorHandler(CSharpTokenType.Plus, '=', CSharpTokenType.PlusAssign, '+', CSharpTokenType.Increment) },
|
{ '+', CreateTwoCharOperatorHandler(SyntaxKind.Plus, '=', SyntaxKind.PlusAssign, '+', SyntaxKind.Increment) },
|
||||||
{ '=', CreateTwoCharOperatorHandler(CSharpTokenType.Assign, '=', CSharpTokenType.Equals, '>', CSharpTokenType.GreaterThanEqual) },
|
{ '=', CreateTwoCharOperatorHandler(SyntaxKind.Assign, '=', SyntaxKind.Equals, '>', SyntaxKind.GreaterThanEqual) },
|
||||||
{ '!', CreateTwoCharOperatorHandler(CSharpTokenType.Not, '=', CSharpTokenType.NotEqual) },
|
{ '!', CreateTwoCharOperatorHandler(SyntaxKind.Not, '=', SyntaxKind.NotEqual) },
|
||||||
{ '%', CreateTwoCharOperatorHandler(CSharpTokenType.Modulo, '=', CSharpTokenType.ModuloAssign) },
|
{ '%', CreateTwoCharOperatorHandler(SyntaxKind.Modulo, '=', SyntaxKind.ModuloAssign) },
|
||||||
{ '*', CreateTwoCharOperatorHandler(CSharpTokenType.Star, '=', CSharpTokenType.MultiplyAssign) },
|
{ '*', CreateTwoCharOperatorHandler(SyntaxKind.Star, '=', SyntaxKind.MultiplyAssign) },
|
||||||
{ ':', CreateTwoCharOperatorHandler(CSharpTokenType.Colon, ':', CSharpTokenType.DoubleColon) },
|
{ ':', CreateTwoCharOperatorHandler(SyntaxKind.Colon, ':', SyntaxKind.DoubleColon) },
|
||||||
{ '?', CreateTwoCharOperatorHandler(CSharpTokenType.QuestionMark, '?', CSharpTokenType.NullCoalesce) },
|
{ '?', CreateTwoCharOperatorHandler(SyntaxKind.QuestionMark, '?', SyntaxKind.NullCoalesce) },
|
||||||
{ '^', CreateTwoCharOperatorHandler(CSharpTokenType.Xor, '=', CSharpTokenType.XorAssign) },
|
{ '^', CreateTwoCharOperatorHandler(SyntaxKind.Xor, '=', SyntaxKind.XorAssign) },
|
||||||
{ '(', () => CSharpTokenType.LeftParenthesis },
|
{ '(', () => SyntaxKind.LeftParenthesis },
|
||||||
{ ')', () => CSharpTokenType.RightParenthesis },
|
{ ')', () => SyntaxKind.RightParenthesis },
|
||||||
{ '{', () => CSharpTokenType.LeftBrace },
|
{ '{', () => SyntaxKind.LeftBrace },
|
||||||
{ '}', () => CSharpTokenType.RightBrace },
|
{ '}', () => SyntaxKind.RightBrace },
|
||||||
{ '[', () => CSharpTokenType.LeftBracket },
|
{ '[', () => SyntaxKind.LeftBracket },
|
||||||
{ ']', () => CSharpTokenType.RightBracket },
|
{ ']', () => SyntaxKind.RightBracket },
|
||||||
{ ',', () => CSharpTokenType.Comma },
|
{ ',', () => SyntaxKind.Comma },
|
||||||
{ ';', () => CSharpTokenType.Semicolon },
|
{ ';', () => SyntaxKind.Semicolon },
|
||||||
{ '~', () => CSharpTokenType.Tilde },
|
{ '~', () => SyntaxKind.Tilde },
|
||||||
{ '#', () => CSharpTokenType.Hash }
|
{ '#', () => SyntaxKind.Hash }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,11 +133,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private new CSharpTokenizerState? CurrentState => (CSharpTokenizerState?)base.CurrentState;
|
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()
|
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
|
// 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;
|
var tokenLength = Buffer.Length;
|
||||||
|
|
||||||
|
|
@ -177,7 +178,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case CSharpTokenType.IntegerLiteral:
|
case SyntaxKind.IntegerLiteral:
|
||||||
switch (Buffer[0])
|
switch (Buffer[0])
|
||||||
{
|
{
|
||||||
case '0':
|
case '0':
|
||||||
|
|
@ -202,13 +203,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return "9";
|
return "9";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CSharpTokenType.NewLine:
|
case SyntaxKind.NewLine:
|
||||||
if (Buffer[0] == '\n')
|
if (Buffer[0] == '\n')
|
||||||
{
|
{
|
||||||
return "\n";
|
return "\n";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CSharpTokenType.WhiteSpace:
|
case SyntaxKind.Whitespace:
|
||||||
if (Buffer[0] == ' ')
|
if (Buffer[0] == ' ')
|
||||||
{
|
{
|
||||||
return " ";
|
return " ";
|
||||||
|
|
@ -218,57 +219,57 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return "\t";
|
return "\t";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CSharpTokenType.Minus:
|
case SyntaxKind.Minus:
|
||||||
return "-";
|
return "-";
|
||||||
case CSharpTokenType.Not:
|
case SyntaxKind.Not:
|
||||||
return "!";
|
return "!";
|
||||||
case CSharpTokenType.Modulo:
|
case SyntaxKind.Modulo:
|
||||||
return "%";
|
return "%";
|
||||||
case CSharpTokenType.And:
|
case SyntaxKind.And:
|
||||||
return "&";
|
return "&";
|
||||||
case CSharpTokenType.LeftParenthesis:
|
case SyntaxKind.LeftParenthesis:
|
||||||
return "(";
|
return "(";
|
||||||
case CSharpTokenType.RightParenthesis:
|
case SyntaxKind.RightParenthesis:
|
||||||
return ")";
|
return ")";
|
||||||
case CSharpTokenType.Star:
|
case SyntaxKind.Star:
|
||||||
return "*";
|
return "*";
|
||||||
case CSharpTokenType.Comma:
|
case SyntaxKind.Comma:
|
||||||
return ",";
|
return ",";
|
||||||
case CSharpTokenType.Dot:
|
case SyntaxKind.Dot:
|
||||||
return ".";
|
return ".";
|
||||||
case CSharpTokenType.Slash:
|
case SyntaxKind.Slash:
|
||||||
return "/";
|
return "/";
|
||||||
case CSharpTokenType.Colon:
|
case SyntaxKind.Colon:
|
||||||
return ":";
|
return ":";
|
||||||
case CSharpTokenType.Semicolon:
|
case SyntaxKind.Semicolon:
|
||||||
return ";";
|
return ";";
|
||||||
case CSharpTokenType.QuestionMark:
|
case SyntaxKind.QuestionMark:
|
||||||
return "?";
|
return "?";
|
||||||
case CSharpTokenType.RightBracket:
|
case SyntaxKind.RightBracket:
|
||||||
return "]";
|
return "]";
|
||||||
case CSharpTokenType.LeftBracket:
|
case SyntaxKind.LeftBracket:
|
||||||
return "[";
|
return "[";
|
||||||
case CSharpTokenType.Xor:
|
case SyntaxKind.Xor:
|
||||||
return "^";
|
return "^";
|
||||||
case CSharpTokenType.LeftBrace:
|
case SyntaxKind.LeftBrace:
|
||||||
return "{";
|
return "{";
|
||||||
case CSharpTokenType.Or:
|
case SyntaxKind.Or:
|
||||||
return "|";
|
return "|";
|
||||||
case CSharpTokenType.RightBrace:
|
case SyntaxKind.RightBrace:
|
||||||
return "}";
|
return "}";
|
||||||
case CSharpTokenType.Tilde:
|
case SyntaxKind.Tilde:
|
||||||
return "~";
|
return "~";
|
||||||
case CSharpTokenType.Plus:
|
case SyntaxKind.Plus:
|
||||||
return "+";
|
return "+";
|
||||||
case CSharpTokenType.LessThan:
|
case SyntaxKind.LessThan:
|
||||||
return "<";
|
return "<";
|
||||||
case CSharpTokenType.Assign:
|
case SyntaxKind.Assign:
|
||||||
return "=";
|
return "=";
|
||||||
case CSharpTokenType.GreaterThan:
|
case SyntaxKind.GreaterThan:
|
||||||
return ">";
|
return ">";
|
||||||
case CSharpTokenType.Hash:
|
case SyntaxKind.Hash:
|
||||||
return "#";
|
return "#";
|
||||||
case CSharpTokenType.Transition:
|
case SyntaxKind.Transition:
|
||||||
return "@";
|
return "@";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -277,53 +278,53 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case CSharpTokenType.NewLine:
|
case SyntaxKind.NewLine:
|
||||||
return "\r\n";
|
return "\r\n";
|
||||||
case CSharpTokenType.Arrow:
|
case SyntaxKind.Arrow:
|
||||||
return "->";
|
return "->";
|
||||||
case CSharpTokenType.Decrement:
|
case SyntaxKind.Decrement:
|
||||||
return "--";
|
return "--";
|
||||||
case CSharpTokenType.MinusAssign:
|
case SyntaxKind.MinusAssign:
|
||||||
return "-=";
|
return "-=";
|
||||||
case CSharpTokenType.NotEqual:
|
case SyntaxKind.NotEqual:
|
||||||
return "!=";
|
return "!=";
|
||||||
case CSharpTokenType.ModuloAssign:
|
case SyntaxKind.ModuloAssign:
|
||||||
return "%=";
|
return "%=";
|
||||||
case CSharpTokenType.AndAssign:
|
case SyntaxKind.AndAssign:
|
||||||
return "&=";
|
return "&=";
|
||||||
case CSharpTokenType.DoubleAnd:
|
case SyntaxKind.DoubleAnd:
|
||||||
return "&&";
|
return "&&";
|
||||||
case CSharpTokenType.MultiplyAssign:
|
case SyntaxKind.MultiplyAssign:
|
||||||
return "*=";
|
return "*=";
|
||||||
case CSharpTokenType.DivideAssign:
|
case SyntaxKind.DivideAssign:
|
||||||
return "/=";
|
return "/=";
|
||||||
case CSharpTokenType.DoubleColon:
|
case SyntaxKind.DoubleColon:
|
||||||
return "::";
|
return "::";
|
||||||
case CSharpTokenType.NullCoalesce:
|
case SyntaxKind.NullCoalesce:
|
||||||
return "??";
|
return "??";
|
||||||
case CSharpTokenType.XorAssign:
|
case SyntaxKind.XorAssign:
|
||||||
return "^=";
|
return "^=";
|
||||||
case CSharpTokenType.OrAssign:
|
case SyntaxKind.OrAssign:
|
||||||
return "|=";
|
return "|=";
|
||||||
case CSharpTokenType.DoubleOr:
|
case SyntaxKind.DoubleOr:
|
||||||
return "||";
|
return "||";
|
||||||
case CSharpTokenType.PlusAssign:
|
case SyntaxKind.PlusAssign:
|
||||||
return "+=";
|
return "+=";
|
||||||
case CSharpTokenType.Increment:
|
case SyntaxKind.Increment:
|
||||||
return "++";
|
return "++";
|
||||||
case CSharpTokenType.LessThanEqual:
|
case SyntaxKind.LessThanEqual:
|
||||||
return "<=";
|
return "<=";
|
||||||
case CSharpTokenType.LeftShift:
|
case SyntaxKind.LeftShift:
|
||||||
return "<<";
|
return "<<";
|
||||||
case CSharpTokenType.Equals:
|
case SyntaxKind.Equals:
|
||||||
return "==";
|
return "==";
|
||||||
case CSharpTokenType.GreaterThanEqual:
|
case SyntaxKind.GreaterThanEqual:
|
||||||
if (Buffer[0] == '=')
|
if (Buffer[0] == '=')
|
||||||
{
|
{
|
||||||
return "=>";
|
return "=>";
|
||||||
}
|
}
|
||||||
return ">=";
|
return ">=";
|
||||||
case CSharpTokenType.RightShift:
|
case SyntaxKind.RightShift:
|
||||||
return ">>";
|
return ">>";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -333,9 +334,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case CSharpTokenType.LeftShiftAssign:
|
case SyntaxKind.LeftShiftAssign:
|
||||||
return "<<=";
|
return "<<=";
|
||||||
case CSharpTokenType.RightShiftAssign:
|
case SyntaxKind.RightShiftAssign:
|
||||||
return ">>=";
|
return ">>=";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -343,9 +344,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return base.GetTokenContent(type);
|
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()
|
private StateResult Data()
|
||||||
|
|
@ -359,13 +360,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
}
|
}
|
||||||
return Stay(EndToken(CSharpTokenType.NewLine));
|
return Stay(EndToken(SyntaxKind.NewLine));
|
||||||
}
|
}
|
||||||
else if (ParserHelpers.IsWhitespace(CurrentCharacter))
|
else if (ParserHelpers.IsWhitespace(CurrentCharacter))
|
||||||
{
|
{
|
||||||
// CSharp Spec §2.3.3
|
// CSharp Spec §2.3.3
|
||||||
TakeUntil(c => !ParserHelpers.IsWhitespace(c));
|
TakeUntil(c => !ParserHelpers.IsWhitespace(c));
|
||||||
return Stay(EndToken(CSharpTokenType.WhiteSpace));
|
return Stay(EndToken(SyntaxKind.Whitespace));
|
||||||
}
|
}
|
||||||
else if (IsIdentifierStart(CurrentCharacter))
|
else if (IsIdentifierStart(CurrentCharacter))
|
||||||
{
|
{
|
||||||
|
|
@ -390,7 +391,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
return RealLiteral();
|
return RealLiteral();
|
||||||
}
|
}
|
||||||
return Stay(Single(CSharpTokenType.Dot));
|
return Stay(Single(SyntaxKind.Dot));
|
||||||
case '/':
|
case '/':
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
if (CurrentCharacter == '/')
|
if (CurrentCharacter == '/')
|
||||||
|
|
@ -406,11 +407,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
else if (CurrentCharacter == '=')
|
else if (CurrentCharacter == '=')
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return Stay(EndToken(CSharpTokenType.DivideAssign));
|
return Stay(EndToken(SyntaxKind.DivideAssign));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Stay(EndToken(CSharpTokenType.Slash));
|
return Stay(EndToken(SyntaxKind.Slash));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return Stay(EndToken(Operator()));
|
return Stay(EndToken(Operator()));
|
||||||
|
|
@ -429,78 +430,78 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
return Transition(
|
return Transition(
|
||||||
CSharpTokenizerState.AfterRazorCommentTransition,
|
CSharpTokenizerState.AfterRazorCommentTransition,
|
||||||
EndToken(CSharpTokenType.RazorCommentTransition));
|
EndToken(SyntaxKind.RazorCommentTransition));
|
||||||
}
|
}
|
||||||
else if (CurrentCharacter == '@')
|
else if (CurrentCharacter == '@')
|
||||||
{
|
{
|
||||||
// Could be escaped comment transition
|
// Could be escaped comment transition
|
||||||
return Transition(
|
return Transition(
|
||||||
CSharpTokenizerState.EscapedRazorCommentTransition,
|
CSharpTokenizerState.EscapedRazorCommentTransition,
|
||||||
EndToken(CSharpTokenType.Transition));
|
EndToken(SyntaxKind.Transition));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Stay(EndToken(CSharpTokenType.Transition));
|
return Stay(EndToken(SyntaxKind.Transition));
|
||||||
}
|
}
|
||||||
|
|
||||||
private StateResult EscapedRazorCommentTransition()
|
private StateResult EscapedRazorCommentTransition()
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Transition));
|
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.Transition));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CSharpTokenType Operator()
|
private SyntaxKind Operator()
|
||||||
{
|
{
|
||||||
var first = CurrentCharacter;
|
var first = CurrentCharacter;
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
Func<CSharpTokenType> handler;
|
Func<SyntaxKind> handler;
|
||||||
if (_operatorHandlers.TryGetValue(first, out handler))
|
if (_operatorHandlers.TryGetValue(first, out handler))
|
||||||
{
|
{
|
||||||
return handler();
|
return handler();
|
||||||
}
|
}
|
||||||
return CSharpTokenType.Unknown;
|
return SyntaxKind.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CSharpTokenType LessThanOperator()
|
private SyntaxKind LessThanOperator()
|
||||||
{
|
{
|
||||||
if (CurrentCharacter == '=')
|
if (CurrentCharacter == '=')
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return CSharpTokenType.LessThanEqual;
|
return SyntaxKind.LessThanEqual;
|
||||||
}
|
}
|
||||||
return CSharpTokenType.LessThan;
|
return SyntaxKind.LessThan;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CSharpTokenType GreaterThanOperator()
|
private SyntaxKind GreaterThanOperator()
|
||||||
{
|
{
|
||||||
if (CurrentCharacter == '=')
|
if (CurrentCharacter == '=')
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return CSharpTokenType.GreaterThanEqual;
|
return SyntaxKind.GreaterThanEqual;
|
||||||
}
|
}
|
||||||
return CSharpTokenType.GreaterThan;
|
return SyntaxKind.GreaterThan;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CSharpTokenType MinusOperator()
|
private SyntaxKind MinusOperator()
|
||||||
{
|
{
|
||||||
if (CurrentCharacter == '>')
|
if (CurrentCharacter == '>')
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return CSharpTokenType.Arrow;
|
return SyntaxKind.Arrow;
|
||||||
}
|
}
|
||||||
else if (CurrentCharacter == '-')
|
else if (CurrentCharacter == '-')
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return CSharpTokenType.Decrement;
|
return SyntaxKind.Decrement;
|
||||||
}
|
}
|
||||||
else if (CurrentCharacter == '=')
|
else if (CurrentCharacter == '=')
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
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 () =>
|
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 () =>
|
return () =>
|
||||||
{
|
{
|
||||||
|
|
@ -550,14 +551,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
RazorDiagnosticFactory.CreateParsing_UnterminatedStringLiteral(
|
RazorDiagnosticFactory.CreateParsing_UnterminatedStringLiteral(
|
||||||
new SourceSpan(CurrentStart, contentLength: 1 /* end of file */)));
|
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));
|
TakeUntil(c => c == '\\' || c == quote || ParserHelpers.IsNewLine(c));
|
||||||
if (CurrentCharacter == '\\')
|
if (CurrentCharacter == '\\')
|
||||||
|
|
@ -594,7 +595,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
RazorDiagnosticFactory.CreateParsing_BlockCommentNotTerminated(
|
RazorDiagnosticFactory.CreateParsing_BlockCommentNotTerminated(
|
||||||
new SourceSpan(CurrentStart, contentLength: 1 /* end of file */)));
|
new SourceSpan(CurrentStart, contentLength: 1 /* end of file */)));
|
||||||
|
|
||||||
return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Comment));
|
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.CSharpComment));
|
||||||
}
|
}
|
||||||
if (CurrentCharacter == '*')
|
if (CurrentCharacter == '*')
|
||||||
{
|
{
|
||||||
|
|
@ -602,7 +603,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
if (CurrentCharacter == '/')
|
if (CurrentCharacter == '/')
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return Transition(CSharpTokenizerState.Data, EndToken(CSharpTokenType.Comment));
|
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.CSharpComment));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Stay();
|
return Stay();
|
||||||
|
|
@ -612,7 +613,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
private StateResult SingleLineComment()
|
private StateResult SingleLineComment()
|
||||||
{
|
{
|
||||||
TakeUntil(c => ParserHelpers.IsNewLine(c));
|
TakeUntil(c => ParserHelpers.IsNewLine(c));
|
||||||
return Stay(EndToken(CSharpTokenType.Comment));
|
return Stay(EndToken(SyntaxKind.CSharpComment));
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSharp Spec §2.4.4
|
// CSharp Spec §2.4.4
|
||||||
|
|
@ -632,7 +633,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
TakeUntil(c => !IsHexDigit(c));
|
TakeUntil(c => !IsHexDigit(c));
|
||||||
TakeIntegerSuffix();
|
TakeIntegerSuffix();
|
||||||
return Stay(EndToken(CSharpTokenType.IntegerLiteral));
|
return Stay(EndToken(SyntaxKind.IntegerLiteral));
|
||||||
}
|
}
|
||||||
|
|
||||||
private StateResult DecimalLiteral()
|
private StateResult DecimalLiteral()
|
||||||
|
|
@ -650,7 +651,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TakeIntegerSuffix();
|
TakeIntegerSuffix();
|
||||||
return Stay(EndToken(CSharpTokenType.IntegerLiteral));
|
return Stay(EndToken(SyntaxKind.IntegerLiteral));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -669,7 +670,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
}
|
}
|
||||||
return Stay(EndToken(CSharpTokenType.RealLiteral));
|
return Stay(EndToken(SyntaxKind.RealLiteral));
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSharp Spec §2.4.4.3
|
// CSharp Spec §2.4.4.3
|
||||||
|
|
@ -708,21 +709,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Debug.Assert(IsIdentifierStart(CurrentCharacter));
|
Debug.Assert(IsIdentifierStart(CurrentCharacter));
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
TakeUntil(c => !IsIdentifierPart(c));
|
TakeUntil(c => !IsIdentifierPart(c));
|
||||||
CSharpToken token = null;
|
SyntaxToken token = null;
|
||||||
if (HaveContent)
|
if (HaveContent)
|
||||||
{
|
{
|
||||||
CSharpKeyword keyword;
|
CSharpKeyword keyword;
|
||||||
var type = CSharpTokenType.Identifier;
|
var type = SyntaxKind.Identifier;
|
||||||
var tokenContent = Buffer.ToString();
|
var tokenContent = Buffer.ToString();
|
||||||
if (_keywords.TryGetValue(tokenContent, out keyword))
|
if (_keywords.TryGetValue(tokenContent, out keyword))
|
||||||
{
|
{
|
||||||
type = CSharpTokenType.Keyword;
|
type = SyntaxKind.Keyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
token = new CSharpToken(tokenContent, type)
|
token = SyntaxFactory.Token(type, tokenContent);
|
||||||
{
|
|
||||||
Keyword = type == CSharpTokenType.Keyword ? (CSharpKeyword?)keyword : null,
|
|
||||||
};
|
|
||||||
|
|
||||||
Buffer.Clear();
|
Buffer.Clear();
|
||||||
CurrentErrors.Clear();
|
CurrentErrors.Clear();
|
||||||
|
|
@ -736,7 +734,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return Transition((int)state, result: null);
|
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);
|
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');
|
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
|
private enum CSharpTokenizerState
|
||||||
{
|
{
|
||||||
Data,
|
Data,
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
internal class CodeBlockEditHandler : SpanEditHandler
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
|
|
@ -17,11 +18,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
protected override StateResult Dispatch()
|
protected override StateResult Dispatch()
|
||||||
{
|
{
|
||||||
var result = base.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;
|
_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;
|
_visitedFirstTokenLineEnd = true;
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override CSharpToken NextToken()
|
public override SyntaxToken NextToken()
|
||||||
{
|
{
|
||||||
// Post-Condition: Buffer should be empty at the start of Next()
|
// Post-Condition: Buffer should be empty at the start of Next()
|
||||||
Debug.Assert(Buffer.Length == 0);
|
Debug.Assert(Buffer.Length == 0);
|
||||||
|
|
@ -48,15 +49,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsValidTokenType(CSharpTokenType type)
|
private bool IsValidTokenType(SyntaxKind kind)
|
||||||
{
|
{
|
||||||
return type != CSharpTokenType.WhiteSpace &&
|
return kind != SyntaxKind.Whitespace &&
|
||||||
type != CSharpTokenType.NewLine &&
|
kind != SyntaxKind.NewLine &&
|
||||||
type != CSharpTokenType.Comment &&
|
kind != SyntaxKind.CSharpComment &&
|
||||||
type != CSharpTokenType.RazorComment &&
|
kind != SyntaxKind.RazorCommentLiteral &&
|
||||||
type != CSharpTokenType.RazorCommentStar &&
|
kind != SyntaxKind.RazorCommentStar &&
|
||||||
type != CSharpTokenType.RazorCommentTransition &&
|
kind != SyntaxKind.RazorCommentTransition &&
|
||||||
type != CSharpTokenType.Transition;
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
|
|
@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
protected override StateResult Dispatch()
|
protected override StateResult Dispatch()
|
||||||
{
|
{
|
||||||
var result = base.Dispatch();
|
var result = base.Dispatch();
|
||||||
if (result.Result != null && IsValidTokenType(result.Result.Type))
|
if (result.Result != null && IsValidTokenType(result.Result.Kind))
|
||||||
{
|
{
|
||||||
_visitedFirstTokenStart = true;
|
_visitedFirstTokenStart = true;
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override HtmlToken NextToken()
|
public override SyntaxToken NextToken()
|
||||||
{
|
{
|
||||||
// Post-Condition: Buffer should be empty at the start of Next()
|
// Post-Condition: Buffer should be empty at the start of Next()
|
||||||
Debug.Assert(Buffer.Length == 0);
|
Debug.Assert(Buffer.Length == 0);
|
||||||
|
|
@ -43,14 +44,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsValidTokenType(HtmlTokenType type)
|
private bool IsValidTokenType(SyntaxKind kind)
|
||||||
{
|
{
|
||||||
return type != HtmlTokenType.WhiteSpace &&
|
return kind != SyntaxKind.Whitespace &&
|
||||||
type != HtmlTokenType.NewLine &&
|
kind != SyntaxKind.NewLine &&
|
||||||
type != HtmlTokenType.RazorComment &&
|
kind != SyntaxKind.RazorCommentLiteral &&
|
||||||
type != HtmlTokenType.RazorCommentStar &&
|
kind != SyntaxKind.RazorCommentStar &&
|
||||||
type != HtmlTokenType.RazorCommentTransition &&
|
kind != SyntaxKind.RazorCommentTransition &&
|
||||||
type != HtmlTokenType.Transition;
|
kind != SyntaxKind.Transition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,11 @@
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
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();
|
private static readonly HtmlLanguageCharacteristics _instance = new HtmlLanguageCharacteristics();
|
||||||
|
|
||||||
|
|
@ -19,47 +20,47 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
get { return _instance; }
|
get { return _instance; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetSample(HtmlTokenType type)
|
public override string GetSample(SyntaxKind type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case HtmlTokenType.Text:
|
case SyntaxKind.Text:
|
||||||
return Resources.HtmlToken_Text;
|
return Resources.HtmlToken_Text;
|
||||||
case HtmlTokenType.WhiteSpace:
|
case SyntaxKind.Whitespace:
|
||||||
return Resources.HtmlToken_WhiteSpace;
|
return Resources.HtmlToken_WhiteSpace;
|
||||||
case HtmlTokenType.NewLine:
|
case SyntaxKind.NewLine:
|
||||||
return Resources.HtmlToken_NewLine;
|
return Resources.HtmlToken_NewLine;
|
||||||
case HtmlTokenType.OpenAngle:
|
case SyntaxKind.OpenAngle:
|
||||||
return "<";
|
return "<";
|
||||||
case HtmlTokenType.Bang:
|
case SyntaxKind.Bang:
|
||||||
return "!";
|
return "!";
|
||||||
case HtmlTokenType.ForwardSlash:
|
case SyntaxKind.ForwardSlash:
|
||||||
return "/";
|
return "/";
|
||||||
case HtmlTokenType.QuestionMark:
|
case SyntaxKind.QuestionMark:
|
||||||
return "?";
|
return "?";
|
||||||
case HtmlTokenType.DoubleHyphen:
|
case SyntaxKind.DoubleHyphen:
|
||||||
return "--";
|
return "--";
|
||||||
case HtmlTokenType.LeftBracket:
|
case SyntaxKind.LeftBracket:
|
||||||
return "[";
|
return "[";
|
||||||
case HtmlTokenType.CloseAngle:
|
case SyntaxKind.CloseAngle:
|
||||||
return ">";
|
return ">";
|
||||||
case HtmlTokenType.RightBracket:
|
case SyntaxKind.RightBracket:
|
||||||
return "]";
|
return "]";
|
||||||
case HtmlTokenType.Equals:
|
case SyntaxKind.Equals:
|
||||||
return "=";
|
return "=";
|
||||||
case HtmlTokenType.DoubleQuote:
|
case SyntaxKind.DoubleQuote:
|
||||||
return "\"";
|
return "\"";
|
||||||
case HtmlTokenType.SingleQuote:
|
case SyntaxKind.SingleQuote:
|
||||||
return "'";
|
return "'";
|
||||||
case HtmlTokenType.Transition:
|
case SyntaxKind.Transition:
|
||||||
return "@";
|
return "@";
|
||||||
case HtmlTokenType.Colon:
|
case SyntaxKind.Colon:
|
||||||
return ":";
|
return ":";
|
||||||
case HtmlTokenType.RazorComment:
|
case SyntaxKind.RazorCommentLiteral:
|
||||||
return Resources.HtmlToken_RazorComment;
|
return Resources.HtmlToken_RazorComment;
|
||||||
case HtmlTokenType.RazorCommentStar:
|
case SyntaxKind.RazorCommentStar:
|
||||||
return "*";
|
return "*";
|
||||||
case HtmlTokenType.RazorCommentTransition:
|
case SyntaxKind.RazorCommentTransition:
|
||||||
return "@";
|
return "@";
|
||||||
default:
|
default:
|
||||||
return Resources.Token_Unknown;
|
return Resources.Token_Unknown;
|
||||||
|
|
@ -71,57 +72,57 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return new HtmlTokenizer(source);
|
return new HtmlTokenizer(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override HtmlTokenType FlipBracket(HtmlTokenType bracket)
|
public override SyntaxKind FlipBracket(SyntaxKind bracket)
|
||||||
{
|
{
|
||||||
switch (bracket)
|
switch (bracket)
|
||||||
{
|
{
|
||||||
case HtmlTokenType.LeftBracket:
|
case SyntaxKind.LeftBracket:
|
||||||
return HtmlTokenType.RightBracket;
|
return SyntaxKind.RightBracket;
|
||||||
case HtmlTokenType.OpenAngle:
|
case SyntaxKind.OpenAngle:
|
||||||
return HtmlTokenType.CloseAngle;
|
return SyntaxKind.CloseAngle;
|
||||||
case HtmlTokenType.RightBracket:
|
case SyntaxKind.RightBracket:
|
||||||
return HtmlTokenType.LeftBracket;
|
return SyntaxKind.LeftBracket;
|
||||||
case HtmlTokenType.CloseAngle:
|
case SyntaxKind.CloseAngle:
|
||||||
return HtmlTokenType.OpenAngle;
|
return SyntaxKind.OpenAngle;
|
||||||
default:
|
default:
|
||||||
Debug.Fail("FlipBracket must be called with a bracket character");
|
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)
|
switch (type)
|
||||||
{
|
{
|
||||||
case KnownTokenType.CommentStart:
|
case KnownTokenType.CommentStart:
|
||||||
return HtmlTokenType.RazorCommentTransition;
|
return SyntaxKind.RazorCommentTransition;
|
||||||
case KnownTokenType.CommentStar:
|
case KnownTokenType.CommentStar:
|
||||||
return HtmlTokenType.RazorCommentStar;
|
return SyntaxKind.RazorCommentStar;
|
||||||
case KnownTokenType.CommentBody:
|
case KnownTokenType.CommentBody:
|
||||||
return HtmlTokenType.RazorComment;
|
return SyntaxKind.RazorCommentLiteral;
|
||||||
case KnownTokenType.Identifier:
|
case KnownTokenType.Identifier:
|
||||||
return HtmlTokenType.Text;
|
return SyntaxKind.Text;
|
||||||
case KnownTokenType.Keyword:
|
case KnownTokenType.Keyword:
|
||||||
return HtmlTokenType.Text;
|
return SyntaxKind.Text;
|
||||||
case KnownTokenType.NewLine:
|
case KnownTokenType.NewLine:
|
||||||
return HtmlTokenType.NewLine;
|
return SyntaxKind.NewLine;
|
||||||
case KnownTokenType.Transition:
|
case KnownTokenType.Transition:
|
||||||
return HtmlTokenType.Transition;
|
return SyntaxKind.Transition;
|
||||||
case KnownTokenType.WhiteSpace:
|
case KnownTokenType.WhiteSpace:
|
||||||
return HtmlTokenType.WhiteSpace;
|
return SyntaxKind.Whitespace;
|
||||||
default:
|
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.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
// Tokenizer _loosely_ based on http://dev.w3.org/html5/spec/Overview.html#tokenization
|
// 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 = '@';
|
private const char TransitionChar = '@';
|
||||||
|
|
||||||
|
|
@ -21,24 +22,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
private new HtmlTokenizerState? CurrentState => (HtmlTokenizerState?)base.CurrentState;
|
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()
|
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
|
// 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;
|
var tokenLength = Buffer.Length;
|
||||||
|
|
||||||
|
|
@ -74,27 +75,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case HtmlTokenType.OpenAngle:
|
case SyntaxKind.OpenAngle:
|
||||||
return "<";
|
return "<";
|
||||||
case HtmlTokenType.Bang:
|
case SyntaxKind.Bang:
|
||||||
return "!";
|
return "!";
|
||||||
case HtmlTokenType.ForwardSlash:
|
case SyntaxKind.ForwardSlash:
|
||||||
return "/";
|
return "/";
|
||||||
case HtmlTokenType.QuestionMark:
|
case SyntaxKind.QuestionMark:
|
||||||
return "?";
|
return "?";
|
||||||
case HtmlTokenType.LeftBracket:
|
case SyntaxKind.LeftBracket:
|
||||||
return "[";
|
return "[";
|
||||||
case HtmlTokenType.CloseAngle:
|
case SyntaxKind.CloseAngle:
|
||||||
return ">";
|
return ">";
|
||||||
case HtmlTokenType.RightBracket:
|
case SyntaxKind.RightBracket:
|
||||||
return "]";
|
return "]";
|
||||||
case HtmlTokenType.Equals:
|
case SyntaxKind.Equals:
|
||||||
return "=";
|
return "=";
|
||||||
case HtmlTokenType.DoubleQuote:
|
case SyntaxKind.DoubleQuote:
|
||||||
return "\"";
|
return "\"";
|
||||||
case HtmlTokenType.SingleQuote:
|
case SyntaxKind.SingleQuote:
|
||||||
return "'";
|
return "'";
|
||||||
case HtmlTokenType.WhiteSpace:
|
case SyntaxKind.Whitespace:
|
||||||
if (Buffer[0] == ' ')
|
if (Buffer[0] == ' ')
|
||||||
{
|
{
|
||||||
return " ";
|
return " ";
|
||||||
|
|
@ -104,7 +105,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return "\t";
|
return "\t";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HtmlTokenType.NewLine:
|
case SyntaxKind.NewLine:
|
||||||
if (Buffer[0] == '\n')
|
if (Buffer[0] == '\n')
|
||||||
{
|
{
|
||||||
return "\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";
|
return "\r\n";
|
||||||
}
|
}
|
||||||
|
|
@ -139,17 +140,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
return Transition(
|
return Transition(
|
||||||
HtmlTokenizerState.AfterRazorCommentTransition,
|
HtmlTokenizerState.AfterRazorCommentTransition,
|
||||||
EndToken(HtmlTokenType.RazorCommentTransition));
|
EndToken(SyntaxKind.RazorCommentTransition));
|
||||||
}
|
}
|
||||||
else if (CurrentCharacter == '@')
|
else if (CurrentCharacter == '@')
|
||||||
{
|
{
|
||||||
// Could be escaped comment transition
|
// Could be escaped comment transition
|
||||||
return Transition(
|
return Transition(
|
||||||
HtmlTokenizerState.EscapedRazorCommentTransition,
|
HtmlTokenizerState.EscapedRazorCommentTransition,
|
||||||
EndToken(HtmlTokenType.Transition));
|
EndToken(SyntaxKind.Transition));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Stay(EndToken(HtmlTokenType.Transition));
|
return Stay(EndToken(SyntaxKind.Transition));
|
||||||
}
|
}
|
||||||
else if (AtToken())
|
else if (AtToken())
|
||||||
{
|
{
|
||||||
|
|
@ -164,7 +165,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
private StateResult EscapedRazorCommentTransition()
|
private StateResult EscapedRazorCommentTransition()
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return Transition(HtmlTokenizerState.Data, EndToken(HtmlTokenType.Transition));
|
return Transition(HtmlTokenizerState.Data, EndToken(SyntaxKind.Transition));
|
||||||
}
|
}
|
||||||
|
|
||||||
private StateResult Text()
|
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)
|
// 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());
|
Debug.Assert(AtToken());
|
||||||
var sym = CurrentCharacter;
|
var sym = CurrentCharacter;
|
||||||
|
|
@ -201,45 +202,45 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
switch (sym)
|
switch (sym)
|
||||||
{
|
{
|
||||||
case '<':
|
case '<':
|
||||||
return EndToken(HtmlTokenType.OpenAngle);
|
return EndToken(SyntaxKind.OpenAngle);
|
||||||
case '!':
|
case '!':
|
||||||
return EndToken(HtmlTokenType.Bang);
|
return EndToken(SyntaxKind.Bang);
|
||||||
case '/':
|
case '/':
|
||||||
return EndToken(HtmlTokenType.ForwardSlash);
|
return EndToken(SyntaxKind.ForwardSlash);
|
||||||
case '?':
|
case '?':
|
||||||
return EndToken(HtmlTokenType.QuestionMark);
|
return EndToken(SyntaxKind.QuestionMark);
|
||||||
case '[':
|
case '[':
|
||||||
return EndToken(HtmlTokenType.LeftBracket);
|
return EndToken(SyntaxKind.LeftBracket);
|
||||||
case '>':
|
case '>':
|
||||||
return EndToken(HtmlTokenType.CloseAngle);
|
return EndToken(SyntaxKind.CloseAngle);
|
||||||
case ']':
|
case ']':
|
||||||
return EndToken(HtmlTokenType.RightBracket);
|
return EndToken(SyntaxKind.RightBracket);
|
||||||
case '=':
|
case '=':
|
||||||
return EndToken(HtmlTokenType.Equals);
|
return EndToken(SyntaxKind.Equals);
|
||||||
case '"':
|
case '"':
|
||||||
return EndToken(HtmlTokenType.DoubleQuote);
|
return EndToken(SyntaxKind.DoubleQuote);
|
||||||
case '\'':
|
case '\'':
|
||||||
return EndToken(HtmlTokenType.SingleQuote);
|
return EndToken(SyntaxKind.SingleQuote);
|
||||||
case '-':
|
case '-':
|
||||||
Debug.Assert(CurrentCharacter == '-');
|
Debug.Assert(CurrentCharacter == '-');
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return EndToken(HtmlTokenType.DoubleHyphen);
|
return EndToken(SyntaxKind.DoubleHyphen);
|
||||||
default:
|
default:
|
||||||
Debug.Fail("Unexpected token!");
|
Debug.Fail("Unexpected token!");
|
||||||
return EndToken(HtmlTokenType.Unknown);
|
return EndToken(SyntaxKind.Unknown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HtmlToken Whitespace()
|
private SyntaxToken Whitespace()
|
||||||
{
|
{
|
||||||
while (ParserHelpers.IsWhitespace(CurrentCharacter))
|
while (ParserHelpers.IsWhitespace(CurrentCharacter))
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
}
|
}
|
||||||
return EndToken(HtmlTokenType.WhiteSpace);
|
return EndToken(SyntaxKind.Whitespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HtmlToken Newline()
|
private SyntaxToken Newline()
|
||||||
{
|
{
|
||||||
Debug.Assert(ParserHelpers.IsNewLine(CurrentCharacter));
|
Debug.Assert(ParserHelpers.IsNewLine(CurrentCharacter));
|
||||||
// CSharp Spec §2.3.1
|
// CSharp Spec §2.3.1
|
||||||
|
|
@ -249,7 +250,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
}
|
}
|
||||||
return EndToken(HtmlTokenType.NewLine);
|
return EndToken(SyntaxKind.NewLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AtToken()
|
private bool AtToken()
|
||||||
|
|
@ -274,7 +275,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return Transition((int)state, result: null);
|
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);
|
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.
|
// 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.
|
// 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
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
internal interface ITokenizer
|
internal interface ITokenizer
|
||||||
{
|
{
|
||||||
IToken NextToken();
|
SyntaxToken NextToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
private readonly ISet<string> _keywords;
|
private readonly ISet<string> _keywords;
|
||||||
private readonly IReadOnlyCollection<string> _readOnlyKeywords;
|
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)
|
: base(tokenizer)
|
||||||
{
|
{
|
||||||
_keywords = keywords ?? new HashSet<string>();
|
_keywords = keywords ?? new HashSet<string>();
|
||||||
|
|
@ -172,20 +173,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
for (var i = 0; i < target.Tokens.Count; i++)
|
for (var i = 0; i < target.Tokens.Count; i++)
|
||||||
{
|
{
|
||||||
var token = target.Tokens[i] as CSharpToken;
|
var token = target.Tokens[i];
|
||||||
|
|
||||||
if (token == null)
|
if (token == null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokenStartIndex = token.Start.AbsoluteIndex;
|
var tokenStartIndex = token.Position;
|
||||||
var tokenEndIndex = tokenStartIndex + token.Content.Length;
|
var tokenEndIndex = token.EndPosition;
|
||||||
|
|
||||||
// We're looking for the first token that contains the SourceChange.
|
// We're looking for the first token that contains the SourceChange.
|
||||||
if (tokenEndIndex > change.Span.AbsoluteIndex)
|
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.
|
// 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;
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newToken = (CSharpToken)newTokens.First();
|
var newToken = newTokens.First();
|
||||||
if (newToken.Type == CSharpTokenType.Identifier)
|
if (newToken.Kind == SyntaxKind.Identifier)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -241,8 +242,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
var changeStart = change.Span.AbsoluteIndex;
|
var changeStart = change.Span.AbsoluteIndex;
|
||||||
var changeLength = change.Span.Length;
|
var changeLength = change.Span.Length;
|
||||||
var changeEnd = changeStart + changeLength;
|
var changeEnd = changeStart + changeLength;
|
||||||
var tokens = target.Tokens.Cast<CSharpToken>().ToArray();
|
if (!IsInsideParenthesis(changeStart, target.Tokens) || !IsInsideParenthesis(changeEnd, target.Tokens))
|
||||||
if (!IsInsideParenthesis(changeStart, tokens) || !IsInsideParenthesis(changeEnd, tokens))
|
|
||||||
{
|
{
|
||||||
// Either the start or end of the delete does not fall inside of parenthesis, unacceptable inner deletion.
|
// Either the start or end of the delete does not fall inside of parenthesis, unacceptable inner deletion.
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -274,8 +274,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokens = target.Tokens.Cast<CSharpToken>().ToArray();
|
if (IsInsideParenthesis(change.Span.AbsoluteIndex, target.Tokens))
|
||||||
if (IsInsideParenthesis(change.Span.AbsoluteIndex, tokens))
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -284,7 +283,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// 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 balanceCount = 0;
|
||||||
var foundInsertionPoint = false;
|
var foundInsertionPoint = false;
|
||||||
|
|
@ -322,9 +321,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// 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)
|
if (tokenStart == position)
|
||||||
{
|
{
|
||||||
// Token is exactly at the insertion point.
|
// Token is exactly at the insertion point.
|
||||||
|
|
@ -342,14 +341,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal static bool TryUpdateBalanceCount(CSharpToken token, ref int count)
|
internal static bool TryUpdateBalanceCount(SyntaxToken token, ref int count)
|
||||||
{
|
{
|
||||||
var updatedCount = count;
|
var updatedCount = count;
|
||||||
if (token.Type == CSharpTokenType.LeftParenthesis)
|
if (token.Kind == SyntaxKind.LeftParenthesis)
|
||||||
{
|
{
|
||||||
updatedCount++;
|
updatedCount++;
|
||||||
}
|
}
|
||||||
else if (token.Type == CSharpTokenType.RightParenthesis)
|
else if (token.Kind == SyntaxKind.RightParenthesis)
|
||||||
{
|
{
|
||||||
if (updatedCount == 0)
|
if (updatedCount == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -358,7 +357,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
updatedCount--;
|
updatedCount--;
|
||||||
}
|
}
|
||||||
else if (token.Type == CSharpTokenType.StringLiteral)
|
else if (token.Kind == SyntaxKind.StringLiteral)
|
||||||
{
|
{
|
||||||
var content = token.Content;
|
var content = token.Content;
|
||||||
if (content.Length > 0 && content[content.Length - 1] != '"')
|
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;
|
var content = token.Content;
|
||||||
if (content.Length > 0 && content[content.Length - 1] != '\'')
|
if (content.Length > 0 && content[content.Length - 1] != '\'')
|
||||||
|
|
|
||||||
|
|
@ -3,30 +3,29 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
internal abstract class LanguageCharacteristics<TTokenizer, TToken, TTokenType>
|
internal abstract class LanguageCharacteristics<TTokenizer>
|
||||||
where TTokenType : struct
|
where TTokenizer : Tokenizer
|
||||||
where TTokenizer : Tokenizer<TToken, TTokenType>
|
|
||||||
where TToken : TokenBase<TTokenType>
|
|
||||||
{
|
{
|
||||||
public abstract string GetSample(TTokenType type);
|
public abstract string GetSample(SyntaxKind type);
|
||||||
public abstract TTokenizer CreateTokenizer(ITextDocument source);
|
public abstract TTokenizer CreateTokenizer(ITextDocument source);
|
||||||
public abstract TTokenType FlipBracket(TTokenType bracket);
|
public abstract SyntaxKind FlipBracket(SyntaxKind bracket);
|
||||||
public abstract TToken CreateMarkerToken();
|
public abstract SyntaxToken CreateMarkerToken();
|
||||||
|
|
||||||
public virtual IEnumerable<TToken> TokenizeString(string content)
|
public virtual IEnumerable<SyntaxToken> TokenizeString(string content)
|
||||||
{
|
{
|
||||||
return TokenizeString(SourceLocation.Zero, 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))
|
using (var reader = new SeekableTextReader(input, start.FilePath))
|
||||||
{
|
{
|
||||||
var tok = CreateTokenizer(reader);
|
var tok = CreateTokenizer(reader);
|
||||||
TToken token;
|
SyntaxToken token;
|
||||||
while ((token = tok.NextToken()) != null)
|
while ((token = tok.NextToken()) != null)
|
||||||
{
|
{
|
||||||
yield return token;
|
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);
|
return IsKnownTokenType(token, KnownTokenType.WhiteSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsNewLine(TToken token)
|
public virtual bool IsNewLine(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return IsKnownTokenType(token, KnownTokenType.NewLine);
|
return IsKnownTokenType(token, KnownTokenType.NewLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsIdentifier(TToken token)
|
public virtual bool IsIdentifier(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return IsKnownTokenType(token, KnownTokenType.Identifier);
|
return IsKnownTokenType(token, KnownTokenType.Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsKeyword(TToken token)
|
public virtual bool IsKeyword(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return IsKnownTokenType(token, KnownTokenType.Keyword);
|
return IsKnownTokenType(token, KnownTokenType.Keyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsTransition(TToken token)
|
public virtual bool IsTransition(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return IsKnownTokenType(token, KnownTokenType.Transition);
|
return IsKnownTokenType(token, KnownTokenType.Transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsCommentStart(TToken token)
|
public virtual bool IsCommentStart(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return IsKnownTokenType(token, KnownTokenType.CommentStart);
|
return IsKnownTokenType(token, KnownTokenType.CommentStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsCommentStar(TToken token)
|
public virtual bool IsCommentStar(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return IsKnownTokenType(token, KnownTokenType.CommentStar);
|
return IsKnownTokenType(token, KnownTokenType.CommentStar);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsCommentBody(TToken token)
|
public virtual bool IsCommentBody(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return IsKnownTokenType(token, KnownTokenType.CommentBody);
|
return IsKnownTokenType(token, KnownTokenType.CommentBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsUnknown(TToken token)
|
public virtual bool IsUnknown(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return IsKnownTokenType(token, KnownTokenType.Unknown);
|
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);
|
var left = CreateToken(token.Content.Substring(0, splitAt), leftType, RazorDiagnostic.EmptyArray);
|
||||||
|
|
||||||
TToken right = null;
|
SyntaxToken right = null;
|
||||||
if (splitAt < token.Content.Length)
|
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);
|
return Tuple.Create(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract TTokenType GetKnownTokenType(KnownTokenType type);
|
public abstract SyntaxKind GetKnownTokenType(KnownTokenType type);
|
||||||
|
|
||||||
public virtual bool KnowsTokenType(KnownTokenType type)
|
public virtual bool KnowsTokenType(KnownTokenType type)
|
||||||
{
|
{
|
||||||
return type == KnownTokenType.Unknown || !Equals(GetKnownTokenType(type), GetKnownTokenType(KnownTokenType.Unknown));
|
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.Kind = SpanKindInternal.Markup;
|
||||||
builder.ChunkGenerator = new MarkupChunkGenerator();
|
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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
internal class Span : SyntaxTreeNode
|
internal class Span : SyntaxTreeNode
|
||||||
{
|
{
|
||||||
|
private static readonly List<SyntaxToken> EmptyTokenList = new List<SyntaxToken>(0);
|
||||||
private static readonly int TypeHashCode = typeof(Span).GetHashCode();
|
private static readonly int TypeHashCode = typeof(Span).GetHashCode();
|
||||||
|
private IReadOnlyList<Syntax.InternalSyntax.SyntaxToken> _greenTokens;
|
||||||
private string _content;
|
private string _content;
|
||||||
private int? _length;
|
private int? _length;
|
||||||
private SourceLocation _start;
|
private SourceLocation _start;
|
||||||
|
|
@ -23,7 +27,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
public ISpanChunkGenerator ChunkGenerator { get; private set; }
|
public ISpanChunkGenerator ChunkGenerator { get; private set; }
|
||||||
|
|
||||||
public SpanKindInternal Kind { 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
|
// Allow test code to re-link spans
|
||||||
public Span Previous { get; internal set; }
|
public Span Previous { get; internal set; }
|
||||||
|
|
@ -31,6 +36,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public SpanEditHandler EditHandler { get; private set; }
|
public SpanEditHandler EditHandler { get; private set; }
|
||||||
|
|
||||||
|
public SyntaxNode SyntaxNode { get; private set; }
|
||||||
|
|
||||||
public override bool IsBlock => false;
|
public override bool IsBlock => false;
|
||||||
|
|
||||||
public override int Length
|
public override int Length
|
||||||
|
|
@ -93,19 +100,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
public void ReplaceWith(SpanBuilder builder)
|
public void ReplaceWith(SpanBuilder builder)
|
||||||
{
|
{
|
||||||
Kind = builder.Kind;
|
Kind = builder.Kind;
|
||||||
Tokens = builder.Tokens;
|
_greenTokens = builder.Tokens;
|
||||||
|
|
||||||
for (var i = 0; i <Tokens.Count; i++)
|
|
||||||
{
|
|
||||||
Tokens[i].Parent = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditHandler = builder.EditHandler;
|
EditHandler = builder.EditHandler;
|
||||||
ChunkGenerator = builder.ChunkGenerator ?? SpanChunkGenerator.Null;
|
ChunkGenerator = builder.ChunkGenerator ?? SpanChunkGenerator.Null;
|
||||||
_start = builder.Start;
|
_start = builder.Start;
|
||||||
|
SyntaxNode = builder.SyntaxNode?.CreateRed(parent: null, position: _start.AbsoluteIndex);
|
||||||
_content = null;
|
_content = null;
|
||||||
_length = 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();
|
Parent?.ChildChanged();
|
||||||
|
|
||||||
// Since we took references to the values in SpanBuilder, clear its references out
|
// Since we took references to the values in SpanBuilder, clear its references out
|
||||||
|
|
@ -145,8 +162,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override bool EquivalentTo(SyntaxTreeNode node)
|
public override bool EquivalentTo(SyntaxTreeNode node)
|
||||||
{
|
{
|
||||||
var other = node as Span;
|
return node is Span other &&
|
||||||
return other != null &&
|
|
||||||
Kind.Equals(other.Kind) &&
|
Kind.Equals(other.Kind) &&
|
||||||
Start.Equals(other.Start) &&
|
Start.Equals(other.Start) &&
|
||||||
EditHandler.Equals(other.EditHandler) &&
|
EditHandler.Equals(other.EditHandler) &&
|
||||||
|
|
@ -161,12 +177,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
var other = obj as Span;
|
return obj is Span other &&
|
||||||
return other != null &&
|
|
||||||
Kind.Equals(other.Kind) &&
|
Kind.Equals(other.Kind) &&
|
||||||
EditHandler.Equals(other.EditHandler) &&
|
EditHandler.Equals(other.EditHandler) &&
|
||||||
ChunkGenerator.Equals(other.ChunkGenerator) &&
|
ChunkGenerator.Equals(other.ChunkGenerator) &&
|
||||||
Tokens.SequenceEqual(other.Tokens);
|
Tokens.SequenceEqual(other.Tokens, SyntaxTokenComparer.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
|
|
@ -185,5 +200,28 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
var spanBuilder = new SpanBuilder(this);
|
var spanBuilder = new SpanBuilder(this);
|
||||||
return spanBuilder.Build();
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
internal class SpanBuilder
|
internal class SpanBuilder
|
||||||
{
|
{
|
||||||
private SourceLocation _start;
|
private SourceLocation _start;
|
||||||
private List<IToken> _tokens;
|
private List<SyntaxToken> _tokens;
|
||||||
private SourceLocationTracker _tracker;
|
private SourceLocationTracker _tracker;
|
||||||
|
|
||||||
public SpanBuilder(Span original)
|
public SpanBuilder(Span original)
|
||||||
|
|
@ -20,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
_start = original.Start;
|
_start = original.Start;
|
||||||
ChunkGenerator = original.ChunkGenerator;
|
ChunkGenerator = original.ChunkGenerator;
|
||||||
|
|
||||||
_tokens = new List<IToken>(original.Tokens);
|
_tokens = new List<SyntaxToken>(original.Tokens.Select(t =>t.Green));
|
||||||
_tracker = new SourceLocationTracker(original.Start);
|
_tracker = new SourceLocationTracker(original.Start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,6 +35,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Start = location;
|
Start = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Syntax.GreenNode SyntaxNode { get; private set; }
|
||||||
|
|
||||||
public ISpanChunkGenerator ChunkGenerator { get; set; }
|
public ISpanChunkGenerator ChunkGenerator { get; set; }
|
||||||
|
|
||||||
public SourceLocation Start
|
public SourceLocation Start
|
||||||
|
|
@ -49,13 +53,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public SpanKindInternal Kind { get; set; }
|
public SpanKindInternal Kind { get; set; }
|
||||||
|
|
||||||
public IReadOnlyList<IToken> Tokens
|
public IReadOnlyList<SyntaxToken> Tokens
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_tokens == null)
|
if (_tokens == null)
|
||||||
{
|
{
|
||||||
_tokens = new List<IToken>();
|
_tokens = new List<SyntaxToken>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _tokens;
|
return _tokens;
|
||||||
|
|
@ -69,22 +73,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
// Need to potentially allocate a new list because Span.ReplaceWith takes ownership
|
// Need to potentially allocate a new list because Span.ReplaceWith takes ownership
|
||||||
// of the original list.
|
// of the original list.
|
||||||
_tokens = null;
|
_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;
|
ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
Start = SourceLocation.Undefined;
|
Start = SourceLocation.Undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Span Build()
|
public Span Build(SyntaxKind syntaxKind = SyntaxKind.Unknown)
|
||||||
{
|
{
|
||||||
|
SyntaxNode = GetSyntaxNode(syntaxKind);
|
||||||
|
|
||||||
var span = new Span(this);
|
var span = new Span(this);
|
||||||
|
|
||||||
for (var i = 0; i < span.Tokens.Count; i++)
|
|
||||||
{
|
|
||||||
var token = span.Tokens[i];
|
|
||||||
token.Parent = span;
|
|
||||||
}
|
|
||||||
|
|
||||||
return span;
|
return span;
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
_tokens?.Clear();
|
_tokens?.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Accept(IToken token)
|
public void Accept(SyntaxToken token)
|
||||||
{
|
{
|
||||||
if (token == null)
|
if (token == null)
|
||||||
{
|
{
|
||||||
|
|
@ -109,5 +109,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
_tokens.Add(token);
|
_tokens.Add(token);
|
||||||
_tracker.UpdateLocation(token.Content);
|
_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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
|
|
@ -11,12 +11,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
private static readonly int TypeHashCode = typeof(SpanEditHandler).GetHashCode();
|
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)
|
: this(tokenizer, AcceptedCharactersInternal.Any)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpanEditHandler(Func<string, IEnumerable<IToken>> tokenizer, AcceptedCharactersInternal accepted)
|
public SpanEditHandler(Func<string, IEnumerable<SyntaxToken>> tokenizer, AcceptedCharactersInternal accepted)
|
||||||
{
|
{
|
||||||
AcceptedCharacters = accepted;
|
AcceptedCharacters = accepted;
|
||||||
Tokenizer = tokenizer;
|
Tokenizer = tokenizer;
|
||||||
|
|
@ -24,9 +24,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public AcceptedCharactersInternal AcceptedCharacters { get; set; }
|
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);
|
return new SpanEditHandler(tokenizer);
|
||||||
}
|
}
|
||||||
|
|
@ -116,8 +116,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
var other = obj as SpanEditHandler;
|
return obj is SpanEditHandler other &&
|
||||||
return other != null &&
|
|
||||||
GetType() == other.GetType() &&
|
GetType() == other.GetType() &&
|
||||||
AcceptedCharacters == other.AcceptedCharacters;
|
AcceptedCharacters == other.AcceptedCharacters;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
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"| />
|
// 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 capturedAttributeValueStart = false;
|
||||||
var attributeValueStartLocation = span.Start;
|
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.
|
// 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.
|
// 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)
|
if (afterEquals)
|
||||||
{
|
{
|
||||||
|
|
@ -186,9 +187,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
attributeValueStartLocation = token.Start;
|
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've captured all leading whitespace prior to the attribute name.
|
||||||
// We're now at: " |asp-for='...'" or " |asp-for=..."
|
// We're now at: " |asp-for='...'" or " |asp-for=..."
|
||||||
|
|
@ -196,10 +197,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
var nameBuilder = new StringBuilder();
|
var nameBuilder = new StringBuilder();
|
||||||
// Move the indexer past the attribute name tokens.
|
// 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];
|
var nameToken = tokens[j];
|
||||||
if (!HtmlMarkupParser.IsValidAttributeNameToken(nameToken))
|
if (!HtmlMarkupParser.IsValidAttributeNameToken(nameToken.Green))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -213,7 +214,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
name = nameBuilder.ToString();
|
name = nameBuilder.ToString();
|
||||||
attributeValueStartLocation = SourceLocationTracker.Advance(attributeValueStartLocation, name);
|
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've captured all leading whitespace and the attribute name.
|
||||||
// We're now at: " asp-for|='...'" or " asp-for|=..."
|
// We're now at: " asp-for|='...'" or " asp-for|=..."
|
||||||
|
|
@ -227,19 +228,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
i++; // Start from the token after '='.
|
i++; // Start from the token after '='.
|
||||||
} while (i < htmlTokens.Length &&
|
} while (i < tokens.Count &&
|
||||||
(htmlTokens[i].Type == HtmlTokenType.WhiteSpace ||
|
(tokens[i].Kind == SyntaxKind.Whitespace ||
|
||||||
htmlTokens[i].Type == HtmlTokenType.NewLine));
|
tokens[i].Kind == SyntaxKind.NewLine));
|
||||||
|
|
||||||
// Check for attribute start values, aka single or double quote
|
// 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;
|
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.
|
// If there's a start quote then there must be an end quote to be valid, skip it.
|
||||||
tokenOffset = 1;
|
tokenOffset = 1;
|
||||||
|
|
@ -260,7 +261,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
afterEquals = true;
|
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
|
// 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.
|
// 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
|
var nameTokens = childSpan
|
||||||
.Tokens
|
.Tokens
|
||||||
.OfType<HtmlToken>()
|
.SkipWhile(token => !HtmlMarkupParser.IsValidAttributeNameToken(token.Green)) // Skip prefix
|
||||||
.SkipWhile(token => !HtmlMarkupParser.IsValidAttributeNameToken(token)) // Skip prefix
|
.TakeWhile(nameToken => HtmlMarkupParser.IsValidAttributeNameToken(nameToken.Green))
|
||||||
.TakeWhile(nameToken => HtmlMarkupParser.IsValidAttributeNameToken(nameToken))
|
|
||||||
.Select(nameToken => nameToken.Content);
|
.Select(nameToken => nameToken.Content);
|
||||||
|
|
||||||
var name = string.Concat(nameTokens);
|
var name = string.Concat(nameTokens);
|
||||||
|
|
@ -362,12 +362,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
var result = CreateTryParseResult(name, descriptors, processedBoundAttributeNames);
|
var result = CreateTryParseResult(name, descriptors, processedBoundAttributeNames);
|
||||||
|
|
||||||
var firstChild = builder.Children[0] as Span;
|
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;
|
var token = firstChild.Tokens[firstChild.Tokens.Count - 1];
|
||||||
switch (htmlToken.Type)
|
switch (token.Kind)
|
||||||
{
|
{
|
||||||
case HtmlTokenType.Equals:
|
case SyntaxKind.Equals:
|
||||||
if (builder.Children.Count == 2 &&
|
if (builder.Children.Count == 2 &&
|
||||||
builder.Children[1] is Span value &&
|
builder.Children[1] is Span value &&
|
||||||
value.Kind == SpanKindInternal.Markup)
|
value.Kind == SpanKindInternal.Markup)
|
||||||
|
|
@ -385,10 +385,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
result.AttributeStructure = AttributeStructure.DoubleQuotes;
|
result.AttributeStructure = AttributeStructure.DoubleQuotes;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HtmlTokenType.DoubleQuote:
|
case SyntaxKind.DoubleQuote:
|
||||||
result.AttributeStructure = AttributeStructure.DoubleQuotes;
|
result.AttributeStructure = AttributeStructure.DoubleQuotes;
|
||||||
break;
|
break;
|
||||||
case HtmlTokenType.SingleQuote:
|
case SyntaxKind.SingleQuote:
|
||||||
result.AttributeStructure = AttributeStructure.SingleQuotes;
|
result.AttributeStructure = AttributeStructure.SingleQuotes;
|
||||||
break;
|
break;
|
||||||
default:
|
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
|
// 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.
|
// than a single HTML token. Do not ignore those other tokens.
|
||||||
var tokenCount = endSpan.Tokens.Count();
|
var tokenCount = endSpan.Tokens.Count;
|
||||||
var endToken = tokenCount == 1 ? (HtmlToken)endSpan.Tokens.First() : null;
|
var endToken = tokenCount == 1 ? endSpan.Tokens.First() : null;
|
||||||
|
|
||||||
// Checking to see if it's a quoted attribute, if so we should remove end quote
|
// Checking to see if it's a quoted attribute, if so we should remove end quote
|
||||||
if (endToken != null && IsQuote(endToken))
|
if (endToken != null && IsQuote(endToken))
|
||||||
|
|
@ -614,8 +614,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
// expression).
|
// expression).
|
||||||
var firstNonWhitespaceToken = span
|
var firstNonWhitespaceToken = span
|
||||||
.Tokens
|
.Tokens
|
||||||
.OfType<HtmlToken>()
|
.First(token => token.Kind != SyntaxKind.Whitespace && token.Kind != SyntaxKind.NewLine);
|
||||||
.First(token => token.Type != HtmlTokenType.WhiteSpace && token.Type != HtmlTokenType.NewLine);
|
|
||||||
|
|
||||||
var location = new SourceSpan(firstNonWhitespaceToken.Start, attributeName.Length);
|
var location = new SourceSpan(firstNonWhitespaceToken.Start, attributeName.Length);
|
||||||
return location;
|
return location;
|
||||||
|
|
@ -716,10 +715,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return firstBoundAttribute;
|
return firstBoundAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsQuote(HtmlToken htmlToken)
|
private static bool IsQuote(SyntaxToken token)
|
||||||
{
|
{
|
||||||
return htmlToken.Type == HtmlTokenType.DoubleQuote ||
|
return token.Kind == SyntaxKind.DoubleQuote ||
|
||||||
htmlToken.Type == HtmlTokenType.SingleQuote;
|
token.Kind == SyntaxKind.SingleQuote;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConfigureNonStringAttribute(SpanBuilder builder, bool isDuplicateAttribute)
|
private static void ConfigureNonStringAttribute(SpanBuilder builder, bool isDuplicateAttribute)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
|
|
@ -370,10 +371,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
var childOffset = 0;
|
var childOffset = 0;
|
||||||
if (childSpan.Tokens.Count > 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 &&
|
if (potentialQuote != null &&
|
||||||
(potentialQuote.Type == HtmlTokenType.DoubleQuote ||
|
(potentialQuote.Kind == SyntaxKind.DoubleQuote ||
|
||||||
potentialQuote.Type == HtmlTokenType.SingleQuote))
|
potentialQuote.Kind == SyntaxKind.SingleQuote))
|
||||||
{
|
{
|
||||||
childOffset = 1;
|
childOffset = 1;
|
||||||
}
|
}
|
||||||
|
|
@ -407,23 +408,23 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
// Entire attribute is a string
|
// Entire attribute is a string
|
||||||
for (var j = 0; j < endValueMarker; j++)
|
for (var j = 0; j < endValueMarker; j++)
|
||||||
{
|
{
|
||||||
var htmlToken = (HtmlToken)childSpan.Tokens[j];
|
var token = childSpan.Tokens[j];
|
||||||
|
|
||||||
if (!afterEquals)
|
if (!afterEquals)
|
||||||
{
|
{
|
||||||
afterEquals = htmlToken.Type == HtmlTokenType.Equals;
|
afterEquals = token.Kind == SyntaxKind.Equals;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!atValue)
|
if (!atValue)
|
||||||
{
|
{
|
||||||
atValue = htmlToken.Type != HtmlTokenType.WhiteSpace &&
|
atValue = token.Kind != SyntaxKind.Whitespace &&
|
||||||
htmlToken.Type != HtmlTokenType.NewLine;
|
token.Kind != SyntaxKind.NewLine;
|
||||||
|
|
||||||
if (atValue)
|
if (atValue)
|
||||||
{
|
{
|
||||||
if (htmlToken.Type == HtmlTokenType.DoubleQuote ||
|
if (token.Kind == SyntaxKind.DoubleQuote ||
|
||||||
htmlToken.Type == HtmlTokenType.SingleQuote)
|
token.Kind == SyntaxKind.SingleQuote)
|
||||||
{
|
{
|
||||||
endValueMarker--;
|
endValueMarker--;
|
||||||
}
|
}
|
||||||
|
|
@ -431,14 +432,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
// Current token is considered the value (unquoted). Add its content to the
|
// Current token is considered the value (unquoted). Add its content to the
|
||||||
// attribute value builder before we move past it.
|
// attribute value builder before we move past it.
|
||||||
_attributeValueBuilder.Append(htmlToken.Content);
|
_attributeValueBuilder.Append(token.Content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
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)
|
if (tagEnd != null && tagEnd.Kind == SpanKindInternal.Markup)
|
||||||
{
|
{
|
||||||
var endToken = tagEnd.Tokens.Count > 0 ?
|
var endToken = tagEnd.Tokens.Count > 0 ?
|
||||||
tagEnd.Tokens[tagEnd.Tokens.Count - 1] as HtmlToken :
|
tagEnd.Tokens[tagEnd.Tokens.Count - 1] :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
if (endToken != null && endToken.Type == HtmlTokenType.CloseAngle)
|
if (endToken != null && endToken.Kind == SyntaxKind.CloseAngle)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -791,13 +792,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
var childSpan = (Span)child;
|
var childSpan = (Span)child;
|
||||||
HtmlToken textToken = null;
|
SyntaxToken textToken = null;
|
||||||
for (var i = 0; i < childSpan.Tokens.Count; i++)
|
for (var i = 0; i < childSpan.Tokens.Count; i++)
|
||||||
{
|
{
|
||||||
var token = childSpan.Tokens[i] as HtmlToken;
|
var token = childSpan.Tokens[i];
|
||||||
|
|
||||||
if (token != null &&
|
if (token != null &&
|
||||||
(token.Type & (HtmlTokenType.WhiteSpace | HtmlTokenType.Text)) == token.Type)
|
(token.Kind == SyntaxKind.Whitespace || token.Kind == SyntaxKind.Text))
|
||||||
{
|
{
|
||||||
textToken = token;
|
textToken = token;
|
||||||
break;
|
break;
|
||||||
|
|
@ -809,7 +810,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return textToken.Type == HtmlTokenType.WhiteSpace ? null : textToken.Content;
|
return textToken.Kind == SyntaxKind.Whitespace ? null : textToken.Content;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsEndTag(Block tagBlock)
|
private static bool IsEndTag(Block tagBlock)
|
||||||
|
|
@ -819,9 +820,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
var childSpan = (Span)tagBlock.Children.First();
|
var childSpan = (Span)tagBlock.Children.First();
|
||||||
|
|
||||||
// We grab the token that could be forward slash
|
// 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)
|
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.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
internal abstract partial class Tokenizer<TToken, TTokenType> : ITokenizer
|
internal abstract class Tokenizer : ITokenizer
|
||||||
where TTokenType : struct
|
|
||||||
where TToken : TokenBase<TTokenType>
|
|
||||||
{
|
{
|
||||||
protected Tokenizer(ITextDocument source)
|
protected Tokenizer(ITextDocument source)
|
||||||
{
|
{
|
||||||
|
|
@ -31,7 +30,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
protected int? CurrentState { get; set; }
|
protected int? CurrentState { get; set; }
|
||||||
|
|
||||||
protected TToken CurrentToken { get; private set; }
|
protected SyntaxToken CurrenSyntaxToken { get; private set; }
|
||||||
|
|
||||||
public ITextDocument Source { get; private set; }
|
public ITextDocument Source { get; private set; }
|
||||||
|
|
||||||
|
|
@ -42,9 +41,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
get { return Source.Peek() == -1; }
|
get { return Source.Peek() == -1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract TTokenType RazorCommentStarType { get; }
|
public abstract SyntaxKind RazorCommentStarKind { get; }
|
||||||
public abstract TTokenType RazorCommentType { get; }
|
public abstract SyntaxKind RazorCommentKind { get; }
|
||||||
public abstract TTokenType RazorCommentTransitionType { get; }
|
public abstract SyntaxKind RazorCommentTransitionKind { get; }
|
||||||
|
|
||||||
protected bool HaveContent
|
protected bool HaveContent
|
||||||
{
|
{
|
||||||
|
|
@ -64,16 +63,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public SourceLocation CurrentStart { get; private set; }
|
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();
|
protected abstract StateResult Dispatch();
|
||||||
|
|
||||||
IToken ITokenizer.NextToken()
|
SyntaxToken ITokenizer.NextToken()
|
||||||
{
|
{
|
||||||
return NextToken();
|
return NextToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual TToken NextToken()
|
public virtual SyntaxToken NextToken()
|
||||||
{
|
{
|
||||||
// Post-Condition: Buffer should be empty at the start of Next()
|
// Post-Condition: Buffer should be empty at the start of Next()
|
||||||
Debug.Assert(Buffer.Length == 0);
|
Debug.Assert(Buffer.Length == 0);
|
||||||
|
|
@ -95,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual TToken Turn()
|
protected virtual SyntaxToken Turn()
|
||||||
{
|
{
|
||||||
if (CurrentState != null)
|
if (CurrentState != null)
|
||||||
{
|
{
|
||||||
|
|
@ -105,19 +104,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
var next = Dispatch();
|
var next = Dispatch();
|
||||||
|
|
||||||
CurrentState = next.State;
|
CurrentState = next.State;
|
||||||
CurrentToken = next.Result;
|
CurrenSyntaxToken = next.Result;
|
||||||
}
|
}
|
||||||
while (CurrentState != null && CurrentToken == null);
|
while (CurrentState != null && CurrenSyntaxToken == null);
|
||||||
|
|
||||||
if (CurrentState == 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()
|
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
|
/// Returns a result containing the specified output and indicating that the next call to
|
||||||
/// <see cref="Turn"/> should invoke the provided state.
|
/// <see cref="Turn"/> should invoke the provided state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected StateResult Transition(int state, TToken result)
|
protected StateResult Transition(int state, SyntaxToken result)
|
||||||
{
|
{
|
||||||
return new StateResult(state, result);
|
return new StateResult(state, result);
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +158,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return new StateResult((int)state, result: null);
|
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);
|
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
|
/// Returns a result containing the specified output and indicating that the next call to
|
||||||
/// <see cref="Turn"/> should re-invoke the current state.
|
/// <see cref="Turn"/> should re-invoke the current state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected StateResult Stay(TToken result)
|
protected StateResult Stay(SyntaxToken result)
|
||||||
{
|
{
|
||||||
return new StateResult(CurrentState, result);
|
return new StateResult(CurrentState, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TToken Single(TTokenType type)
|
protected SyntaxToken Single(SyntaxKind type)
|
||||||
{
|
{
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return EndToken(type);
|
return EndToken(type);
|
||||||
|
|
@ -199,9 +198,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
CurrentStart = CurrentLocation;
|
CurrentStart = CurrentLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TToken EndToken(TTokenType type)
|
protected SyntaxToken EndToken(SyntaxKind type)
|
||||||
{
|
{
|
||||||
TToken token = null;
|
SyntaxToken token = null;
|
||||||
if (HaveContent)
|
if (HaveContent)
|
||||||
{
|
{
|
||||||
// Perf: Don't allocate a new errors array unless necessary.
|
// Perf: Don't allocate a new errors array unless necessary.
|
||||||
|
|
@ -222,7 +221,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string GetTokenContent(TTokenType type)
|
protected virtual string GetTokenContent(SyntaxKind type)
|
||||||
{
|
{
|
||||||
return Buffer.ToString();
|
return Buffer.ToString();
|
||||||
}
|
}
|
||||||
|
|
@ -278,7 +277,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
AssertCurrent('*');
|
AssertCurrent('*');
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return Transition(1002, EndToken(RazorCommentStarType));
|
return Transition(1002, EndToken(RazorCommentStarKind));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected StateResult RazorCommentBody()
|
protected StateResult RazorCommentBody()
|
||||||
|
|
@ -292,7 +291,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
return Transition(
|
return Transition(
|
||||||
RazorCommentTokenizerState.StarAfterRazorCommentBody,
|
RazorCommentTokenizerState.StarAfterRazorCommentBody,
|
||||||
EndToken(RazorCommentType));
|
EndToken(RazorCommentKind));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -306,7 +305,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Transition(StartState, EndToken(RazorCommentType));
|
return Transition(StartState, EndToken(RazorCommentKind));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected StateResult StarAfterRazorCommentBody()
|
protected StateResult StarAfterRazorCommentBody()
|
||||||
|
|
@ -315,14 +314,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return Transition(
|
return Transition(
|
||||||
RazorCommentTokenizerState.AtTokenAfterRazorCommentBody,
|
RazorCommentTokenizerState.AtTokenAfterRazorCommentBody,
|
||||||
EndToken(RazorCommentStarType));
|
EndToken(RazorCommentStarKind));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected StateResult AtTokenAfterRazorCommentBody()
|
protected StateResult AtTokenAfterRazorCommentBody()
|
||||||
{
|
{
|
||||||
AssertCurrent('@');
|
AssertCurrent('@');
|
||||||
TakeCurrent();
|
TakeCurrent();
|
||||||
return Transition(StartState, EndToken(RazorCommentTransitionType));
|
return Transition(StartState, EndToken(RazorCommentTransitionKind));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -397,7 +396,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
protected struct StateResult
|
protected struct StateResult
|
||||||
{
|
{
|
||||||
public StateResult(int? state, TToken result)
|
public StateResult(int? state, SyntaxToken result)
|
||||||
{
|
{
|
||||||
State = state;
|
State = state;
|
||||||
Result = result;
|
Result = result;
|
||||||
|
|
@ -405,7 +404,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public int? State { get; }
|
public int? State { get; }
|
||||||
|
|
||||||
public TToken Result { get; }
|
public SyntaxToken Result { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LookaheadToken BeginLookahead(ITextBuffer buffer)
|
private static LookaheadToken BeginLookahead(ITextBuffer buffer)
|
||||||
|
|
|
||||||
|
|
@ -5,36 +5,37 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
internal abstract partial class TokenizerBackedParser<TTokenizer, TToken, TTokenType> : ParserBase
|
internal abstract class TokenizerBackedParser<TTokenizer> : ParserBase
|
||||||
where TTokenType : struct
|
where TTokenizer : Tokenizer
|
||||||
where TTokenizer : Tokenizer<TToken, TTokenType>
|
|
||||||
where TToken : TokenBase<TTokenType>
|
|
||||||
{
|
{
|
||||||
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)
|
: base(context)
|
||||||
{
|
{
|
||||||
Language = language;
|
Language = language;
|
||||||
|
|
||||||
var languageTokenizer = Language.CreateTokenizer(Context.Source);
|
var languageTokenizer = Language.CreateTokenizer(Context.Source);
|
||||||
_tokenizer = new TokenizerView<TTokenizer, TToken, TTokenType>(languageTokenizer);
|
_tokenizer = new TokenizerView<TTokenizer>(languageTokenizer);
|
||||||
Span = new SpanBuilder(CurrentLocation);
|
Span = new SpanBuilder(CurrentLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ParserState ParserState { get; set; }
|
||||||
|
|
||||||
protected SpanBuilder Span { get; private set; }
|
protected SpanBuilder Span { get; private set; }
|
||||||
|
|
||||||
protected Action<SpanBuilder> SpanConfig { get; set; }
|
protected Action<SpanBuilder> SpanConfig { get; set; }
|
||||||
|
|
||||||
protected TToken CurrentToken
|
protected SyntaxToken CurrentToken
|
||||||
{
|
{
|
||||||
get { return _tokenizer.Current; }
|
get { return _tokenizer.Current; }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TToken PreviousToken { get; private set; }
|
protected SyntaxToken PreviousToken { get; private set; }
|
||||||
|
|
||||||
protected SourceLocation CurrentLocation => _tokenizer.Tokenizer.CurrentLocation;
|
protected SourceLocation CurrentLocation => _tokenizer.Tokenizer.CurrentLocation;
|
||||||
|
|
||||||
|
|
@ -45,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
get { return _tokenizer.EndOfFile; }
|
get { return _tokenizer.EndOfFile; }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LanguageCharacteristics<TTokenizer, TToken, TTokenType> Language { get; }
|
protected LanguageCharacteristics<TTokenizer> Language { get; }
|
||||||
|
|
||||||
protected virtual void HandleEmbeddedTransition()
|
protected virtual void HandleEmbeddedTransition()
|
||||||
{
|
{
|
||||||
|
|
@ -58,21 +59,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public override void BuildSpan(SpanBuilder span, SourceLocation start, string content)
|
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)
|
protected void Initialize(SpanBuilder span)
|
||||||
{
|
{
|
||||||
if (SpanConfig != null)
|
SpanConfig?.Invoke(span);
|
||||||
{
|
|
||||||
SpanConfig(span);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TToken Lookahead(int count)
|
protected SyntaxToken Lookahead(int count)
|
||||||
{
|
{
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -84,7 +82,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
// We add 1 in order to store the current token.
|
// 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;
|
var currentToken = CurrentToken;
|
||||||
|
|
||||||
tokens[0] = 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>
|
/// <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>
|
/// <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>
|
/// <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)
|
if (condition == null)
|
||||||
{
|
{
|
||||||
|
|
@ -124,7 +122,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
var matchFound = false;
|
var matchFound = false;
|
||||||
|
|
||||||
var tokens = new List<TToken>();
|
var tokens = new List<SyntaxToken>();
|
||||||
tokens.Add(CurrentToken);
|
tokens.Add(CurrentToken);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
|
@ -163,14 +161,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
[Conditional("DEBUG")]
|
[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)
|
if (token != null)
|
||||||
{
|
{
|
||||||
|
|
@ -180,7 +178,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Put the specified tokens back in the input stream. The provided list MUST be in the ORDER THE TOKENS WERE READ. The
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If a document contains tokens: a, b, c, d, e, f
|
/// 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,
|
/// 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
|
/// in that case, want to put c, b and a back into the stream, so "a, b, c" is the CORRECT order
|
||||||
/// </remarks>
|
/// </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);
|
PutBack(token);
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +205,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
protected internal bool Balance(BalancingModes mode)
|
protected internal bool Balance(BalancingModes mode)
|
||||||
{
|
{
|
||||||
var left = CurrentToken.Type;
|
var left = CurrentToken.Kind;
|
||||||
var right = Language.FlipBracket(left);
|
var right = Language.FlipBracket(left);
|
||||||
var start = CurrentStart;
|
var start = CurrentStart;
|
||||||
AcceptAndMoveNext();
|
AcceptAndMoveNext();
|
||||||
|
|
@ -223,21 +221,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return Balance(mode, left, right, start);
|
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 startPosition = CurrentStart.AbsoluteIndex;
|
||||||
var nesting = 1;
|
var nesting = 1;
|
||||||
if (!EndOfFile)
|
if (!EndOfFile)
|
||||||
{
|
{
|
||||||
var syms = new List<TToken>();
|
var tokens = new List<SyntaxToken>();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (IsAtEmbeddedTransition(
|
if (IsAtEmbeddedTransition(
|
||||||
(mode & BalancingModes.AllowCommentsAndTemplates) == BalancingModes.AllowCommentsAndTemplates,
|
(mode & BalancingModes.AllowCommentsAndTemplates) == BalancingModes.AllowCommentsAndTemplates,
|
||||||
(mode & BalancingModes.AllowEmbeddedTransitions) == BalancingModes.AllowEmbeddedTransitions))
|
(mode & BalancingModes.AllowEmbeddedTransitions) == BalancingModes.AllowEmbeddedTransitions))
|
||||||
{
|
{
|
||||||
Accept(syms);
|
Accept(tokens);
|
||||||
syms.Clear();
|
tokens.Clear();
|
||||||
HandleEmbeddedTransition();
|
HandleEmbeddedTransition();
|
||||||
|
|
||||||
// Reset backtracking since we've already outputted some spans.
|
// Reset backtracking since we've already outputted some spans.
|
||||||
|
|
@ -253,7 +251,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
if (nesting > 0)
|
if (nesting > 0)
|
||||||
{
|
{
|
||||||
syms.Add(CurrentToken);
|
tokens.Add(CurrentToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (nesting > 0 && NextToken());
|
while (nesting > 0 && NextToken());
|
||||||
|
|
@ -275,29 +273,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Accept(syms);
|
Accept(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Accept all the tokens we saw
|
// Accept all the tokens we saw
|
||||||
Accept(syms);
|
Accept(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nesting == 0;
|
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;
|
var cur = CurrentToken;
|
||||||
if (NextToken())
|
if (NextToken())
|
||||||
|
|
@ -317,14 +315,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return false;
|
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()
|
protected internal bool AcceptAndMoveNext()
|
||||||
|
|
@ -333,11 +331,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return NextToken();
|
return NextToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TToken AcceptSingleWhiteSpaceCharacter()
|
protected SyntaxToken AcceptSingleWhiteSpaceCharacter()
|
||||||
{
|
{
|
||||||
if (Language.IsWhiteSpace(CurrentToken))
|
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);
|
Accept(pair.Item1);
|
||||||
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
||||||
NextToken();
|
NextToken();
|
||||||
|
|
@ -346,19 +344,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return null;
|
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);
|
Accept(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected internal void Accept(TToken token)
|
protected internal void Accept(SyntaxToken token)
|
||||||
{
|
{
|
||||||
if (token != null)
|
if (token != null)
|
||||||
{
|
{
|
||||||
foreach (var error in token.Errors)
|
foreach (var error in token.GetDiagnostics())
|
||||||
{
|
{
|
||||||
Context.ErrorSink.OnError(error);
|
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;
|
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);
|
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);
|
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);
|
Configure(null, accepts);
|
||||||
Output();
|
Output(syntaxKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Output()
|
private void Output(SyntaxKind syntaxKind)
|
||||||
{
|
{
|
||||||
if (Span.Tokens.Count > 0)
|
if (Span.Tokens.Count > 0)
|
||||||
{
|
{
|
||||||
var nextStart = Span.End;
|
var nextStart = Span.End;
|
||||||
|
|
||||||
var builtSpan = Span.Build();
|
var builtSpan = Span.Build(syntaxKind);
|
||||||
Context.Builder.Add(builtSpan);
|
Context.Builder.Add(builtSpan);
|
||||||
Initialize(Span);
|
Initialize(Span);
|
||||||
|
|
||||||
|
|
@ -437,7 +435,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
protected IDisposable PushSpanConfig(Action<SpanBuilder, Action<SpanBuilder>> newConfig)
|
protected IDisposable PushSpanConfig(Action<SpanBuilder, Action<SpanBuilder>> newConfig)
|
||||||
{
|
{
|
||||||
Action<SpanBuilder> old = SpanConfig;
|
var old = SpanConfig;
|
||||||
ConfigureSpan(newConfig);
|
ConfigureSpan(newConfig);
|
||||||
return new DisposableAction(() => SpanConfig = old);
|
return new DisposableAction(() => SpanConfig = old);
|
||||||
}
|
}
|
||||||
|
|
@ -450,7 +448,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
protected void ConfigureSpan(Action<SpanBuilder, Action<SpanBuilder>> config)
|
protected void ConfigureSpan(Action<SpanBuilder, Action<SpanBuilder>> config)
|
||||||
{
|
{
|
||||||
Action<SpanBuilder> prev = SpanConfig;
|
var prev = SpanConfig;
|
||||||
if (config == null)
|
if (config == null)
|
||||||
{
|
{
|
||||||
SpanConfig = null;
|
SpanConfig = null;
|
||||||
|
|
@ -467,9 +465,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Expected(Language.GetKnownTokenType(type));
|
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();
|
AcceptAndMoveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,7 +476,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return Optional(Language.GetKnownTokenType(type));
|
return Optional(Language.GetKnownTokenType(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected internal bool Optional(TTokenType type)
|
protected internal bool Optional(SyntaxKind type)
|
||||||
{
|
{
|
||||||
if (At(type))
|
if (At(type))
|
||||||
{
|
{
|
||||||
|
|
@ -498,61 +496,61 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return true;
|
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
|
// 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
|
// 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));
|
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();
|
return ReadWhileLazy(condition).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TToken AcceptWhiteSpaceInLines()
|
protected SyntaxToken AcceptWhiteSpaceInLines()
|
||||||
{
|
{
|
||||||
TToken lastWs = null;
|
SyntaxToken lastWs = null;
|
||||||
while (Language.IsWhiteSpace(CurrentToken) || Language.IsNewLine(CurrentToken))
|
while (Language.IsWhiteSpace(CurrentToken) || Language.IsNewLine(CurrentToken))
|
||||||
{
|
{
|
||||||
// Capture the previous whitespace node
|
// 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.
|
// 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.
|
// 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))
|
while (EnsureCurrent() && condition(CurrentToken))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
// 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.
|
// 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
|
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
internal class TokenizerView<TTokenizer, TToken, TTokenType>
|
internal class TokenizerView<TTokenizer>
|
||||||
where TTokenType : struct
|
where TTokenizer : Tokenizer
|
||||||
where TTokenizer : Tokenizer<TToken, TTokenType>
|
|
||||||
where TToken : TokenBase<TTokenType>
|
|
||||||
{
|
{
|
||||||
public TokenizerView(TTokenizer tokenizer)
|
public TokenizerView(TTokenizer tokenizer)
|
||||||
{
|
{
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public TTokenizer Tokenizer { get; private set; }
|
public TTokenizer Tokenizer { get; private set; }
|
||||||
public bool EndOfFile { get; private set; }
|
public bool EndOfFile { get; private set; }
|
||||||
public TToken Current { get; private set; }
|
public SyntaxToken Current { get; private set; }
|
||||||
|
|
||||||
public ITextDocument Source
|
public ITextDocument Source
|
||||||
{
|
{
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return !EndOfFile;
|
return !EndOfFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PutBack(TToken token)
|
public void PutBack(SyntaxToken token)
|
||||||
{
|
{
|
||||||
Source.Position -= token.Content.Length;
|
Source.Position -= token.Content.Length;
|
||||||
Current = null;
|
Current = null;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Runtime.CompilerServices;
|
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.Razor.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.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.
|
// 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,
|
Unknown,
|
||||||
|
List,
|
||||||
|
Whitespace,
|
||||||
|
NewLine,
|
||||||
|
Colon,
|
||||||
|
QuestionMark,
|
||||||
|
RightBracket,
|
||||||
|
LeftBracket,
|
||||||
|
Equals,
|
||||||
|
Transition,
|
||||||
|
|
||||||
|
// HTML
|
||||||
|
Text,
|
||||||
|
OpenAngle,
|
||||||
|
Bang,
|
||||||
|
ForwardSlash,
|
||||||
|
DoubleHyphen,
|
||||||
|
CloseAngle,
|
||||||
|
DoubleQuote,
|
||||||
|
SingleQuote,
|
||||||
|
|
||||||
|
// CSharp literals
|
||||||
Identifier,
|
Identifier,
|
||||||
Keyword,
|
Keyword,
|
||||||
IntegerLiteral,
|
IntegerLiteral,
|
||||||
NewLine,
|
CSharpComment,
|
||||||
WhiteSpace,
|
|
||||||
Comment,
|
|
||||||
RealLiteral,
|
RealLiteral,
|
||||||
CharacterLiteral,
|
CharacterLiteral,
|
||||||
StringLiteral,
|
StringLiteral,
|
||||||
|
|
||||||
// Operators
|
// CSharp operators
|
||||||
Arrow,
|
Arrow,
|
||||||
Minus,
|
Minus,
|
||||||
Decrement,
|
Decrement,
|
||||||
|
|
@ -37,12 +81,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Slash,
|
Slash,
|
||||||
DivideAssign,
|
DivideAssign,
|
||||||
DoubleColon,
|
DoubleColon,
|
||||||
Colon,
|
|
||||||
Semicolon,
|
Semicolon,
|
||||||
QuestionMark,
|
|
||||||
NullCoalesce,
|
NullCoalesce,
|
||||||
RightBracket,
|
|
||||||
LeftBracket,
|
|
||||||
XorAssign,
|
XorAssign,
|
||||||
Xor,
|
Xor,
|
||||||
LeftBrace,
|
LeftBrace,
|
||||||
|
|
@ -59,17 +99,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
LeftShift,
|
LeftShift,
|
||||||
LeftShiftAssign,
|
LeftShiftAssign,
|
||||||
Assign,
|
Assign,
|
||||||
Equals,
|
|
||||||
GreaterThan,
|
GreaterThan,
|
||||||
GreaterThanEqual,
|
GreaterThanEqual,
|
||||||
RightShift,
|
RightShift,
|
||||||
RightShiftAssign,
|
RightShiftAssign,
|
||||||
Hash,
|
Hash,
|
||||||
Transition,
|
|
||||||
|
|
||||||
// Razor specific
|
// Razor specific
|
||||||
RazorCommentTransition,
|
RazorCommentLiteral,
|
||||||
RazorCommentStar,
|
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;
|
_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)
|
if (fileSystem == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(fileSystem));
|
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
|
// 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.
|
// 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.
|
// 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>
|
<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>
|
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
|
||||||
<EnableApiCheck>false</EnableApiCheck>
|
<EnableApiCheck>false</EnableApiCheck>
|
||||||
|
<DefineConstants>$(DefineConstants);DOCUMENT_SERVICE_FACTORY</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,36 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
return Create(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), configure);
|
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));
|
throw new ArgumentNullException(nameof(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
Project = project;
|
ProjectInternal = project;
|
||||||
State = state;
|
State = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultProjectSnapshot Project { get; }
|
public DefaultProjectSnapshot ProjectInternal { get; }
|
||||||
|
|
||||||
public DocumentState State { get; }
|
public DocumentState State { get; }
|
||||||
|
|
||||||
|
|
@ -35,9 +35,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
public override string TargetPath => State.HostDocument.TargetPath;
|
public override string TargetPath => State.HostDocument.TargetPath;
|
||||||
|
|
||||||
|
public override ProjectSnapshot Project => ProjectInternal;
|
||||||
|
|
||||||
|
public override bool SupportsOutput => true;
|
||||||
|
|
||||||
public override IReadOnlyList<DocumentSnapshot> GetImports()
|
public override IReadOnlyList<DocumentSnapshot> GetImports()
|
||||||
{
|
{
|
||||||
return State.Imports.GetImports(Project, this);
|
return State.GetImports(ProjectInternal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<SourceText> GetTextAsync()
|
public override Task<SourceText> GetTextAsync()
|
||||||
|
|
@ -50,10 +54,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
return State.GetTextVersionAsync();
|
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.
|
var (output, _, _) = await State.GetGeneratedOutputAndVersionAsync(ProjectInternal, this).ConfigureAwait(false);
|
||||||
return State.GeneratedOutput.GetGeneratedOutputInitializationTask(Project, this);
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool TryGetText(out SourceText result)
|
public override bool TryGetText(out SourceText result)
|
||||||
|
|
@ -68,9 +72,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
public override bool TryGetGeneratedOutput(out RazorCodeDocument result)
|
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;
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
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()
|
public override RazorProjectEngine GetProjectEngine()
|
||||||
{
|
{
|
||||||
return State.ProjectEngine.GetProjectEngine(this);
|
return State.ProjectEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()
|
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()
|
||||||
{
|
{
|
||||||
// IMPORTANT: Don't put more code here. We want this to return a cached task.
|
// 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)
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var projects = new ProjectSnapshot[_projects.Count];
|
var projects = new ProjectSnapshot[_projects.Count];
|
||||||
foreach (var entry in _projects)
|
foreach (var entry in _projects)
|
||||||
{
|
{
|
||||||
if (entry.Value.Snapshot == null)
|
projects[i++] = entry.Value.GetSnapshot();
|
||||||
{
|
|
||||||
entry.Value.Snapshot = new DefaultProjectSnapshot(entry.Value.State);
|
|
||||||
}
|
|
||||||
|
|
||||||
projects[i++] = entry.Value.Snapshot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return projects;
|
return projects;
|
||||||
|
|
@ -115,12 +110,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
if (_projects.TryGetValue(filePath, out var entry))
|
if (_projects.TryGetValue(filePath, out var entry))
|
||||||
{
|
{
|
||||||
if (entry.Snapshot == null)
|
return entry.GetSnapshot();
|
||||||
{
|
|
||||||
entry.Snapshot = new DefaultProjectSnapshot(entry.State);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry.Snapshot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -175,8 +165,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Document updates can no-op.
|
// Document updates can no-op.
|
||||||
if (!object.ReferenceEquals(state, entry.State))
|
if (!object.ReferenceEquals(state, entry.State))
|
||||||
{
|
{
|
||||||
_projects[hostProject.FilePath] = new Entry(state);
|
var oldSnapshot = entry.GetSnapshot();
|
||||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentAdded));
|
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.
|
// Document updates can no-op.
|
||||||
if (!object.ReferenceEquals(state, entry.State))
|
if (!object.ReferenceEquals(state, entry.State))
|
||||||
{
|
{
|
||||||
_projects[hostProject.FilePath] = new Entry(state);
|
var oldSnapshot = entry.GetSnapshot();
|
||||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentRemoved));
|
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))
|
entry.State.Documents.TryGetValue(documentFilePath, out var older))
|
||||||
{
|
{
|
||||||
ProjectState state;
|
ProjectState state;
|
||||||
SourceText olderText;
|
|
||||||
VersionStamp olderVersion;
|
|
||||||
|
|
||||||
var currentText = sourceText;
|
var currentText = sourceText;
|
||||||
if (older.TryGetText(out olderText) &&
|
if (older.TryGetText(out var olderText) &&
|
||||||
older.TryGetTextVersion(out olderVersion))
|
older.TryGetTextVersion(out var olderVersion))
|
||||||
{
|
{
|
||||||
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
|
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
|
||||||
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
|
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
|
||||||
|
|
@ -256,8 +248,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Document updates can no-op.
|
// Document updates can no-op.
|
||||||
if (!object.ReferenceEquals(state, entry.State))
|
if (!object.ReferenceEquals(state, entry.State))
|
||||||
{
|
{
|
||||||
_projects[projectFilePath] = new Entry(state);
|
var oldSnapshot = entry.GetSnapshot();
|
||||||
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
|
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.
|
// Document updates can no-op.
|
||||||
if (!object.ReferenceEquals(state, entry.State))
|
if (!object.ReferenceEquals(state, entry.State))
|
||||||
{
|
{
|
||||||
_projects[projectFilePath] = new Entry(state);
|
var oldSnapshot = entry.GetSnapshot();
|
||||||
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
|
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))
|
entry.State.Documents.TryGetValue(documentFilePath, out var older))
|
||||||
{
|
{
|
||||||
ProjectState state;
|
ProjectState state;
|
||||||
SourceText olderText;
|
|
||||||
VersionStamp olderVersion;
|
|
||||||
|
|
||||||
var currentText = sourceText;
|
var currentText = sourceText;
|
||||||
if (older.TryGetText(out olderText) &&
|
if (older.TryGetText(out var olderText) &&
|
||||||
older.TryGetTextVersion(out olderVersion))
|
older.TryGetTextVersion(out var olderVersion))
|
||||||
{
|
{
|
||||||
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
|
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
|
||||||
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
|
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
|
||||||
|
|
@ -346,8 +340,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Document updates can no-op.
|
// Document updates can no-op.
|
||||||
if (!object.ReferenceEquals(state, entry.State))
|
if (!object.ReferenceEquals(state, entry.State))
|
||||||
{
|
{
|
||||||
_projects[projectFilePath] = new Entry(state);
|
var oldSnapshot = entry.GetSnapshot();
|
||||||
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
|
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.
|
// Document updates can no-op.
|
||||||
if (!object.ReferenceEquals(state, entry.State))
|
if (!object.ReferenceEquals(state, entry.State))
|
||||||
{
|
{
|
||||||
_projects[projectFilePath] = new Entry(state);
|
var oldSnapshot = entry.GetSnapshot();
|
||||||
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
|
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 workspaceProject = GetWorkspaceProject(hostProject.FilePath);
|
||||||
|
|
||||||
var state = ProjectState.Create(Workspace.Services, hostProject, workspaceProject);
|
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.
|
// 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)
|
public override void HostProjectChanged(HostProject hostProject)
|
||||||
|
|
@ -429,9 +428,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// HostProject updates can no-op.
|
// HostProject updates can no-op.
|
||||||
if (!object.ReferenceEquals(state, entry.State))
|
if (!object.ReferenceEquals(state, entry.State))
|
||||||
{
|
{
|
||||||
_projects[hostProject.FilePath] = new Entry(state);
|
var oldSnapshot = entry.GetSnapshot();
|
||||||
|
entry = new Entry(state);
|
||||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectChanged));
|
_projects[hostProject.FilePath] = entry;
|
||||||
|
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -445,12 +445,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
_foregroundDispatcher.AssertForegroundThread();
|
_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.
|
// 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)
|
if (entry.State.WorkspaceProject == null)
|
||||||
{
|
{
|
||||||
var state = entry.State.WithWorkspaceProject(workspaceProject);
|
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))
|
(entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Version.GetNewerVersion(workspaceProject.Version) == workspaceProject.Version))
|
||||||
{
|
{
|
||||||
var state = entry.State.WithWorkspaceProject(workspaceProject);
|
var state = entry.State.WithWorkspaceProject(workspaceProject);
|
||||||
|
|
||||||
// WorkspaceProject updates can no-op. This can be the case if a build is triggered, but we've
|
// WorkspaceProject updates can no-op. This can be the case if a build is triggered, but we've
|
||||||
// already seen the update.
|
// already seen the update.
|
||||||
if (!object.ReferenceEquals(state, entry.State))
|
if (!object.ReferenceEquals(state, entry.State))
|
||||||
{
|
{
|
||||||
_projects[workspaceProject.FilePath] = new Entry(state);
|
var oldSnapshot = entry.GetSnapshot();
|
||||||
|
entry = new Entry(state);
|
||||||
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
_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.
|
// OK there's another WorkspaceProject, use that.
|
||||||
state = entry.State.WithWorkspaceProject(otherWorkspaceProject);
|
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
|
else
|
||||||
{
|
{
|
||||||
state = entry.State.WithWorkspaceProject(null);
|
|
||||||
_projects[workspaceProject.FilePath] = new Entry(state);
|
|
||||||
|
|
||||||
// Notify listeners of a change because we've removed computed 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));
|
throw new ArgumentNullException(nameof(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
_errorReporter.ReportError(exception, workspaceProject);
|
_errorReporter.ReportError(exception, workspaceProject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -644,13 +651,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
private class Entry
|
private class Entry
|
||||||
{
|
{
|
||||||
public ProjectSnapshot Snapshot;
|
public ProjectSnapshot SnapshotUnsafe;
|
||||||
public readonly ProjectState State;
|
public readonly ProjectState State;
|
||||||
|
|
||||||
public Entry(ProjectState state)
|
public Entry(ProjectState state)
|
||||||
{
|
{
|
||||||
State = 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 string TargetPath { get; }
|
||||||
|
|
||||||
|
public abstract ProjectSnapshot Project { get; }
|
||||||
|
|
||||||
|
public abstract bool SupportsOutput { get; }
|
||||||
|
|
||||||
public abstract IReadOnlyList<DocumentSnapshot> GetImports();
|
public abstract IReadOnlyList<DocumentSnapshot> GetImports();
|
||||||
|
|
||||||
public abstract Task<SourceText> GetTextAsync();
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis.Host;
|
using Microsoft.CodeAnalysis.Host;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
|
||||||
|
|
@ -18,14 +21,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
private readonly object _lock;
|
private readonly object _lock;
|
||||||
|
|
||||||
|
private ComputedStateTracker _computedState;
|
||||||
|
|
||||||
private Func<Task<TextAndVersion>> _loader;
|
private Func<Task<TextAndVersion>> _loader;
|
||||||
private Task<TextAndVersion> _loaderTask;
|
private Task<TextAndVersion> _loaderTask;
|
||||||
private SourceText _sourceText;
|
private SourceText _sourceText;
|
||||||
private VersionStamp? _version;
|
private VersionStamp? _version;
|
||||||
|
|
||||||
private DocumentGeneratedOutputTracker _generatedOutput;
|
|
||||||
private DocumentImportsTracker _imports;
|
|
||||||
|
|
||||||
public static DocumentState Create(
|
public static DocumentState Create(
|
||||||
HostWorkspaceServices services,
|
HostWorkspaceServices services,
|
||||||
HostDocument hostDocument,
|
HostDocument hostDocument,
|
||||||
|
|
@ -65,42 +67,37 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
public HostWorkspaceServices Services { get; }
|
public HostWorkspaceServices Services { get; }
|
||||||
|
|
||||||
public DocumentGeneratedOutputTracker GeneratedOutput
|
public GeneratedCodeContainer GeneratedCodeContainer => HostDocument.GeneratedCodeContainer;
|
||||||
|
|
||||||
|
public bool IsGeneratedOutputResultAvailable => ComputedState.IsResultAvailable == true;
|
||||||
|
|
||||||
|
private ComputedStateTracker ComputedState
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_generatedOutput == null)
|
if (_computedState == null)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
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
|
return ComputedState.GetGeneratedOutputAndVersionAsync(project, document);
|
||||||
{
|
}
|
||||||
if (_imports == null)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_imports == null)
|
|
||||||
{
|
|
||||||
_imports = new DocumentImportsTracker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _imports;
|
public IReadOnlyList<DocumentSnapshot> GetImports(DefaultProjectSnapshot project)
|
||||||
}
|
{
|
||||||
|
return GetImportsCore(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SourceText> GetTextAsync()
|
public async Task<SourceText> GetTextAsync()
|
||||||
|
|
@ -178,6 +175,23 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
state._version = _version;
|
state._version = _version;
|
||||||
state._loaderTask = _loaderTask;
|
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;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,8 +204,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
state._version = _version;
|
state._version = _version;
|
||||||
state._loaderTask = _loaderTask;
|
state._loaderTask = _loaderTask;
|
||||||
|
|
||||||
// Opportunistically cache the generated code
|
// Optimisically cache the computed state
|
||||||
state._generatedOutput = _generatedOutput?.Fork();
|
state._computedState = new ComputedStateTracker(state, _computedState);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
@ -203,6 +217,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
throw new ArgumentNullException(nameof(sourceText));
|
throw new ArgumentNullException(nameof(sourceText));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not cache the computed state
|
||||||
|
|
||||||
return new DocumentState(Services, HostDocument, sourceText, version, null);
|
return new DocumentState(Services, HostDocument, sourceText, version, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +229,239 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
throw new ArgumentNullException(nameof(loader));
|
throw new ArgumentNullException(nameof(loader));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not cache the computed state
|
||||||
|
|
||||||
return new DocumentState(Services, HostDocument, null, null, loader);
|
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;
|
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()
|
public override RazorProjectEngine GetProjectEngine()
|
||||||
{
|
{
|
||||||
return _projectEngine.Value;
|
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