From 52dc49f2b9e6da35e530687578aad26d0ed24629 Mon Sep 17 00:00:00 2001
From: Ajay Bhargav Baaskaran
Date: Wed, 27 Jun 2018 15:56:20 -0700
Subject: [PATCH 01/18] Prereq for converting tag helper tests
---
.../Legacy/TagHelperRewritingTestBase.cs | 6 ++
.../Language/Legacy/ParserTestBase.cs | 18 +++--
.../Language/Legacy/SyntaxTreeNodeWriter.cs | 70 ++++++++++++++++++-
3 files changed, 87 insertions(+), 7 deletions(-)
diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperRewritingTestBase.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperRewritingTestBase.cs
index 8271cee1a9..770591d394 100644
--- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperRewritingTestBase.cs
+++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperRewritingTestBase.cs
@@ -68,6 +68,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
.OrderBy(error => error.Span.AbsoluteIndex)
.ToList();
+ if (UseBaselineTests && !IsTheory)
+ {
+ BaselineTest(actualTree, verifySyntaxTree: false, actualErrors.ToArray());
+ return;
+ }
+
EvaluateRazorErrors(actualErrors, expectedErrors.ToList());
EvaluateParseTree(actualTree, expectedOutput);
}
diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/ParserTestBase.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/ParserTestBase.cs
index 4089843e22..34b9269fe0 100644
--- a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/ParserTestBase.cs
+++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/ParserTestBase.cs
@@ -162,16 +162,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Regex.Replace(content, "(?
Date: Thu, 28 Jun 2018 16:48:57 -0700
Subject: [PATCH 02/18] Bumping version from 2.2.0 to 3.0.0
---
version.props | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/version.props b/version.props
index 88598148cd..584473c9bb 100644
--- a/version.props
+++ b/version.props
@@ -1,7 +1,7 @@
-
+
- 2.2.0
- preview1
+ 3.0.0
+ alpha1
$(VersionPrefix)
$(VersionPrefix)-$(VersionSuffix)-final
t000
From 57fbd1c499bf38310305fa7b08814a61b6400c1b Mon Sep 17 00:00:00 2001
From: "Nate McMaster (automated)"
Date: Mon, 2 Jul 2018 12:40:33 -0700
Subject: [PATCH 03/18] [automated] Change default branch to master
---
.travis.yml | 2 +-
korebuild.json | 4 ++--
run.ps1 | 6 +++---
run.sh | 2 +-
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index e75fe73221..d56301c453 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,7 +12,7 @@ os:
osx_image: xcode8.2
branches:
only:
- - dev
+ - master
- /^release\/.*$/
- /^(.*\/)?ci-.*$/
before_install:
diff --git a/korebuild.json b/korebuild.json
index 6a6da7bd51..cb46ef120b 100644
--- a/korebuild.json
+++ b/korebuild.json
@@ -1,6 +1,6 @@
{
- "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/dev/tools/korebuild.schema.json",
- "channel": "dev",
+ "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json",
+ "channel": "master",
"toolsets": {
"visualstudio": {
"required": false,
diff --git a/run.ps1 b/run.ps1
index 3b27382468..34604c7175 100644
--- a/run.ps1
+++ b/run.ps1
@@ -52,8 +52,8 @@ in the file are overridden by command line parameters.
Example config file:
```json
{
- "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/dev/tools/korebuild.schema.json",
- "channel": "dev",
+ "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json",
+ "channel": "master",
"toolsSource": "https://aspnetcore.blob.core.windows.net/buildtools"
}
```
@@ -192,7 +192,7 @@ if (!$DotNetHome) {
else { Join-Path $PSScriptRoot '.dotnet'}
}
-if (!$Channel) { $Channel = 'dev' }
+if (!$Channel) { $Channel = 'master' }
if (!$ToolsSource) { $ToolsSource = 'https://aspnetcore.blob.core.windows.net/buildtools' }
# Execute
diff --git a/run.sh b/run.sh
index 02aac15874..61f7a53385 100755
--- a/run.sh
+++ b/run.sh
@@ -248,7 +248,7 @@ if [ -f "$config_file" ]; then
[ ! -z "${config_tools_source:-}" ] && tools_source="$config_tools_source"
fi
-[ -z "$channel" ] && channel='dev'
+[ -z "$channel" ] && channel='master'
[ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools'
get_korebuild
From d4c652a7d57b4d4e19949550f06118f3d966230c Mon Sep 17 00:00:00 2001
From: "ASP.NET CI"
Date: Tue, 3 Jul 2018 16:24:43 +0000
Subject: [PATCH 04/18] Update dependencies.props
[auto-updated: dependencies]
---
build/dependencies.props | 16 ++++++++--------
korebuild-lock.txt | 4 ++--
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/build/dependencies.props b/build/dependencies.props
index c83a59e6f3..db673f1ad8 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -4,20 +4,20 @@
0.10.13
- 2.2.0-preview1-17090
- 2.2.0-preview1-34530
- 2.2.0-preview1-34530
- 2.2.0-preview1-34530
+ 3.0.0-alpha1-10000
+ 3.0.0-alpha1-10016
+ 3.0.0-alpha1-10016
+ 3.0.0-alpha1-10016
15.6.82
15.6.82
15.6.82
2.8.0
2.8.0
- 2.2.0-preview1-34530
- 2.2.0-preview1-34530
+ 3.0.0-alpha1-10016
+ 3.0.0-alpha1-10016
2.2.0-preview1-26618-02
- 2.2.0-preview1-34530
- 2.2.0-preview1-34530
+ 3.0.0-alpha1-10016
+ 3.0.0-alpha1-10016
2.0.0
2.1.0
2.2.0-preview1-26618-02
diff --git a/korebuild-lock.txt b/korebuild-lock.txt
index 3e694b2ed8..3f870e51fe 100644
--- a/korebuild-lock.txt
+++ b/korebuild-lock.txt
@@ -1,2 +1,2 @@
-version:2.2.0-preview1-17090
-commithash:b19e903e946579cd9482089bce7d917e8bacd765
+version:3.0.0-alpha1-10000
+commithash:b7b88d08d55abc8b71de9abf16e26fc713e332cd
From 61aeae10233e52025090e836f3cbf70a13a2679f Mon Sep 17 00:00:00 2001
From: "ASP.NET CI"
Date: Sun, 8 Jul 2018 20:06:52 +0000
Subject: [PATCH 05/18] Update dependencies.props
[auto-updated: dependencies]
---
build/dependencies.props | 18 +++++++++---------
korebuild-lock.txt | 4 ++--
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/build/dependencies.props b/build/dependencies.props
index db673f1ad8..9179085d61 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -4,20 +4,20 @@
0.10.13
- 3.0.0-alpha1-10000
- 3.0.0-alpha1-10016
- 3.0.0-alpha1-10016
- 3.0.0-alpha1-10016
+ 3.0.0-alpha1-10005
+ 3.0.0-alpha1-10044
+ 3.0.0-alpha1-10044
+ 3.0.0-alpha1-10044
15.6.82
15.6.82
15.6.82
2.8.0
2.8.0
- 3.0.0-alpha1-10016
- 3.0.0-alpha1-10016
+ 3.0.0-alpha1-10044
+ 3.0.0-alpha1-10044
2.2.0-preview1-26618-02
- 3.0.0-alpha1-10016
- 3.0.0-alpha1-10016
+ 3.0.0-alpha1-10044
+ 3.0.0-alpha1-10044
2.0.0
2.1.0
2.2.0-preview1-26618-02
@@ -59,7 +59,7 @@
2.9.0-beta4-62911-02
0.8.0
2.3.1
- 2.4.0-beta.1.build3945
+ 2.4.0-rc.1.build4038
diff --git a/korebuild-lock.txt b/korebuild-lock.txt
index 3f870e51fe..2395ab5956 100644
--- a/korebuild-lock.txt
+++ b/korebuild-lock.txt
@@ -1,2 +1,2 @@
-version:3.0.0-alpha1-10000
-commithash:b7b88d08d55abc8b71de9abf16e26fc713e332cd
+version:3.0.0-alpha1-10005
+commithash:05570853de976a526462ca140a55b1ac59d9a351
From a06c2b243f5e446a04d9439a87d62f6794c6c1ff Mon Sep 17 00:00:00 2001
From: "ASP.NET CI"
Date: Sun, 15 Jul 2018 20:09:12 +0000
Subject: [PATCH 06/18] Update dependencies.props
[auto-updated: dependencies]
---
build/dependencies.props | 28 ++++++++++++++--------------
korebuild-lock.txt | 4 ++--
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/build/dependencies.props b/build/dependencies.props
index 9179085d61..7d0045e5df 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -4,22 +4,22 @@
0.10.13
- 3.0.0-alpha1-10005
- 3.0.0-alpha1-10044
- 3.0.0-alpha1-10044
- 3.0.0-alpha1-10044
+ 3.0.0-alpha1-10009
+ 3.0.0-alpha1-10081
+ 3.0.0-alpha1-10081
+ 3.0.0-alpha1-10081
15.6.82
15.6.82
15.6.82
2.8.0
2.8.0
- 3.0.0-alpha1-10044
- 3.0.0-alpha1-10044
- 2.2.0-preview1-26618-02
- 3.0.0-alpha1-10044
- 3.0.0-alpha1-10044
- 2.0.0
- 2.1.0
+ 3.0.0-alpha1-10081
+ 3.0.0-alpha1-10081
+ 3.0.0-preview1-26710-03
+ 3.0.0-alpha1-10081
+ 3.0.0-alpha1-10081
+ 2.0.9
+ 2.1.2
2.2.0-preview1-26618-02
15.6.1
15.0.26606
@@ -43,9 +43,9 @@
2.0.3
11.0.2
1.1.92
- 4.6.0-preview1-26617-01
+ 4.6.0-preview1-26708-04
4.3.0
- 4.6.0-preview1-26617-01
+ 4.6.0-preview1-26708-04
9.0.1
2.9.0-beta4-62911-02
2.9.0-beta4-62911-02
@@ -57,7 +57,7 @@
2.9.0-beta4-62911-02
2.9.0-beta4-62911-02
2.9.0-beta4-62911-02
- 0.8.0
+ 0.9.0
2.3.1
2.4.0-rc.1.build4038
diff --git a/korebuild-lock.txt b/korebuild-lock.txt
index 2395ab5956..c6adb40215 100644
--- a/korebuild-lock.txt
+++ b/korebuild-lock.txt
@@ -1,2 +1,2 @@
-version:3.0.0-alpha1-10005
-commithash:05570853de976a526462ca140a55b1ac59d9a351
+version:3.0.0-alpha1-10009
+commithash:86be4707e47d2f1930a982f2b59eacfc4196da33
From a5bcb5fdc27e0df95256ba0ab6d2af4707311973 Mon Sep 17 00:00:00 2001
From: Ryan Nowak
Date: Tue, 10 Jul 2018 09:51:06 -0700
Subject: [PATCH 07/18] Make master build a 16.0 VSIX
This change updates the master branch of Razor to start producing a VS16
branded VSIX.
I haven't updated any package dependencies at this time, just changed the
version number and make our version range dependencies more flexible.
---
.../Microsoft.VisualStudio.RazorExtension.csproj | 2 +-
.../source.extension.vsixmanifest | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj b/tooling/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj
index 491559471e..6bdb8d5376 100644
--- a/tooling/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj
+++ b/tooling/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj
@@ -1,7 +1,7 @@
- 15.8
+ 16.0
99999
diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/source.extension.vsixmanifest b/tooling/Microsoft.VisualStudio.RazorExtension/source.extension.vsixmanifest
index 253dcc8b70..d50e2d376b 100644
--- a/tooling/Microsoft.VisualStudio.RazorExtension/source.extension.vsixmanifest
+++ b/tooling/Microsoft.VisualStudio.RazorExtension/source.extension.vsixmanifest
@@ -13,11 +13,11 @@
-
+
-
-
+
+
From 52304f76a980d1bdb65e86e80ddbb10e51b3a6e1 Mon Sep 17 00:00:00 2001
From: "ASP.NET CI"
Date: Sun, 22 Jul 2018 13:11:16 -0700
Subject: [PATCH 08/18] Update dependencies.props
[auto-updated: dependencies]
---
build/dependencies.props | 16 ++++++++--------
korebuild-lock.txt | 4 ++--
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/build/dependencies.props b/build/dependencies.props
index 7d0045e5df..e4656f54ad 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -4,20 +4,20 @@
0.10.13
- 3.0.0-alpha1-10009
- 3.0.0-alpha1-10081
- 3.0.0-alpha1-10081
- 3.0.0-alpha1-10081
+ 3.0.0-alpha1-10011
+ 3.0.0-alpha1-10123
+ 3.0.0-alpha1-10123
+ 3.0.0-alpha1-10123
15.6.82
15.6.82
15.6.82
2.8.0
2.8.0
- 3.0.0-alpha1-10081
- 3.0.0-alpha1-10081
+ 3.0.0-alpha1-10123
+ 3.0.0-alpha1-10123
3.0.0-preview1-26710-03
- 3.0.0-alpha1-10081
- 3.0.0-alpha1-10081
+ 3.0.0-alpha1-10123
+ 3.0.0-alpha1-10123
2.0.9
2.1.2
2.2.0-preview1-26618-02
diff --git a/korebuild-lock.txt b/korebuild-lock.txt
index c6adb40215..4cfdfb010e 100644
--- a/korebuild-lock.txt
+++ b/korebuild-lock.txt
@@ -1,2 +1,2 @@
-version:3.0.0-alpha1-10009
-commithash:86be4707e47d2f1930a982f2b59eacfc4196da33
+version:3.0.0-alpha1-10011
+commithash:717c2eb1f91dafd2580c1a9b8e5064d12dd8c054
From 1de182b6b8f6f89f1197a2f6a20c04f5142553de Mon Sep 17 00:00:00 2001
From: Eilon Lipton
Date: Tue, 24 Jul 2018 10:55:13 -0700
Subject: [PATCH 09/18] Update CONTRIBUTING.md
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 64ff041d5c..eac4268e4c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,4 @@
Contributing
======
-Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/dev/CONTRIBUTING.md) in the Home repo.
+Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo.
From 9e73caa4adc2e8fad6b7398b9ceb9caa9473aeea Mon Sep 17 00:00:00 2001
From: "ASP.NET CI"
Date: Sun, 29 Jul 2018 20:07:24 +0000
Subject: [PATCH 10/18] Update dependencies.props
[auto-updated: dependencies]
---
build/dependencies.props | 27 ++++++++++++++-------------
korebuild-lock.txt | 4 ++--
2 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/build/dependencies.props b/build/dependencies.props
index e4656f54ad..3e4d6ec313 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -4,20 +4,20 @@
0.10.13
- 3.0.0-alpha1-10011
- 3.0.0-alpha1-10123
- 3.0.0-alpha1-10123
- 3.0.0-alpha1-10123
+ 3.0.0-alpha1-10015
+ 3.0.0-alpha1-10173
+ 3.0.0-alpha1-10173
+ 3.0.0-alpha1-10173
15.6.82
15.6.82
15.6.82
2.8.0
2.8.0
- 3.0.0-alpha1-10123
- 3.0.0-alpha1-10123
- 3.0.0-preview1-26710-03
- 3.0.0-alpha1-10123
- 3.0.0-alpha1-10123
+ 3.0.0-alpha1-10173
+ 3.0.0-alpha1-10173
+ 3.0.0-preview1-26727-03
+ 3.0.0-alpha1-10173
+ 3.0.0-alpha1-10173
2.0.9
2.1.2
2.2.0-preview1-26618-02
@@ -43,9 +43,9 @@
2.0.3
11.0.2
1.1.92
- 4.6.0-preview1-26708-04
+ 4.6.0-preview1-26727-04
4.3.0
- 4.6.0-preview1-26708-04
+ 4.6.0-preview1-26727-04
9.0.1
2.9.0-beta4-62911-02
2.9.0-beta4-62911-02
@@ -57,10 +57,11 @@
2.9.0-beta4-62911-02
2.9.0-beta4-62911-02
2.9.0-beta4-62911-02
- 0.9.0
+ 0.10.0
2.3.1
- 2.4.0-rc.1.build4038
+ 2.4.0
+
diff --git a/korebuild-lock.txt b/korebuild-lock.txt
index 4cfdfb010e..8c70cbad9f 100644
--- a/korebuild-lock.txt
+++ b/korebuild-lock.txt
@@ -1,2 +1,2 @@
-version:3.0.0-alpha1-10011
-commithash:717c2eb1f91dafd2580c1a9b8e5064d12dd8c054
+version:3.0.0-alpha1-10015
+commithash:3f36e5c2f061712f76f2766c435d2555681d5c55
From 94a131414627d4eda52d1ffd6649277134f906a3 Mon Sep 17 00:00:00 2001
From: "N. Taylor Mullen"
Date: Mon, 30 Jul 2018 18:07:54 -0700
Subject: [PATCH 11/18] Add IVT to strong name shim for investigative purposes.
---
.../Properties/AssemblyInfo.cs | 1 +
.../Properties/AssemblyInfo.cs | 1 +
src/Microsoft.CodeAnalysis.Razor/Properties/AssemblyInfo.cs | 2 +-
.../Properties/AssemblyInfo.cs | 1 +
4 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Razor.Language/Properties/AssemblyInfo.cs
index 7214d8baca..e47484c273 100644
--- a/src/Microsoft.AspNetCore.Razor.Language/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.AspNetCore.Razor.Language/Properties/AssemblyInfo.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Properties/AssemblyInfo.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Properties/AssemblyInfo.cs
index fefb5cd1de..36e4a39988 100644
--- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Properties/AssemblyInfo.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Microsoft.CodeAnalysis.Razor/Properties/AssemblyInfo.cs b/src/Microsoft.CodeAnalysis.Razor/Properties/AssemblyInfo.cs
index 0de18b91dc..b19b6cc23a 100644
--- a/src/Microsoft.CodeAnalysis.Razor/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.CodeAnalysis.Razor/Properties/AssemblyInfo.cs
@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
-
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Microsoft.VisualStudio.Editor.Razor/Properties/AssemblyInfo.cs b/src/Microsoft.VisualStudio.Editor.Razor/Properties/AssemblyInfo.cs
index 47ff95c911..b862d878ba 100644
--- a/src/Microsoft.VisualStudio.Editor.Razor/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.VisualStudio.Editor.Razor/Properties/AssemblyInfo.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
From cc4fbfe2d95fa75afd6238b7185fdd09b5a76045 Mon Sep 17 00:00:00 2001
From: "ASP.NET CI"
Date: Sun, 5 Aug 2018 20:02:00 +0000
Subject: [PATCH 12/18] Update dependencies.props
[auto-updated: dependencies]
---
build/dependencies.props | 18 +++++++++---------
korebuild-lock.txt | 4 ++--
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/build/dependencies.props b/build/dependencies.props
index 3e4d6ec313..910334101b 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -4,20 +4,20 @@
0.10.13
- 3.0.0-alpha1-10015
- 3.0.0-alpha1-10173
- 3.0.0-alpha1-10173
- 3.0.0-alpha1-10173
+ 3.0.0-alpha1-20180731.2
+ 3.0.0-alpha1-10221
+ 3.0.0-alpha1-10221
+ 3.0.0-alpha1-10221
15.6.82
15.6.82
15.6.82
2.8.0
2.8.0
- 3.0.0-alpha1-10173
- 3.0.0-alpha1-10173
- 3.0.0-preview1-26727-03
- 3.0.0-alpha1-10173
- 3.0.0-alpha1-10173
+ 3.0.0-alpha1-10221
+ 3.0.0-alpha1-10221
+ 3.0.0-preview1-26731-01
+ 3.0.0-alpha1-10221
+ 3.0.0-alpha1-10221
2.0.9
2.1.2
2.2.0-preview1-26618-02
diff --git a/korebuild-lock.txt b/korebuild-lock.txt
index 8c70cbad9f..fe0f897b25 100644
--- a/korebuild-lock.txt
+++ b/korebuild-lock.txt
@@ -1,2 +1,2 @@
-version:3.0.0-alpha1-10015
-commithash:3f36e5c2f061712f76f2766c435d2555681d5c55
+version:3.0.0-alpha1-20180731.2
+commithash:1179f1083695ac9213c8a70a4d1d6c45a52caf41
From aec88e3eba339f91f75a4d1aa68ff7736aee34ee Mon Sep 17 00:00:00 2001
From: "N. Taylor Mullen"
Date: Wed, 6 Jun 2018 14:30:42 -0700
Subject: [PATCH 13/18] Change Razor directive completions to use new
completions API.
- Kept the same behavior as we previously had with Razor directive completions.
- Attempted adding additional functionalities such as lighting up Razor directive completion when completion was invoked on top of Razor directives (non-C#) but ran into issues involving the core HTML editor not consuming the new completion APIs yet. That's something we'll have to re-visit once they move to the new completion APIs.
- Added tests to validate all aspects of new completion APIs.
- Made completion provider turn on and off based off of feature flag.
#1743
#1813
---
build/dependencies.props | 3 +-
...Microsoft.VisualStudio.Editor.Razor.csproj | 4 +-
.../RazorDirectiveCompletionProvider.cs | 28 +-
.../RazorDirectiveCompletionSource.cs | 167 ++++++++
.../RazorDirectiveCompletionSourceProvider.cs | 64 +++
.../RazorDirectiveCompletionProviderTest.cs | 73 +++-
...orDirectiveCompletionSourceProviderTest.cs | 99 +++++
.../RazorDirectiveCompletionSourceTest.cs | 369 ++++++++++++++++++
8 files changed, 786 insertions(+), 21 deletions(-)
create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionSource.cs
create mode 100644 src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionSourceProvider.cs
create mode 100644 test/Microsoft.VisualStudio.Editor.Razor.Test/RazorDirectiveCompletionSourceProviderTest.cs
create mode 100644 test/Microsoft.VisualStudio.Editor.Razor.Test/RazorDirectiveCompletionSourceTest.cs
diff --git a/build/dependencies.props b/build/dependencies.props
index 910334101b..3668a75360 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -25,6 +25,7 @@
15.0.26606
15.6.161-preview
15.6.161-preview
+ 15.8.519
7.10.6070
15.3.224
2.0.6142705
@@ -42,7 +43,7 @@
4.7.49
2.0.3
11.0.2
- 1.1.92
+ 1.3.23
4.6.0-preview1-26727-04
4.3.0
4.6.0-preview1-26727-04
diff --git a/src/Microsoft.VisualStudio.Editor.Razor/Microsoft.VisualStudio.Editor.Razor.csproj b/src/Microsoft.VisualStudio.Editor.Razor/Microsoft.VisualStudio.Editor.Razor.csproj
index aa50d39965..0022ff4c94 100644
--- a/src/Microsoft.VisualStudio.Editor.Razor/Microsoft.VisualStudio.Editor.Razor.csproj
+++ b/src/Microsoft.VisualStudio.Editor.Razor/Microsoft.VisualStudio.Editor.Razor.csproj
@@ -9,7 +9,9 @@
-
+
+
+
diff --git a/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionProvider.cs b/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionProvider.cs
index 6a2d017716..fc3a4b9ed3 100644
--- a/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionProvider.cs
+++ b/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionProvider.cs
@@ -13,9 +13,9 @@ using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
-using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Tags;
using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Projection;
@@ -36,16 +36,33 @@ namespace Microsoft.VisualStudio.Editor.Razor
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
};
private readonly Lazy _codeDocumentProvider;
+ private readonly IAsyncCompletionBroker _asyncCompletionBroker;
+ private readonly RazorTextBufferProvider _textBufferProvider;
[ImportingConstructor]
- public RazorDirectiveCompletionProvider([Import(typeof(RazorCodeDocumentProvider))] Lazy codeDocumentProvider)
+ public RazorDirectiveCompletionProvider(
+ [Import(typeof(RazorCodeDocumentProvider))] Lazy codeDocumentProvider,
+ IAsyncCompletionBroker asyncCompletionBroker,
+ RazorTextBufferProvider textBufferProvider)
{
if (codeDocumentProvider == null)
{
throw new ArgumentNullException(nameof(codeDocumentProvider));
}
+ if (asyncCompletionBroker == null)
+ {
+ throw new ArgumentNullException(nameof(asyncCompletionBroker));
+ }
+
+ if (textBufferProvider == null)
+ {
+ throw new ArgumentNullException(nameof(textBufferProvider));
+ }
+
_codeDocumentProvider = codeDocumentProvider;
+ _asyncCompletionBroker = asyncCompletionBroker;
+ _textBufferProvider = textBufferProvider;
}
public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
@@ -86,6 +103,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
return Task.CompletedTask;
}
+ if (!_textBufferProvider.TryGetFromDocument(context.Document, out var textBuffer) ||
+ !_asyncCompletionBroker.IsCompletionSupported(textBuffer.ContentType))
+ {
+ // Completion is not supported.
+ return Task.CompletedTask;
+ }
+
var result = AddCompletionItems(context);
return result;
diff --git a/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionSource.cs b/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionSource.cs
new file mode 100644
index 0000000000..06677a843c
--- /dev/null
+++ b/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionSource.cs
@@ -0,0 +1,167 @@
+// 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.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.AspNetCore.Razor.Language.Legacy;
+using Microsoft.CodeAnalysis.Razor;
+using Microsoft.VisualStudio.Core.Imaging;
+using Microsoft.VisualStudio.Imaging;
+using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
+using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Adornments;
+
+namespace Microsoft.VisualStudio.Editor.Razor
+{
+ internal class RazorDirectiveCompletionSource : IAsyncCompletionSource
+ {
+ // Internal for testing
+ internal static readonly object DescriptionKey = new object();
+ internal static readonly ImageElement DirectiveImageGlyph = new ImageElement(
+ new ImageId(KnownImageIds.ImageCatalogGuid, KnownImageIds.Type),
+ "Razor Directive.");
+ internal static readonly ImmutableArray DirectiveCompletionFilters = new[] {
+ new CompletionFilter("Razor Directive", "r", DirectiveImageGlyph)
+ }.ToImmutableArray();
+ private static readonly IEnumerable DefaultDirectives = new[]
+ {
+ CSharpCodeParser.AddTagHelperDirectiveDescriptor,
+ CSharpCodeParser.RemoveTagHelperDirectiveDescriptor,
+ CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
+ };
+
+ // Internal for testing
+ internal readonly VisualStudioRazorParser _parser;
+ private readonly ForegroundDispatcher _foregroundDispatcher;
+
+ public RazorDirectiveCompletionSource(
+ VisualStudioRazorParser parser,
+ ForegroundDispatcher foregroundDispatcher)
+ {
+ if (parser == null)
+ {
+ throw new ArgumentNullException(nameof(parser));
+ }
+
+ if (foregroundDispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(foregroundDispatcher));
+ }
+
+ _parser = parser;
+ _foregroundDispatcher = foregroundDispatcher;
+ }
+
+ public Task GetCompletionContextAsync(
+ InitialTrigger trigger,
+ SnapshotPoint triggerLocation,
+ SnapshotSpan applicableSpan,
+ CancellationToken token)
+ {
+ _foregroundDispatcher.AssertBackgroundThread();
+
+ var syntaxTree = _parser.CodeDocument?.GetSyntaxTree();
+ if (!AtDirectiveCompletionPoint(syntaxTree, triggerLocation))
+ {
+ return Task.FromResult(CompletionContext.Empty);
+ }
+
+ var completionItems = GetCompletionItems(syntaxTree);
+ var context = new CompletionContext(completionItems.ToImmutableArray());
+ return Task.FromResult(context);
+ }
+
+ public Task
0.10.13
- 3.0.0-alpha1-20180731.2
- 3.0.0-alpha1-10221
- 3.0.0-alpha1-10221
- 3.0.0-alpha1-10221
+ 3.0.0-alpha1-20180810.1
+ 3.0.0-alpha1-10275
+ 3.0.0-alpha1-10275
+ 3.0.0-alpha1-10275
15.6.82
15.6.82
15.6.82
2.8.0
2.8.0
- 3.0.0-alpha1-10221
- 3.0.0-alpha1-10221
- 3.0.0-preview1-26731-01
- 3.0.0-alpha1-10221
- 3.0.0-alpha1-10221
+ 3.0.0-alpha1-10275
+ 3.0.0-alpha1-10275
+ 3.0.0-preview1-26810-02
+ 3.0.0-alpha1-10275
+ 3.0.0-alpha1-10275
2.0.9
2.1.2
2.2.0-preview1-26618-02
@@ -44,9 +44,9 @@
2.0.3
11.0.2
1.3.23
- 4.6.0-preview1-26727-04
+ 4.6.0-preview1-26807-04
4.3.0
- 4.6.0-preview1-26727-04
+ 4.6.0-preview1-26807-04
9.0.1
2.9.0-beta4-62911-02
2.9.0-beta4-62911-02
diff --git a/korebuild-lock.txt b/korebuild-lock.txt
index fe0f897b25..c53be3fca9 100644
--- a/korebuild-lock.txt
+++ b/korebuild-lock.txt
@@ -1,2 +1,2 @@
-version:3.0.0-alpha1-20180731.2
-commithash:1179f1083695ac9213c8a70a4d1d6c45a52caf41
+version:3.0.0-alpha1-20180810.1
+commithash:45c32b4f020e14a9295be31866051a18d293309d
From 9a9931d59fb9f6c8cd1dd7a9dc98b2c7d4085b41 Mon Sep 17 00:00:00 2001
From: Ajay Bhargav Baaskaran
Date: Mon, 13 Aug 2018 11:26:24 -0700
Subject: [PATCH 18/18] Initial infrastrucure for a better html parser
(#2522)
Initial infrastrucure for a better html parser
- Added new syntax node types that follow Roslyn's red/green pattern
- Modified the html text node in the current syntax tree to use the new
nodes
- Updated test infrastructure
- Generated baselines
---
.../Legacy/CSharpToken.cs | 11 +
.../Legacy/HtmlMarkupParser.cs | 15 +-
.../Legacy/HtmlToken.cs | 43 +
.../Legacy/IToken.cs | 6 +
.../Legacy/Span.cs | 4 +
.../Legacy/SpanBuilder.cs | 30 +-
.../Legacy/TokenBase.cs | 10 +-
.../Legacy/TokenizerBackedParser.cs | 21 +-
.../Syntax/ArrayElement.cs | 58 ++
.../Syntax/GreenNode.cs | 514 ++++++++++++
.../Syntax/HtmlNodeSyntax.cs | 20 +
.../Syntax/HtmlTextSyntax.cs | 42 +
.../Syntax/HtmlTextTokenSyntax.cs | 27 +
.../Syntax/InternalSyntax/HtmlNodeSyntax.cs | 28 +
.../Syntax/InternalSyntax/HtmlTextSyntax.cs | 59 ++
.../InternalSyntax/HtmlTextTokenSyntax.cs | 53 ++
.../InternalSyntax/NewLineTokenSyntax.cs | 50 ++
.../InternalSyntax/PunctuationSyntax.cs | 45 +
.../Syntax/InternalSyntax/SyntaxFactory.cs | 38 +
.../Syntax/InternalSyntax/SyntaxList.cs | 418 ++++++++++
.../InternalSyntax/SyntaxListBuilder.cs | 201 +++++
.../InternalSyntax/SyntaxListBuilderOfT.cs | 115 +++
.../Syntax/InternalSyntax/SyntaxListOfT.cs | 139 ++++
.../Syntax/InternalSyntax/SyntaxToken.cs | 119 +++
.../Syntax/InternalSyntax/SyntaxTrivia.cs | 80 ++
.../Syntax/InternalSyntax/SyntaxVisitor.cs | 43 +
.../InternalSyntax/UnknownTokenSyntax.cs | 50 ++
.../InternalSyntax/WhitespaceTokenSyntax.cs | 50 ++
.../Syntax/NewLineTokenSyntax.cs | 27 +
.../Syntax/NodeFlags.cs | 21 +
.../Syntax/ObjectPool.cs | 248 ++++++
.../Syntax/ParserState.cs | 14 +
.../Syntax/PunctuationSyntax.cs | 27 +
.../Syntax/SpecializedCollections.cs | 169 ++++
.../Syntax/SyntaxAnnotation.cs | 93 +++
.../Syntax/SyntaxKind.cs | 32 +
.../Syntax/SyntaxList.cs | 118 +++
.../Syntax/SyntaxListBuilder.cs | 172 ++++
.../Syntax/SyntaxListBuilderExtensions.cs | 29 +
.../Syntax/SyntaxListBuilderOfT.cs | 93 +++
.../Syntax/SyntaxListOfT.cs | 606 ++++++++++++++
.../Syntax/SyntaxNode.cs | 252 ++++++
.../Syntax/SyntaxToken.cs | 91 ++
.../Syntax/SyntaxTrivia.cs | 54 ++
.../Syntax/SyntaxTriviaList.cs | 785 ++++++++++++++++++
.../Syntax/SyntaxTriviaListBuilder.cs | 138 +++
.../Syntax/SyntaxVisitor.cs | 43 +
.../Syntax/TextSpan.cs | 261 ++++++
.../Syntax/UnknownTokenSyntax.cs | 27 +
.../Syntax/WhitespaceTokenSyntax.cs | 27 +
...rectiveAutoCompleteAtStartOfFile.stree.txt | 4 +-
...rWhiteSpaceCommentsAndDirectives.stree.txt | 13 +-
...escriptor_UnderstandsRazorBlocks.stree.txt | 13 +-
.../Parse_SectionDirective.stree.txt | 13 +-
...rCommentWithExtraNewLineInMarkup.stree.txt | 4 +-
.../RazorCommentsSurroundingMarkup.stree.txt | 4 +-
...aceMultipleLinesBelowSectionName.stree.txt | 4 +-
...ceBetweenSectionNameAndOpenBrace.stree.txt | 4 +-
.../HandlesUnterminatedSection.stree.txt | 9 +-
...rserOutputsErrorOnNestedSections.stree.txt | 4 +-
.../ParsesNamedSectionCorrectly.stree.txt | 4 +-
...NotFollowedByIdentifierStartChar.stree.txt | 4 +-
...lockIfNameNotFollowedByOpenBrace.stree.txt | 4 +-
...tmlCommentSupportsMultipleDashes.stree.txt | 12 +-
...lyHandlesOddlySpacedHTMLElements.stree.txt | 9 +-
...ineOfMarkupWithEmbeddedStatement.stree.txt | 7 +-
...hToCodeOnEmailAddressInAttribute.stree.txt | 9 +-
...pportsDollarOpenBraceCombination.stree.txt | 4 +-
.../RendersTextPseudoTagAsMarkup.stree.txt | 4 +-
.../VoidElementFollowedByOtherTag.stree.txt | 435 +++++-----
...oCodeIfThereIsNoMarkupOnThatLine.stree.txt | 4 +-
...TreatsTwoAtSignsAsEscapeSequence.stree.txt | 7 +-
...TreatsTwoAtSignsAsEscapeSequence.stree.txt | 7 +-
...oCodeIfThereIsNoMarkupOnThatLine.stree.txt | 4 +-
.../CanHandleSymbolBoundAttributes3.stree.txt | 9 +-
.../CanHandleSymbolBoundAttributes4.stree.txt | 9 +-
...eatesErrorForMalformedTagHelper6.stree.txt | 4 +-
...eatesErrorForMalformedTagHelper7.stree.txt | 7 +-
...eTreeRewriter_AllowsInvalidHtml1.stree.txt | 7 +-
...TreeRewriter_AllowsInvalidHtml10.stree.txt | 7 +-
...eTreeRewriter_AllowsInvalidHtml6.stree.txt | 7 +-
...eTreeRewriter_AllowsInvalidHtml7.stree.txt | 7 +-
...eTreeRewriter_AllowsInvalidHtml8.stree.txt | 7 +-
...eTreeRewriter_AllowsInvalidHtml9.stree.txt | 15 +-
...atesErrorForIncompleteTagHelper2.stree.txt | 11 +-
...atesErrorForIncompleteTagHelper3.stree.txt | 11 +-
...atesErrorForIncompleteTagHelper4.stree.txt | 11 +-
...plexAttributeTagHelperTagBlocks3.stree.txt | 9 +-
...plexAttributeTagHelperTagBlocks4.stree.txt | 9 +-
...plexAttributeTagHelperTagBlocks5.stree.txt | 12 +-
...plexAttributeTagHelperTagBlocks6.stree.txt | 12 +-
...plexAttributeTagHelperTagBlocks7.stree.txt | 28 +-
...writesComplexTagHelperTagBlocks5.stree.txt | 4 +-
...writesComplexTagHelperTagBlocks6.stree.txt | 4 +-
...writesComplexTagHelperTagBlocks7.stree.txt | 11 +-
...writesComplexTagHelperTagBlocks8.stree.txt | 11 +-
...esOddlySpacedTagHelperTagBlocks2.stree.txt | 9 +-
...esOddlySpacedTagHelperTagBlocks3.stree.txt | 12 +-
...RewritesPlainTagHelperTagBlocks2.stree.txt | 9 +-
...RewritesPlainTagHelperTagBlocks3.stree.txt | 12 +-
...RewritesPlainTagHelperTagBlocks4.stree.txt | 28 +-
...writer_RewritesScriptTagHelpers3.stree.txt | 8 +-
...writer_RewritesScriptTagHelpers4.stree.txt | 4 +-
...writer_RewritesScriptTagHelpers6.stree.txt | 14 +-
...writer_RewritesScriptTagHelpers7.stree.txt | 14 +-
...r_RewritesSelfClosingTagHelpers2.stree.txt | 14 +-
...r_RewritesSelfClosingTagHelpers3.stree.txt | 4 +-
...esTagHelpersWithPlainAttributes2.stree.txt | 9 +-
...esTagHelpersWithPlainAttributes3.stree.txt | 12 +-
...esTagHelpersWithPlainAttributes4.stree.txt | 28 +-
...gHelpersWithQuotelessAttributes2.stree.txt | 9 +-
...gHelpersWithQuotelessAttributes3.stree.txt | 9 +-
...gHelpersWithQuotelessAttributes4.stree.txt | 12 +-
...gHelpersWithQuotelessAttributes5.stree.txt | 28 +-
.../AllowsPrefixedTagHelpers10.stree.txt | 13 +-
.../AllowsPrefixedTagHelpers2.stree.txt | 13 +-
.../AllowsRazorCommentsAsChildren.stree.txt | 4 +-
.../AllowsRazorMarkupInHtmlComment.stree.txt | 4 +-
...lowsSimpleHtmlCommentsAsChildren.stree.txt | 4 +-
...entOptOutHTML_WithAttributeData3.stree.txt | 13 +-
...ElementOptOutHTML_WithBlockData4.stree.txt | 13 +-
...dleInvalidChildrenWithWhitespace.stree.txt | 24 +-
...pleTagHelpersWithAllowedChildren.stree.txt | 9 +-
...lpersWithAllowedChildren_OneNull.stree.txt | 9 +-
...sNotRewriteSpecialTagTagHelpers3.stree.txt | 23 +-
...sNotRewriteSpecialTagTagHelpers4.stree.txt | 9 +-
...sNotRewriteSpecialTagTagHelpers5.stree.txt | 7 +-
...sNotRewriteSpecialTagTagHelpers6.stree.txt | 25 +-
...sNotRewriteSpecialTagTagHelpers7.stree.txt | 29 +-
...sNotRewriteSpecialTagTagHelpers8.stree.txt | 11 +-
...riteTextTagTransitionTagHelpers1.stree.txt | 9 +-
...CreateTagHelperBlocksCorrectly12.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly13.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly18.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly21.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly22.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly23.stree.txt | 12 +-
...CreateTagHelperBlocksCorrectly25.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly27.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly28.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly29.stree.txt | 13 +-
...CreateTagHelperBlocksCorrectly30.stree.txt | 12 +-
...sCreateTagHelperBlocksCorrectly7.stree.txt | 13 +-
...sCreateTagHelperBlocksCorrectly8.stree.txt | 13 +-
...sCreateTagHelperBlocksCorrectly9.stree.txt | 12 +-
...ewritesNestedTagHelperTagBlocks2.stree.txt | 11 +-
...ewritesNestedTagHelperTagBlocks3.stree.txt | 16 +-
...ewritesNestedTagHelperTagBlocks4.stree.txt | 16 +-
.../UnderstandsAllowedChildren10.stree.txt | 21 +-
.../UnderstandsAllowedChildren11.stree.txt | 21 +-
.../UnderstandsAllowedChildren14.stree.txt | 12 +-
.../UnderstandsAllowedChildren2.stree.txt | 8 +-
.../UnderstandsAllowedChildren4.stree.txt | 4 +-
.../UnderstandsAllowedChildren6.stree.txt | 4 +-
.../UnderstandsAllowedChildren7.stree.txt | 8 +-
.../UnderstandsAllowedChildren8.stree.txt | 8 +-
.../UnderstandsAllowedChildren9.stree.txt | 19 +-
.../Language/Legacy/RawTextToken.cs | 6 +-
.../Language/Legacy/SyntaxTreeNodeWriter.cs | 51 ++
159 files changed, 6494 insertions(+), 758 deletions(-)
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/ArrayElement.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/GreenNode.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlNodeSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlTextSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlTextTokenSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlNodeSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlTextSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlTextTokenSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/NewLineTokenSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/PunctuationSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxFactory.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxList.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListBuilder.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListBuilderOfT.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListOfT.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxToken.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxTrivia.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxVisitor.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/UnknownTokenSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/WhitespaceTokenSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/NewLineTokenSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/NodeFlags.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/ObjectPool.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/ParserState.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/PunctuationSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SpecializedCollections.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxAnnotation.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxKind.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxList.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilder.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilderExtensions.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilderOfT.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListOfT.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxNode.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxToken.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTrivia.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTriviaList.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTriviaListBuilder.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxVisitor.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/TextSpan.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/UnknownTokenSyntax.cs
create mode 100644 src/Microsoft.AspNetCore.Razor.Language/Syntax/WhitespaceTokenSyntax.cs
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpToken.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpToken.cs
index c928f0880e..02428c2691 100644
--- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpToken.cs
+++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpToken.cs
@@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@@ -33,6 +35,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public CSharpKeyword? Keyword { get; set; }
+ protected override SyntaxToken GetSyntaxToken()
+ {
+ switch (Type)
+ {
+ default:
+ return SyntaxFactory.UnknownToken(Content, Errors.ToArray());
+ }
+ }
+
public override bool Equals(object obj)
{
var other = obj as CSharpToken;
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs
index 4f4d710616..c1292b44bd 100644
--- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs
+++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs
@@ -1590,6 +1590,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Span.Start = CurrentLocation;
+ ParserState = ParserState.Misc;
NextToken();
while (!EndOfFile)
{
@@ -1636,7 +1637,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
- Output(SpanKindInternal.Markup);
+ if (ParserState == ParserState.Content)
+ {
+ Output(SpanKindInternal.Markup, SyntaxKind.HtmlText);
+ }
+ else
+ {
+ Output(SpanKindInternal.Markup);
+ }
// Start tag block
var tagBlock = Context.Builder.StartBlock(BlockKindInternal.Tag);
@@ -1645,6 +1653,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (!At(HtmlTokenType.ForwardSlash))
{
+ ParserState = ParserState.StartTag;
OptionalBangEscape();
// Parsing a start tag
@@ -1655,6 +1664,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Optional(HtmlTokenType.ForwardSlash);
Optional(HtmlTokenType.CloseAngle);
+ ParserState = ParserState.Content;
+
// If the script tag expects javascript content then we should do minimal parsing until we reach
// the end script tag. Don't want to incorrectly parse a "var tag = '';" as an HTML tag.
if (scriptTag && !CurrentScriptTagExpectsHtml())
@@ -1670,6 +1681,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
// Parsing an end tag
// This section can accept things like: '
' or '' etc.
+ ParserState = ParserState.EndTag;
Optional(HtmlTokenType.ForwardSlash);
// Whitespace here is invalid (according to the spec)
@@ -1677,6 +1689,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Optional(HtmlTokenType.Text);
Optional(HtmlTokenType.WhiteSpace);
Optional(HtmlTokenType.CloseAngle);
+ ParserState = ParserState.Content;
}
Output(SpanKindInternal.Markup);
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlToken.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlToken.cs
index 2d52ed14e2..9659d85360 100644
--- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlToken.cs
+++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlToken.cs
@@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@@ -30,5 +32,46 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
throw new ArgumentNullException(nameof(content));
}
}
+
+ protected override SyntaxToken GetSyntaxToken()
+ {
+ switch (Type)
+ {
+ case HtmlTokenType.Text:
+ return SyntaxFactory.HtmlTextToken(Content, Errors.ToArray());
+ case HtmlTokenType.WhiteSpace:
+ return SyntaxFactory.WhitespaceToken(Content, Errors.ToArray());
+ case HtmlTokenType.NewLine:
+ return SyntaxFactory.NewLineToken(Content, Errors.ToArray());
+ case HtmlTokenType.OpenAngle:
+ return SyntaxFactory.Punctuation(SyntaxKind.OpenAngle, Content, Errors.ToArray());
+ case HtmlTokenType.Bang:
+ return SyntaxFactory.Punctuation(SyntaxKind.Bang, Content, Errors.ToArray());
+ case HtmlTokenType.ForwardSlash:
+ return SyntaxFactory.Punctuation(SyntaxKind.ForwardSlash, Content, Errors.ToArray());
+ case HtmlTokenType.QuestionMark:
+ return SyntaxFactory.Punctuation(SyntaxKind.QuestionMark, Content, Errors.ToArray());
+ case HtmlTokenType.DoubleHyphen:
+ return SyntaxFactory.Punctuation(SyntaxKind.DoubleHyphen, Content, Errors.ToArray());
+ case HtmlTokenType.LeftBracket:
+ return SyntaxFactory.Punctuation(SyntaxKind.LeftBracket, Content, Errors.ToArray());
+ case HtmlTokenType.CloseAngle:
+ return SyntaxFactory.Punctuation(SyntaxKind.CloseAngle, Content, Errors.ToArray());
+ case HtmlTokenType.RightBracket:
+ return SyntaxFactory.Punctuation(SyntaxKind.RightBracket, Content, Errors.ToArray());
+ case HtmlTokenType.Equals:
+ return SyntaxFactory.Punctuation(SyntaxKind.Equals, Content, Errors.ToArray());
+ case HtmlTokenType.DoubleQuote:
+ return SyntaxFactory.Punctuation(SyntaxKind.DoubleQuote, Content, Errors.ToArray());
+ case HtmlTokenType.SingleQuote:
+ return SyntaxFactory.Punctuation(SyntaxKind.SingleQuote, Content, Errors.ToArray());
+ case HtmlTokenType.Transition:
+ return SyntaxFactory.Punctuation(SyntaxKind.Transition, Content, Errors.ToArray());
+ case HtmlTokenType.Colon:
+ return SyntaxFactory.Punctuation(SyntaxKind.Colon, Content, Errors.ToArray());
+ default:
+ return SyntaxFactory.UnknownToken(Content, Errors.ToArray());
+ }
+ }
}
}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/IToken.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/IToken.cs
index 2f8eb1214d..a57f9dbf4d 100644
--- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/IToken.cs
+++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/IToken.cs
@@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
+
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
internal interface IToken
@@ -10,5 +12,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
string Content { get; }
SourceLocation Start { get; }
+
+ SyntaxKind SyntaxKind { get; }
+
+ SyntaxToken SyntaxToken { get; }
}
}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/Span.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/Span.cs
index fbf9f9f2c2..ec448d0dc4 100644
--- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/Span.cs
+++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/Span.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@@ -31,6 +32,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public SpanEditHandler EditHandler { get; private set; }
+ public HtmlNodeSyntax SyntaxNode { get; private set; }
+
public override bool IsBlock => false;
public override int Length
@@ -94,6 +97,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
Kind = builder.Kind;
Tokens = builder.Tokens;
+ SyntaxNode = builder.SyntaxNode;
for (var i = 0; i (SyntaxListBuilder.Create());
+ foreach (var token in Tokens)
+ {
+ if (token.SyntaxKind == SyntaxKind.Unknown)
+ {
+ Debug.Assert(false, $"Unexpected html token {((HtmlToken)token).Type}");
+ continue;
+ }
+
+ textTokens.Add(token.SyntaxToken);
+ }
+ var textResult = textTokens.ToList();
+ return SyntaxFactory.HtmlText(new SyntaxList(textResult.Node));
+ }
+
+ return null;
+ }
}
}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenBase.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenBase.cs
index 562a41f5bc..ade6bfc95c 100644
--- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenBase.cs
+++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenBase.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
@@ -58,10 +59,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
+ public SyntaxKind SyntaxKind => SyntaxToken.Kind;
+
+ public SyntaxToken SyntaxToken => GetSyntaxToken();
+
+ protected abstract SyntaxToken GetSyntaxToken();
+
public override bool Equals(object obj)
{
- var other = obj as TokenBase;
- return other != null &&
+ return obj is TokenBase other &&
string.Equals(Content, other.Content, StringComparison.Ordinal) &&
Type.Equals(other.Type);
}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs
index af5a08831f..f7b054ecff 100644
--- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs
+++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
@@ -25,6 +26,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Span = new SpanBuilder(CurrentLocation);
}
+ protected ParserState ParserState { get; set; }
+
protected SpanBuilder Span { get; private set; }
protected Action SpanConfig { get; set; }
@@ -34,6 +37,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
get { return _tokenizer.Current; }
}
+ protected SyntaxToken CurrentSyntaxToken => CurrentToken?.SyntaxToken;
+
protected TToken PreviousToken { get; private set; }
protected SourceLocation CurrentLocation => _tokenizer.Tokenizer.CurrentLocation;
@@ -388,31 +393,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
- protected internal void Output(SpanKindInternal kind)
+ protected internal void Output(SpanKindInternal kind, SyntaxKind syntaxKind = SyntaxKind.Unknown)
{
Configure(kind, null);
- Output();
+ Output(syntaxKind);
}
- protected internal void Output(SpanKindInternal kind, AcceptedCharactersInternal accepts)
+ protected internal void Output(SpanKindInternal kind, AcceptedCharactersInternal accepts, SyntaxKind syntaxKind = SyntaxKind.Unknown)
{
Configure(kind, accepts);
- Output();
+ Output(syntaxKind);
}
- protected internal void Output(AcceptedCharactersInternal accepts)
+ protected internal void Output(AcceptedCharactersInternal accepts, SyntaxKind syntaxKind = SyntaxKind.Unknown)
{
Configure(null, accepts);
- Output();
+ Output(syntaxKind);
}
- private void Output()
+ private void Output(SyntaxKind syntaxKind)
{
if (Span.Tokens.Count > 0)
{
var nextStart = Span.End;
- var builtSpan = Span.Build();
+ var builtSpan = Span.Build(syntaxKind);
Context.Builder.Add(builtSpan);
Initialize(Span);
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/ArrayElement.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/ArrayElement.cs
new file mode 100644
index 0000000000..85345e657b
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/ArrayElement.cs
@@ -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
+ {
+ public T Value;
+
+ public static implicit operator T(ArrayElement element)
+ {
+ return element.Value;
+ }
+
+ //NOTE: there is no opposite conversion operator T -> ArrayElement
+ //
+ // 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)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[] MakeElementArray(T[] items)
+ {
+ if (items == null)
+ {
+ return null;
+ }
+
+ var array = new ArrayElement[items.Length];
+ for (var i = 0; i < items.Length; i++)
+ {
+ array[i].Value = items[i];
+ }
+
+ return array;
+ }
+
+ public static T[] MakeArray(ArrayElement[] 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;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/GreenNode.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/GreenNode.cs
new file mode 100644
index 0000000000..e90374ad2b
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/GreenNode.cs
@@ -0,0 +1,514 @@
+// 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();
+ private static readonly SyntaxAnnotation[] EmptyAnnotations = Array.Empty();
+ private static readonly ConditionalWeakTable DiagnosticsTable =
+ new ConditionalWeakTable();
+ private static readonly ConditionalWeakTable AnnotationsTable =
+ new ConditionalWeakTable();
+
+ 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();
+ 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 Factories
+ public virtual GreenNode CreateList(IEnumerable 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
+
+ internal virtual GreenNode Accept(InternalSyntax.SyntaxVisitor visitor)
+ {
+ return visitor.Visit(this);
+ }
+
+ #region StaticMethods
+
+ private static void ProcessStack(TextWriter writer,
+ Stack 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
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlNodeSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlNodeSyntax.cs
new file mode 100644
index 0000000000..c247183c1d
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlNodeSyntax.cs
@@ -0,0 +1,20 @@
+// 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 HtmlNodeSyntax : SyntaxNode
+ {
+ internal HtmlNodeSyntax(GreenNode green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ internal new InternalSyntax.HtmlNodeSyntax Green => (InternalSyntax.HtmlNodeSyntax)base.Green;
+
+ internal override SyntaxNode Accept(SyntaxVisitor visitor)
+ {
+ return visitor.VisitHtmlNode(this);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlTextSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlTextSyntax.cs
new file mode 100644
index 0000000000..a28dc9bbdd
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlTextSyntax.cs
@@ -0,0 +1,42 @@
+// 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 class HtmlTextSyntax : HtmlNodeSyntax
+ {
+ private SyntaxNode _textTokens;
+
+ internal HtmlTextSyntax(GreenNode green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ public SyntaxList TextTokens => new SyntaxList(GetRed(ref _textTokens, 0));
+
+ public string Value => TextTokens[0]?.ToFullString() ?? string.Empty;
+
+ internal override SyntaxNode Accept(SyntaxVisitor visitor)
+ {
+ return visitor.VisitHtmlText(this);
+ }
+
+ internal override SyntaxNode GetCachedSlot(int index)
+ {
+ switch (index)
+ {
+ case 0: return _textTokens;
+ default: return null;
+ }
+ }
+
+ internal override SyntaxNode GetNodeSlot(int slot)
+ {
+ switch (slot)
+ {
+ case 0: return GetRed(ref _textTokens, 0);
+ default: return null;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlTextTokenSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlTextTokenSyntax.cs
new file mode 100644
index 0000000000..9130b5c5d4
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/HtmlTextTokenSyntax.cs
@@ -0,0 +1,27 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax
+{
+ internal class HtmlTextTokenSyntax : SyntaxToken
+ {
+ internal HtmlTextTokenSyntax(GreenNode green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ internal new InternalSyntax.HtmlTextTokenSyntax Green => (InternalSyntax.HtmlTextTokenSyntax)base.Green;
+
+ public string Value => Text;
+
+ internal override SyntaxToken WithLeadingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.HtmlTextTokenSyntax(Text, trivia?.Green, GetTrailingTrivia().Node?.Green).CreateRed(Parent, Position) as HtmlTextTokenSyntax;
+ }
+
+ internal override SyntaxToken WithTrailingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.HtmlTextTokenSyntax(Text, GetLeadingTrivia().Node?.Green, trivia?.Green).CreateRed(Parent, Position) as HtmlTextTokenSyntax;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlNodeSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlNodeSyntax.cs
new file mode 100644
index 0000000000..4289dfa683
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlNodeSyntax.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
+{
+ internal abstract class HtmlNodeSyntax : GreenNode
+ {
+ protected HtmlNodeSyntax(SyntaxKind kind)
+ : base(kind)
+ {
+ }
+
+ protected HtmlNodeSyntax(SyntaxKind kind, int fullWidth)
+ : base(kind, fullWidth)
+ {
+ }
+
+ protected HtmlNodeSyntax(SyntaxKind kind, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
+ : base(kind, diagnostics, annotations)
+ {
+ }
+
+ internal override GreenNode Accept(SyntaxVisitor visitor)
+ {
+ return visitor.VisitHtmlNode(this);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlTextSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlTextSyntax.cs
new file mode 100644
index 0000000000..57e3c4f113
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlTextSyntax.cs
@@ -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.InternalSyntax
+{
+ internal class HtmlTextSyntax : HtmlNodeSyntax
+ {
+ private readonly GreenNode _value;
+
+ internal HtmlTextSyntax(GreenNode value) : base(SyntaxKind.HtmlText)
+ {
+ SlotCount = 1;
+ _value = value;
+ AdjustFlagsAndWidth(value);
+ }
+
+ internal HtmlTextSyntax(GreenNode value, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
+ : base(SyntaxKind.HtmlText, diagnostics, annotations)
+ {
+ SlotCount = 1;
+ _value = value;
+ AdjustFlagsAndWidth(value);
+ }
+
+ internal SyntaxList TextTokens => new SyntaxList(_value);
+
+ internal override GreenNode GetSlot(int index)
+ {
+ switch (index)
+ {
+ case 0: return _value;
+ }
+
+ throw new InvalidOperationException();
+ }
+
+ internal override GreenNode Accept(SyntaxVisitor visitor)
+ {
+ return visitor.VisitHtmlText(this);
+ }
+
+ internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
+ {
+ return new Syntax.HtmlTextSyntax(this, parent, position);
+ }
+
+ internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
+ {
+ return new HtmlTextSyntax(_value, diagnostics, GetAnnotations());
+ }
+
+ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
+ {
+ return new HtmlTextSyntax(_value, GetDiagnostics(), annotations);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlTextTokenSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlTextTokenSyntax.cs
new file mode 100644
index 0000000000..68d6eecd97
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/HtmlTextTokenSyntax.cs
@@ -0,0 +1,53 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
+{
+ internal class HtmlTextTokenSyntax : SyntaxToken
+ {
+ internal HtmlTextTokenSyntax(string text, params RazorDiagnostic[] diagnostics)
+ : base(SyntaxKind.HtmlTextLiteralToken, text, null, null, diagnostics, null)
+ {
+ }
+
+ internal HtmlTextTokenSyntax(string text, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(SyntaxKind.HtmlTextLiteralToken, text, leadingTrivia, trailingTrivia)
+ {
+ }
+
+ protected HtmlTextTokenSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(kind, name, leadingTrivia, trailingTrivia)
+ {
+ }
+
+ protected HtmlTextTokenSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
+ : base(kind, name, leadingTrivia, trailingTrivia, diagnostics, annotations)
+ {
+ }
+
+ internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
+ {
+ return new Syntax.HtmlTextTokenSyntax(this, parent, position);
+ }
+
+ public override SyntaxToken TokenWithLeadingTrivia(GreenNode trivia)
+ {
+ return new HtmlTextTokenSyntax(Kind, Text, trivia, TrailingTrivia);
+ }
+
+ public override SyntaxToken TokenWithTrailingTrivia(GreenNode trivia)
+ {
+ return new HtmlTextTokenSyntax(Kind, Text, LeadingTrivia, trivia);
+ }
+
+ internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
+ {
+ return new HtmlTextTokenSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, diagnostics, GetAnnotations());
+ }
+
+ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
+ {
+ return new HtmlTextTokenSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, GetDiagnostics(), annotations);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/NewLineTokenSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/NewLineTokenSyntax.cs
new file mode 100644
index 0000000000..97d47e9fb9
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/NewLineTokenSyntax.cs
@@ -0,0 +1,50 @@
+// 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 class NewLineTokenSyntax : SyntaxToken
+ {
+ internal NewLineTokenSyntax(string text, params RazorDiagnostic[] diagnostics)
+ : base(SyntaxKind.NewLine, text, null, null, diagnostics, null)
+ {
+ }
+
+ internal NewLineTokenSyntax(string text, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(SyntaxKind.NewLine, text, leadingTrivia, trailingTrivia)
+ {
+ }
+
+ protected NewLineTokenSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(kind, name, leadingTrivia, trailingTrivia)
+ {
+ }
+
+ protected NewLineTokenSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
+ : base(kind, name, leadingTrivia, trailingTrivia, diagnostics, annotations)
+ {
+ }
+
+ internal override SyntaxNode CreateRed(SyntaxNode parent, int position) => new Syntax.NewLineTokenSyntax(this, parent, position);
+
+ public override SyntaxToken TokenWithLeadingTrivia(GreenNode trivia)
+ {
+ return new NewLineTokenSyntax(Kind, Text, trivia, TrailingTrivia);
+ }
+
+ public override SyntaxToken TokenWithTrailingTrivia(GreenNode trivia)
+ {
+ return new NewLineTokenSyntax(Kind, Text, LeadingTrivia, trivia);
+ }
+
+ internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
+ {
+ return new NewLineTokenSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, diagnostics, GetAnnotations());
+ }
+
+ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
+ {
+ return new NewLineTokenSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, GetDiagnostics(), annotations);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/PunctuationSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/PunctuationSyntax.cs
new file mode 100644
index 0000000000..0c2bb06b61
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/PunctuationSyntax.cs
@@ -0,0 +1,45 @@
+// 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 class PunctuationSyntax : SyntaxToken
+ {
+ internal PunctuationSyntax(SyntaxKind kind, string name, RazorDiagnostic[] diagnostics)
+ : this(kind, name, null, null, diagnostics, null)
+ {
+ }
+
+ internal PunctuationSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : this(kind, name, leadingTrivia, trailingTrivia, null, null)
+ {
+ }
+
+ internal PunctuationSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
+ : base(kind, name, leadingTrivia, trailingTrivia, diagnostics, annotations)
+ {
+ }
+
+ internal override SyntaxNode CreateRed(SyntaxNode parent, int position) => new Syntax.PunctuationSyntax(this, parent, position);
+
+ public override SyntaxToken TokenWithLeadingTrivia(GreenNode trivia)
+ {
+ return new PunctuationSyntax(Kind, Text, trivia, TrailingTrivia);
+ }
+
+ public override SyntaxToken TokenWithTrailingTrivia(GreenNode trivia)
+ {
+ return new PunctuationSyntax(Kind, Text, LeadingTrivia, trivia);
+ }
+
+ internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
+ {
+ return new PunctuationSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, diagnostics, GetAnnotations());
+ }
+
+ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
+ {
+ return new PunctuationSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, GetDiagnostics(), annotations);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxFactory.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxFactory.cs
new file mode 100644
index 0000000000..a6f5ce2e1f
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxFactory.cs
@@ -0,0 +1,38 @@
+// 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 static class SyntaxFactory
+ {
+ internal static HtmlTextSyntax HtmlText(SyntaxList textTokens)
+ {
+ return new HtmlTextSyntax(textTokens.Node);
+ }
+
+ internal static HtmlTextTokenSyntax HtmlTextToken(string text, params RazorDiagnostic[] diagnostics)
+ {
+ return new HtmlTextTokenSyntax(text, diagnostics);
+ }
+
+ internal static WhitespaceTokenSyntax WhitespaceToken(string text, params RazorDiagnostic[] diagnostics)
+ {
+ return new WhitespaceTokenSyntax(text, diagnostics);
+ }
+
+ internal static NewLineTokenSyntax NewLineToken(string text, params RazorDiagnostic[] diagnostics)
+ {
+ return new NewLineTokenSyntax(text, diagnostics);
+ }
+
+ internal static PunctuationSyntax Punctuation(SyntaxKind syntaxKind, string text, params RazorDiagnostic[] diagnostics)
+ {
+ return new PunctuationSyntax(syntaxKind, text, diagnostics);
+ }
+
+ internal static UnknownTokenSyntax UnknownToken(string text, params RazorDiagnostic[] diagnostics)
+ {
+ return new UnknownTokenSyntax(text, diagnostics);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxList.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxList.cs
new file mode 100644
index 0000000000..cd6270b599
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxList.cs
@@ -0,0 +1,418 @@
+// 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[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[] 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[] 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[left.SlotCount + right.SlotCount];
+ leftList.CopyTo(tmp, 0);
+ rightList.CopyTo(tmp, left.SlotCount);
+ return List(tmp);
+ }
+ else
+ {
+ var tmp = new ArrayElement[left.SlotCount + 1];
+ leftList.CopyTo(tmp, 0);
+ tmp[left.SlotCount].Value = right;
+ return List(tmp);
+ }
+ }
+ else if (rightList != null)
+ {
+ var tmp = new ArrayElement[rightList.SlotCount + 1];
+ tmp[0].Value = left;
+ rightList.CopyTo(tmp, 1);
+ return List(tmp);
+ }
+ else
+ {
+ return List(left, right);
+ }
+ }
+
+ 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[] 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[] 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[] children;
+
+ internal WithManyChildrenBase(ArrayElement[] children)
+ {
+ this.children = children;
+ this.InitializeChildren();
+ }
+
+ internal WithManyChildrenBase(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations, ArrayElement[] 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[] 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[] children)
+ : base(children)
+ {
+ }
+
+ internal WithManyChildren(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations, ArrayElement[] 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[] children)
+ : base(children)
+ {
+ _childOffsets = CalculateOffsets(children);
+ }
+
+ internal WithLotsOfChildren(RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations, ArrayElement[] children, int[] childOffsets)
+ : base(diagnostics, annotations, children)
+ {
+ _childOffsets = childOffsets;
+ }
+
+ public override int GetSlotOffset(int index)
+ {
+ return _childOffsets[index];
+ }
+
+ ///
+ /// Find the slot that contains the given offset.
+ ///
+ /// The target offset. Must be between 0 and .
+ /// The slot index of the slot containing the given offset.
+ ///
+ /// This implementation uses a binary search to find the first slot that contains
+ /// the given offset.
+ ///
+ public override int FindSlotIndexContainingOffset(int offset)
+ {
+ Debug.Assert(offset >= 0 && offset < FullWidth);
+ return BinarySearchUpperBound(_childOffsets, offset) - 1;
+ }
+
+ private static int[] CalculateOffsets(ArrayElement[] 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);
+ }
+
+ ///
+ /// Search a sorted integer array for the target value in O(log N) time.
+ ///
+ /// The array of integers which must be sorted in ascending order.
+ /// The target value.
+ /// An index in the array pointing to the position where should be
+ /// inserted in order to maintain the sorted order. All values to the right of this position will be
+ /// strictly greater than . Note that this may return a position off the end
+ /// of the array if all elements are less than or equal to .
+ 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;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListBuilder.cs
new file mode 100644
index 0000000000..fae8e718a7
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListBuilder.cs
@@ -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[] _nodes;
+
+ public int Count { get; private set; }
+
+ public SyntaxListBuilder(int size)
+ {
+ _nodes = new ArrayElement[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 list)
+ {
+ this.AddRange(list, 0, list.Count);
+ }
+
+ public void AddRange(SyntaxList 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(SyntaxList list) where TNode : GreenNode
+ {
+ this.AddRange(list, 0, list.Count);
+ }
+
+ public void AddRange(SyntaxList list, int offset, int length) where TNode : GreenNode
+ {
+ AddRange(new SyntaxList(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[Count];
+ Array.Copy(_nodes, tmp, Count);
+ return SyntaxList.List(tmp);
+ }
+ }
+
+ public SyntaxList ToList()
+ {
+ return new SyntaxList(ToListNode());
+ }
+
+ public SyntaxList ToList() where TNode : GreenNode
+ {
+ return new SyntaxList(ToListNode());
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListBuilderOfT.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListBuilderOfT.cs
new file mode 100644
index 0000000000..65a11313f6
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListBuilderOfT.cs
@@ -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 where TNode : GreenNode
+ {
+ private readonly SyntaxListBuilder _builder;
+
+ public SyntaxListBuilder(int size)
+ : this(new SyntaxListBuilder(size))
+ {
+ }
+
+ public static SyntaxListBuilder Create()
+ {
+ return new SyntaxListBuilder(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 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 nodes)
+ {
+ _builder.AddRange(nodes);
+ }
+
+ public void AddRange(SyntaxList nodes, int offset, int length)
+ {
+ _builder.AddRange(nodes, offset, length);
+ }
+
+ public bool Any(SyntaxKind kind)
+ {
+ return _builder.Any(kind);
+ }
+
+ public SyntaxList ToList()
+ {
+ return _builder.ToList();
+ }
+
+ public GreenNode ToListNode()
+ {
+ return _builder.ToListNode();
+ }
+
+ public static implicit operator SyntaxListBuilder(SyntaxListBuilder builder)
+ {
+ return builder._builder;
+ }
+
+ public static implicit operator SyntaxList(SyntaxListBuilder builder)
+ {
+ if (builder._builder != null)
+ {
+ return builder.ToList();
+ }
+
+ return default(SyntaxList);
+ }
+
+ public SyntaxList ToList() where TDerived : GreenNode
+ {
+ return new SyntaxList(ToListNode());
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListOfT.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListOfT.cs
new file mode 100644
index 0000000000..945781978d
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxListOfT.cs
@@ -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
+ 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 left, SyntaxList right)
+ {
+ return (left._node == right._node);
+ }
+
+ public static bool operator !=(SyntaxList left, SyntaxList right)
+ {
+ return !(left._node == right._node);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return (obj is SyntaxList && (_node == ((SyntaxList)obj)._node));
+ }
+
+ public override int GetHashCode()
+ {
+ return _node != null ? _node.GetHashCode() : 0;
+ }
+
+ public static implicit operator SyntaxList(TNode node)
+ {
+ return new SyntaxList(node);
+ }
+
+ public static implicit operator SyntaxList(SyntaxList nodes)
+ {
+ return new SyntaxList(nodes._node);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxToken.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxToken.cs
new file mode 100644
index 0000000000..d9167704b5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxToken.cs
@@ -0,0 +1,119 @@
+// 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 abstract class SyntaxToken : GreenNode
+ {
+ internal SyntaxToken(SyntaxKind tokenKind, string text, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(tokenKind, text.Length)
+ {
+ Text = text;
+ LeadingTrivia = leadingTrivia;
+ AdjustFlagsAndWidth(leadingTrivia);
+ TrailingTrivia = trailingTrivia;
+ AdjustFlagsAndWidth(trailingTrivia);
+ }
+
+ internal SyntaxToken(SyntaxKind tokenKind, string text, GreenNode leadingTrivia, GreenNode trailingTrivia, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
+ : base(tokenKind, text.Length, diagnostics, annotations)
+ {
+ Text = text;
+ LeadingTrivia = leadingTrivia;
+ AdjustFlagsAndWidth(leadingTrivia);
+ TrailingTrivia = trailingTrivia;
+ AdjustFlagsAndWidth(trailingTrivia);
+ }
+
+ public string Text { get; }
+
+ public GreenNode LeadingTrivia { get; }
+
+ public GreenNode TrailingTrivia { get; }
+
+ internal override bool IsToken => true;
+
+ public override int Width => Text.Length;
+
+ 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(Text);
+
+ 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 abstract SyntaxToken TokenWithLeadingTrivia(GreenNode trivia);
+
+ public sealed override GreenNode WithTrailingTrivia(GreenNode trivia)
+ {
+ return TokenWithTrailingTrivia(trivia);
+ }
+
+ public abstract SyntaxToken TokenWithTrailingTrivia(GreenNode trivia);
+
+ protected override sealed int GetSlotCount()
+ {
+ return 0;
+ }
+
+ internal override sealed GreenNode GetSlot(int index)
+ {
+ throw new InvalidOperationException();
+ }
+
+ internal override GreenNode Accept(SyntaxVisitor visitor)
+ {
+ return visitor.VisitSyntaxToken(this);
+ }
+
+ public override string ToString()
+ {
+ return Text;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxTrivia.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxTrivia.cs
new file mode 100644
index 0000000000..6618b20c69
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxTrivia.cs
@@ -0,0 +1,80 @@
+// 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);
+ }
+
+ internal override GreenNode Accept(SyntaxVisitor visitor)
+ {
+ return visitor.VisitSyntaxTrivia(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);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxVisitor.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxVisitor.cs
new file mode 100644
index 0000000000..5e54b752f3
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/SyntaxVisitor.cs
@@ -0,0 +1,43 @@
+// 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 class SyntaxVisitor
+ {
+ public virtual GreenNode Visit(GreenNode node)
+ {
+ if (node != null)
+ {
+ return node.Accept(this);
+ }
+
+ return null;
+ }
+
+ public virtual GreenNode VisitSyntaxNode(GreenNode node)
+ {
+ return node;
+ }
+
+ public virtual GreenNode VisitHtmlNode(HtmlNodeSyntax node)
+ {
+ return VisitSyntaxNode(node);
+ }
+
+ public virtual GreenNode VisitHtmlText(HtmlTextSyntax node)
+ {
+ return VisitHtmlNode(node);
+ }
+
+ public virtual SyntaxToken VisitSyntaxToken(SyntaxToken token)
+ {
+ return token;
+ }
+
+ public virtual SyntaxTrivia VisitSyntaxTrivia(SyntaxTrivia trivia)
+ {
+ return trivia;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/UnknownTokenSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/UnknownTokenSyntax.cs
new file mode 100644
index 0000000000..4c6cd4608b
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/UnknownTokenSyntax.cs
@@ -0,0 +1,50 @@
+// 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 class UnknownTokenSyntax : SyntaxToken
+ {
+ internal UnknownTokenSyntax(string text, params RazorDiagnostic[] diagnostics)
+ : base(SyntaxKind.Unknown, text, null, null, diagnostics, null)
+ {
+ }
+
+ internal UnknownTokenSyntax(string text, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(SyntaxKind.Unknown, text, leadingTrivia, trailingTrivia)
+ {
+ }
+
+ protected UnknownTokenSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(kind, name, leadingTrivia, trailingTrivia)
+ {
+ }
+
+ protected UnknownTokenSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
+ : base(kind, name, leadingTrivia, trailingTrivia, diagnostics, annotations)
+ {
+ }
+
+ internal override SyntaxNode CreateRed(SyntaxNode parent, int position) => new Syntax.UnknownTokenSyntax(this, parent, position);
+
+ public override SyntaxToken TokenWithLeadingTrivia(GreenNode trivia)
+ {
+ return new UnknownTokenSyntax(Kind, Text, trivia, TrailingTrivia);
+ }
+
+ public override SyntaxToken TokenWithTrailingTrivia(GreenNode trivia)
+ {
+ return new UnknownTokenSyntax(Kind, Text, LeadingTrivia, trivia);
+ }
+
+ internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
+ {
+ return new UnknownTokenSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, diagnostics, GetAnnotations());
+ }
+
+ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
+ {
+ return new UnknownTokenSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, GetDiagnostics(), annotations);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/WhitespaceTokenSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/WhitespaceTokenSyntax.cs
new file mode 100644
index 0000000000..b576b60e3a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/InternalSyntax/WhitespaceTokenSyntax.cs
@@ -0,0 +1,50 @@
+// 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 class WhitespaceTokenSyntax : SyntaxToken
+ {
+ internal WhitespaceTokenSyntax(string text, params RazorDiagnostic[] diagnostics)
+ : base(SyntaxKind.Whitespace, text, null, null, diagnostics, null)
+ {
+ }
+
+ internal WhitespaceTokenSyntax(string text, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(SyntaxKind.Whitespace, text, leadingTrivia, trailingTrivia)
+ {
+ }
+
+ protected WhitespaceTokenSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia)
+ : base(kind, name, leadingTrivia, trailingTrivia)
+ {
+ }
+
+ protected WhitespaceTokenSyntax(SyntaxKind kind, string name, GreenNode leadingTrivia, GreenNode trailingTrivia, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
+ : base(kind, name, leadingTrivia, trailingTrivia, diagnostics, annotations)
+ {
+ }
+
+ internal override SyntaxNode CreateRed(SyntaxNode parent, int position) => new Syntax.WhitespaceTokenSyntax(this, parent, position);
+
+ public override SyntaxToken TokenWithLeadingTrivia(GreenNode trivia)
+ {
+ return new WhitespaceTokenSyntax(Kind, Text, trivia, TrailingTrivia);
+ }
+
+ public override SyntaxToken TokenWithTrailingTrivia(GreenNode trivia)
+ {
+ return new WhitespaceTokenSyntax(Kind, Text, LeadingTrivia, trivia);
+ }
+
+ internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
+ {
+ return new WhitespaceTokenSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, diagnostics, GetAnnotations());
+ }
+
+ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
+ {
+ return new WhitespaceTokenSyntax(Kind, Text, LeadingTrivia, TrailingTrivia, GetDiagnostics(), annotations);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/NewLineTokenSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/NewLineTokenSyntax.cs
new file mode 100644
index 0000000000..d164f069e9
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/NewLineTokenSyntax.cs
@@ -0,0 +1,27 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax
+{
+ internal class NewLineTokenSyntax : SyntaxToken
+ {
+ internal NewLineTokenSyntax(GreenNode green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ internal new InternalSyntax.NewLineTokenSyntax Green => (InternalSyntax.NewLineTokenSyntax)base.Green;
+
+ public string Value => Text;
+
+ internal override SyntaxToken WithLeadingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.NewLineTokenSyntax(Text, trivia?.Green, GetTrailingTrivia().Node?.Green).CreateRed(Parent, Position) as NewLineTokenSyntax;
+ }
+
+ internal override SyntaxToken WithTrailingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.NewLineTokenSyntax(Text, GetLeadingTrivia().Node?.Green, trivia?.Green).CreateRed(Parent, Position) as NewLineTokenSyntax;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/NodeFlags.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/NodeFlags.cs
new file mode 100644
index 0000000000..5dd4850e58
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/NodeFlags.cs
@@ -0,0 +1,21 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+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,
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/ObjectPool.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/ObjectPool.cs
new file mode 100644
index 0000000000..2df35b64ac
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/ObjectPool.cs
@@ -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
+{
+ ///
+ /// 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".
+ ///
+ internal class ObjectPool where T : class
+ {
+ private struct Element
+ {
+ internal T Value;
+ }
+
+ ///
+ /// Not using because this file is linked into the (debugger) Formatter,
+ /// which does not have that type (since it compiles against .NET 2.0).
+ ///
+ 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 leakTrackers = new ConditionalWeakTable();
+
+ 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;
+ }
+
+ ///
+ /// Produces an instance.
+ ///
+ ///
+ /// 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.
+ ///
+ 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;
+ }
+
+ ///
+ /// Returns objects to the pool.
+ ///
+ ///
+ /// 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.
+ ///
+ 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;
+ }
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ [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?");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/ParserState.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/ParserState.cs
new file mode 100644
index 0000000000..adb81967dd
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/ParserState.cs
@@ -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,
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/PunctuationSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/PunctuationSyntax.cs
new file mode 100644
index 0000000000..0f25740ecd
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/PunctuationSyntax.cs
@@ -0,0 +1,27 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax
+{
+ internal class PunctuationSyntax : SyntaxToken
+ {
+ internal PunctuationSyntax(GreenNode green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ internal new InternalSyntax.PunctuationSyntax Green => (InternalSyntax.PunctuationSyntax)base.Green;
+
+ public string Punctuation => Text;
+
+ internal override SyntaxToken WithLeadingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.PunctuationSyntax(Kind, Text, trivia?.Green, GetTrailingTrivia().Node?.Green).CreateRed(Parent, Position) as PunctuationSyntax;
+ }
+
+ internal override SyntaxToken WithTrailingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.PunctuationSyntax(Kind, Text, GetLeadingTrivia().Node?.Green, trivia?.Green).CreateRed(Parent, Position) as PunctuationSyntax;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SpecializedCollections.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SpecializedCollections.cs
new file mode 100644
index 0000000000..3e1094003f
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SpecializedCollections.cs
@@ -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 EmptyEnumerator()
+ {
+ return Empty.Enumerator.Instance;
+ }
+
+ public static IEnumerable EmptyEnumerable()
+ {
+ return Empty.List.Instance;
+ }
+
+ public static ICollection EmptyCollection()
+ {
+ return Empty.List.Instance;
+ }
+
+ public static IList EmptyList()
+ {
+ return Empty.List.Instance;
+ }
+
+ public static IReadOnlyList EmptyReadOnlyList()
+ {
+ return Empty.List.Instance;
+ }
+
+ private class Empty
+ {
+ internal class Enumerator : Enumerator, IEnumerator
+ {
+ public static new readonly IEnumerator Instance = new Enumerator();
+
+ 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 : IEnumerable
+ {
+ // 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 _enumerator = Enumerator.Instance;
+
+ public IEnumerator GetEnumerator()
+ {
+ return _enumerator;
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+ internal class Collection : Enumerable, ICollection
+ {
+ public static readonly ICollection Instance = new Collection();
+
+ 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 : Collection, IList, IReadOnlyList
+ {
+ public static readonly new List Instance = new List();
+
+ 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();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxAnnotation.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxAnnotation.cs
new file mode 100644
index 0000000000..dbb72fb1ae
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxAnnotation.cs
@@ -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
+{
+ ///
+ /// 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.
+ ///
+ [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
+ internal sealed class SyntaxAnnotation : IEquatable
+ {
+ // 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();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxKind.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxKind.cs
new file mode 100644
index 0000000000..46dad28dee
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxKind.cs
@@ -0,0 +1,32 @@
+// 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 SyntaxKind : byte
+ {
+ Unknown,
+ List,
+ Whitespace,
+ NewLine,
+
+ // HTML
+ HtmlText,
+ HtmlDocument,
+ HtmlDeclaration,
+ HtmlTextLiteralToken,
+ OpenAngle,
+ Bang,
+ ForwardSlash,
+ QuestionMark,
+ DoubleHyphen,
+ LeftBracket,
+ CloseAngle,
+ RightBracket,
+ Equals,
+ DoubleQuote,
+ SingleQuote,
+ Transition,
+ Colon,
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxList.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxList.cs
new file mode 100644
index 0000000000..e57b65599f
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxList.cs
@@ -0,0 +1,118 @@
+// 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)
+ {
+ }
+
+ internal override SyntaxNode Accept(SyntaxVisitor visitor)
+ {
+ return 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[] _children;
+
+ internal WithManyChildren(InternalSyntax.SyntaxList green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ _children = new ArrayElement[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];
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilder.cs
new file mode 100644
index 0000000000..415b8352bb
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilder.cs
@@ -0,0 +1,172 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Diagnostics;
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax
+{
+ internal class SyntaxListBuilder
+ {
+ private ArrayElement[] _nodes;
+
+ public int Count { get; private set; }
+
+ public SyntaxListBuilder(int size)
+ {
+ _nodes = new ArrayElement[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 list)
+ {
+ AddRange(list, 0, list.Count);
+ }
+
+ public void AddRange(SyntaxList 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(SyntaxList list) where TNode : SyntaxNode
+ {
+ AddRange(list, 0, list.Count);
+ }
+
+ public void AddRange(SyntaxList list, int offset, int count) where TNode : SyntaxNode
+ {
+ AddRange(new SyntaxList(list.Node), offset, count);
+ }
+
+ private void Grow(int size)
+ {
+ var tmp = new ArrayElement[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[Count];
+ for (var i = 0; i < Count; i++)
+ {
+ tmp[i].Value = _nodes[i].Value;
+ }
+
+ return InternalSyntax.SyntaxList.List(tmp);
+ }
+ }
+
+ public static implicit operator SyntaxList(SyntaxListBuilder builder)
+ {
+ if (builder == null)
+ {
+ return default(SyntaxList);
+ }
+
+ return builder.ToList();
+ }
+
+ internal void RemoveLast()
+ {
+ Count -= 1;
+ _nodes[Count] = default(ArrayElement);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilderExtensions.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilderExtensions.cs
new file mode 100644
index 0000000000..5b813c05b1
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilderExtensions.cs
@@ -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 ToList(this SyntaxListBuilder builder)
+ {
+ if (builder == null || builder.Count == 0)
+ {
+ return default(SyntaxList);
+ }
+
+ return new SyntaxList(builder.ToListNode().CreateRed());
+ }
+
+ public static SyntaxList ToList(this SyntaxListBuilder builder)
+ where TNode : SyntaxNode
+ {
+ if (builder == null || builder.Count == 0)
+ {
+ return new SyntaxList();
+ }
+
+ return new SyntaxList(builder.ToListNode().CreateRed());
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilderOfT.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilderOfT.cs
new file mode 100644
index 0000000000..8fcd9f2b3a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListBuilderOfT.cs
@@ -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
+ where TNode : SyntaxNode
+ {
+ private readonly SyntaxListBuilder _builder;
+
+ public SyntaxListBuilder(int size)
+ : this(new SyntaxListBuilder(size))
+ {
+ }
+
+ public static SyntaxListBuilder Create()
+ {
+ return new SyntaxListBuilder(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 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 nodes)
+ {
+ _builder.AddRange(nodes);
+ }
+
+ public void AddRange(SyntaxList nodes, int offset, int length)
+ {
+ _builder.AddRange(nodes, offset, length);
+ }
+
+ public bool Any(SyntaxKind kind)
+ {
+ return _builder.Any(kind);
+ }
+
+ public SyntaxList ToList()
+ {
+ return _builder.ToList();
+ }
+
+ public static implicit operator SyntaxListBuilder(SyntaxListBuilder builder)
+ {
+ return builder._builder;
+ }
+
+ public static implicit operator SyntaxList(SyntaxListBuilder builder)
+ {
+ if (builder._builder != null)
+ {
+ return builder.ToList();
+ }
+
+ return default(SyntaxList);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListOfT.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListOfT.cs
new file mode 100644
index 0000000000..158e82859f
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxListOfT.cs
@@ -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 : IReadOnlyList, IEquatable>
+ where TNode : SyntaxNode
+ {
+ public SyntaxList(SyntaxNode node)
+ {
+ Node = node;
+ }
+
+ ///
+ /// Creates a singleton list of syntax nodes.
+ ///
+ /// The single element node.
+ public SyntaxList(TNode node)
+ : this((SyntaxNode)node)
+ {
+ }
+
+ ///
+ /// Creates a list of syntax nodes.
+ ///
+ /// A sequence of element nodes.
+ public SyntaxList(IEnumerable nodes)
+ : this(CreateNode(nodes))
+ {
+ }
+
+ private static SyntaxNode CreateNode(IEnumerable nodes)
+ {
+ if (nodes == null)
+ {
+ return null;
+ }
+
+ var builder = (nodes is ICollection collection) ? new SyntaxListBuilder(collection.Count) : SyntaxListBuilder.Create();
+
+ foreach (var node in nodes)
+ {
+ builder.Add(node);
+ }
+
+ return builder.ToList().Node;
+ }
+
+ internal SyntaxNode Node { get; }
+
+ ///
+ /// The number of nodes in the list.
+ ///
+ public int Count
+ {
+ get
+ {
+ return Node == null ? 0 : (Node.IsList ? Node.SlotCount : 1);
+ }
+ }
+
+ ///
+ /// Gets the node at the specified index.
+ ///
+ /// The zero-based index of the node to get or set.
+ /// The node at the specified index.
+ 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;
+ }
+
+ ///
+ /// The absolute span of the list elements in characters, including the leading and trailing trivia of the first and last elements.
+ ///
+ public TextSpan FullSpan
+ {
+ get
+ {
+ if (Count == 0)
+ {
+ return default(TextSpan);
+ }
+ else
+ {
+ return TextSpan.FromBounds(this[0].FullSpan.Start, this[Count - 1].FullSpan.End);
+ }
+ }
+ }
+
+ ///
+ /// The absolute span of the list elements in characters, not including the leading and trailing trivia of the first and last elements.
+ ///
+ public TextSpan Span
+ {
+ get
+ {
+ if (Count == 0)
+ {
+ return default(TextSpan);
+ }
+ else
+ {
+ return TextSpan.FromBounds(this[0].Span.Start, this[Count - 1].Span.End);
+ }
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// The string representation of the nodes in this list, not including
+ /// the first node's leading trivia and the last node's trailing trivia.
+ ///
+ public override string ToString()
+ {
+ return Node != null ? Node.ToString() : string.Empty;
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// The full string representation of the nodes in this list including
+ /// the first node's leading trivia and the last node's trailing trivia.
+ ///
+ public string ToFullString()
+ {
+ return Node != null ? Node.ToFullString() : string.Empty;
+ }
+
+ ///
+ /// Creates a new list with the specified node added at the end.
+ ///
+ /// The node to add.
+ public SyntaxList Add(TNode node)
+ {
+ return Insert(Count, node);
+ }
+
+ ///
+ /// Creates a new list with the specified nodes added at the end.
+ ///
+ /// The nodes to add.
+ public SyntaxList AddRange(IEnumerable nodes)
+ {
+ return InsertRange(Count, nodes);
+ }
+
+ ///
+ /// Creates a new list with the specified node inserted at the index.
+ ///
+ /// The index to insert at.
+ /// The node to insert.
+ public SyntaxList Insert(int index, TNode node)
+ {
+ if (node == null)
+ {
+ throw new ArgumentNullException(nameof(node));
+ }
+
+ return InsertRange(index, new[] { node });
+ }
+
+ ///
+ /// Creates a new list with the specified nodes inserted at the index.
+ ///
+ /// The index to insert at.
+ /// The nodes to insert.
+ public SyntaxList InsertRange(int index, IEnumerable 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);
+ }
+ }
+
+ ///
+ /// Creates a new list with the element at specified index removed.
+ ///
+ /// The index of the element to remove.
+ public SyntaxList RemoveAt(int index)
+ {
+ if (index < 0 || index > Count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ return Remove(this[index]);
+ }
+
+ ///
+ /// Creates a new list with the element removed.
+ ///
+ /// The element to remove.
+ public SyntaxList Remove(TNode node)
+ {
+ return CreateList(this.Where(x => x != node).ToList());
+ }
+
+ ///
+ /// Creates a new list with the specified element replaced with the new node.
+ ///
+ /// The element to replace.
+ /// The new node.
+ public SyntaxList Replace(TNode nodeInList, TNode newNode)
+ {
+ return ReplaceRange(nodeInList, new[] { newNode });
+ }
+
+ ///
+ /// Creates a new list with the specified element replaced with new nodes.
+ ///
+ /// The element to replace.
+ /// The new nodes.
+ public SyntaxList ReplaceRange(TNode nodeInList, IEnumerable 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 CreateList(List items)
+ {
+ if (items.Count == 0)
+ {
+ return default(SyntaxList);
+ }
+ else
+ {
+ return CreateList(items[0].Green, items);
+ }
+ }
+
+ static SyntaxList CreateList(GreenNode creator, List items)
+ {
+ if (items.Count == 0)
+ {
+ return default(SyntaxList);
+ }
+
+ var newGreen = creator.CreateList(items.Select(n => n.Green));
+ return new SyntaxList(newGreen.CreateRed());
+ }
+
+ ///
+ /// The first node in the list.
+ ///
+ public TNode First()
+ {
+ return this[0];
+ }
+
+ ///
+ /// The first node in the list or default if the list is empty.
+ ///
+ public TNode FirstOrDefault()
+ {
+ if (this.Any())
+ {
+ return this[0];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// The last node in the list.
+ ///
+ public TNode Last()
+ {
+ return this[Count - 1];
+ }
+
+ ///
+ /// The last node in the list or default if the list is empty.
+ ///
+ public TNode LastOrDefault()
+ {
+ if (Any())
+ {
+ return this[Count - 1];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// True if the list has at least one node.
+ ///
+ public bool Any()
+ {
+ Debug.Assert(Node == null || Count != 0);
+ return Node != null;
+ }
+
+ // for debugging
+ private TNode[] Nodes
+ {
+ get { return this.ToArray(); }
+ }
+
+ ///
+ /// Get's the enumerator for this list.
+ ///
+ public Enumerator GetEnumerator()
+ {
+ return new Enumerator(in this);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ if (this.Any())
+ {
+ return new EnumeratorImpl(this);
+ }
+
+ return SpecializedCollections.EmptyEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ if (this.Any())
+ {
+ return new EnumeratorImpl(this);
+ }
+
+ return SpecializedCollections.EmptyEnumerator();
+ }
+
+ public static bool operator ==(SyntaxList left, SyntaxList right)
+ {
+ return left.Node == right.Node;
+ }
+
+ public static bool operator !=(SyntaxList left, SyntaxList right)
+ {
+ return left.Node != right.Node;
+ }
+
+ public bool Equals(SyntaxList other)
+ {
+ return Node == other.Node;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is SyntaxList && Equals((SyntaxList)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return Node?.GetHashCode() ?? 0;
+ }
+
+ public static implicit operator SyntaxList(SyntaxList nodes)
+ {
+ return new SyntaxList(nodes.Node);
+ }
+
+ public static implicit operator SyntaxList(SyntaxList nodes)
+ {
+ return new SyntaxList(nodes.Node);
+ }
+
+ ///
+ /// The index of the node in this list, or -1 if the node is not in the list.
+ ///
+ 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 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 predicate)
+ {
+ for (var i = Count - 1; i >= 0; i--)
+ {
+ if (predicate(this[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public struct Enumerator
+ {
+ private readonly SyntaxList _list;
+ private int _index;
+
+ internal Enumerator(in SyntaxList 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
+ {
+ private Enumerator _e;
+
+ internal EnumeratorImpl(in SyntaxList 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();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxNode.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxNode.cs
new file mode 100644
index 0000000000..d55504d46e
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxNode.cs
@@ -0,0 +1,252 @@
+// 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;
+
+ internal abstract SyntaxNode 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;
+ }
+
+ protected T GetRed(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;
+ }
+
+ 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 override string ToString()
+ {
+ return Green.ToString();
+ }
+
+ public virtual string ToFullString()
+ {
+ return Green.ToFullString();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxToken.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxToken.cs
new file mode 100644
index 0000000000..d3ffa0096c
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxToken.cs
@@ -0,0 +1,91 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax
+{
+ internal abstract class SyntaxToken : SyntaxNode
+ {
+ internal SyntaxToken(GreenNode green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ internal new InternalSyntax.SyntaxToken Green => (InternalSyntax.SyntaxToken)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();
+ }
+
+ internal override SyntaxNode Accept(SyntaxVisitor visitor)
+ {
+ return visitor.VisitSyntaxToken(this);
+ }
+
+ internal abstract SyntaxToken WithLeadingTriviaCore(SyntaxNode trivia);
+
+ internal abstract SyntaxToken WithTrailingTriviaCore(SyntaxNode trivia);
+
+ public SyntaxToken WithLeadingTrivia(SyntaxNode trivia) => WithLeadingTriviaCore(trivia);
+
+ public SyntaxToken WithTrailingTrivia(SyntaxNode trivia) => WithTrailingTriviaCore(trivia);
+
+ public SyntaxToken WithLeadingTrivia(IEnumerable trivia)
+ {
+ var greenList = trivia?.Select(t => t.Green);
+ return WithLeadingTriviaCore(Green.CreateList(greenList)?.CreateRed());
+ }
+
+ public SyntaxToken WithTrailingTrivia(IEnumerable trivia)
+ {
+ var greenList = trivia?.Select(t => t.Green);
+ return WithTrailingTriviaCore(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 Text;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTrivia.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTrivia.cs
new file mode 100644
index 0000000000..c576fee3d0
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTrivia.cs
@@ -0,0 +1,54 @@
+// 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();
+ }
+
+ internal override SyntaxNode Accept(SyntaxVisitor visitor)
+ {
+ return visitor.VisitSyntaxTrivia(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;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTriviaList.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTriviaList.cs
new file mode 100644
index 0000000000..2097e9391c
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTriviaList.cs
@@ -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, IReadOnlyList
+ {
+ 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;
+ }
+
+ ///
+ /// Creates a list of trivia.
+ ///
+ /// An array of trivia.
+ public SyntaxTriviaList(params SyntaxTrivia[] trivias)
+ : this(CreateNode(trivias), 0, 0)
+ {
+ }
+
+ ///
+ /// Creates a list of trivia.
+ ///
+ /// A sequence of trivia.
+ public SyntaxTriviaList(IEnumerable 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];
+ }
+
+ ///
+ /// Gets the trivia at the specified index.
+ ///
+ /// The zero-based index of the trivia to get.
+ /// The token at the specified index.
+ ///
+ /// is less than 0.-or- is equal to or greater than .
+ 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));
+ }
+ }
+
+ ///
+ /// The absolute span of the list elements in characters, including the leading and trailing trivia of the first and last elements.
+ ///
+ public TextSpan FullSpan
+ {
+ get
+ {
+ if (Node == null)
+ {
+ return default(TextSpan);
+ }
+
+ return new TextSpan(Position, Node.FullWidth);
+ }
+ }
+
+ ///
+ /// The absolute span of the list elements in characters, not including the leading and trailing trivia of the first and last elements.
+ ///
+ public TextSpan Span
+ {
+ get
+ {
+ if (Node == null)
+ {
+ return default(TextSpan);
+ }
+
+ return TextSpan.FromBounds(Position + Node.Green.GetLeadingTriviaWidth(),
+ Position + Node.FullWidth - Node.Green.GetTrailingTriviaWidth());
+ }
+ }
+
+ ///
+ /// Returns the first trivia in the list.
+ ///
+ /// The first trivia in the list.
+ /// The list is empty.
+ public SyntaxTrivia First()
+ {
+ if (Any())
+ {
+ return this[0];
+ }
+
+ throw new InvalidOperationException();
+ }
+
+ ///
+ /// Returns the last trivia in the list.
+ ///
+ /// The last trivia in the list.
+ /// The list is empty.
+ public SyntaxTrivia Last()
+ {
+ if (Any())
+ {
+ return this[Count - 1];
+ }
+
+ throw new InvalidOperationException();
+ }
+
+ ///
+ /// Does this list have any items.
+ ///
+ public bool Any()
+ {
+ return Node != null;
+ }
+
+ ///
+ /// Returns a list which contains all elements of in reversed order.
+ ///
+ /// which contains all elements of in reversed order
+ 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;
+ }
+
+ ///
+ /// Creates a new with the specified trivia added to the end.
+ ///
+ /// The trivia to add.
+ public SyntaxTriviaList Add(SyntaxTrivia trivia)
+ {
+ return Insert(Count, trivia);
+ }
+
+ ///
+ /// Creates a new with the specified trivia added to the end.
+ ///
+ /// The trivia to add.
+ public SyntaxTriviaList AddRange(IEnumerable trivia)
+ {
+ return InsertRange(Count, trivia);
+ }
+
+ ///
+ /// Creates a new with the specified trivia inserted at the index.
+ ///
+ /// The index in the list to insert the trivia at.
+ /// The trivia to insert.
+ 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 s_builderPool =
+ new ObjectPool(() => 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);
+ }
+ }
+
+ ///
+ /// Creates a new with the specified trivia inserted at the index.
+ ///
+ /// The index in the list to insert the trivia at.
+ /// The trivia to insert.
+ public SyntaxTriviaList InsertRange(int index, IEnumerable 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 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);
+ }
+ }
+
+ ///
+ /// Creates a new with the element at the specified index removed.
+ ///
+ /// The index identifying the element to remove.
+ 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);
+ }
+
+ ///
+ /// Creates a new with the specified element removed.
+ ///
+ /// The trivia element to remove.
+ public SyntaxTriviaList Remove(SyntaxTrivia triviaInList)
+ {
+ var index = IndexOf(triviaInList);
+ if (index >= 0 && index < Count)
+ {
+ return RemoveAt(index);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Creates a new with the specified element replaced with new trivia.
+ ///
+ /// The trivia element to replace.
+ /// The trivia to replace the element with.
+ public SyntaxTriviaList Replace(SyntaxTrivia triviaInList, SyntaxTrivia newTrivia)
+ {
+ if (newTrivia == default(SyntaxTrivia))
+ {
+ throw new ArgumentOutOfRangeException(nameof(newTrivia));
+ }
+
+ return ReplaceRange(triviaInList, new[] { newTrivia });
+ }
+
+ ///
+ /// Creates a new with the specified element replaced with new trivia.
+ ///
+ /// The trivia element to replace.
+ /// The trivia to replace the element with.
+ public SyntaxTriviaList ReplaceRange(SyntaxTrivia triviaInList, IEnumerable 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 IEnumerable.GetEnumerator()
+ {
+ if (Node == null)
+ {
+ return SpecializedCollections.EmptyEnumerator();
+ }
+
+ return new EnumeratorImpl(this);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ if (Node == null)
+ {
+ return SpecializedCollections.EmptyEnumerator();
+ }
+
+ return new EnumeratorImpl(this);
+ }
+
+ ///
+ /// get the green node at the specific slot
+ ///
+ 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;
+ }
+
+ ///
+ /// Copy number of items starting at from this list into starting at .
+ ///
+ 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);
+ }
+
+ ///
+ /// Reversed enumerable.
+ ///
+ public struct Reversed : IEnumerable, IEquatable
+ {
+ private SyntaxTriviaList _list;
+
+ public Reversed(in SyntaxTriviaList list)
+ {
+ _list = list;
+ }
+
+ public Enumerator GetEnumerator()
+ {
+ return new Enumerator(in _list);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ if (_list.Count == 0)
+ {
+ return SpecializedCollections.EmptyEnumerator();
+ }
+
+ return new ReversedEnumeratorImpl(in _list);
+ }
+
+ IEnumerator
+ IEnumerable.GetEnumerator()
+ {
+ if (_list.Count == 0)
+ {
+ return SpecializedCollections.EmptyEnumerator();
+ }
+
+ 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
+ {
+ 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
+ {
+ 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()
+ {
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTriviaListBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTriviaListBuilder.cs
new file mode 100644
index 0000000000..aa0a8caaec
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxTriviaListBuilder.cs
@@ -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 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 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[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);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxVisitor.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxVisitor.cs
new file mode 100644
index 0000000000..08d833e515
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/SyntaxVisitor.cs
@@ -0,0 +1,43 @@
+// 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 SyntaxVisitor
+ {
+ public virtual SyntaxNode Visit(SyntaxNode node)
+ {
+ if (node != null)
+ {
+ return node.Accept(this);
+ }
+
+ return null;
+ }
+
+ public virtual SyntaxNode VisitSyntaxNode(SyntaxNode node)
+ {
+ return node;
+ }
+
+ public virtual SyntaxNode VisitHtmlNode(HtmlNodeSyntax node)
+ {
+ return VisitSyntaxNode(node);
+ }
+
+ public virtual SyntaxNode VisitHtmlText(HtmlTextSyntax node)
+ {
+ return VisitHtmlNode(node);
+ }
+
+ public virtual SyntaxToken VisitSyntaxToken(SyntaxToken token)
+ {
+ return token;
+ }
+
+ public virtual SyntaxTrivia VisitSyntaxTrivia(SyntaxTrivia trivia)
+ {
+ return trivia;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/TextSpan.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/TextSpan.cs
new file mode 100644
index 0000000000..e80562b01b
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/TextSpan.cs
@@ -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
+{
+ ///
+ /// 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.
+ ///
+ internal readonly struct TextSpan : IEquatable, IComparable
+ {
+ ///
+ /// Creates a TextSpan instance beginning with the position Start and having the Length
+ /// specified with .
+ ///
+ 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;
+ }
+
+ ///
+ /// Start point of the span.
+ ///
+ public int Start { get; }
+
+ ///
+ /// End of the span.
+ ///
+ public int End => Start + Length;
+
+ ///
+ /// Length of the span.
+ ///
+ public int Length { get; }
+
+ ///
+ /// Determines whether or not the span is empty.
+ ///
+ public bool IsEmpty => Length == 0;
+
+ ///
+ /// Determines whether the position lies within the span.
+ ///
+ ///
+ /// The position to check.
+ ///
+ ///
+ /// true if the position is greater than or equal to Start and strictly less
+ /// than End, otherwise false.
+ ///
+ public bool Contains(int position)
+ {
+ return unchecked((uint)(position - Start) < (uint)Length);
+ }
+
+ ///
+ /// Determines whether falls completely within this span.
+ ///
+ ///
+ /// The span to check.
+ ///
+ ///
+ /// true if the specified span falls completely within this span, otherwise false.
+ ///
+ public bool Contains(TextSpan span)
+ {
+ return span.Start >= Start && span.End <= End;
+ }
+
+ ///
+ /// Determines whether 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.
+ ///
+ ///
+ /// The span to check.
+ ///
+ ///
+ /// true if the spans overlap, otherwise false.
+ ///
+ public bool OverlapsWith(TextSpan span)
+ {
+ var overlapStart = Math.Max(Start, span.Start);
+ var overlapEnd = Math.Min(End, span.End);
+
+ return overlapStart < overlapEnd;
+ }
+
+ ///
+ /// Returns the overlap with the given span, or null if there is no overlap.
+ ///
+ ///
+ /// The span to check.
+ ///
+ ///
+ /// The overlap of the spans, or null if the overlap is empty.
+ ///
+ 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;
+ }
+
+ ///
+ /// Determines whether 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.
+ ///
+ ///
+ /// The span to check.
+ ///
+ ///
+ /// true if the spans intersect, otherwise false.
+ ///
+ public bool IntersectsWith(TextSpan span)
+ {
+ return span.Start <= End && span.End >= Start;
+ }
+
+ ///
+ /// Determines whether intersects this span.
+ /// A position is considered to intersect if it is between the start and
+ /// end positions (inclusive) of this span.
+ ///
+ ///
+ /// The position to check.
+ ///
+ ///
+ /// true if the position intersects, otherwise false.
+ ///
+ public bool IntersectsWith(int position)
+ {
+ return unchecked((uint)(position - Start) <= (uint)Length);
+ }
+
+ ///
+ /// Returns the intersection with the given span, or null if there is no intersection.
+ ///
+ ///
+ /// The span to check.
+ ///
+ ///
+ /// The intersection of the spans, or null if the intersection is empty.
+ ///
+ 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;
+ }
+
+ ///
+ /// Creates a new from and positions as opposed to a position and length.
+ ///
+ /// The returned TextSpan contains the range with inclusive,
+ /// and exclusive.
+ ///
+ 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);
+ }
+
+ ///
+ /// Determines if two instances of are the same.
+ ///
+ public static bool operator ==(TextSpan left, TextSpan right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Determines if two instances of are different.
+ ///
+ public static bool operator !=(TextSpan left, TextSpan right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ /// Determines if current instance of is equal to another.
+ ///
+ public bool Equals(TextSpan other)
+ {
+ return Start == other.Start && Length == other.Length;
+ }
+
+ ///
+ /// Determines if current instance of is equal to another.
+ ///
+ public override bool Equals(object obj)
+ {
+ return obj is TextSpan && Equals((TextSpan)obj);
+ }
+
+ ///
+ /// Produces a hash code for .
+ ///
+ public override int GetHashCode()
+ {
+ var combiner = new HashCodeCombiner();
+ combiner.Add(Start);
+ combiner.Add(Length);
+
+ return combiner.CombinedHash;
+ }
+
+ ///
+ /// Provides a string representation for .
+ ///
+ public override string ToString()
+ {
+ return $"[{Start}..{End})";
+ }
+
+ ///
+ /// Compares current instance of with another.
+ ///
+ public int CompareTo(TextSpan other)
+ {
+ var diff = Start - other.Start;
+ if (diff != 0)
+ {
+ return diff;
+ }
+
+ return Length - other.Length;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/UnknownTokenSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/UnknownTokenSyntax.cs
new file mode 100644
index 0000000000..238b36d1af
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/UnknownTokenSyntax.cs
@@ -0,0 +1,27 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax
+{
+ internal class UnknownTokenSyntax : SyntaxToken
+ {
+ internal UnknownTokenSyntax(GreenNode green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ internal new InternalSyntax.UnknownTokenSyntax Green => (InternalSyntax.UnknownTokenSyntax)base.Green;
+
+ public string Value => Text;
+
+ internal override SyntaxToken WithLeadingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.UnknownTokenSyntax(Text, trivia?.Green, GetTrailingTrivia().Node?.Green).CreateRed(Parent, Position) as UnknownTokenSyntax;
+ }
+
+ internal override SyntaxToken WithTrailingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.UnknownTokenSyntax(Text, GetLeadingTrivia().Node?.Green, trivia?.Green).CreateRed(Parent, Position) as UnknownTokenSyntax;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Language/Syntax/WhitespaceTokenSyntax.cs b/src/Microsoft.AspNetCore.Razor.Language/Syntax/WhitespaceTokenSyntax.cs
new file mode 100644
index 0000000000..5a8222f802
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Language/Syntax/WhitespaceTokenSyntax.cs
@@ -0,0 +1,27 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Razor.Language.Syntax
+{
+ internal class WhitespaceTokenSyntax : SyntaxToken
+ {
+ internal WhitespaceTokenSyntax(GreenNode green, SyntaxNode parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ internal new InternalSyntax.WhitespaceTokenSyntax Green => (InternalSyntax.WhitespaceTokenSyntax)base.Green;
+
+ public string Value => Text;
+
+ internal override SyntaxToken WithLeadingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.WhitespaceTokenSyntax(Text, trivia?.Green, GetTrailingTrivia().Node?.Green).CreateRed(Parent, Position) as WhitespaceTokenSyntax;
+ }
+
+ internal override SyntaxToken WithTrailingTriviaCore(SyntaxNode trivia)
+ {
+ return new InternalSyntax.WhitespaceTokenSyntax(Text, GetLeadingTrivia().Node?.Green, trivia?.Green).CreateRed(Parent, Position) as WhitespaceTokenSyntax;
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpAutoCompleteTest/SectionDirectiveAutoCompleteAtStartOfFile.stree.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpAutoCompleteTest/SectionDirectiveAutoCompleteAtStartOfFile.stree.txt
index cab4006af3..d59300fc29 100644
--- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpAutoCompleteTest/SectionDirectiveAutoCompleteAtStartOfFile.stree.txt
+++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpAutoCompleteTest/SectionDirectiveAutoCompleteAtStartOfFile.stree.txt
@@ -19,8 +19,8 @@ Directive block - Gen];
- Markup span - Gen - [Foo] - SpanEditHandler;Accepts:Any - (22:1,3) - Tokens:1
- HtmlTokenType.Text;[Foo];
+ SyntaxKind.HtmlText - [Foo] - [22..25) - FullWidth: 3 - Slots: 1
+ SyntaxKind.HtmlTextLiteralToken;[Foo];
Tag block - Gen - 4 - (25:1,6)
Markup span - Gen - [] - SpanEditHandler;Accepts:Any - (25:1,6) - Tokens:4
HtmlTokenType.OpenAngle;[<];
diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/DirectiveDescriptor_FileScoped_CanBeBeneathOtherWhiteSpaceCommentsAndDirectives.stree.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/DirectiveDescriptor_FileScoped_CanBeBeneathOtherWhiteSpaceCommentsAndDirectives.stree.txt
index 203fd7aee0..15bd38868f 100644
--- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/DirectiveDescriptor_FileScoped_CanBeBeneathOtherWhiteSpaceCommentsAndDirectives.stree.txt
+++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/DirectiveDescriptor_FileScoped_CanBeBeneathOtherWhiteSpaceCommentsAndDirectives.stree.txt
@@ -51,12 +51,13 @@ Markup block - Gen - 130 - (0:0,0)
HtmlTokenType.OpenAngle;[<];
HtmlTokenType.Text;[p];
HtmlTokenType.CloseAngle;[>];
- Markup span - Gen - [This is extra] - SpanEditHandler;Accepts:Any - (113:5,3) - Tokens:5
- HtmlTokenType.Text;[This];
- HtmlTokenType.WhiteSpace;[ ];
- HtmlTokenType.Text;[is];
- HtmlTokenType.WhiteSpace;[ ];
- HtmlTokenType.Text;[extra];
+ SyntaxKind.HtmlText - [This is extra] - [113..126) - FullWidth: 13 - Slots: 1
+ SyntaxKind.List - [This is extra] - [113..126) - FullWidth: 13 - Slots: 5
+ SyntaxKind.HtmlTextLiteralToken;[This];
+ SyntaxKind.Whitespace;[ ];
+ SyntaxKind.HtmlTextLiteralToken;[is];
+ SyntaxKind.Whitespace;[ ];
+ SyntaxKind.HtmlTextLiteralToken;[extra];
Tag block - Gen - 4 - (126:5,16)
Markup span - Gen - [] - SpanEditHandler;Accepts:Any - (126:5,16) - Tokens:4
HtmlTokenType.OpenAngle;[<];
diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/DirectiveDescriptor_UnderstandsRazorBlocks.stree.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/DirectiveDescriptor_UnderstandsRazorBlocks.stree.txt
index c5bde0c85c..fad1a9c949 100644
--- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/DirectiveDescriptor_UnderstandsRazorBlocks.stree.txt
+++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/DirectiveDescriptor_UnderstandsRazorBlocks.stree.txt
@@ -19,12 +19,13 @@ Directive block - Gen - 33 - (0:0,0)
HtmlTokenType.OpenAngle;[<];
HtmlTokenType.Text;[p];
HtmlTokenType.CloseAngle;[>];
- Markup span - Gen - [F{o}o] - SpanEditHandler;Accepts:Any - (22:0,22) - Tokens:5
- HtmlTokenType.Text;[F];
- HtmlTokenType.Text;[{];
- HtmlTokenType.Text;[o];
- HtmlTokenType.Text;[}];
- HtmlTokenType.Text;[o];
+ SyntaxKind.HtmlText - [F{o}o] - [22..27) - FullWidth: 5 - Slots: 1
+ SyntaxKind.List - [F{o}o] - [22..27) - FullWidth: 5 - Slots: 5
+ SyntaxKind.HtmlTextLiteralToken;[F];
+ SyntaxKind.HtmlTextLiteralToken;[{];
+ SyntaxKind.HtmlTextLiteralToken;[o];
+ SyntaxKind.HtmlTextLiteralToken;[}];
+ SyntaxKind.HtmlTextLiteralToken;[o];
Tag block - Gen - 4 - (27:0,27)
Markup span - Gen - [] - SpanEditHandler;Accepts:Any - (27:0,27) - Tokens:4
HtmlTokenType.OpenAngle;[<];
diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/Parse_SectionDirective.stree.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/Parse_SectionDirective.stree.txt
index 2af805b4b0..77d3e85124 100644
--- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/Parse_SectionDirective.stree.txt
+++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpDirectivesTest/Parse_SectionDirective.stree.txt
@@ -19,12 +19,13 @@ Directive block - Gen - 32 - (0:0,0
HtmlTokenType.OpenAngle;[<];
HtmlTokenType.Text;[p];
HtmlTokenType.CloseAngle;[>];
- Markup span - Gen - [F{o}o] - SpanEditHandler;Accepts:Any - (21:0,21) - Tokens:5
- HtmlTokenType.Text;[F];
- HtmlTokenType.Text;[{];
- HtmlTokenType.Text;[o];
- HtmlTokenType.Text;[}];
- HtmlTokenType.Text;[o];
+ SyntaxKind.HtmlText - [F{o}o] - [21..26) - FullWidth: 5 - Slots: 1
+ SyntaxKind.List - [F{o}o] - [21..26) - FullWidth: 5 - Slots: 5
+ SyntaxKind.HtmlTextLiteralToken;[F];
+ SyntaxKind.HtmlTextLiteralToken;[{];
+ SyntaxKind.HtmlTextLiteralToken;[o];
+ SyntaxKind.HtmlTextLiteralToken;[}];
+ SyntaxKind.HtmlTextLiteralToken;[o];
Tag block - Gen - 4 - (26:0,26)
Markup span - Gen - [] - SpanEditHandler;Accepts:Any - (26:0,26) - Tokens:4
HtmlTokenType.OpenAngle;[<];
diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpRazorCommentsTest/RazorCommentWithExtraNewLineInMarkup.stree.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpRazorCommentsTest/RazorCommentWithExtraNewLineInMarkup.stree.txt
index f1c4b4d734..45a7650f51 100644
--- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpRazorCommentsTest/RazorCommentWithExtraNewLineInMarkup.stree.txt
+++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/ParserTests/CSharpRazorCommentsTest/RazorCommentWithExtraNewLineInMarkup.stree.txt
@@ -33,8 +33,8 @@ Markup block - Gen - 45 - (0:0,0)
HtmlTokenType.RazorCommentTransition;[@];
Markup span - Gen