Merge branch 'nimullen/13494.prereq' into release/3.1

This commit is contained in:
N. Taylor Mullen 2019-09-20 11:46:12 -07:00
Родитель f283e4a7c0 15b495df9c
Коммит e980416506
331 изменённых файлов: 32019 добавлений и 8 удалений

7
.gitignore поставляемый
Просмотреть файл

@ -119,6 +119,7 @@ Session.vim
# Visual Studio Code
.vscode/
.vscode-test/
# Private test configuration and binaries.
config.ps1
@ -132,4 +133,8 @@ node_modules/
*.pyc
# Rider
.idea/
.idea/
# VSCode extension outputs
dist/
out/

7
.vscode/extensions.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,7 @@
{
"recommendations": [
"ms-vscode.vscode-typescript-tslint-plugin",
"ms-vscode.csharp",
"EditorConfig.EditorConfig"
]
}

53
.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,53 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"${workspaceFolder}/src/Razor/test/testapps/",
"--extensionDevelopmentPath=${workspaceFolder}/src/Razor/src/Microsoft.AspNetCore.Razor.VSCode.Extension"
],
"outFiles": [
"${workspaceFolder}/src/Razor/src/Microsoft.AspNetCore.Razor.VSCode.Extension/dist/**/*.js",
"${workspaceFolder}/src/Razor/src/Microsoft.AspNetCore.Razor.VSCode/dist/**/*.js",
],
"preLaunchTask": "CompileExtension"
},
{
"type": "node",
"request": "launch",
"name": "Run Unit Tests",
"runtimeExecutable": "yarn",
"cwd": "${workspaceFolder}/src/Razor/test/Microsoft.AspNetCore.Razor.VSCode.Test",
"runtimeArgs": [
"test:debug"
],
"port": 9229,
"sourceMaps": true,
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "CompileUnitTests"
},
{
"name": "Run Functional Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"${workspaceFolder}/src/Razor/test/testapps/",
"--extensionDevelopmentPath=${workspaceFolder}/src/Razor/src/Microsoft.AspNetCore.Razor.VSCode.Extension",
"--extensionTestsPath=${workspaceFolder}/src/Razor/test/Microsoft.AspNetCore.Razor.VSCode.FunctionalTest/dist/index",
],
"outFiles": [
"${workspaceFolder}/src/Razor/test/Microsoft.AspNetCore.Razor.VSCode.FunctionalTest/dist/**/*.js",
],
"preLaunchTask": "CompileFunctionalTest"
},
]
}

9
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,9 @@
{
"files.trimTrailingWhitespace": true,
"files.associations": {
"*.*proj": "xml",
"*.props": "xml",
"*.targets": "xml",
"*.tasks": "xml"
}
}

53
.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,53 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "CompilePackage",
"command": "dotnet",
"args": [ "build" ],
"options": {
"cwd": "src/Razor/src/Microsoft.AspNetCore.Razor.VSCode/"
},
"problemMatcher": "$tsc-watch",
"presentation": {
"reveal": "silent"
}
},
{
"label": "CompileExtension",
"command": "dotnet",
"args": [ "build" ],
"options": {
"cwd": "src/Razor/src/Microsoft.AspNetCore.Razor.VSCode.Extension/"
},
"problemMatcher": "$tsc-watch",
"presentation": {
"reveal": "silent"
}
},
{
"label": "CompileUnitTests",
"command": "dotnet",
"args": [ "build" ],
"options": {
"cwd": "src/Razor/test/Microsoft.AspNetCore.Razor.VSCode.Test/"
},
"problemMatcher": "$tsc-watch",
"presentation": {
"reveal": "silent"
}
},
{
"label": "CompileFunctionalTest",
"command": "dotnet",
"args": [ "build" ],
"options": {
"cwd": "src/Razor/test/Microsoft.AspNetCore.Razor.VSCode.FunctionalTest/"
},
"problemMatcher": "$tsc-watch",
"presentation": {
"reveal": "silent"
}
},
]
}

Просмотреть файл

@ -10,6 +10,7 @@
<Import Project="Sdk.props" Sdk="Microsoft.DotNet.Arcade.Sdk" />
<Import Project="eng\MPack.props" />
<Import Project="eng\targets\Npm.Common.props" Condition="'$(MSBuildProjectExtension)' == '.npmproj'" />
<Import
Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))\AspNetCoreSettings.props"

Просмотреть файл

@ -1,7 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Project="Sdk.targets" Sdk="Microsoft.DotNet.Arcade.Sdk" />
<PropertyGroup>
<PackageVersion Condition=" '$(PackageVersion)' == '' ">$(Version)</PackageVersion>
</PropertyGroup>
<Import Project="eng\MPack.targets" />
<Import Project="eng\targets\Npm.Common.targets" Condition="'$(MSBuildProjectExtension)' == '.npmproj'" />
<!-- Workaround https://github.com/dotnet/cli/issues/10528 -->
<PropertyGroup>

Просмотреть файл

@ -48,6 +48,10 @@ stages:
name: NetCoreInternal-Pool
queue: BuildPool.Windows.10.Amd64.VS2019
steps:
- task: NodeTool@0
displayName: Install Node 10.x
inputs:
versionSpec: 10.x
- task: NuGetCommand@2
displayName: 'Clear NuGet caches'
condition: succeeded()
@ -77,7 +81,7 @@ stages:
chmod +x $HOME/bin/jq
echo "##vso[task.prependpath]$HOME/bin"
displayName: Install jq
- script: ./eng/scripts/ci-source-build.sh --ci --configuration $(_BuildConfig)
- script: ./eng/scripts/ci-source-build.sh --ci --configuration $(_BuildConfig) /p:BuildNodeJs=false
displayName: Run ci-source-build.sh
- task: PublishBuildArtifacts@1
@ -158,6 +162,10 @@ stages:
- group: DotNet-Blob-Feed
- group: DotNet-Symbol-Server-Pats
steps:
- task: NodeTool@0
displayName: Install Node 10.x
inputs:
versionSpec: 10.x
- task: NuGetCommand@2
displayName: 'Clear NuGet caches'
condition: succeeded()
@ -226,6 +234,10 @@ stages:
release:
_BuildConfig: Release
steps:
- task: NodeTool@0
displayName: Install Node 10.x
inputs:
versionSpec: 10.x
- script: eng/common/cibuild.sh
--configuration $(_BuildConfig)
--prepareMachine
@ -255,6 +267,10 @@ stages:
release:
_BuildConfig: Release
steps:
- task: NodeTool@0
displayName: Install Node 10.x
inputs:
versionSpec: 10.x
- script: eng/common/cibuild.sh
--configuration $(_BuildConfig)
--prepareMachine

Просмотреть файл

@ -8,4 +8,24 @@
Text="$(RazorExtensionVSIXName) was not generated."
Condition="!Exists('$(VSSetupDir)$(Configuration)\$(RazorExtensionVSIXName)')" />
</Target>
<Target Name="_ZipLanguageServerBinaries" AfterTargets="Pack" Condition=" '$(OS)' == 'Windows_NT' ">
<!-- Build a .zip file from each platform's directory and write it to 'artifacts' -->
<PropertyGroup>
<RidsPublishDir>$(ArtifactsDir)LanguageServer\$(Configuration)\</RidsPublishDir>
<ZipOutputDir>$(RidsPublishDir)</ZipOutputDir>
</PropertyGroup>
<ItemGroup>
<LanguageServiceBinaryDir Include="$([System.IO.Directory]::GetDirectories(&quot;$(RidsPublishDir)&quot;))" />
<LanguageServiceBinary Include="@(LanguageServiceBinaryDir)">
<SourceDir>%(LanguageServiceBinaryDir.Identity)</SourceDir>
<ZipFile>$(ZipOutputDir)RazorLanguageServer-%(LanguageServiceBinaryDir.Filename)-$(PackageVersion).zip</ZipFile>
</LanguageServiceBinary>
</ItemGroup>
<MakeDir Directories="$(ZipOutputDir)" />
<Delete Files="%(LanguageServiceBinary.ZipFile)" />
<Exec Command="powershell.exe -NonInteractive -command &quot;&amp;{ Write-Host &quot;Writing %(LanguageServiceBinary.ZipFile)...&quot; ; Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory('%(LanguageServiceBinary.SourceDir)', '%(LanguageServiceBinary.ZipFile)') }&quot;" />
</Target>
</Project>

Просмотреть файл

@ -0,0 +1,12 @@
<Project>
<Target Name="_PublishLanguageServerRids" AfterTargets="Pack" Condition=" '$(OS)' == 'Windows_NT' ">
<PropertyGroup>
<LanguageServerProject>$(MSBuildThisFileDirectory)..\src\Razor\src\Microsoft.AspNetCore.Razor.LanguageServer\Microsoft.AspNetCore.Razor.LanguageServer.csproj</LanguageServerProject>
</PropertyGroup>
<MSBuild Projects="$(LanguageServerProject)"
Targets="PublishAllRids" />
</Target>
</Project>

Просмотреть файл

@ -1,9 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildNodeJs Condition="'$(BuildNodeJs)' == ''">true</BuildNodeJs>
</PropertyGroup>
<ItemGroup>
<ProjectToBuild Condition="'$(OS)'=='WINDOWS_NT' and '$(SdkTaskProjects)'==''" Include="$(MSBuildThisFileDirectory)..\src\Razor\Razor.sln" />
<!-- Exclude VSIX projects on non-Windows -->
<ProjectToBuild Condition="'$(OS)'!='WINDOWS_NT' and '$(SdkTaskProjects)'==''" Include="$(MSBuildThisFileDirectory)..\src\Razor\Razor.Slim.sln" />
<NodeJsProjects Include="$(RepoRoot)src\Razor\src\**\*.npmproj" RestoreInParallel="false"/>
<NodeJsProjects Include="$(RepoRoot)src\Razor\test\**\*.npmproj" RestoreInParallel="false" />
<ProjectToBuild Include="@(NodeJsProjects)" Condition="'$(BuildNodeJs)' == 'true'" />
</ItemGroup>
</Project>

14
eng/Tools.props Normal file
Просмотреть файл

@ -0,0 +1,14 @@
<Project>
<ItemGroup>
<!--
This is here to workaround flakiness in the NuGet SDK resolver in MSBuild.
Arcade will run a pre-restore for these packages. This works more consistently than the SDK resolution which uses global.json.
Without this here, we see regular failures with 'error MSB4236: The SDK 'Yarn.MSBuild' specified could not be found.'
Since this project is evaluated before .npmproj files are loaded, this should cause the package to end
up in the NuGet cache ahead of time.
This is not needed in source build.
-->
<PackageReference Condition="'$(DotNetBuildFromSource)' != 'true'" Include="Yarn.MSBuild" Version="1.15.2" />
</ItemGroup>
</Project>

Просмотреть файл

@ -77,6 +77,7 @@
<PropertyGroup Label="Manual">
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<MicrosoftBuildFrameworkPackageVersion>15.8.166</MicrosoftBuildFrameworkPackageVersion>
<MicrosoftBuildLocatorPackageVersion>1.2.6</MicrosoftBuildLocatorPackageVersion>
<MicrosoftBuildPackageVersion>15.8.166</MicrosoftBuildPackageVersion>
<MicrosoftBuildUtilitiesCorePackageVersion>15.8.166</MicrosoftBuildUtilitiesCorePackageVersion>
<MicrosoftCodeAnalysisCommonPackageVersion>3.3.0</MicrosoftCodeAnalysisCommonPackageVersion>
@ -110,6 +111,8 @@
<MoqPackageVersion>4.10.0</MoqPackageVersion>
<!-- STOP!!! We need to reference the version of JSON that our HOSTS supprt. -->
<NewtonsoftJsonPackageVersion>9.0.1</NewtonsoftJsonPackageVersion>
<OmniSharpExtensionsLanguageServerPackageVersion>0.13.1</OmniSharpExtensionsLanguageServerPackageVersion>
<OmniSharpMSBuildPackageVersion>1.33.0</OmniSharpMSBuildPackageVersion>
<VS_NewtonsoftJsonPackageVersion>12.0.1</VS_NewtonsoftJsonPackageVersion>
<VSMAC_NewtonsoftJsonPackageVersion>10.0.3</VSMAC_NewtonsoftJsonPackageVersion>
<TEST_NewtonsoftJsonPackageVersion>12.0.1</TEST_NewtonsoftJsonPackageVersion>

Просмотреть файл

@ -0,0 +1,12 @@
const path = require("path");
const fs = require("fs");
const args = process.argv.slice(2);
const packageJson = args[0];
const packageVersion = args[1];
const fileContent = fs.readFileSync(packageJson);
const updatedContent = fileContent.toString().replace(/\"link:.*\"/g, `">=${packageVersion}"`);
if (fileContent != updatedContent) {
fs.writeFileSync(packageJson, updatedContent);
}

Просмотреть файл

@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<NpmTestArgs>test</NpmTestArgs>
<Configuration Condition="'$(Configuration)' == '' AND '$(ContinuousIntegrationBuild)' == 'true'">Release</Configuration>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<PackOnBuild>false</PackOnBuild>
</PropertyGroup>
</Project>

Просмотреть файл

@ -0,0 +1,126 @@
<Project DefaultTargets="Build" InitialTargets="_CheckForInvalidConfiguration">
<!-- Version of this SDK is set in global.json -->
<Sdk Name="Yarn.MSBuild" />
<PropertyGroup>
<NormalizedPackageId>$(PackageId.Replace('@','').Replace('/','-'))</NormalizedPackageId>
<PackageFileName>$(NormalizedPackageId)-$(PackageVersion).tgz</PackageFileName>
<PackageJson>$(MSBuildProjectDirectory)\package.json</PackageJson>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)' == ''">$(MSBuildProjectDirectory)\obj\</BaseIntermediateOutputPath>
<IntermediateOutputPath>$([MSBuild]::NormalizeDirectory('$(BaseIntermediateOutputPath)'))$(Configuration)\</IntermediateOutputPath>
<InstallArgs Condition="'$(RestoreLockedMode)' == 'true'">$(InstallArgs) --frozen-lockfile</InstallArgs>
<InstallArgs>$(InstallArgs) --ignore-engines</InstallArgs>
<_BackupPackageJson>$(IntermediateOutputPath)$(MSBuildProjectName).package.json.bak</_BackupPackageJson>
<BuildDependsOn>
PrepareForBuild;
ResolveProjectReferences;
_Build;
</BuildDependsOn>
<NpmBuildArgs Condition="'$(NpmBuildArgs)' == ''">run build</NpmBuildArgs>
</PropertyGroup>
<ItemGroup>
<TSFiles Include="src\**\*.ts" />
<TSFiles Include="test\**\*.ts" />
<TSFiles Include="package.json" />
<TSFiles Include="*.npmproj" />
<BuildOutputFiles Include="$(BaseIntermediateOutputPath)\build-sentinel" />
<BuildOutputFiles Include="dist\**\*.js" />
</ItemGroup>
<Target Name="_CheckForInvalidConfiguration">
<Error Text="Missing expected property: PackageId" Condition="'$(IsPackable)' != 'false' and '$(PackageId)' == ''" />
<Exec ContinueOnError="true" Command="node -v" StandardOutputImportance="Low">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
</Exec>
<Error Text="Building *.npmproj but NodeJS was not detected on path. Ensure NodeJS is on path or disable building NodeJS projects with /p:BuildNodeJs=false. Skipping NodeJS projects will also skip managed projects depending on them, including Components, Mvc and Analysers." Condition="'$(ErrorCode)' != '0'"/>
</Target>
<Target Name="Restore">
<Message Importance="High" Text="Running yarn install on $(MSBuildProjectFullPath)" />
<Yarn Command="install --mutex network $(InstallArgs)" StandardOutputImportance="High" StandardErrorImportance="High" />
</Target>
<Target Name="PrepareForBuild">
<MakeDir Directories="$(IntermediateOutputPath);$(PackageOutputPath)" />
</Target>
<Target Name="ResolveProjectReferences">
<MSBuild Projects="@(ProjectReference)"
BuildInParallel="true" />
</Target>
<Target Name="GetBuildInputCacheFile">
<Hash ItemsToHash="@(TSFiles)">
<Output TaskParameter="HashResult" PropertyName="_TSFileHash" />
</Hash>
<WriteLinesToFile
Lines="$(_TSFileHash)"
File="$(BaseIntermediateOutputPath)tsfiles.cache"
Overwrite="True"
WriteOnlyWhenDifferent="True" />
</Target>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)">
<CallTarget Targets="_Pack" Condition="'$(PackOnBuild)' == 'true'" />
</Target>
<Target Name="_Build"
Condition="'$(IsBuildable)' != 'false'"
DependsOnTargets="GetBuildInputCacheFile"
Inputs="@(TSFiles);$(BaseIntermediateOutputPath)tsfiles.cache"
Outputs="@(BuildOutputFiles)">
<Yarn Command="$(NpmBuildArgs)" StandardOutputImportance="High" StandardErrorImportance="High" />
<WriteLinesToFile Overwrite="true" File="$(BaseIntermediateOutputPath)build-sentinel" />
</Target>
<PropertyGroup>
<PackDependsOn>
$(PackDependsOn);
PrepareForBuild
</PackDependsOn>
<PackDependsOn Condition="'$(NoBuild)' != 'true'">
$(PackDependsOn);
Build;
_Pack
</PackDependsOn>
</PropertyGroup>
<Target Name="_Pack" Condition="'$(IsPackable)' == 'true'"
Inputs="@(TSFiles)"
Outputs="$(PackageOutputPath)\$(PackageFileName)">
<PropertyGroup>
<_PackageTargetPath>$(MSBuildProjectDirectory)\$(PackageFileName)</_PackageTargetPath>
</PropertyGroup>
<Copy SourceFiles="$(PackageJson)" DestinationFiles="$(_BackupPackageJson)" />
<Yarn Command="version --no-git-tag-version --new-version $(PackageVersion)" />
<Exec Command="node $(MSBuildThisFileDirectory)..\scripts\update-packagejson-links.js $(PackageJson) $(PackageVersion)" />
<Yarn Command="pack --filename $(PackageFileName)" />
<Move SourceFiles="$(_PackageTargetPath)" DestinationFolder="$(PackageOutputPath)" />
<Message Importance="High" Text="$(MSBuildProjectName) -> $(_PackageTargetPath)" />
<CallTarget Targets="_RestoreBackupPackageJsonFile" />
<OnError ExecuteTargets="_RestoreBackupPackageJsonFile" />
</Target>
<Target Name="Pack" Condition="'$(IsPackable)' == 'true'" DependsOnTargets="$(PackDependsOn)" />
<Target Name="_RestoreBackupPackageJsonFile">
<Move SourceFiles="$(_BackupPackageJson)" DestinationFiles="$(PackageJson)" />
</Target>
<Target Name="Test" Condition="'$(IsTestProject)' == 'true'">
<Message Importance="High" Text="Running npm tests for $(MSBuildProjectName)" />
<Yarn Command="$(NpmTestArgs)" StandardOutputImportance="High" StandardErrorImportance="High" />
</Target>
</Project>

Просмотреть файл

@ -18,6 +18,7 @@
"version": "3.1.100-preview1-014024"
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19461.7"
"Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19461.7",
"Yarn.MSBuild": "1.15.2"
}
}

Просмотреть файл

@ -86,9 +86,25 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Mac.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test.ComponentShim", "test\Microsoft.AspNetCore.Razor.Test.ComponentShim\Microsoft.AspNetCore.Razor.Test.ComponentShim.csproj", "{5B232E77-F0D3-4298-9A5D-D965788D7A79}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.LiveShare.Razor", "src\Microsoft.VisualStudio.LiveShare.Razor\Microsoft.VisualStudio.LiveShare.Razor.csproj", "{20193C6A-8981-447F-99B3-120DD3B06279}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.LiveShare.Razor", "src\Microsoft.VisualStudio.LiveShare.Razor\Microsoft.VisualStudio.LiveShare.Razor.csproj", "{20193C6A-8981-447F-99B3-120DD3B06279}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.LiveShare.Razor.Test", "test\Microsoft.VisualStudio.LiveShare.Razor.Test\Microsoft.VisualStudio.LiveShare.Razor.Test.csproj", "{9A27DD55-E8CD-4C03-A89B-A7348B787660}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.LiveShare.Razor.Test", "test\Microsoft.VisualStudio.LiveShare.Razor.Test\Microsoft.VisualStudio.LiveShare.Razor.Test.csproj", "{9A27DD55-E8CD-4C03-A89B-A7348B787660}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.LanguageServer.Common", "src\Microsoft.AspNetCore.Razor.LanguageServer.Common\Microsoft.AspNetCore.Razor.LanguageServer.Common.csproj", "{F2B59848-345E-4ECB-ADDB-277F3C937B9C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.LanguageServer", "src\Microsoft.AspNetCore.Razor.LanguageServer\Microsoft.AspNetCore.Razor.LanguageServer.csproj", "{1D15867E-E50F-4107-92A4-BBC2EE6B088C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.LanguageServer.Common.Test", "test\Microsoft.AspNetCore.Razor.LanguageServer.Common.Test\Microsoft.AspNetCore.Razor.LanguageServer.Common.Test.csproj", "{6C8A42B5-B41C-4334-959F-684E647A24E1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.LanguageServer.Test", "test\Microsoft.AspNetCore.Razor.LanguageServer.Test\Microsoft.AspNetCore.Razor.LanguageServer.Test.csproj", "{FBAE9975-77BE-411B-A1A3-4790C8A367EF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.LanguageServer.Test.Common", "test\Microsoft.AspNetCore.Razor.LanguageServer.Test.Common\Microsoft.AspNetCore.Razor.LanguageServer.Test.Common.csproj", "{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.OmniSharpPlugin.StrongNamed", "src\Microsoft.AspNetCore.Razor.OmniSharpPlugin.StrongNamed\Microsoft.AspNetCore.Razor.OmniSharpPlugin.StrongNamed.csproj", "{525CCD97-7E6E-404C-8CD3-736E5649C858}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.OmniSharpPlugin", "src\Microsoft.AspNetCore.Razor.OmniSharpPlugin\Microsoft.AspNetCore.Razor.OmniSharpPlugin.csproj", "{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.OmniSharpPlugin.Test", "test\Microsoft.AspNetCore.Razor.OmniSharpPlugin.Test\Microsoft.AspNetCore.Razor.OmniSharpPlugin.Test.csproj", "{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -410,6 +426,70 @@ Global
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.Release|Any CPU.Build.0 = Release|Any CPU
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.ReleaseNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.ReleaseNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{F2B59848-345E-4ECB-ADDB-277F3C937B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2B59848-345E-4ECB-ADDB-277F3C937B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2B59848-345E-4ECB-ADDB-277F3C937B9C}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{F2B59848-345E-4ECB-ADDB-277F3C937B9C}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{F2B59848-345E-4ECB-ADDB-277F3C937B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2B59848-345E-4ECB-ADDB-277F3C937B9C}.Release|Any CPU.Build.0 = Release|Any CPU
{F2B59848-345E-4ECB-ADDB-277F3C937B9C}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{F2B59848-345E-4ECB-ADDB-277F3C937B9C}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{1D15867E-E50F-4107-92A4-BBC2EE6B088C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1D15867E-E50F-4107-92A4-BBC2EE6B088C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D15867E-E50F-4107-92A4-BBC2EE6B088C}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{1D15867E-E50F-4107-92A4-BBC2EE6B088C}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{1D15867E-E50F-4107-92A4-BBC2EE6B088C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D15867E-E50F-4107-92A4-BBC2EE6B088C}.Release|Any CPU.Build.0 = Release|Any CPU
{1D15867E-E50F-4107-92A4-BBC2EE6B088C}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{1D15867E-E50F-4107-92A4-BBC2EE6B088C}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{6C8A42B5-B41C-4334-959F-684E647A24E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C8A42B5-B41C-4334-959F-684E647A24E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C8A42B5-B41C-4334-959F-684E647A24E1}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{6C8A42B5-B41C-4334-959F-684E647A24E1}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{6C8A42B5-B41C-4334-959F-684E647A24E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C8A42B5-B41C-4334-959F-684E647A24E1}.Release|Any CPU.Build.0 = Release|Any CPU
{6C8A42B5-B41C-4334-959F-684E647A24E1}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{6C8A42B5-B41C-4334-959F-684E647A24E1}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{FBAE9975-77BE-411B-A1A3-4790C8A367EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FBAE9975-77BE-411B-A1A3-4790C8A367EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FBAE9975-77BE-411B-A1A3-4790C8A367EF}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{FBAE9975-77BE-411B-A1A3-4790C8A367EF}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{FBAE9975-77BE-411B-A1A3-4790C8A367EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FBAE9975-77BE-411B-A1A3-4790C8A367EF}.Release|Any CPU.Build.0 = Release|Any CPU
{FBAE9975-77BE-411B-A1A3-4790C8A367EF}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{FBAE9975-77BE-411B-A1A3-4790C8A367EF}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.Release|Any CPU.Build.0 = Release|Any CPU
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{525CCD97-7E6E-404C-8CD3-736E5649C858}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{525CCD97-7E6E-404C-8CD3-736E5649C858}.Debug|Any CPU.Build.0 = Debug|Any CPU
{525CCD97-7E6E-404C-8CD3-736E5649C858}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{525CCD97-7E6E-404C-8CD3-736E5649C858}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{525CCD97-7E6E-404C-8CD3-736E5649C858}.Release|Any CPU.ActiveCfg = Release|Any CPU
{525CCD97-7E6E-404C-8CD3-736E5649C858}.Release|Any CPU.Build.0 = Release|Any CPU
{525CCD97-7E6E-404C-8CD3-736E5649C858}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{525CCD97-7E6E-404C-8CD3-736E5649C858}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.Release|Any CPU.Build.0 = Release|Any CPU
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.Release|Any CPU.Build.0 = Release|Any CPU
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -454,6 +534,14 @@ Global
{5B232E77-F0D3-4298-9A5D-D965788D7A79} = {92463391-81BE-462B-AC3C-78C6C760741F}
{20193C6A-8981-447F-99B3-120DD3B06279} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{9A27DD55-E8CD-4C03-A89B-A7348B787660} = {92463391-81BE-462B-AC3C-78C6C760741F}
{F2B59848-345E-4ECB-ADDB-277F3C937B9C} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{1D15867E-E50F-4107-92A4-BBC2EE6B088C} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{6C8A42B5-B41C-4334-959F-684E647A24E1} = {92463391-81BE-462B-AC3C-78C6C760741F}
{FBAE9975-77BE-411B-A1A3-4790C8A367EF} = {92463391-81BE-462B-AC3C-78C6C760741F}
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8} = {92463391-81BE-462B-AC3C-78C6C760741F}
{525CCD97-7E6E-404C-8CD3-736E5649C858} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE} = {92463391-81BE-462B-AC3C-78C6C760741F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26}

Просмотреть файл

@ -1099,8 +1099,6 @@ namespace Microsoft.AspNetCore.Razor.Language
if (node.Source != null)
{
Debug.Assert(node.Source.Value.FilePath != null);
node.Source = new SourceSpan(
node.Source.Value.FilePath,
node.Source.Value.AbsoluteIndex,

Просмотреть файл

@ -0,0 +1,49 @@
// 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;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
public class AdhocLanguageServices : HostLanguageServices
{
private readonly HostWorkspaceServices _workspaceServices;
private readonly IEnumerable<ILanguageService> _languageServices;
public AdhocLanguageServices(HostWorkspaceServices workspaceServices, IEnumerable<ILanguageService> languageServices)
{
if (workspaceServices == null)
{
throw new ArgumentNullException(nameof(workspaceServices));
}
if (languageServices == null)
{
throw new ArgumentNullException(nameof(languageServices));
}
_workspaceServices = workspaceServices;
_languageServices = languageServices;
}
public override HostWorkspaceServices WorkspaceServices => _workspaceServices;
public override string Language => RazorLanguage.Name;
public override TLanguageService GetService<TLanguageService>()
{
var service = _languageServices.OfType<TLanguageService>().FirstOrDefault();
if (service == null)
{
throw new InvalidOperationException($"Razor language services not configured properly, missing language service '{typeof(TLanguageService).FullName}'.");
}
return service;
}
}
}

Просмотреть файл

@ -0,0 +1,49 @@
// 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;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
public class AdhocServices : HostServices
{
private readonly IEnumerable<IWorkspaceService> _workspaceServices;
private readonly IEnumerable<ILanguageService> _razorLanguageServices;
private AdhocServices(IEnumerable<IWorkspaceService> workspaceServices, IEnumerable<ILanguageService> razorLanguageServices)
{
if (workspaceServices == null)
{
throw new ArgumentNullException(nameof(workspaceServices));
}
if (razorLanguageServices == null)
{
throw new ArgumentNullException(nameof(razorLanguageServices));
}
_workspaceServices = workspaceServices;
_razorLanguageServices = razorLanguageServices;
}
protected override HostWorkspaceServices CreateWorkspaceServices(Workspace workspace)
{
if (workspace == null)
{
throw new ArgumentNullException(nameof(workspace));
}
return new AdhocWorkspaceServices(this, _workspaceServices, _razorLanguageServices, workspace);
}
public static HostServices Create(IEnumerable<ILanguageService> razorLanguageServices)
=> Create(Enumerable.Empty<IWorkspaceService>(), razorLanguageServices);
public static HostServices Create(IEnumerable<IWorkspaceService> workspaceServices, IEnumerable<ILanguageService> razorLanguageServices)
=> new AdhocServices(workspaceServices, razorLanguageServices);
}
}

Просмотреть файл

@ -0,0 +1,89 @@
// 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;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
public class AdhocWorkspaceServices : HostWorkspaceServices
{
private static readonly Workspace DefaultWorkspace = new AdhocWorkspace();
private readonly HostServices _hostServices;
private readonly HostLanguageServices _razorLanguageServices;
private readonly IEnumerable<IWorkspaceService> _workspaceServices;
private readonly Workspace _workspace;
public AdhocWorkspaceServices(
HostServices hostServices,
IEnumerable<IWorkspaceService> workspaceServices,
IEnumerable<ILanguageService> languageServices,
Workspace workspace)
{
if (hostServices == null)
{
throw new ArgumentNullException(nameof(hostServices));
}
if (workspaceServices == null)
{
throw new ArgumentNullException(nameof(workspaceServices));
}
if (languageServices == null)
{
throw new ArgumentNullException(nameof(languageServices));
}
if (workspace == null)
{
throw new ArgumentNullException(nameof(workspace));
}
_hostServices = hostServices;
_workspaceServices = workspaceServices;
_workspace = workspace;
_razorLanguageServices = new AdhocLanguageServices(this, languageServices);
}
public override HostServices HostServices => _hostServices;
public override Workspace Workspace => _workspace;
public override TWorkspaceService GetService<TWorkspaceService>()
{
var service = _workspaceServices.OfType<TWorkspaceService>().FirstOrDefault();
if (service == null)
{
// Fallback to default host services to resolve roslyn specific features.
service = DefaultWorkspace.Services.GetService<TWorkspaceService>();
}
return service;
}
public override HostLanguageServices GetLanguageServices(string languageName)
{
if (languageName == RazorLanguage.Name)
{
return _razorLanguageServices;
}
// Fallback to default host services to resolve roslyn specific features.
return DefaultWorkspace.Services.GetLanguageServices(languageName);
}
public override IEnumerable<string> SupportedLanguages => new[] { RazorLanguage.Name };
public override bool IsSupported(string languageName) => languageName == RazorLanguage.Name;
public override IEnumerable<TLanguageService> FindLanguageServices<TLanguageService>(MetadataFilter filter) => throw new NotImplementedException();
}
}

Просмотреть файл

@ -0,0 +1,33 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
[ExportCustomProjectEngineFactory("Default", SupportsSerialization = true)]
internal class DefaultProjectEngineFactory : IProjectEngineFactory
{
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
CompilerFeatures.Register(b);
configure?.Invoke(b);
// See comments on MangleClassNames
var componentDocumentClassifier = b.Features.OfType<ComponentDocumentClassifierPass>().FirstOrDefault();
if (componentDocumentClassifier != null)
{
componentDocumentClassifier.MangleClassNames = true;
}
});
}
}
}

Просмотреть файл

@ -0,0 +1,64 @@
// 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.Net;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
public class FilePathNormalizer
{
public string NormalizeDirectory(string directoryFilePath)
{
var normalized = Normalize(directoryFilePath);
if (!normalized.EndsWith("/"))
{
normalized += '/';
}
return normalized;
}
public string Normalize(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return "/";
}
var decodedPath = WebUtility.UrlDecode(filePath);
var normalized = decodedPath.Replace('\\', '/');
if (normalized[0] != '/')
{
normalized = '/' + normalized;
}
return normalized;
}
public string GetDirectory(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
throw new InvalidOperationException(filePath);
}
var normalizedPath = Normalize(filePath);
var lastSeparatorIndex = normalizedPath.LastIndexOf('/');
var directory = normalizedPath.Substring(0, lastSeparatorIndex + 1);
return directory;
}
public bool FilePathsEquivalent(string filePath1, string filePath2)
{
var normalizedFilePath1 = Normalize(filePath1);
var normalizedFilePath2 = Normalize(filePath2);
return FilePathComparer.Instance.Equals(normalizedFilePath1, normalizedFilePath2);
}
}
}

Просмотреть файл

@ -0,0 +1,49 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class HostDocumentComparer : IEqualityComparer<HostDocument>
{
public static readonly HostDocumentComparer Instance = new HostDocumentComparer();
private HostDocumentComparer()
{
}
public bool Equals(HostDocument x, HostDocument y)
{
if (x.FileKind != y.FileKind)
{
return false;
}
if (!FilePathComparer.Instance.Equals(x.FilePath, y.FilePath))
{
return false;
}
if (!FilePathComparer.Instance.Equals(x.TargetPath, y.TargetPath))
{
return false;
}
return true;
}
public int GetHashCode(HostDocument hostDocument)
{
var combiner = HashCodeCombiner.Start();
combiner.Add(hostDocument.FilePath, FilePathComparer.Instance);
combiner.Add(hostDocument.TargetPath, FilePathComparer.Instance);
combiner.Add(hostDocument.FileKind);
return combiner.CombinedHash;
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains common assets that are used in the Razor language server and other assemblies.</Description>
<EnableApiCheck>false</EnableApiCheck>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
<ProjectReference Include="..\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,40 @@
// 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.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
public class ProjectEngineFactories
{
public static readonly Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] Factories =
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[]
{
// Razor based configurations
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
() => new DefaultProjectEngineFactory(),
new ExportCustomProjectEngineFactoryAttribute("Default") { SupportsSerialization = true }),
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
() => new ProjectEngineFactory_1_0(),
new ExportCustomProjectEngineFactoryAttribute("MVC-1.0") { SupportsSerialization = true }),
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
() => new ProjectEngineFactory_1_1(),
new ExportCustomProjectEngineFactoryAttribute("MVC-1.1") { SupportsSerialization = true }),
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
() => new ProjectEngineFactory_2_0(),
new ExportCustomProjectEngineFactoryAttribute("MVC-2.0") { SupportsSerialization = true }),
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
() => new ProjectEngineFactory_2_1(),
new ExportCustomProjectEngineFactoryAttribute("MVC-2.1") { SupportsSerialization = true }),
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
() => new ProjectEngineFactory_3_0(),
new ExportCustomProjectEngineFactoryAttribute("MVC-3.0") { SupportsSerialization = true }),
// Unsupported (Legacy/System.Web.Razor)
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
() => new ProjectEngineFactory_Unsupported(),
new ExportCustomProjectEngineFactoryAttribute(UnsupportedRazorConfiguration.Instance.ConfigurationName) { SupportsSerialization = true }),
};
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// 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.Reflection;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class ProjectEngineFactory_1_0 : IProjectEngineFactory
{
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X";
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
var assemblyName = new AssemblyName(typeof(RazorProjectEngine).Assembly.FullName);
assemblyName.Name = AssemblyName;
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
var initializer = extension.CreateInitializer();
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
initializer.Initialize(b);
configure?.Invoke(b);
});
}
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// 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.Reflection;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class ProjectEngineFactory_1_1 : IProjectEngineFactory
{
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X";
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
var assemblyName = new AssemblyName(typeof(RazorProjectEngine).Assembly.FullName);
assemblyName.Name = AssemblyName;
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
var initializer = extension.CreateInitializer();
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
initializer.Initialize(b);
configure?.Invoke(b);
});
}
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// 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.Reflection;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class ProjectEngineFactory_2_0 : IProjectEngineFactory
{
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X";
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
var assemblyName = new AssemblyName(typeof(RazorProjectEngine).Assembly.FullName);
assemblyName.Name = AssemblyName;
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
var initializer = extension.CreateInitializer();
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
initializer.Initialize(b);
configure?.Invoke(b);
});
}
}
}

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class ProjectEngineFactory_2_1 : IProjectEngineFactory
{
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X";
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
var assemblyName = new AssemblyName(typeof(RazorProjectEngine).Assembly.FullName);
assemblyName.Name = AssemblyName;
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
var initializer = extension.CreateInitializer();
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
initializer.Initialize(b);
configure?.Invoke(b);
});
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// 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.Reflection;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class ProjectEngineFactory_3_0 : IProjectEngineFactory
{
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions";
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
var assemblyName = new AssemblyName(typeof(RazorProjectEngine).Assembly.FullName);
assemblyName.Name = AssemblyName;
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
var initializer = extension.CreateInitializer();
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
CompilerFeatures.Register(b);
initializer.Initialize(b);
configure?.Invoke(b);
var componentDocumentClassifier = b.Features.OfType<ComponentDocumentClassifierPass>().FirstOrDefault();
if (componentDocumentClassifier != null)
{
componentDocumentClassifier.MangleClassNames = true;
}
});
}
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class ProjectEngineFactory_Unsupported : IProjectEngineFactory
{
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
return RazorProjectEngine.Create(configuration, fileSystem, builder =>
{
var csharpLoweringIndex = builder.Phases.IndexOf(builder.Phases.OfType<IRazorCSharpLoweringPhase>().Single());
builder.Phases[csharpLoweringIndex] = new UnsupportedCSharpLoweringPhase();
});
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// 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.LanguageServer.Common
{
internal static class ProjectSerializationFormat
{
public static string Version => "0.2";
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// 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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
[assembly: InternalsVisibleTo("rzls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.OmniSharpPlugin.StrongNamed, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.Common.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

Просмотреть файл

@ -0,0 +1,39 @@
// 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.AspNetCore.Razor.Language;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal static class RazorCodeDocumentExtensions
{
private static readonly object UnsupportedKey = new object();
public static bool IsUnsupported(this RazorCodeDocument document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
var unsupportedObj = document.Items[UnsupportedKey];
if (unsupportedObj == null)
{
return false;
}
return (bool)unsupportedObj;
}
public static void SetUnsupported(this RazorCodeDocument document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
document.Items[UnsupportedKey] = true;
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// 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.LanguageServer.Common.Serialization
{
internal sealed class DocumentSnapshotHandle
{
public DocumentSnapshotHandle(
string filePath,
string targetPath,
string fileKind)
{
if (filePath == null)
{
throw new ArgumentNullException(nameof(filePath));
}
if (targetPath == null)
{
throw new ArgumentNullException(nameof(targetPath));
}
if (fileKind == null)
{
throw new ArgumentNullException(nameof(fileKind));
}
FilePath = filePath;
TargetPath = targetPath;
FileKind = fileKind;
}
public string FilePath { get; }
public string TargetPath { get; }
public string FileKind { get; }
}
}

Просмотреть файл

@ -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.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
// FullProjectSnapshotHandle exists in order to allow ProjectSnapshots to be serialized and then deserialized.
// It has named "Full" because there's a similar concept in core Razor of a ProjectSnapshotHandle. In Razor's
// case that handle doesn't contain ProjectWorkspaceState information
internal sealed class FullProjectSnapshotHandle
{
public FullProjectSnapshotHandle(
string filePath,
RazorConfiguration configuration,
string rootNamespace,
ProjectWorkspaceState projectWorkspaceState,
IReadOnlyList<DocumentSnapshotHandle> documents)
{
if (filePath == null)
{
throw new ArgumentNullException(nameof(filePath));
}
if (documents == null)
{
throw new ArgumentNullException(nameof(documents));
}
FilePath = filePath;
Configuration = configuration;
RootNamespace = rootNamespace;
ProjectWorkspaceState = projectWorkspaceState;
Documents = documents;
}
public string FilePath { get; }
public RazorConfiguration Configuration { get; }
public string RootNamespace { get; }
public ProjectWorkspaceState ProjectWorkspaceState { get; }
public IReadOnlyList<DocumentSnapshotHandle> Documents { get; }
}
}

Просмотреть файл

@ -0,0 +1,100 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
internal class FullProjectSnapshotHandleJsonConverter : JsonConverter
{
public static readonly FullProjectSnapshotHandleJsonConverter Instance = new FullProjectSnapshotHandleJsonConverter();
private const string SerializationFormatPropertyName = "SerializationFormat";
public override bool CanConvert(Type objectType)
{
return typeof(FullProjectSnapshotHandle).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
return null;
}
var obj = JObject.Load(reader);
// We need to add a serialization format to the project response to indicate that this version of the code is compatible with what's being serialized.
// This scenario typically happens when a user has an incompatible serialized project snapshot but is using the latest Razor bits.
if (!obj.TryGetValue(SerializationFormatPropertyName, out var serializationFormatToken))
{
// Pre-serialization format release.
return null;
}
var serializationFormat = serializationFormatToken.Value<string>();
if (serializationFormat != ProjectSerializationFormat.Version)
{
// Unknown serialization format.
return null;
}
var filePath = obj[nameof(FullProjectSnapshotHandle.FilePath)].Value<string>();
var configuration = obj[nameof(FullProjectSnapshotHandle.Configuration)].ToObject<RazorConfiguration>(serializer);
var rootNamespace = obj[nameof(FullProjectSnapshotHandle.RootNamespace)].ToObject<string>(serializer);
var projectWorkspaceState = obj[nameof(FullProjectSnapshotHandle.ProjectWorkspaceState)].ToObject<ProjectWorkspaceState>(serializer);
var documents = obj[nameof(FullProjectSnapshotHandle.Documents)].ToObject<DocumentSnapshotHandle[]>(serializer);
return new FullProjectSnapshotHandle(filePath, configuration, rootNamespace, projectWorkspaceState, documents);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var handle = (FullProjectSnapshotHandle)value;
writer.WriteStartObject();
writer.WritePropertyName(nameof(FullProjectSnapshotHandle.FilePath));
writer.WriteValue(handle.FilePath);
if (handle.Configuration == null)
{
writer.WritePropertyName(nameof(FullProjectSnapshotHandle.Configuration));
writer.WriteNull();
}
else
{
writer.WritePropertyName(nameof(FullProjectSnapshotHandle.Configuration));
serializer.Serialize(writer, handle.Configuration);
}
if (handle.ProjectWorkspaceState == null)
{
writer.WritePropertyName(nameof(FullProjectSnapshotHandle.ProjectWorkspaceState));
writer.WriteNull();
}
else
{
writer.WritePropertyName(nameof(FullProjectSnapshotHandle.ProjectWorkspaceState));
serializer.Serialize(writer, handle.ProjectWorkspaceState);
}
writer.WritePropertyName(nameof(FullProjectSnapshotHandle.RootNamespace));
writer.WriteValue(handle.RootNamespace);
writer.WritePropertyName(nameof(FullProjectSnapshotHandle.Documents));
serializer.Serialize(writer, handle.Documents);
writer.WritePropertyName(SerializationFormatPropertyName);
writer.WriteValue(ProjectSerializationFormat.Version);
writer.WriteEndObject();
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// 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 Newtonsoft.Json;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
internal static class JsonConverterCollectionExtensions
{
public static readonly IReadOnlyList<JsonConverter> RazorConverters = new List<JsonConverter>()
{
TagHelperDescriptorJsonConverter.Instance,
RazorDiagnosticJsonConverter.Instance,
RazorExtensionJsonConverter.Instance,
RazorConfigurationJsonConverter.Instance,
FullProjectSnapshotHandleJsonConverter.Instance,
ProjectSnapshotJsonConverter.Instance,
};
public static void RegisterRazorConverters(this JsonConverterCollection collection)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
for (var i = 0; i < RazorConverters.Count; i++)
{
collection.Add(RazorConverters[i]);
}
}
}
}

Просмотреть файл

@ -0,0 +1,46 @@
// 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 Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
internal class ProjectSnapshotJsonConverter : JsonConverter
{
public static readonly ProjectSnapshotJsonConverter Instance = new ProjectSnapshotJsonConverter();
public override bool CanRead => false;
public override bool CanWrite => true;
public override bool CanConvert(Type objectType)
{
return typeof(ProjectSnapshot).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotSupportedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var project = (ProjectSnapshot)value;
var documents = new List<DocumentSnapshotHandle>();
foreach (var documentFilePath in project.DocumentFilePaths)
{
var document = project.GetDocument(documentFilePath);
var documentHandle = new DocumentSnapshotHandle(document.FilePath, document.TargetPath, document.FileKind);
documents.Add(documentHandle);
}
var handle = new FullProjectSnapshotHandle(project.FilePath, project.Configuration, project.RootNamespace, project.ProjectWorkspaceState, documents);
FullProjectSnapshotHandleJsonConverter.Instance.WriteJson(writer, handle, serializer);
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@
// 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.
// This class is a copy from the Razor repo.
using System;
using Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
internal class RazorConfigurationJsonConverter : JsonConverter
{
public static readonly RazorConfigurationJsonConverter Instance = new RazorConfigurationJsonConverter();
public override bool CanConvert(Type objectType)
{
return typeof(RazorConfiguration).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
return null;
}
var obj = JObject.Load(reader);
var configurationName = obj[nameof(RazorConfiguration.ConfigurationName)].Value<string>();
var languageVersionValue = obj[nameof(RazorConfiguration.LanguageVersion)].Value<string>();
var extensions = obj[nameof(RazorConfiguration.Extensions)].ToObject<RazorExtension[]>(serializer);
if (!RazorLanguageVersion.TryParse(languageVersionValue, out var languageVersion))
{
languageVersion = RazorLanguageVersion.Version_2_1;
}
return RazorConfiguration.Create(languageVersion, configurationName, extensions);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var configuration = (RazorConfiguration)value;
writer.WriteStartObject();
writer.WritePropertyName(nameof(RazorConfiguration.ConfigurationName));
writer.WriteValue(configuration.ConfigurationName);
writer.WritePropertyName(nameof(RazorConfiguration.LanguageVersion));
if (configuration.LanguageVersion == RazorLanguageVersion.Experimental)
{
writer.WriteValue("Experimental");
}
else
{
writer.WriteValue(configuration.LanguageVersion.ToString());
}
writer.WritePropertyName(nameof(RazorConfiguration.Extensions));
serializer.Serialize(writer, configuration.Extensions);
writer.WriteEndObject();
}
}
}

Просмотреть файл

@ -0,0 +1,76 @@
// 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.
// This class is a copy from the Razor repo.
using System;
using System.Globalization;
using Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
internal class RazorDiagnosticJsonConverter : JsonConverter
{
public static readonly RazorDiagnosticJsonConverter Instance = new RazorDiagnosticJsonConverter();
private const string RazorDiagnosticMessageKey = "Message";
public override bool CanConvert(Type objectType)
{
return typeof(RazorDiagnostic).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
return null;
}
var diagnostic = JObject.Load(reader);
var id = diagnostic[nameof(RazorDiagnostic.Id)].Value<string>();
var severity = diagnostic[nameof(RazorDiagnostic.Severity)].Value<int>();
var message = diagnostic[RazorDiagnosticMessageKey].Value<string>();
var span = diagnostic[nameof(RazorDiagnostic.Span)].Value<JObject>();
var filePath = span[nameof(SourceSpan.FilePath)].Value<string>();
var absoluteIndex = span[nameof(SourceSpan.AbsoluteIndex)].Value<int>();
var lineIndex = span[nameof(SourceSpan.LineIndex)].Value<int>();
var characterIndex = span[nameof(SourceSpan.CharacterIndex)].Value<int>();
var length = span[nameof(SourceSpan.Length)].Value<int>();
var descriptor = new RazorDiagnosticDescriptor(id, () => message, (RazorDiagnosticSeverity)severity);
var sourceSpan = new SourceSpan(filePath, absoluteIndex, lineIndex, characterIndex, length);
return RazorDiagnostic.Create(descriptor, sourceSpan);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var diagnostic = (RazorDiagnostic)value;
writer.WriteStartObject();
WriteProperty(writer, nameof(RazorDiagnostic.Id), diagnostic.Id);
WriteProperty(writer, nameof(RazorDiagnostic.Severity), (int)diagnostic.Severity);
WriteProperty(writer, RazorDiagnosticMessageKey, diagnostic.GetMessage(CultureInfo.CurrentCulture));
writer.WritePropertyName(nameof(RazorDiagnostic.Span));
writer.WriteStartObject();
WriteProperty(writer, nameof(SourceSpan.FilePath), diagnostic.Span.FilePath);
WriteProperty(writer, nameof(SourceSpan.AbsoluteIndex), diagnostic.Span.AbsoluteIndex);
WriteProperty(writer, nameof(SourceSpan.LineIndex), diagnostic.Span.LineIndex);
WriteProperty(writer, nameof(SourceSpan.CharacterIndex), diagnostic.Span.CharacterIndex);
WriteProperty(writer, nameof(SourceSpan.Length), diagnostic.Span.Length);
writer.WriteEndObject();
writer.WriteEndObject();
}
private void WriteProperty<T>(JsonWriter writer, string key, T value)
{
writer.WritePropertyName(key);
writer.WriteValue(value);
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
// 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.
// This class is a copy from the Razor repo.
using System;
using Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
internal class RazorExtensionJsonConverter : JsonConverter
{
public static readonly RazorExtensionJsonConverter Instance = new RazorExtensionJsonConverter();
public override bool CanConvert(Type objectType)
{
return typeof(RazorExtension).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
return null;
}
var obj = JObject.Load(reader);
var extensionName = obj[nameof(RazorExtension.ExtensionName)].Value<string>();
return new SerializedRazorExtension(extensionName);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var extension = (RazorExtension)value;
writer.WriteStartObject();
writer.WritePropertyName(nameof(RazorExtension.ExtensionName));
writer.WriteValue(extension.ExtensionName);
writer.WriteEndObject();
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// 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.
// This class is a copy from the Razor repo.
using System;
using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
internal class SerializedRazorExtension : RazorExtension
{
public SerializedRazorExtension(string extensionName)
{
if (extensionName == null)
{
throw new ArgumentNullException(nameof(extensionName));
}
ExtensionName = extensionName;
}
public override string ExtensionName { get; }
}
}

Просмотреть файл

@ -0,0 +1,444 @@
// 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.
// This class is a copy from the Razor repo.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization
{
internal class TagHelperDescriptorJsonConverter : JsonConverter
{
public static readonly TagHelperDescriptorJsonConverter Instance = new TagHelperDescriptorJsonConverter();
public override bool CanConvert(Type objectType)
{
return typeof(TagHelperDescriptor).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
return null;
}
var descriptor = JObject.Load(reader);
var descriptorKind = descriptor[nameof(TagHelperDescriptor.Kind)].Value<string>();
var typeName = descriptor[nameof(TagHelperDescriptor.Name)].Value<string>();
var assemblyName = descriptor[nameof(TagHelperDescriptor.AssemblyName)].Value<string>();
var tagMatchingRules = descriptor[nameof(TagHelperDescriptor.TagMatchingRules)].Value<JArray>();
var boundAttributes = descriptor[nameof(TagHelperDescriptor.BoundAttributes)].Value<JArray>();
var childTags = descriptor[nameof(TagHelperDescriptor.AllowedChildTags)].Value<JArray>();
var documentation = descriptor[nameof(TagHelperDescriptor.Documentation)].Value<string>();
var tagOutputHint = descriptor[nameof(TagHelperDescriptor.TagOutputHint)].Value<string>();
var caseSensitive = descriptor[nameof(TagHelperDescriptor.CaseSensitive)].Value<bool>();
var diagnostics = descriptor[nameof(TagHelperDescriptor.Diagnostics)].Value<JArray>();
var metadata = descriptor[nameof(TagHelperDescriptor.Metadata)].Value<JObject>();
var builder = TagHelperDescriptorBuilder.Create(descriptorKind, typeName, assemblyName);
builder.Documentation = documentation;
builder.TagOutputHint = tagOutputHint;
builder.CaseSensitive = caseSensitive;
foreach (var tagMatchingRule in tagMatchingRules)
{
var rule = tagMatchingRule.Value<JObject>();
builder.TagMatchingRule(b => ReadTagMatchingRule(b, rule, serializer));
}
foreach (var boundAttribute in boundAttributes)
{
var attribute = boundAttribute.Value<JObject>();
builder.BindAttribute(b => ReadBoundAttribute(b, attribute, serializer));
}
foreach (var childTag in childTags)
{
var tag = childTag.Value<JObject>();
builder.AllowChildTag(childTagBuilder => ReadAllowedChildTag(childTagBuilder, tag, serializer));
}
foreach (var diagnostic in diagnostics)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
}
var metadataReader = metadata.CreateReader();
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
foreach (var item in metadataValue)
{
builder.Metadata[item.Key] = item.Value;
}
return builder.Build();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var tagHelper = (TagHelperDescriptor)value;
writer.WriteStartObject();
writer.WritePropertyName(nameof(TagHelperDescriptor.Kind));
writer.WriteValue(tagHelper.Kind);
writer.WritePropertyName(nameof(TagHelperDescriptor.Name));
writer.WriteValue(tagHelper.Name);
writer.WritePropertyName(nameof(TagHelperDescriptor.AssemblyName));
writer.WriteValue(tagHelper.AssemblyName);
writer.WritePropertyName(nameof(TagHelperDescriptor.Documentation));
writer.WriteValue(tagHelper.Documentation);
writer.WritePropertyName(nameof(TagHelperDescriptor.TagOutputHint));
writer.WriteValue(tagHelper.TagOutputHint);
writer.WritePropertyName(nameof(TagHelperDescriptor.CaseSensitive));
writer.WriteValue(tagHelper.CaseSensitive);
writer.WritePropertyName(nameof(TagHelperDescriptor.TagMatchingRules));
writer.WriteStartArray();
foreach (var ruleDescriptor in tagHelper.TagMatchingRules)
{
WriteTagMatchingRule(writer, ruleDescriptor, serializer);
}
writer.WriteEndArray();
writer.WritePropertyName(nameof(TagHelperDescriptor.BoundAttributes));
writer.WriteStartArray();
foreach (var boundAttribute in tagHelper.BoundAttributes)
{
WriteBoundAttribute(writer, boundAttribute, serializer);
}
writer.WriteEndArray();
writer.WritePropertyName(nameof(TagHelperDescriptor.AllowedChildTags));
writer.WriteStartArray();
foreach (var allowedChildTag in tagHelper.AllowedChildTags)
{
WriteAllowedChildTags(writer, allowedChildTag, serializer);
}
writer.WriteEndArray();
writer.WritePropertyName(nameof(TagHelperDescriptor.Diagnostics));
serializer.Serialize(writer, tagHelper.Diagnostics);
writer.WritePropertyName(nameof(TagHelperDescriptor.Metadata));
WriteMetadata(writer, tagHelper.Metadata);
writer.WriteEndObject();
}
private static void WriteAllowedChildTags(JsonWriter writer, AllowedChildTagDescriptor allowedChildTag, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName(nameof(AllowedChildTagDescriptor.Name));
writer.WriteValue(allowedChildTag.Name);
writer.WritePropertyName(nameof(AllowedChildTagDescriptor.DisplayName));
writer.WriteValue(allowedChildTag.DisplayName);
writer.WritePropertyName(nameof(AllowedChildTagDescriptor.Diagnostics));
serializer.Serialize(writer, allowedChildTag.Diagnostics);
writer.WriteEndObject();
}
private static void WriteBoundAttribute(JsonWriter writer, BoundAttributeDescriptor boundAttribute, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Kind));
writer.WriteValue(boundAttribute.Kind);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Name));
writer.WriteValue(boundAttribute.Name);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.TypeName));
writer.WriteValue(boundAttribute.TypeName);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IsEnum));
writer.WriteValue(boundAttribute.IsEnum);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerNamePrefix));
writer.WriteValue(boundAttribute.IndexerNamePrefix);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerTypeName));
writer.WriteValue(boundAttribute.IndexerTypeName);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Documentation));
writer.WriteValue(boundAttribute.Documentation);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Diagnostics));
serializer.Serialize(writer, boundAttribute.Diagnostics);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Metadata));
WriteMetadata(writer, boundAttribute.Metadata);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.BoundAttributeParameters));
writer.WriteStartArray();
foreach (var boundAttributeParameter in boundAttribute.BoundAttributeParameters)
{
WriteBoundAttributeParameter(writer, boundAttributeParameter, serializer);
}
writer.WriteEndArray();
writer.WriteEndObject();
}
private static void WriteBoundAttributeParameter(JsonWriter writer, BoundAttributeParameterDescriptor boundAttributeParameter, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Kind));
writer.WriteValue(boundAttributeParameter.Kind);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Name));
writer.WriteValue(boundAttributeParameter.Name);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.TypeName));
writer.WriteValue(boundAttributeParameter.TypeName);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.IsEnum));
writer.WriteValue(boundAttributeParameter.IsEnum);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Documentation));
writer.WriteValue(boundAttributeParameter.Documentation);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Diagnostics));
serializer.Serialize(writer, boundAttributeParameter.Diagnostics);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Metadata));
WriteMetadata(writer, boundAttributeParameter.Metadata);
writer.WriteEndObject();
}
private static void WriteMetadata(JsonWriter writer, IReadOnlyDictionary<string, string> metadata)
{
writer.WriteStartObject();
foreach (var kvp in metadata)
{
writer.WritePropertyName(kvp.Key);
writer.WriteValue(kvp.Value);
}
writer.WriteEndObject();
}
private static void WriteTagMatchingRule(JsonWriter writer, TagMatchingRuleDescriptor ruleDescriptor, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagName));
writer.WriteValue(ruleDescriptor.TagName);
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.ParentTag));
writer.WriteValue(ruleDescriptor.ParentTag);
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagStructure));
writer.WriteValue(ruleDescriptor.TagStructure);
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Attributes));
writer.WriteStartArray();
foreach (var requiredAttribute in ruleDescriptor.Attributes)
{
WriteRequiredAttribute(writer, requiredAttribute, serializer);
}
writer.WriteEndArray();
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Diagnostics));
serializer.Serialize(writer, ruleDescriptor.Diagnostics);
writer.WriteEndObject();
}
private static void WriteRequiredAttribute(JsonWriter writer, RequiredAttributeDescriptor requiredAttribute, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Name));
writer.WriteValue(requiredAttribute.Name);
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.NameComparison));
writer.WriteValue(requiredAttribute.NameComparison);
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Value));
writer.WriteValue(requiredAttribute.Value);
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.ValueComparison));
writer.WriteValue(requiredAttribute.ValueComparison);
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Diagnostics));
serializer.Serialize(writer, requiredAttribute.Diagnostics);
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Metadata));
WriteMetadata(writer, requiredAttribute.Metadata);
writer.WriteEndObject();
}
private static void ReadTagMatchingRule(TagMatchingRuleDescriptorBuilder builder, JObject rule, JsonSerializer serializer)
{
var tagName = rule[nameof(TagMatchingRuleDescriptor.TagName)].Value<string>();
var attributes = rule[nameof(TagMatchingRuleDescriptor.Attributes)].Value<JArray>();
var parentTag = rule[nameof(TagMatchingRuleDescriptor.ParentTag)].Value<string>();
var tagStructure = rule[nameof(TagMatchingRuleDescriptor.TagStructure)].Value<int>();
var diagnostics = rule[nameof(TagMatchingRuleDescriptor.Diagnostics)].Value<JArray>();
builder.TagName = tagName;
builder.ParentTag = parentTag;
builder.TagStructure = (TagStructure)tagStructure;
foreach (var attribute in attributes)
{
var attibuteValue = attribute.Value<JObject>();
builder.Attribute(b => ReadRequiredAttribute(b, attibuteValue, serializer));
}
foreach (var diagnostic in diagnostics)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
}
}
private static void ReadRequiredAttribute(RequiredAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer)
{
var name = attribute[nameof(RequiredAttributeDescriptor.Name)].Value<string>();
var nameComparison = attribute[nameof(RequiredAttributeDescriptor.NameComparison)].Value<int>();
var value = attribute[nameof(RequiredAttributeDescriptor.Value)].Value<string>();
var valueComparison = attribute[nameof(RequiredAttributeDescriptor.ValueComparison)].Value<int>();
var diagnostics = attribute[nameof(RequiredAttributeDescriptor.Diagnostics)].Value<JArray>();
var metadata = attribute[nameof(RequiredAttributeDescriptor.Metadata)].Value<JObject>();
builder.Name = name;
builder.NameComparisonMode = (RequiredAttributeDescriptor.NameComparisonMode)nameComparison;
builder.Value = value;
builder.ValueComparisonMode = (RequiredAttributeDescriptor.ValueComparisonMode)valueComparison;
foreach (var diagnostic in diagnostics)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
}
var metadataReader = metadata.CreateReader();
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
foreach (var item in metadataValue)
{
builder.Metadata[item.Key] = item.Value;
}
}
private static void ReadAllowedChildTag(AllowedChildTagDescriptorBuilder builder, JObject childTag, JsonSerializer serializer)
{
var name = childTag[nameof(AllowedChildTagDescriptor.Name)].Value<string>();
var displayName = childTag[nameof(AllowedChildTagDescriptor.DisplayName)].Value<string>();
var diagnostics = childTag[nameof(AllowedChildTagDescriptor.Diagnostics)].Value<JArray>();
builder.Name = name;
builder.DisplayName = displayName;
foreach (var diagnostic in diagnostics)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
}
}
private static void ReadBoundAttribute(BoundAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer)
{
var descriptorKind = attribute[nameof(BoundAttributeDescriptor.Kind)].Value<string>();
var name = attribute[nameof(BoundAttributeDescriptor.Name)].Value<string>();
var typeName = attribute[nameof(BoundAttributeDescriptor.TypeName)].Value<string>();
var isEnum = attribute[nameof(BoundAttributeDescriptor.IsEnum)].Value<bool>();
var indexerNamePrefix = attribute[nameof(BoundAttributeDescriptor.IndexerNamePrefix)].Value<string>();
var indexerTypeName = attribute[nameof(BoundAttributeDescriptor.IndexerTypeName)].Value<string>();
var documentation = attribute[nameof(BoundAttributeDescriptor.Documentation)].Value<string>();
var diagnostics = attribute[nameof(BoundAttributeDescriptor.Diagnostics)].Value<JArray>();
var metadata = attribute[nameof(BoundAttributeDescriptor.Metadata)].Value<JObject>();
var boundAttributeParameters = attribute[nameof(BoundAttributeDescriptor.BoundAttributeParameters)].Value<JArray>();
builder.Name = name;
builder.TypeName = typeName;
builder.Documentation = documentation;
if (indexerNamePrefix != null)
{
builder.AsDictionary(indexerNamePrefix, indexerTypeName);
}
if (isEnum)
{
builder.IsEnum = true;
}
foreach (var diagnostic in diagnostics)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
}
var metadataReader = metadata.CreateReader();
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
foreach (var item in metadataValue)
{
builder.Metadata[item.Key] = item.Value;
}
foreach (var boundAttributeParameter in boundAttributeParameters)
{
var parameter = boundAttributeParameter.Value<JObject>();
builder.BindAttributeParameter(b => ReadBoundAttributeParameter(b, parameter, serializer));
}
}
private static void ReadBoundAttributeParameter(BoundAttributeParameterDescriptorBuilder builder, JObject parameter, JsonSerializer serializer)
{
var descriptorKind = parameter[nameof(BoundAttributeParameterDescriptor.Kind)].Value<string>();
var name = parameter[nameof(BoundAttributeParameterDescriptor.Name)].Value<string>();
var typeName = parameter[nameof(BoundAttributeParameterDescriptor.TypeName)].Value<string>();
var isEnum = parameter[nameof(BoundAttributeParameterDescriptor.IsEnum)].Value<bool>();
var documentation = parameter[nameof(BoundAttributeParameterDescriptor.Documentation)].Value<string>();
var diagnostics = parameter[nameof(BoundAttributeParameterDescriptor.Diagnostics)].Value<JArray>();
var metadata = parameter[nameof(BoundAttributeParameterDescriptor.Metadata)].Value<JObject>();
builder.Name = name;
builder.TypeName = typeName;
builder.Documentation = documentation;
if (isEnum)
{
builder.IsEnum = true;
}
foreach (var diagnostic in diagnostics)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
}
var metadataReader = metadata.CreateReader();
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
foreach (var item in metadataValue)
{
builder.Metadata[item.Key] = item.Value;
}
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class UnsupportedCSharpLoweringPhase : RazorEnginePhaseBase, IRazorCSharpLoweringPhase
{
internal const string UnsupportedDisclaimer = "// Razor CSharp output is not supported for this project's version of Razor.";
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var documentNode = codeDocument.GetDocumentIntermediateNode();
ThrowForMissingDocumentDependency(documentNode);
var cSharpDocument = RazorCSharpDocument.Create(
UnsupportedDisclaimer,
documentNode.Options,
Enumerable.Empty<RazorDiagnostic>());
codeDocument.SetCSharpDocument(cSharpDocument);
codeDocument.SetUnsupported();
}
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// 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.AspNetCore.Razor.Language;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
public static class UnsupportedRazorConfiguration
{
public static readonly RazorConfiguration Instance = RazorConfiguration.Create(
RazorLanguageVersion.Version_1_0,
"UnsupportedRazor",
new[] { new UnsupportedRazorExtension("UnsupportedRazorExtension"), });
private class UnsupportedRazorExtension : RazorExtension
{
public UnsupportedRazorExtension(string extensionName)
{
if (extensionName == null)
{
throw new ArgumentNullException(nameof(extensionName));
}
ExtensionName = extensionName;
}
public override string ExtensionName { get; }
}
}
}

Просмотреть файл

@ -0,0 +1,75 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
{
internal class VSCodeForegroundDispatcher : ForegroundDispatcher
{
public override bool IsForegroundThread => Thread.CurrentThread.ManagedThreadId == ForegroundTaskScheduler.Instance.ForegroundThreadId;
public override TaskScheduler ForegroundScheduler { get; } = ForegroundTaskScheduler.Instance;
public override TaskScheduler BackgroundScheduler { get; } = TaskScheduler.Default;
internal class ForegroundTaskScheduler : TaskScheduler
{
public static ForegroundTaskScheduler Instance = new ForegroundTaskScheduler();
private readonly Thread _thread;
private readonly BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
private ForegroundTaskScheduler()
{
_thread = new Thread(ThreadStart)
{
IsBackground = true,
};
_thread.Start();
}
public int ForegroundThreadId => _thread.ManagedThreadId;
public override int MaximumConcurrencyLevel => 1;
protected override void QueueTask(Task task) => _tasks.Add(task);
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If the task was previously queued it means that we're ensuring it's running on our single threaded scheduler.
// Otherwise, we can't enforce that behavior and therefore need it to be re-queued before execution.
if (taskWasPreviouslyQueued)
{
return TryExecuteTask(task);
}
return false;
}
protected override IEnumerable<Task> GetScheduledTasks() => _tasks.ToArray();
private void ThreadStart()
{
while (true)
{
try
{
var task = _tasks.Take();
TryExecuteTask(task);
}
catch (ThreadAbortException)
{
// Fires when things shut down or in tests. Swallow thread abort exceptions and bail out.
return;
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,439 @@
// 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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class BackgroundDocumentGenerator : ProjectSnapshotChangeTrigger
{
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly DocumentVersionCache _documentVersionCache;
private readonly IEnumerable<DocumentProcessedListener> _documentProcessedListeners;
private readonly ILanguageServer _router;
private readonly ILogger _logger;
private readonly Dictionary<string, DocumentSnapshot> _work;
private ProjectSnapshotManagerBase _projectManager;
private Timer _timer;
public BackgroundDocumentGenerator(
ForegroundDispatcher foregroundDispatcher,
DocumentVersionCache documentVersionCache,
IEnumerable<DocumentProcessedListener> documentProcessedListeners,
ILanguageServer router,
ILoggerFactory loggerFactory)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (documentVersionCache == null)
{
throw new ArgumentNullException(nameof(documentVersionCache));
}
if (documentProcessedListeners == null)
{
throw new ArgumentNullException(nameof(documentProcessedListeners));
}
if (router == null)
{
throw new ArgumentNullException(nameof(router));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_foregroundDispatcher = foregroundDispatcher;
_documentVersionCache = documentVersionCache;
_documentProcessedListeners = documentProcessedListeners;
_router = router;
_logger = loggerFactory.CreateLogger<BackgroundDocumentGenerator>();
_work = new Dictionary<string, DocumentSnapshot>(StringComparer.Ordinal);
}
// For testing only
protected BackgroundDocumentGenerator(
ForegroundDispatcher foregroundDispatcher,
ILoggerFactory loggerFactory)
{
_foregroundDispatcher = foregroundDispatcher;
_logger = loggerFactory.CreateLogger<BackgroundDocumentGenerator>();
_work = new Dictionary<string, DocumentSnapshot>(StringComparer.Ordinal);
_documentProcessedListeners = Enumerable.Empty<DocumentProcessedListener>();
}
public bool HasPendingNotifications
{
get
{
lock (_work)
{
return _work.Count > 0;
}
}
}
public TimeSpan Delay { get; set; } = TimeSpan.Zero;
public bool IsScheduledOrRunning => _timer != null;
// Used in tests to ensure we can control when background work starts.
public ManualResetEventSlim BlockBackgroundWorkStart { get; set; }
// Used in tests to ensure we can know when background work finishes.
public ManualResetEventSlim NotifyBackgroundWorkStarting { get; set; }
// Used in unit tests to ensure we can know when background has captured its current workload.
public ManualResetEventSlim NotifyBackgroundCapturedWorkload { get; set; }
// Used in tests to ensure we can control when background work completes.
public ManualResetEventSlim BlockBackgroundWorkCompleting { get; set; }
// Used in tests to ensure we can know when background work finishes.
public ManualResetEventSlim NotifyBackgroundWorkCompleted { get; set; }
public override void Initialize(ProjectSnapshotManagerBase projectManager)
{
if (projectManager == null)
{
throw new ArgumentNullException(nameof(projectManager));
}
_projectManager = projectManager;
_projectManager.Changed += ProjectSnapshotManager_Changed;
foreach (var documentProcessedListener in _documentProcessedListeners)
{
documentProcessedListener.Initialize(_projectManager);
}
}
private void OnStartingBackgroundWork()
{
if (BlockBackgroundWorkStart != null)
{
BlockBackgroundWorkStart.Wait();
BlockBackgroundWorkStart.Reset();
}
if (NotifyBackgroundWorkStarting != null)
{
NotifyBackgroundWorkStarting.Set();
}
}
private void OnCompletingBackgroundWork()
{
if (BlockBackgroundWorkCompleting != null)
{
BlockBackgroundWorkCompleting.Wait();
BlockBackgroundWorkCompleting.Reset();
}
}
private void OnCompletedBackgroundWork()
{
if (NotifyBackgroundWorkCompleted != null)
{
NotifyBackgroundWorkCompleted.Set();
}
}
private void OnBackgroundCapturedWorkload()
{
if (NotifyBackgroundCapturedWorkload != null)
{
NotifyBackgroundCapturedWorkload.Set();
}
}
// Internal for testing
internal void Enqueue(DocumentSnapshot document)
{
_foregroundDispatcher.AssertForegroundThread();
lock (_work)
{
// We only want to store the last 'seen' version of any given document. That way when we pick one to process
// it's always the best version to use.
_work[document.FilePath] = document;
StartWorker();
}
}
private void StartWorker()
{
// Access to the timer is protected by the lock in Synchronize and in Timer_Tick
if (_timer == null)
{
// Timer will fire after a fixed delay, but only once.
_timer = new Timer(Timer_Tick, null, Delay, Timeout.InfiniteTimeSpan);
}
}
private async void Timer_Tick(object state)
{
try
{
_foregroundDispatcher.AssertBackgroundThread();
OnStartingBackgroundWork();
KeyValuePair<string, DocumentSnapshot>[] work;
lock (_work)
{
work = _work.ToArray();
_work.Clear();
}
OnBackgroundCapturedWorkload();
for (var i = 0; i < work.Length; i++)
{
var document = work[i].Value;
try
{
await document.GetGeneratedOutputAsync();
}
catch (Exception ex)
{
ReportError(ex);
_logger.LogError("Error when processing document: " + document.FilePath);
}
}
OnCompletingBackgroundWork();
await Task.Factory.StartNew(
() =>
{
ReportUnsynchronizableContent(work);
NotifyDocumentsProcessed(work);
},
CancellationToken.None,
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler);
lock (_work)
{
// Resetting the timer allows another batch of work to start.
_timer.Dispose();
_timer = null;
// If more work came in while we were running start the worker again.
if (_work.Count > 0)
{
StartWorker();
}
}
OnCompletedBackgroundWork();
}
catch (Exception ex)
{
// This is something totally unexpected, let's just send it over to the workspace.
_logger.LogError("Unexpected error processing document: " + ex.Message);
ReportError(ex);
_timer?.Dispose();
_timer = null;
}
}
private void NotifyDocumentsProcessed(KeyValuePair<string, DocumentSnapshot>[] work)
{
for (var i = 0; i < work.Length; i++)
{
foreach (var documentProcessedTrigger in _documentProcessedListeners)
{
documentProcessedTrigger.DocumentProcessed(work[i].Value);
}
}
}
private void ProjectSnapshotManager_Changed(object sender, ProjectChangeEventArgs args)
{
_foregroundDispatcher.AssertForegroundThread();
switch (args.Kind)
{
case ProjectChangeKind.ProjectAdded:
{
var projectSnapshot = args.Newer;
foreach (var documentFilePath in projectSnapshot.DocumentFilePaths)
{
if (_projectManager.IsDocumentOpen(documentFilePath))
{
var document = projectSnapshot.GetDocument(documentFilePath);
Enqueue(document);
}
}
break;
}
case ProjectChangeKind.ProjectChanged:
{
var projectSnapshot = args.Newer;
foreach (var documentFilePath in projectSnapshot.DocumentFilePaths)
{
if (_projectManager.IsDocumentOpen(documentFilePath))
{
var document = projectSnapshot.GetDocument(documentFilePath);
Enqueue(document);
}
}
break;
}
case ProjectChangeKind.DocumentAdded:
{
var projectSnapshot = args.Newer;
var document = projectSnapshot.GetDocument(args.DocumentFilePath);
if (_projectManager.IsDocumentOpen(args.DocumentFilePath))
{
Enqueue(document);
}
foreach (var relatedDocument in projectSnapshot.GetRelatedDocuments(document))
{
if (_projectManager.IsDocumentOpen(relatedDocument.FilePath))
{
Enqueue(relatedDocument);
}
}
break;
}
case ProjectChangeKind.DocumentChanged:
{
var projectSnapshot = args.Newer;
var document = projectSnapshot.GetDocument(args.DocumentFilePath);
if (_projectManager.IsDocumentOpen(args.DocumentFilePath))
{
Enqueue(document);
}
foreach (var relatedDocument in projectSnapshot.GetRelatedDocuments(document))
{
if (_projectManager.IsDocumentOpen(relatedDocument.FilePath))
{
Enqueue(relatedDocument);
}
}
break;
}
case ProjectChangeKind.DocumentRemoved:
{
var olderProject = args.Older;
var document = olderProject.GetDocument(args.DocumentFilePath);
foreach (var relatedDocument in olderProject.GetRelatedDocuments(document))
{
var newerRelatedDocument = args.Newer.GetDocument(relatedDocument.FilePath);
if (_projectManager.IsDocumentOpen(newerRelatedDocument.FilePath))
{
Enqueue(newerRelatedDocument);
}
}
break;
}
case ProjectChangeKind.ProjectRemoved:
{
// ignore
break;
}
default:
throw new InvalidOperationException($"Unknown ProjectChangeKind {args.Kind}");
}
}
// Internal virtual for testing
internal virtual void ReportUnsynchronizableContent(KeyValuePair<string, DocumentSnapshot>[] work)
{
_foregroundDispatcher.AssertForegroundThread();
// This method deals with reporting unsynchronized content. At this point we've force evaluation of each document
// in the work queue; however, some documents may be identical versions of the last synchronized document if
// one's content does not differ. In this case, the output of the two generated documents is the same but we still
// need to let the client know that we've processed its latest text change. This allows the client to understand
// when it's operating on out-of-date output.
for (var i = 0; i < work.Length; i++)
{
var document = work[i].Value;
if (!(document is DefaultDocumentSnapshot defaultDocument))
{
continue;
}
if (!_documentVersionCache.TryGetDocumentVersion(document, out var syncVersion))
{
// Document is no longer important.
continue;
}
var latestSynchronizedDocument = defaultDocument.State.HostDocument.GeneratedCodeContainer.LatestDocument;
if (latestSynchronizedDocument == null ||
latestSynchronizedDocument == document)
{
// Already up-to-date
continue;
}
if (IdenticalOutputAfterParse(document, latestSynchronizedDocument, syncVersion))
{
// Documents are identical but we didn't synchronize them because they didn't need to be re-evaluated.
var request = new UpdateCSharpBufferRequest()
{
HostDocumentFilePath = document.FilePath,
Changes = Array.Empty<TextChange>(),
HostDocumentVersion = syncVersion
};
_router.Client.SendRequest("updateCSharpBuffer", request);
}
}
}
private bool IdenticalOutputAfterParse(DocumentSnapshot document, DocumentSnapshot latestSynchronizedDocument, long syncVersion)
{
return latestSynchronizedDocument.TryGetTextVersion(out var latestSourceVersion) &&
document.TryGetTextVersion(out var documentSourceVersion) &&
_documentVersionCache.TryGetDocumentVersion(latestSynchronizedDocument, out var lastSynchronizedVersion) &&
syncVersion > lastSynchronizedVersion &&
latestSourceVersion == documentSourceVersion;
}
private void ReportError(Exception ex)
{
GC.KeepAlive(Task.Factory.StartNew(
() => _projectManager.ReportError(ex),
CancellationToken.None,
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler));
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// 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.LanguageServer.Completion
{
internal class AttributeDescriptionInfo
{
public static readonly AttributeDescriptionInfo Default = new AttributeDescriptionInfo(Array.Empty<TagHelperAttributeDescriptionInfo>());
public AttributeDescriptionInfo(IReadOnlyList<TagHelperAttributeDescriptionInfo> associatedAttributeDescriptions)
{
if (associatedAttributeDescriptions == null)
{
throw new ArgumentNullException(nameof(associatedAttributeDescriptions));
}
AssociatedAttributeDescriptions = associatedAttributeDescriptions;
}
public IReadOnlyList<TagHelperAttributeDescriptionInfo> AssociatedAttributeDescriptions { get; }
}
}

Просмотреть файл

@ -0,0 +1,136 @@
// 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.CodeAnalysis.Razor.Completion;
using Newtonsoft.Json.Linq;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion
{
internal static class CompletionItemExtensions
{
private const string TagHelperElementDataKey = "_TagHelperElementData_";
private const string TagHelperAttributeDataKey = "_TagHelperAttributes_";
private const string AttributeCompletionDataKey = "_AttributeCompletion_";
private const string RazorCompletionItemKind = "_CompletionItemKind_";
public static void SetRazorCompletionKind(this CompletionItem completion, RazorCompletionItemKind completionItemKind)
{
if (completion is null)
{
throw new ArgumentNullException(nameof(completion));
}
var data = completion.Data ?? new JObject();
data[RazorCompletionItemKind] = JToken.FromObject(completionItemKind);
completion.Data = data;
}
public static bool TryGetRazorCompletionKind(this CompletionItem completion, out RazorCompletionItemKind completionItemKind)
{
if (completion is null)
{
throw new ArgumentNullException(nameof(completion));
}
if (completion.Data is JObject data && data.ContainsKey(RazorCompletionItemKind))
{
completionItemKind = data[RazorCompletionItemKind].ToObject<RazorCompletionItemKind>();
return true;
}
completionItemKind = default;
return false;
}
public static bool IsTagHelperElementCompletion(this CompletionItem completion)
{
if (completion.Data is JObject data && data.ContainsKey(TagHelperElementDataKey))
{
return true;
}
return false;
}
public static bool IsTagHelperAttributeCompletion(this CompletionItem completion)
{
if (completion.Data is JObject data && data.ContainsKey(TagHelperAttributeDataKey))
{
return true;
}
return false;
}
public static void SetDescriptionInfo(this CompletionItem completion, ElementDescriptionInfo elementDescriptionInfo)
{
var data = completion.Data ?? new JObject();
data[TagHelperElementDataKey] = JObject.FromObject(elementDescriptionInfo);
completion.Data = data;
}
public static void SetDescriptionInfo(this CompletionItem completion, AttributeDescriptionInfo attributeDescriptionInfo)
{
var data = completion.Data ?? new JObject();
data[TagHelperAttributeDataKey] = JObject.FromObject(attributeDescriptionInfo);
completion.Data = data;
}
public static void SetDescriptionInfo(this CompletionItem completion, AttributeCompletionDescription attributeDescriptionInfo)
{
if (completion is null)
{
throw new ArgumentNullException(nameof(completion));
}
if (attributeDescriptionInfo is null)
{
throw new ArgumentNullException(nameof(attributeDescriptionInfo));
}
var data = completion.Data ?? new JObject();
data[AttributeCompletionDataKey] = JObject.FromObject(attributeDescriptionInfo);
completion.Data = data;
}
public static ElementDescriptionInfo GetElementDescriptionInfo(this CompletionItem completion)
{
if (completion.Data is JObject data && data.ContainsKey(TagHelperElementDataKey))
{
var descriptionInfo = data[TagHelperElementDataKey].ToObject<ElementDescriptionInfo>();
return descriptionInfo;
}
return ElementDescriptionInfo.Default;
}
public static AttributeDescriptionInfo GetTagHelperAttributeDescriptionInfo(this CompletionItem completion)
{
if (completion.Data is JObject data && data.ContainsKey(TagHelperAttributeDataKey))
{
var descriptionInfo = data[TagHelperAttributeDataKey].ToObject<AttributeDescriptionInfo>();
return descriptionInfo;
}
return AttributeDescriptionInfo.Default;
}
public static AttributeCompletionDescription GetAttributeDescriptionInfo(this CompletionItem completion)
{
if (completion is null)
{
throw new ArgumentNullException(nameof(completion));
}
if (completion.Data is JObject data && data.ContainsKey(AttributeCompletionDataKey))
{
var descriptionInfo = data[AttributeCompletionDataKey].ToObject<AttributeCompletionDescription>();
return descriptionInfo;
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,449 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.VisualStudio.Editor.Razor;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using RazorTagHelperCompletionService = Microsoft.VisualStudio.Editor.Razor.TagHelperCompletionService;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion
{
internal class DefaultTagHelperCompletionService : TagHelperCompletionService
{
private static readonly Container<string> AttributeCommitCharacters = new Container<string>(" ");
private static readonly Container<string> ElementCommitCharacters = new Container<string>(" ", ">");
private static readonly HashSet<string> HtmlSchemaTagNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"DOCTYPE",
"a",
"abbr",
"acronym",
"address",
"applet",
"area",
"article",
"aside",
"audio",
"b",
"base",
"basefont",
"bdi",
"bdo",
"big",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"center",
"cite",
"code",
"col",
"colgroup",
"data",
"datalist",
"dd",
"del",
"details",
"dfn",
"dialog",
"dir",
"div",
"dl",
"dt",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"font",
"footer",
"form",
"frame",
"frameset",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hr",
"html",
"i",
"iframe",
"img",
"input",
"ins",
"kbd",
"label",
"legend",
"li",
"link",
"main",
"map",
"mark",
"meta",
"meter",
"nav",
"noframes",
"noscript",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"pre",
"progress",
"q",
"rp",
"rt",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"small",
"source",
"span",
"strike",
"strong",
"style",
"sub",
"summary",
"sup",
"svg",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"tt",
"u",
"ul",
"var",
"video",
"wbr",
};
private readonly RazorTagHelperCompletionService _razorTagHelperCompletionService;
public DefaultTagHelperCompletionService(RazorTagHelperCompletionService razorCompletionService)
{
if (razorCompletionService == null)
{
throw new ArgumentNullException(nameof(razorCompletionService));
}
_razorTagHelperCompletionService = razorCompletionService;
}
public override IReadOnlyList<CompletionItem> GetCompletionsAt(SourceSpan location, RazorCodeDocument codeDocument)
{
if (codeDocument == null)
{
throw new ArgumentNullException(nameof(codeDocument));
}
var syntaxTree = codeDocument.GetSyntaxTree();
var change = new SourceChange(location, "");
var owner = syntaxTree.Root.LocateOwner(change);
if (owner == null)
{
Debug.Fail("Owner should never be null.");
return Array.Empty<CompletionItem>();
}
var parent = owner.Parent;
if (TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) &&
containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex))
{
var stringifiedAttributes = StringifyAttributes(attributes);
var elementCompletions = GetElementCompletions(parent, containingTagNameToken.Content, stringifiedAttributes, codeDocument);
return elementCompletions;
}
if (TryGetAttributeInfo(parent, out containingTagNameToken, out var selectedAttributeName, out attributes) &&
attributes.Span.IntersectsWith(location.AbsoluteIndex))
{
var stringifiedAttributes = StringifyAttributes(attributes);
var attributeCompletions = GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, codeDocument);
return attributeCompletions;
}
// Invalid location for TagHelper completions.
return Array.Empty<CompletionItem>();
}
private static bool TryGetAttributeInfo(SyntaxNode attribute, out SyntaxToken containingTagNameToken, out string selectedAttributeName, out SyntaxList<RazorSyntaxNode> attributeNodes)
{
if ((attribute is MarkupMiscAttributeContentSyntax ||
attribute is MarkupMinimizedAttributeBlockSyntax ||
attribute is MarkupAttributeBlockSyntax ||
attribute is MarkupTagHelperAttributeSyntax ||
attribute is MarkupMinimizedTagHelperAttributeSyntax ||
attribute is MarkupTagHelperDirectiveAttributeSyntax ||
attribute is MarkupMinimizedTagHelperDirectiveAttributeSyntax) &&
TryGetElementInfo(attribute.Parent, out containingTagNameToken, out attributeNodes))
{
selectedAttributeName = null;
return true;
}
containingTagNameToken = null;
selectedAttributeName = null;
attributeNodes = default;
return false;
}
private IReadOnlyList<CompletionItem> GetAttributeCompletions(
SyntaxNode containingAttribute,
string containingTagName,
string selectedAttributeName,
IEnumerable<KeyValuePair<string, string>> attributes,
RazorCodeDocument codeDocument)
{
var ancestors = containingAttribute.Parent.Ancestors();
var tagHelperDocumentContext = codeDocument.GetTagHelperContext();
var nonDirectiveAttributeTagHelpers = tagHelperDocumentContext.TagHelpers.Where(tagHelper => !tagHelper.BoundAttributes.Any(attribute => attribute.IsDirectiveAttribute()));
var filteredContext = TagHelperDocumentContext.Create(tagHelperDocumentContext.Prefix, nonDirectiveAttributeTagHelpers);
var (ancestorTagName, ancestorIsTagHelper) = GetNearestAncestorTagInfo(ancestors);
var attributeCompletionContext = new AttributeCompletionContext(
filteredContext,
existingCompletions: Enumerable.Empty<string>(),
containingTagName,
selectedAttributeName,
attributes,
ancestorTagName,
ancestorIsTagHelper,
HtmlSchemaTagNames.Contains);
var completionItems = new List<CompletionItem>();
var completionResult = _razorTagHelperCompletionService.GetAttributeCompletions(attributeCompletionContext);
foreach (var completion in completionResult.Completions)
{
var filterText = completion.Key;
// This is a little bit of a hack because the information returned by _razorTagHelperCompletionService.GetAttributeCompletions
// does not have enough information for us to determine if a completion is an indexer completion or not. Therefore we have to
// jump through a few hoops below to:
// 1. Determine if this specific completion is an indexer based completion
// 2. Resolve an appropriate snippet if it is. This is more troublesome because we need to remove the ... suffix to accurately
// build a snippet that makes sense for the user to type.
var indexerCompletion = filterText.EndsWith("...");
if (indexerCompletion)
{
filterText = filterText.Substring(0, filterText.Length - 3);
}
var insertTextFormat = InsertTextFormat.Snippet;
if (!TryResolveAttributeInsertionSnippet(filterText, completion.Value, indexerCompletion, out var insertText))
{
insertTextFormat = InsertTextFormat.PlainText;
insertText = filterText;
}
var razorCompletionItem = new CompletionItem()
{
Label = completion.Key,
InsertText = insertText,
InsertTextFormat = insertTextFormat,
FilterText = filterText,
SortText = filterText,
Kind = CompletionItemKind.TypeParameter,
CommitCharacters = AttributeCommitCharacters,
};
var attributeDescriptions = completion.Value.Select(boundAttribute => new TagHelperAttributeDescriptionInfo(
boundAttribute.DisplayName,
boundAttribute.GetPropertyName(),
indexerCompletion ? boundAttribute.IndexerTypeName : boundAttribute.TypeName,
boundAttribute.Documentation));
var attributeDescriptionInfo = new AttributeDescriptionInfo(attributeDescriptions.ToList());
razorCompletionItem.SetDescriptionInfo(attributeDescriptionInfo);
completionItems.Add(razorCompletionItem);
}
return completionItems;
}
private IReadOnlyList<CompletionItem> GetElementCompletions(
SyntaxNode containingTag,
string containingTagName,
IEnumerable<KeyValuePair<string, string>> attributes,
RazorCodeDocument codeDocument)
{
var ancestors = containingTag.Ancestors();
var tagHelperDocumentContext = codeDocument.GetTagHelperContext();
var (ancestorTagName, ancestorIsTagHelper) = GetNearestAncestorTagInfo(ancestors);
var elementCompletionContext = new ElementCompletionContext(
tagHelperDocumentContext,
existingCompletions: Enumerable.Empty<string>(),
containingTagName,
attributes,
ancestorTagName,
ancestorIsTagHelper,
HtmlSchemaTagNames.Contains);
var completionItems = new List<CompletionItem>();
var completionResult = _razorTagHelperCompletionService.GetElementCompletions(elementCompletionContext);
foreach (var completion in completionResult.Completions)
{
var razorCompletionItem = new CompletionItem()
{
Label = completion.Key,
InsertText = completion.Key,
FilterText = completion.Key,
SortText = completion.Key,
Kind = CompletionItemKind.TypeParameter,
CommitCharacters = ElementCommitCharacters,
};
var tagHelperDescriptions = completion.Value.Select(tagHelper => new TagHelperDescriptionInfo(tagHelper.GetTypeName(), tagHelper.Documentation));
var elementDescription = new ElementDescriptionInfo(tagHelperDescriptions.ToList());
razorCompletionItem.SetDescriptionInfo(elementDescription);
completionItems.Add(razorCompletionItem);
}
return completionItems;
}
// Internal for testing
internal static IEnumerable<KeyValuePair<string, string>> StringifyAttributes(SyntaxList<RazorSyntaxNode> attributes)
{
var stringifiedAttributes = new List<KeyValuePair<string, string>>();
for (var i = 0; i < attributes.Count; i++)
{
var attribute = attributes[i];
if (attribute is MarkupTagHelperAttributeSyntax tagHelperAttribute)
{
var name = tagHelperAttribute.Name.GetContent();
var value = tagHelperAttribute.Value?.GetContent() ?? string.Empty;
stringifiedAttributes.Add(new KeyValuePair<string, string>(name, value));
}
else if (attribute is MarkupMinimizedTagHelperAttributeSyntax minimizedTagHelperAttribute)
{
var name = minimizedTagHelperAttribute.Name.GetContent();
stringifiedAttributes.Add(new KeyValuePair<string, string>(name, string.Empty));
}
else if (attribute is MarkupAttributeBlockSyntax markupAttribute)
{
var name = markupAttribute.Name.GetContent();
var value = markupAttribute.Value?.GetContent() ?? string.Empty;
stringifiedAttributes.Add(new KeyValuePair<string, string>(name, value));
}
else if (attribute is MarkupMinimizedAttributeBlockSyntax minimizedMarkupAttribute)
{
var name = minimizedMarkupAttribute.Name.GetContent();
stringifiedAttributes.Add(new KeyValuePair<string, string>(name, string.Empty));
}
else if (attribute is MarkupTagHelperDirectiveAttributeSyntax directiveAttribute)
{
var name = directiveAttribute.FullName;
var value = directiveAttribute.Value?.GetContent() ?? string.Empty;
stringifiedAttributes.Add(new KeyValuePair<string, string>(name, value));
}
else if (attribute is MarkupMinimizedTagHelperDirectiveAttributeSyntax minimizedDirectiveAttribute)
{
var name = minimizedDirectiveAttribute.FullName;
stringifiedAttributes.Add(new KeyValuePair<string, string>(name, string.Empty));
}
}
return stringifiedAttributes;
}
// Internal for testing
internal static (string ancestorTagName, bool ancestorIsTagHelper) GetNearestAncestorTagInfo(IEnumerable<SyntaxNode> ancestors)
{
foreach (var ancestor in ancestors)
{
if (ancestor is MarkupElementSyntax element)
{
// It's possible for start tag to be null in malformed cases.
var name = element.StartTag?.Name?.Content ?? string.Empty;
return (name, ancestorIsTagHelper: false);
}
else if (ancestor is MarkupTagHelperElementSyntax tagHelperElement)
{
// It's possible for start tag to be null in malformed cases.
var name = tagHelperElement.StartTag?.Name?.Content ?? string.Empty;
return (name, ancestorIsTagHelper: true);
}
}
return (ancestorTagName: null, ancestorIsTagHelper: false);
}
private static bool TryGetElementInfo(SyntaxNode element, out SyntaxToken containingTagNameToken, out SyntaxList<RazorSyntaxNode> attributeNodes)
{
if (element is MarkupStartTagSyntax startTag)
{
containingTagNameToken = startTag.Name;
attributeNodes = startTag.Attributes;
return true;
}
if (element is MarkupTagHelperStartTagSyntax startTagHelper)
{
containingTagNameToken = startTagHelper.Name;
attributeNodes = startTagHelper.Attributes;
return true;
}
containingTagNameToken = null;
attributeNodes = default;
return false;
}
private bool TryResolveAttributeInsertionSnippet(
string text,
IEnumerable<BoundAttributeDescriptor> boundAttributes,
bool indexerCompletion,
out string snippetText)
{
const string BoolTypeName = "System.Boolean";
// Boolean returning bound attribute, auto-complete to just the attribute name.
if (indexerCompletion)
{
if (boundAttributes.All(boundAttribute => boundAttribute.IndexerTypeName == BoolTypeName))
{
snippetText = null;
return false;
}
snippetText = string.Concat(text, "$1=\"$2\"");
return true;
}
else if (boundAttributes.All(boundAttribute => boundAttribute.TypeName == BoolTypeName))
{
snippetText = null;
return false;
}
snippetText = string.Concat(text, "=\"$1\"");
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,397 @@
// 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;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.Razor.Completion;
using RazorAttributeDescriptionInfo = Microsoft.CodeAnalysis.Razor.Completion.AttributeDescriptionInfo;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion
{
internal class DefaultTagHelperDescriptionFactory : TagHelperDescriptionFactory
{
private static readonly Lazy<Regex> ExtractCrefRegex = new Lazy<Regex>(
() => new Regex("<(see|seealso)[\\s]+cref=\"([^\">]+)\"[^>]*>", RegexOptions.Compiled, TimeSpan.FromSeconds(1)));
private static readonly IReadOnlyDictionary<string, string> PrimitiveDisplayTypeNameLookups = new Dictionary<string, string>(StringComparer.Ordinal)
{
[typeof(byte).FullName] = "byte",
[typeof(sbyte).FullName] = "sbyte",
[typeof(int).FullName] = "int",
[typeof(uint).FullName] = "uint",
[typeof(short).FullName] = "short",
[typeof(ushort).FullName] = "ushort",
[typeof(long).FullName] = "long",
[typeof(ulong).FullName] = "ulong",
[typeof(float).FullName] = "float",
[typeof(double).FullName] = "double",
[typeof(char).FullName] = "char",
[typeof(bool).FullName] = "bool",
[typeof(object).FullName] = "object",
[typeof(string).FullName] = "string",
[typeof(decimal).FullName] = "decimal",
};
public override bool TryCreateDescription(ElementDescriptionInfo elementDescriptionInfo, out string markdown)
{
var associatedTagHelperInfos = elementDescriptionInfo.AssociatedTagHelperDescriptions;
if (associatedTagHelperInfos.Count == 0)
{
markdown = null;
return false;
}
// This generates a markdown description that looks like the following:
// **SomeTagHelper**
//
// The Summary documentation text with `CrefTypeValues` in code.
//
// Additional description infos result in a triple `---` to separate the markdown entries.
var descriptionBuilder = new StringBuilder();
for (var i = 0; i < associatedTagHelperInfos.Count; i++)
{
var descriptionInfo = associatedTagHelperInfos[i];
if (descriptionBuilder.Length > 0)
{
descriptionBuilder.AppendLine();
descriptionBuilder.AppendLine("---");
}
descriptionBuilder.Append("**");
var tagHelperType = descriptionInfo.TagHelperTypeName;
var reducedTypeName = ReduceTypeName(tagHelperType);
descriptionBuilder.Append(reducedTypeName);
descriptionBuilder.AppendLine("**");
descriptionBuilder.AppendLine();
var documentation = descriptionInfo.Documentation;
if (!TryExtractSummary(documentation, out var summaryContent))
{
continue;
}
var finalSummaryContent = CleanSummaryContent(summaryContent);
descriptionBuilder.AppendLine(finalSummaryContent);
}
markdown = descriptionBuilder.ToString();
return true;
}
public override bool TryCreateDescription(AttributeCompletionDescription descriptionInfos, out string markdown)
{
var associatedAttributeInfos = descriptionInfos.DescriptionInfos;
if (associatedAttributeInfos.Count == 0)
{
markdown = null;
return false;
}
// This generates a markdown description that looks like the following:
// **ReturnTypeName** SomeTypeName.**SomeProperty**
//
// The Summary documentation text with `CrefTypeValues` in code.
//
// Additional description infos result in a triple `---` to separate the markdown entries.
var descriptionBuilder = new StringBuilder();
for (var i = 0; i < associatedAttributeInfos.Count; i++)
{
var descriptionInfo = associatedAttributeInfos[i];
if (descriptionBuilder.Length > 0)
{
descriptionBuilder.AppendLine();
descriptionBuilder.AppendLine("---");
}
descriptionBuilder.Append("**");
var returnTypeName = GetSimpleName(descriptionInfo.ReturnTypeName);
var reducedReturnTypeName = ReduceTypeName(returnTypeName);
descriptionBuilder.Append(reducedReturnTypeName);
descriptionBuilder.Append("** ");
var tagHelperTypeName = descriptionInfo.TypeName;
var reducedTagHelperTypeName = ReduceTypeName(tagHelperTypeName);
descriptionBuilder.Append(reducedTagHelperTypeName);
descriptionBuilder.Append(".**");
descriptionBuilder.Append(descriptionInfo.PropertyName);
descriptionBuilder.AppendLine("**");
descriptionBuilder.AppendLine();
var documentation = descriptionInfo.Documentation;
if (!TryExtractSummary(documentation, out var summaryContent))
{
continue;
}
var finalSummaryContent = CleanSummaryContent(summaryContent);
descriptionBuilder.AppendLine(finalSummaryContent);
}
markdown = descriptionBuilder.ToString();
return true;
}
public override bool TryCreateDescription(AttributeDescriptionInfo attributeDescriptionInfo, out string markdown)
{
var convertedDescriptionInfos = new List<RazorAttributeDescriptionInfo>();
foreach (var descriptionInfo in attributeDescriptionInfo.AssociatedAttributeDescriptions)
{
var tagHelperTypeName = ResolveTagHelperTypeName(descriptionInfo);
var converted = new RazorAttributeDescriptionInfo(
descriptionInfo.ReturnTypeName,
tagHelperTypeName,
descriptionInfo.PropertyName,
descriptionInfo.Documentation);
convertedDescriptionInfos.Add(converted);
}
var convertedDescriptionInfo = new AttributeCompletionDescription(convertedDescriptionInfos);
return TryCreateDescription(convertedDescriptionInfo, out markdown);
}
// Internal for testing
internal static string CleanSummaryContent(string summaryContent)
{
// Cleans out all <see cref="..." /> and <seealso cref="..." /> elements. It's possible to
// have additional doc comment types in the summary but none that require cleaning. For instance
// if there's a <para> in the summary element when it's shown in the completion description window
// it'll be serialized as html (wont show).
var crefMatches = ExtractCrefRegex.Value.Matches(summaryContent).Reverse();
var summaryBuilder = new StringBuilder(summaryContent);
foreach (var cref in crefMatches)
{
if (cref.Success)
{
var value = cref.Groups[2].Value;
var reducedValue = ReduceCrefValue(value);
reducedValue = reducedValue.Replace("{", "<").Replace("}", ">");
summaryBuilder.Remove(cref.Index, cref.Length);
summaryBuilder.Insert(cref.Index, $"`{reducedValue}`");
}
}
var lines = summaryBuilder.ToString().Split(new[] { '\n' }, StringSplitOptions.None).Select(line => line.Trim());
var finalSummaryContent = string.Join(Environment.NewLine, lines);
return finalSummaryContent;
}
// Internal for testing
internal static bool TryExtractSummary(string documentation, out string summary)
{
const string summaryStartTag = "<summary>";
const string summaryEndTag = "</summary>";
if (string.IsNullOrEmpty(documentation))
{
summary = null;
return false;
}
var summaryTagStart = documentation.IndexOf(summaryStartTag, StringComparison.OrdinalIgnoreCase);
if (summaryTagStart == -1)
{
summary = null;
return false;
}
var summaryTagEndStart = documentation.IndexOf(summaryEndTag, StringComparison.OrdinalIgnoreCase);
if (summaryTagEndStart == -1)
{
summary = null;
return false;
}
var summaryContentStart = summaryTagStart + summaryStartTag.Length;
var summaryContentLength = summaryTagEndStart - summaryContentStart;
summary = documentation.Substring(summaryContentStart, summaryContentLength);
return true;
}
// Internal for testing
internal static string ReduceCrefValue(string value)
{
// cref values come in the following formats:
// Type = "T:Microsoft.AspNetCore.SomeTagHelpers.SomeTypeName"
// Property = "P:T:Microsoft.AspNetCore.SomeTagHelpers.SomeTypeName.AspAction"
// Member = "M:T:Microsoft.AspNetCore.SomeTagHelpers.SomeTypeName.SomeMethod(System.Collections.Generic.List{System.String})"
if (value.Length < 2)
{
return string.Empty;
}
var type = value[0];
value = value.Substring(2);
switch (type)
{
case 'T':
var reducedCrefType = ReduceTypeName(value);
return reducedCrefType;
case 'P':
case 'M':
// TypeName.MemberName
var reducedCrefProperty = ReduceMemberName(value);
return reducedCrefProperty;
}
return value;
}
// Internal for testing
internal static string GetSimpleName(string typeName)
{
if (PrimitiveDisplayTypeNameLookups.TryGetValue(typeName, out var simpleName))
{
return simpleName;
}
return typeName;
}
// Internal for testing
internal static string ResolveTagHelperTypeName(TagHelperAttributeDescriptionInfo info)
{
// A BoundAttributeDescriptor does not have a direct reference to its parent TagHelper.
// However, when it was constructed the parent TagHelper's type name was embedded into
// its DisplayName. In VSCode we can't use the DisplayName verbatim for descriptions
// because the DisplayName is typically too long to display properly. Therefore we need
// to break it apart and then reconstruct it in a reduced format.
// i.e. this is the format the display name comes in:
// ReturnTypeName SomeTypeName.SomePropertyName
// We must simplify the return type name before using it to determine the type name prefix
// because that is how the display name was originally built (a little hacky).
var simpleReturnType = GetSimpleName(info.ReturnTypeName);
// "ReturnTypeName "
var typeNamePrefixLength = simpleReturnType.Length + 1 /* space */;
// ".SomePropertyName"
var typeNameSuffixLength = /* . */ 1 + info.PropertyName.Length;
// "SomeTypeName"
var typeNameLength = info.DisplayName.Length - typeNamePrefixLength - typeNameSuffixLength;
var tagHelperTypeName = info.DisplayName.Substring(typeNamePrefixLength, typeNameLength);
return tagHelperTypeName;
}
// Internal for testing
internal static string ReduceTypeName(string content) => ReduceFullName(content, reduceWhenDotCount: 1);
// Internal for testing
internal static string ReduceMemberName(string content) => ReduceFullName(content, reduceWhenDotCount: 2);
private static string ReduceFullName(string content, int reduceWhenDotCount)
{
// Starts searching backwards and then substrings everything when it finds enough dots. i.e.
// ReduceFullName("Microsoft.AspNetCore.SomeTagHelpers.SomeTypeName", 1) == "SomeTypeName"
//
// ReduceFullName("Microsoft.AspNetCore.SomeTagHelpers.SomeTypeName.AspAction", 2) == "SomeTypeName.AspAction"
//
// This is also smart enough to ignore nested dots in type generics[<>], methods[()], cref generics[{}].
if (reduceWhenDotCount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(reduceWhenDotCount));
}
var dotsSeen = 0;
var scope = 0;
for (var i = content.Length - 1; i >= 0; i--)
{
do
{
if (content[i] == '}')
{
scope++;
}
else if (content[i] == '{')
{
scope--;
}
if (scope > 0)
{
i--;
}
} while (scope != 0 && i >= 0);
if (i < 0)
{
// Could not balance scope
return content;
}
do
{
if (content[i] == ')')
{
scope++;
}
else if (content[i] == '(')
{
scope--;
}
if (scope > 0)
{
i--;
}
} while (scope != 0 && i >= 0);
if (i < 0)
{
// Could not balance scope
return content;
}
do
{
if (content[i] == '>')
{
scope++;
}
else if (content[i] == '<')
{
scope--;
}
if (scope > 0)
{
i--;
}
} while (scope != 0 && i >= 0);
if (i < 0)
{
// Could not balance scope
return content;
}
if (content[i] == '.')
{
dotsSeen++;
}
if (dotsSeen == reduceWhenDotCount)
{
var piece = content.Substring(i + 1);
return piece;
}
}
// Could not reduce name
return content;
}
}
}

Просмотреть файл

@ -0,0 +1,77 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.CodeAnalysis.Razor.Completion;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion
{
internal class DirectiveAttributeTransitionCompletionItemProvider : DirectiveAttributeCompletionItemProviderBase
{
private static RazorCompletionItem _transitionCompletionItem;
public static RazorCompletionItem TransitionCompletionItem
{
get
{
if (_transitionCompletionItem == null)
{
_transitionCompletionItem = new RazorCompletionItem("@...", "@", RazorCompletionItemKind.Directive);
_transitionCompletionItem.SetDirectiveCompletionDescription(new DirectiveCompletionDescription("Blazor directive attributes"));
}
return _transitionCompletionItem;
}
}
private static readonly IReadOnlyList<RazorCompletionItem> Completions = new[] { TransitionCompletionItem };
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree, TagHelperDocumentContext tagHelperDocumentContext, SourceSpan location)
{
if (!FileKinds.IsComponent(syntaxTree.Options.FileKind))
{
// Directive attributes are only supported in components
return Array.Empty<RazorCompletionItem>();
}
var change = new SourceChange(location, string.Empty);
var owner = syntaxTree.Root.LocateOwner(change);
if (owner == null)
{
return Array.Empty<RazorCompletionItem>();
}
var attribute = owner.Parent;
if (attribute is MarkupMiscAttributeContentSyntax)
{
// This represents a tag when there's no attribute content <InputText | />.
return Completions;
}
if (!TryGetAttributeInfo(attribute, out var name, out var nameLocation, out _, out _))
{
return Array.Empty<RazorCompletionItem>();
}
if (name.StartsWith("@"))
{
// The transition is already provided
return Array.Empty<RazorCompletionItem>();
}
if (!nameLocation.IntersectsWith(location.AbsoluteIndex))
{
// Not operating in the name section
return Array.Empty<RazorCompletionItem>();
}
// This represents a tag when there's no attribute content <InputText | />.
return Completions;
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// 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.LanguageServer.Completion
{
internal class ElementDescriptionInfo
{
public static readonly ElementDescriptionInfo Default = new ElementDescriptionInfo(Array.Empty<TagHelperDescriptionInfo>());
public ElementDescriptionInfo(IReadOnlyList<TagHelperDescriptionInfo> associatedTagHelperDescriptions)
{
if (associatedTagHelperDescriptions == null)
{
throw new ArgumentNullException(nameof(associatedTagHelperDescriptions));
}
AssociatedTagHelperDescriptions = associatedTagHelperDescriptions;
}
public IReadOnlyList<TagHelperDescriptionInfo> AssociatedTagHelperDescriptions { get; }
}
}

Просмотреть файл

@ -0,0 +1,334 @@
// 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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion
{
internal class RazorCompletionEndpoint : ICompletionHandler, ICompletionResolveHandler
{
private static readonly Container<string> DirectiveAttributeCommitCharacters = new Container<string>(" ");
private CompletionCapability _capability;
private readonly ILogger _logger;
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly DocumentResolver _documentResolver;
private readonly RazorCompletionFactsService _completionFactsService;
private readonly TagHelperCompletionService _tagHelperCompletionService;
private readonly TagHelperDescriptionFactory _tagHelperDescriptionFactory;
private static readonly Command RetriggerCompletionCommand = new Command()
{
Name = "editor.action.triggerSuggest",
Title = "Re-trigger completions...",
};
public RazorCompletionEndpoint(
ForegroundDispatcher foregroundDispatcher,
DocumentResolver documentResolver,
RazorCompletionFactsService completionFactsService,
TagHelperCompletionService tagHelperCompletionService,
TagHelperDescriptionFactory tagHelperDescriptionFactory,
ILoggerFactory loggerFactory)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (documentResolver == null)
{
throw new ArgumentNullException(nameof(documentResolver));
}
if (completionFactsService == null)
{
throw new ArgumentNullException(nameof(completionFactsService));
}
if (tagHelperCompletionService == null)
{
throw new ArgumentNullException(nameof(tagHelperCompletionService));
}
if (tagHelperDescriptionFactory == null)
{
throw new ArgumentNullException(nameof(tagHelperDescriptionFactory));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_foregroundDispatcher = foregroundDispatcher;
_documentResolver = documentResolver;
_completionFactsService = completionFactsService;
_tagHelperCompletionService = tagHelperCompletionService;
_tagHelperDescriptionFactory = tagHelperDescriptionFactory;
_logger = loggerFactory.CreateLogger<RazorCompletionEndpoint>();
}
public void SetCapability(CompletionCapability capability)
{
_capability = capability;
}
public async Task<CompletionList> Handle(CompletionParams request, CancellationToken cancellationToken)
{
_foregroundDispatcher.AssertBackgroundThread();
var document = await Task.Factory.StartNew(() =>
{
_documentResolver.TryResolveDocument(request.TextDocument.Uri.AbsolutePath, out var documentSnapshot);
return documentSnapshot;
}, CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
var codeDocument = await document.GetGeneratedOutputAsync();
if (codeDocument.IsUnsupported())
{
return new CompletionList(isIncomplete: false);
}
var syntaxTree = codeDocument.GetSyntaxTree();
var tagHelperDocumentContext = codeDocument.GetTagHelperContext();
var sourceText = await document.GetTextAsync();
var linePosition = new LinePosition((int)request.Position.Line, (int)request.Position.Character);
var hostDocumentIndex = sourceText.Lines.GetPosition(linePosition);
var location = new SourceSpan(hostDocumentIndex, 0);
var directiveCompletionItems = _completionFactsService.GetCompletionItems(syntaxTree, tagHelperDocumentContext, location);
_logger.LogTrace($"Found {directiveCompletionItems.Count} directive completion items.");
var completionItems = new List<CompletionItem>();
foreach (var razorCompletionItem in directiveCompletionItems)
{
if (TryConvert(razorCompletionItem, out var completionItem))
{
completionItems.Add(completionItem);
}
}
var parameterCompletions = completionItems.Where(completionItem => completionItem.TryGetRazorCompletionKind(out var completionKind) && completionKind == RazorCompletionItemKind.DirectiveAttributeParameter);
if (parameterCompletions.Any())
{
// Parameters are present in the completion list, even though TagHelpers are technically valid we shouldn't flood the completion list
// with non parameter completions. Filter out the rest.
completionItems = parameterCompletions.ToList();
}
else
{
var tagHelperCompletionItems = _tagHelperCompletionService.GetCompletionsAt(location, codeDocument);
_logger.LogTrace($"Found {tagHelperCompletionItems.Count} TagHelper completion items.");
completionItems.AddRange(tagHelperCompletionItems);
}
var completionList = new CompletionList(completionItems, isIncomplete: false);
return completionList;
}
public CompletionRegistrationOptions GetRegistrationOptions()
{
return new CompletionRegistrationOptions()
{
DocumentSelector = RazorDefaults.Selector,
ResolveProvider = true,
TriggerCharacters = new Container<string>("@", "<", ":"),
};
}
public bool CanResolve(CompletionItem completionItem)
{
if (completionItem.TryGetRazorCompletionKind(out var completionItemKind))
{
switch (completionItemKind)
{
case RazorCompletionItemKind.DirectiveAttribute:
case RazorCompletionItemKind.DirectiveAttributeParameter:
return true;
}
return false;
}
if (completionItem.IsTagHelperElementCompletion() ||
completionItem.IsTagHelperAttributeCompletion())
{
return true;
}
return false;
}
public Task<CompletionItem> Handle(CompletionItem completionItem, CancellationToken cancellationToken)
{
string markdown = null;
if (completionItem.TryGetRazorCompletionKind(out var completionItemKind))
{
switch (completionItemKind)
{
case RazorCompletionItemKind.DirectiveAttribute:
case RazorCompletionItemKind.DirectiveAttributeParameter:
var descriptionInfo = completionItem.GetAttributeDescriptionInfo();
_tagHelperDescriptionFactory.TryCreateDescription(descriptionInfo, out markdown);
break;
}
}
else
{
if (completionItem.IsTagHelperElementCompletion())
{
var descriptionInfo = completionItem.GetElementDescriptionInfo();
_tagHelperDescriptionFactory.TryCreateDescription(descriptionInfo, out markdown);
}
if (completionItem.IsTagHelperAttributeCompletion())
{
var descriptionInfo = completionItem.GetTagHelperAttributeDescriptionInfo();
_tagHelperDescriptionFactory.TryCreateDescription(descriptionInfo, out markdown);
}
}
if (markdown != null)
{
var documentation = new StringOrMarkupContent(
new MarkupContent()
{
Kind = MarkupKind.Markdown,
Value = markdown,
});
completionItem.Documentation = documentation;
}
return Task.FromResult(completionItem);
}
// Internal for testing
internal static bool TryConvert(RazorCompletionItem razorCompletionItem, out CompletionItem completionItem)
{
switch (razorCompletionItem.Kind)
{
case RazorCompletionItemKind.Directive:
{
// There's not a lot of calculation needed for Directives, go ahead and store the documentation/detail
// on the completion item.
var descriptionInfo = razorCompletionItem.GetDirectiveCompletionDescription();
var directiveCompletionItem = new CompletionItem()
{
Label = razorCompletionItem.DisplayText,
InsertText = razorCompletionItem.InsertText,
FilterText = razorCompletionItem.DisplayText,
SortText = razorCompletionItem.DisplayText,
Detail = descriptionInfo.Description,
Documentation = descriptionInfo.Description,
Kind = CompletionItemKind.Struct,
};
if (razorCompletionItem == DirectiveAttributeTransitionCompletionItemProvider.TransitionCompletionItem)
{
directiveCompletionItem.Command = RetriggerCompletionCommand;
directiveCompletionItem.Kind = CompletionItemKind.TypeParameter;
directiveCompletionItem.Preselect = true;
}
directiveCompletionItem.SetRazorCompletionKind(razorCompletionItem.Kind);
completionItem = directiveCompletionItem;
return true;
}
case RazorCompletionItemKind.DirectiveAttribute:
{
var descriptionInfo = razorCompletionItem.GetAttributeCompletionDescription();
var directiveAttributeCompletionItem = new CompletionItem()
{
Label = razorCompletionItem.DisplayText,
InsertText = razorCompletionItem.InsertText,
FilterText = razorCompletionItem.InsertText,
SortText = razorCompletionItem.InsertText,
Kind = CompletionItemKind.TypeParameter,
};
var indexerCompletion = razorCompletionItem.DisplayText.EndsWith("...");
if (TryResolveDirectiveAttributeInsertionSnippet(razorCompletionItem.InsertText, indexerCompletion, descriptionInfo, out var snippetText))
{
directiveAttributeCompletionItem.InsertText = snippetText;
directiveAttributeCompletionItem.InsertTextFormat = InsertTextFormat.Snippet;
}
directiveAttributeCompletionItem.SetDescriptionInfo(descriptionInfo);
directiveAttributeCompletionItem.SetRazorCompletionKind(razorCompletionItem.Kind);
completionItem = directiveAttributeCompletionItem;
return true;
}
case RazorCompletionItemKind.DirectiveAttributeParameter:
{
var descriptionInfo = razorCompletionItem.GetAttributeCompletionDescription();
var parameterCompletionItem = new CompletionItem()
{
Label = razorCompletionItem.DisplayText,
InsertText = razorCompletionItem.InsertText,
FilterText = razorCompletionItem.InsertText,
SortText = razorCompletionItem.InsertText,
Kind = CompletionItemKind.TypeParameter,
};
parameterCompletionItem.SetDescriptionInfo(descriptionInfo);
parameterCompletionItem.SetRazorCompletionKind(razorCompletionItem.Kind);
completionItem = parameterCompletionItem;
return true;
}
}
completionItem = null;
return false;
}
private static bool TryResolveDirectiveAttributeInsertionSnippet(
string insertText,
bool indexerCompletion,
AttributeCompletionDescription attributeCompletionDescription,
out string snippetText)
{
const string BoolTypeName = "System.Boolean";
var attributeInfos = attributeCompletionDescription.DescriptionInfos;
// Boolean returning bound attribute, auto-complete to just the attribute name.
if (attributeInfos.All(info => info.ReturnTypeName == BoolTypeName))
{
snippetText = null;
return false;
}
if (indexerCompletion)
{
// Indexer completion
snippetText = string.Concat(insertText, "$1=\"$2\"$0");
}
else
{
snippetText = string.Concat(insertText, "=\"$1\"$0");
}
return true;
}
}
}

Просмотреть файл

@ -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.
using System;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion
{
internal class TagHelperAttributeDescriptionInfo
{
public TagHelperAttributeDescriptionInfo(
string displayName,
string propertyName,
string returnTypeName,
string documentation)
{
if (displayName == null)
{
throw new ArgumentNullException(nameof(displayName));
}
if (propertyName == null)
{
throw new ArgumentNullException(nameof(propertyName));
}
if (returnTypeName == null)
{
throw new ArgumentNullException(nameof(returnTypeName));
}
DisplayName = displayName;
PropertyName = propertyName;
ReturnTypeName = returnTypeName;
Documentation = documentation;
}
public string DisplayName { get; }
public string PropertyName { get; }
public string ReturnTypeName { get; }
public string Documentation { get; }
}
}

Просмотреть файл

@ -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.
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion
{
internal abstract class TagHelperCompletionService
{
public abstract IReadOnlyList<CompletionItem> GetCompletionsAt(SourceSpan location, RazorCodeDocument codeDocument);
}
}

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Razor.Completion;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion
{
internal abstract class TagHelperDescriptionFactory
{
public abstract bool TryCreateDescription(ElementDescriptionInfo descriptionInfos, out string markdown);
public abstract bool TryCreateDescription(AttributeDescriptionInfo descriptionInfos, out string markdown);
public abstract bool TryCreateDescription(AttributeCompletionDescription descriptionInfos, out string markdown);
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// 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.LanguageServer.Completion
{
internal class TagHelperDescriptionInfo
{
public TagHelperDescriptionInfo(string tagHelperTypeName, string documentation)
{
if (tagHelperTypeName == null)
{
throw new ArgumentNullException(nameof(tagHelperTypeName));
}
TagHelperTypeName = tagHelperTypeName;
Documentation = documentation;
}
public string TagHelperTypeName { get; }
public string Documentation { get; }
}
}

Просмотреть файл

@ -0,0 +1,193 @@
// 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 Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DefaultDocumentVersionCache : DocumentVersionCache
{
internal const int MaxDocumentTrackingCount = 20;
// Internal for testing
internal readonly Dictionary<string, List<DocumentEntry>> _documentLookup;
private readonly ForegroundDispatcher _foregroundDispatcher;
private ProjectSnapshotManagerBase _projectSnapshotManager;
public DefaultDocumentVersionCache(ForegroundDispatcher foregroundDispatcher)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
_foregroundDispatcher = foregroundDispatcher;
_documentLookup = new Dictionary<string, List<DocumentEntry>>(FilePathComparer.Instance);
}
public override void TrackDocumentVersion(DocumentSnapshot documentSnapshot, long version)
{
if (documentSnapshot == null)
{
throw new ArgumentNullException(nameof(documentSnapshot));
}
if (version < 0)
{
throw new ArgumentOutOfRangeException(nameof(version));
}
_foregroundDispatcher.AssertForegroundThread();
if (!_documentLookup.TryGetValue(documentSnapshot.FilePath, out var documentEntries))
{
documentEntries = new List<DocumentEntry>();
_documentLookup[documentSnapshot.FilePath] = documentEntries;
}
if (documentEntries.Count == MaxDocumentTrackingCount)
{
// Clear the oldest document entry
// With this approach we'll slowly leak memory as new documents are added to the system. We don't clear up
// document file paths where where all of the corresponding entries are expired.
documentEntries.RemoveAt(0);
}
var entry = new DocumentEntry(documentSnapshot, version);
documentEntries.Add(entry);
}
public override bool TryGetDocumentVersion(DocumentSnapshot documentSnapshot, out long version)
{
if (documentSnapshot == null)
{
throw new ArgumentNullException(nameof(documentSnapshot));
}
_foregroundDispatcher.AssertForegroundThread();
if (!_documentLookup.TryGetValue(documentSnapshot.FilePath, out var documentEntries))
{
version = -1;
return false;
}
DocumentEntry entry = null;
for (var i = documentEntries.Count - 1; i >= 0; i--)
{
// We iterate backwards over the entries to prioritize newer entries.
if (documentEntries[i].Document.TryGetTarget(out var document) &&
document == documentSnapshot)
{
entry = documentEntries[i];
break;
}
}
if (entry == null)
{
version = -1;
return false;
}
version = entry.Version;
return true;
}
public override void Initialize(ProjectSnapshotManagerBase projectManager)
{
_projectSnapshotManager = projectManager;
_projectSnapshotManager.Changed += ProjectSnapshotManager_Changed;
}
private void ProjectSnapshotManager_Changed(object sender, ProjectChangeEventArgs args)
{
_foregroundDispatcher.AssertForegroundThread();
switch (args.Kind)
{
case ProjectChangeKind.DocumentChanged:
case ProjectChangeKind.DocumentRemoved:
if (_documentLookup.ContainsKey(args.DocumentFilePath) &&
!_projectSnapshotManager.IsDocumentOpen(args.DocumentFilePath))
{
// Document closed or removed, evict entry.
_documentLookup.Remove(args.DocumentFilePath);
}
break;
}
// Any event that has a project may have changed the state of the documents
// and therefore requires us to mark all existing documents as latest.
if (args.ProjectFilePath == null)
{
return;
}
var project = _projectSnapshotManager.GetLoadedProject(args.ProjectFilePath);
if (project == null)
{
// Project no longer loaded, wait for document removed event.
return;
}
CaptureProjectDocumentsAsLatest(project);
}
// Internal for testing
internal void MarkAsLatestVersion(DocumentSnapshot document)
{
if (!TryGetLatestVersionFromPath(document.FilePath, out var latestVersion))
{
return;
}
// Update our internal tracking state to track the changed document as the latest document.
TrackDocumentVersion(document, latestVersion);
}
// Internal for testing
internal bool TryGetLatestVersionFromPath(string filePath, out long version)
{
if (!_documentLookup.TryGetValue(filePath, out var documentEntries))
{
version = -1;
return false;
}
var latestEntry = documentEntries[documentEntries.Count - 1];
version = latestEntry.Version;
return true;
}
private void CaptureProjectDocumentsAsLatest(ProjectSnapshot projectSnapshot)
{
foreach (var documentPath in projectSnapshot.DocumentFilePaths)
{
if (_documentLookup.ContainsKey(documentPath))
{
var document = projectSnapshot.GetDocument(documentPath);
MarkAsLatestVersion(document);
}
}
}
internal class DocumentEntry
{
public DocumentEntry(DocumentSnapshot document, long version)
{
Document = new WeakReference<DocumentSnapshot>(document);
Version = version;
}
public WeakReference<DocumentSnapshot> Document { get; }
public long Version { get; }
}
}
}

Просмотреть файл

@ -0,0 +1,135 @@
// 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.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using OmniSharp.Extensions.LanguageServer.Server;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DefaultGeneratedCodeContainerStore : GeneratedCodeContainerStore
{
private readonly ConcurrentDictionary<string, GeneratedCodeContainer> _store;
private readonly Lazy<ILanguageServer> _server;
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly DocumentVersionCache _documentVersionCache;
private ProjectSnapshotManagerBase _projectSnapshotManager;
public DefaultGeneratedCodeContainerStore(
ForegroundDispatcher foregroundDispatcher,
DocumentVersionCache documentVersionCache,
Lazy<ILanguageServer> server)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (documentVersionCache == null)
{
throw new ArgumentNullException(nameof(documentVersionCache));
}
if (server == null)
{
throw new ArgumentNullException(nameof(server));
}
_foregroundDispatcher = foregroundDispatcher;
_documentVersionCache = documentVersionCache;
_server = server;
_store = new ConcurrentDictionary<string, GeneratedCodeContainer>(FilePathComparer.Instance);
}
public override GeneratedCodeContainer Get(string physicalFilePath)
{
if (physicalFilePath == null)
{
throw new ArgumentNullException(nameof(physicalFilePath));
}
lock (_store)
{
var codeContainer = _store.GetOrAdd(physicalFilePath, Create);
return codeContainer;
}
}
public override void Initialize(ProjectSnapshotManagerBase projectManager)
{
_projectSnapshotManager = projectManager;
_projectSnapshotManager.Changed += ProjectSnapshotManager_Changed;
}
// Internal for testing
internal void ProjectSnapshotManager_Changed(object sender, ProjectChangeEventArgs args)
{
_foregroundDispatcher.AssertForegroundThread();
switch (args.Kind)
{
case ProjectChangeKind.DocumentChanged:
case ProjectChangeKind.DocumentRemoved:
lock (_store)
{
if (_store.ContainsKey(args.DocumentFilePath) &&
!_projectSnapshotManager.IsDocumentOpen(args.DocumentFilePath))
{
// Document closed or removed, evict entry.
_store.TryRemove(args.DocumentFilePath, out var _);
}
}
break;
}
}
private GeneratedCodeContainer Create(string filePath)
{
var codeContainer = new GeneratedCodeContainer();
codeContainer.GeneratedCodeChanged += (sender, args) =>
{
var generatedCodeContainer = (GeneratedCodeContainer)sender;
IReadOnlyList<TextChange> textChanges;
if (args.NewText.ContentEquals(args.OldText))
{
// If the content is equal then no need to update the underlying CSharp buffer.
textChanges = Array.Empty<TextChange>();
}
else
{
textChanges = args.NewText.GetTextChanges(args.OldText);
}
var latestDocument = generatedCodeContainer.LatestDocument;
Task.Factory.StartNew(() =>
{
if (!_documentVersionCache.TryGetDocumentVersion(latestDocument, out var hostDocumentVersion))
{
// Cache entry doesn't exist, document most likely was evicted from the cache/too old.
return;
}
var request = new UpdateCSharpBufferRequest()
{
HostDocumentFilePath = filePath,
Changes = textChanges,
HostDocumentVersion = hostDocumentVersion,
};
_server.Value.Client.SendRequest("updateCSharpBuffer", request);
}, CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
};
return codeContainer;
}
}
}

Просмотреть файл

@ -0,0 +1,73 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DefaultHostDocumentFactory : HostDocumentFactory
{
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly GeneratedCodeContainerStore _generatedCodeContainerStore;
public DefaultHostDocumentFactory(
ForegroundDispatcher foregroundDispatcher,
GeneratedCodeContainerStore generatedCodeContainerStore)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (generatedCodeContainerStore == null)
{
throw new ArgumentNullException(nameof(generatedCodeContainerStore));
}
_foregroundDispatcher = foregroundDispatcher;
_generatedCodeContainerStore = generatedCodeContainerStore;
}
public override HostDocument Create(string filePath, string targetFilePath)
=> Create(filePath, targetFilePath, fileKind: null);
public override HostDocument Create(string filePath, string targetFilePath, string fileKind)
{
if (filePath == null)
{
throw new ArgumentNullException(nameof(filePath));
}
if (targetFilePath == null)
{
throw new ArgumentNullException(nameof(targetFilePath));
}
var hostDocument = new HostDocument(filePath, targetFilePath, fileKind);
hostDocument.GeneratedCodeContainer.GeneratedCodeChanged += (sender, args) =>
{
var sharedContainer = _generatedCodeContainerStore.Get(filePath);
var container = (GeneratedCodeContainer)sender;
var latestDocument = (DefaultDocumentSnapshot)container.LatestDocument;
Task.Factory.StartNew(async () =>
{
var codeDocument = await latestDocument.GetGeneratedOutputAsync();
sharedContainer.SetOutput(
latestDocument,
codeDocument.GetCSharpDocument(),
container.InputVersion,
container.OutputVersion);
}, CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.BackgroundScheduler);
};
return hostDocument;
}
}
}

Просмотреть файл

@ -0,0 +1,71 @@
// 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;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DefaultProjectSnapshotManagerAccessor : ProjectSnapshotManagerAccessor
{
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly IEnumerable<ProjectSnapshotChangeTrigger> _changeTriggers;
private readonly FilePathNormalizer _filePathNormalizer;
private ProjectSnapshotManagerBase _instance;
public DefaultProjectSnapshotManagerAccessor(
ForegroundDispatcher foregroundDispatcher,
IEnumerable<ProjectSnapshotChangeTrigger> changeTriggers,
FilePathNormalizer filePathNormalizer)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (changeTriggers == null)
{
throw new ArgumentNullException(nameof(changeTriggers));
}
if (filePathNormalizer == null)
{
throw new ArgumentNullException(nameof(filePathNormalizer));
}
_foregroundDispatcher = foregroundDispatcher;
_changeTriggers = changeTriggers;
_filePathNormalizer = filePathNormalizer;
}
public override ProjectSnapshotManagerBase Instance
{
get
{
if (_instance == null)
{
var services = AdhocServices.Create(
workspaceServices: new[]
{
new RemoteProjectSnapshotProjectEngineFactory(_filePathNormalizer)
},
razorLanguageServices: Enumerable.Empty<ILanguageService>());
var workspace = new AdhocWorkspace(services);
_instance = new DefaultProjectSnapshotManager(
_foregroundDispatcher,
new DefaultErrorReporter(),
_changeTriggers,
workspace);
}
return _instance;
}
}
}
}

Просмотреть файл

@ -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.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DefaultRemoteTextLoaderFactory : RemoteTextLoaderFactory
{
private const string GetTextDocumentMethod = "getTextDocument";
private readonly ILanguageServer _router;
private readonly FilePathNormalizer _filePathNormalizer;
public DefaultRemoteTextLoaderFactory(
ILanguageServer router,
FilePathNormalizer filePathNormalizer)
{
if (router == null)
{
throw new ArgumentNullException(nameof(router));
}
if (filePathNormalizer == null)
{
throw new ArgumentNullException(nameof(filePathNormalizer));
}
_router = router;
_filePathNormalizer = filePathNormalizer;
}
public override TextLoader Create(string filePath)
{
if (filePath == null)
{
throw new ArgumentNullException(nameof(filePath));
}
var normalizedPath = _filePathNormalizer.Normalize(filePath);
return new RemoteTextLoader(normalizedPath, _router);
}
private class RemoteTextLoader : TextLoader
{
private readonly string _filePath;
private readonly ILanguageServer _router;
public RemoteTextLoader(string filePath, ILanguageServer router)
{
if (filePath == null)
{
throw new ArgumentNullException(nameof(filePath));
}
if (router == null)
{
throw new ArgumentNullException(nameof(router));
}
_filePath = filePath;
_router = router;
}
public override async Task<TextAndVersion> LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
{
var document = await _router.Client.SendRequest<string, TextDocumentItem>(GetTextDocumentMethod, _filePath);
var sourceText = SourceText.From(document.Text);
var textAndVersion = TextAndVersion.Create(sourceText, VersionStamp.Default);
return textAndVersion;
}
}
}
}

Просмотреть файл

@ -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.
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal abstract class DocumentProcessedListener
{
public abstract void Initialize(ProjectSnapshotManager projectManager);
public abstract void DocumentProcessed(DocumentSnapshot document);
}
}

Просмотреть файл

@ -0,0 +1,34 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DocumentSnapshotTextLoader : TextLoader
{
private readonly DocumentSnapshot _documentSnapshot;
public DocumentSnapshotTextLoader(DocumentSnapshot documentSnapshot)
{
if (documentSnapshot == null)
{
throw new ArgumentNullException(nameof(documentSnapshot));
}
_documentSnapshot = documentSnapshot;
}
public override async Task<TextAndVersion> LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
{
var sourceText = await _documentSnapshot.GetTextAsync();
var textAndVersion = TextAndVersion.Create(sourceText, VersionStamp.Default);
return textAndVersion;
}
}
}

Просмотреть файл

@ -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.
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal abstract class DocumentVersionCache : ProjectSnapshotChangeTrigger
{
public abstract bool TryGetDocumentVersion(DocumentSnapshot documentSnapshot, out long version);
public abstract void TrackDocumentVersion(DocumentSnapshot documentSnapshot, long version);
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal abstract class GeneratedCodeContainerStore : ProjectSnapshotChangeTrigger
{
public abstract GeneratedCodeContainer Get(string physicalFilePath);
}
}

Просмотреть файл

@ -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.
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal abstract class HostDocumentFactory
{
public abstract HostDocument Create(string filePath, string targetFilePath);
public abstract HostDocument Create(string filePath, string targetFilePath, string fileKind);
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.JsonRpc;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
[Serial, Method("razor/languageQuery")]
internal interface IRazorLanguageQueryHandler : IJsonRpcRequestHandler<RazorLanguageQueryParams, RazorLanguageQueryResponse>
{
}
}

Просмотреть файл

@ -0,0 +1,59 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>Exe</OutputType>
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains a Razor language server.</Description>
<EnableApiCheck>false</EnableApiCheck>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;osx-x64;</RuntimeIdentifiers>
<AssemblyName>rzls</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OmniSharp.Extensions.LanguageServer" Version="$(OmniSharpExtensionsLanguageServerPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.LanguageServer.Common\Microsoft.AspNetCore.Razor.LanguageServer.Common.csproj" />
</ItemGroup>
<Target Name="_IncludeOmniSharpPlugin" Condition="Exists('$(PublishDir)')">
<PropertyGroup>
<TargetPluginOutputPath>$(PublishDir)\OmniSharpPlugin</TargetPluginOutputPath>
</PropertyGroup>
<MSBuild Projects="..\Microsoft.AspNetCore.Razor.OmniSharpPlugin\Microsoft.AspNetCore.Razor.OmniSharpPlugin.csproj" Properties="PublishDir=$(TargetPluginOutputPath);RuntimeIdentifier=" Targets="Publish" />
</Target>
<!--
Technique for publishing multiple RIDs from
https://github.com/dotnet/cli/issues/9221#issuecomment-387512008
Example usage:
dotnet msbuild -restore -t:PublishAllRids -p:Configuration=Release
-->
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<!-- Enable roll-forward to latest patch. This allows one restore operation
to apply to all of the self-contained publish operations. -->
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<RidsPublishDir>$(ArtifactsDir)LanguageServer\$(Configuration)\</RidsPublishDir>
</PropertyGroup>
<Target Name="PublishAllRids">
<ItemGroup>
<!-- Transform RuntimeIdentifiers property to item -->
<RuntimeIdentifierForPublish Include="$(RuntimeIdentifiers)" />
<!-- Transform RuntimeIdentifierForPublish items to project items to pass to MSBuild task -->
<ProjectToPublish Include="@(RuntimeIdentifierForPublish->'$(MSBuildProjectFullPath)')">
<AdditionalProperties>RuntimeIdentifier=%(RuntimeIdentifierForPublish.Identity);PublishDir=$(RidsPublishDir)%(RuntimeIdentifierForPublish.Identity)\</AdditionalProperties>
</ProjectToPublish>
</ItemGroup>
<MSBuild Projects="@(ProjectToPublish)"
Targets="Publish;_IncludeOmniSharpPlugin"
BuildInParallel="false" />
</Target>
</Project>

Просмотреть файл

@ -0,0 +1,129 @@
// 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.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization;
using Microsoft.AspNetCore.Razor.LanguageServer.Completion;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.Editor.Razor;
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
using OmniSharp.Extensions.LanguageServer.Server;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
public class Program
{
public static void Main(string[] args)
{
MainAsync(args).Wait();
}
public static async Task MainAsync(string[] args)
{
var logLevel = LogLevel.Information;
for (var i = 0; i < args.Length; i++)
{
if (args[i].IndexOf("debug", StringComparison.OrdinalIgnoreCase) >= 0)
{
while (!Debugger.IsAttached)
{
Thread.Sleep(1000);
}
Debugger.Break();
continue;
}
if (args[i] == "--logLevel" && i + 1 < args.Length)
{
var logLevelString = args[++i];
if (!Enum.TryParse(logLevelString, out logLevel))
{
logLevel = LogLevel.Information;
Console.WriteLine($"Invalid log level '{logLevelString}'. Defaulting to {logLevel.ToString()}.");
}
}
}
Serializer.Instance.JsonSerializer.Converters.RegisterRazorConverters();
var factory = new LoggerFactory();
ILanguageServer server = null;
server = await OmniSharp.Extensions.LanguageServer.Server.LanguageServer.From(options =>
options
.WithInput(Console.OpenStandardInput())
.WithOutput(Console.OpenStandardOutput())
.WithLoggerFactory(factory)
.AddDefaultLoggingProvider()
.WithMinimumLogLevel(logLevel)
.WithHandler<RazorDocumentSynchronizationEndpoint>()
.WithHandler<RazorCompletionEndpoint>()
.WithHandler<RazorLanguageEndpoint>()
.WithHandler<RazorProjectEndpoint>()
.WithServices(services =>
{
services.AddSingleton<RemoteTextLoaderFactory, DefaultRemoteTextLoaderFactory>();
services.AddSingleton<ProjectResolver, DefaultProjectResolver>();
services.AddSingleton<DocumentResolver, DefaultDocumentResolver>();
services.AddSingleton<FilePathNormalizer>();
services.AddSingleton<RazorProjectService, DefaultRazorProjectService>();
services.AddSingleton<ProjectSnapshotChangeTrigger, BackgroundDocumentGenerator>();
services.AddSingleton<DocumentProcessedListener, RazorDiagnosticsPublisher>();
services.AddSingleton<HostDocumentFactory, DefaultHostDocumentFactory>();
services.AddSingleton<ProjectSnapshotManagerAccessor, DefaultProjectSnapshotManagerAccessor>();
services.AddSingleton<TagHelperFactsService, DefaultTagHelperFactsService>();
services.AddSingleton<VisualStudio.Editor.Razor.TagHelperCompletionService, VisualStudio.Editor.Razor.DefaultTagHelperCompletionService>();
services.AddSingleton<TagHelperDescriptionFactory, DefaultTagHelperDescriptionFactory>();
// Completion
services.AddSingleton<Completion.TagHelperCompletionService, Completion.DefaultTagHelperCompletionService>();
services.AddSingleton<RazorCompletionItemProvider, DirectiveCompletionItemProvider>();
services.AddSingleton<RazorCompletionItemProvider, DirectiveAttributeCompletionItemProvider>();
services.AddSingleton<RazorCompletionItemProvider, DirectiveAttributeParameterCompletionItemProvider>();
services.AddSingleton<RazorCompletionItemProvider, DirectiveAttributeTransitionCompletionItemProvider>();
var foregroundDispatcher = new VSCodeForegroundDispatcher();
services.AddSingleton<ForegroundDispatcher>(foregroundDispatcher);
services.AddSingleton<RazorCompletionFactsService, DefaultRazorCompletionFactsService>();
var documentVersionCache = new DefaultDocumentVersionCache(foregroundDispatcher);
services.AddSingleton<DocumentVersionCache>(documentVersionCache);
services.AddSingleton<ProjectSnapshotChangeTrigger>(documentVersionCache);
var containerStore = new DefaultGeneratedCodeContainerStore(
foregroundDispatcher,
documentVersionCache,
new Lazy<ILanguageServer>(() => server));
services.AddSingleton<GeneratedCodeContainerStore>(containerStore);
services.AddSingleton<ProjectSnapshotChangeTrigger>(containerStore);
}));
// Workaround for https://github.com/OmniSharp/csharp-language-server-protocol/issues/106
var languageServer = (OmniSharp.Extensions.LanguageServer.Server.LanguageServer)server;
languageServer.MinimumLogLevel = logLevel;
try
{
var logger = factory.CreateLogger<Program>();
var assemblyInformationAttribute = typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
logger.LogInformation("Razor Language Server version " + assemblyInformationAttribute.InformationalVersion);
}
catch
{
// Swallow exceptions from determining assembly information.
}
await server.WaitForExit;
TempDirectory.Instance.Dispose();
}
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal abstract class ProjectSnapshotManagerAccessor
{
public abstract ProjectSnapshotManagerBase Instance { get; }
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.Embedded.MediatR;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class AddDocumentParams : IRequest
{
public string FilePath { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,64 @@
// 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 Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class DefaultDocumentResolver : DocumentResolver
{
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly ProjectResolver _projectResolver;
private readonly FilePathNormalizer _filePathNormalizer;
public DefaultDocumentResolver(
ForegroundDispatcher foregroundDispatcher,
ProjectResolver projectResolver,
FilePathNormalizer filePathNormalizer)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (projectResolver == null)
{
throw new ArgumentNullException(nameof(projectResolver));
}
if (filePathNormalizer == null)
{
throw new ArgumentNullException(nameof(filePathNormalizer));
}
_foregroundDispatcher = foregroundDispatcher;
_projectResolver = projectResolver;
_filePathNormalizer = filePathNormalizer;
}
public override bool TryResolveDocument(string documentFilePath, out DocumentSnapshot document)
{
_foregroundDispatcher.AssertForegroundThread();
var normalizedPath = _filePathNormalizer.Normalize(documentFilePath);
if (!_projectResolver.TryResolvePotentialProject(normalizedPath, out var project))
{
project = _projectResolver.GetMiscellaneousProject();
}
if (!project.DocumentFilePaths.Contains(normalizedPath, FilePathComparer.Instance))
{
// Miscellaneous project and other tracked projects do not contain document.
document = null;
return false;
}
document = project.GetDocument(normalizedPath);
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,94 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class DefaultProjectResolver : ProjectResolver
{
// Internal for testing
protected internal readonly HostProject _miscellaneousHostProject;
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly FilePathNormalizer _filePathNormalizer;
private readonly ProjectSnapshotManagerAccessor _projectSnapshotManagerAccessor;
public DefaultProjectResolver(
ForegroundDispatcher foregroundDispatcher,
FilePathNormalizer filePathNormalizer,
ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (filePathNormalizer == null)
{
throw new ArgumentNullException(nameof(filePathNormalizer));
}
if (projectSnapshotManagerAccessor == null)
{
throw new ArgumentNullException(nameof(projectSnapshotManagerAccessor));
}
_foregroundDispatcher = foregroundDispatcher;
_filePathNormalizer = filePathNormalizer;
_projectSnapshotManagerAccessor = projectSnapshotManagerAccessor;
var miscellaneousProjectPath = Path.Combine(TempDirectory.Instance.DirectoryPath, "__MISC_RAZOR_PROJECT__");
_miscellaneousHostProject = new HostProject(miscellaneousProjectPath, RazorDefaults.Configuration, RazorDefaults.RootNamespace);
}
public override bool TryResolvePotentialProject(string documentFilePath, out ProjectSnapshot projectSnapshot)
{
if (documentFilePath == null)
{
throw new ArgumentNullException(nameof(documentFilePath));
}
_foregroundDispatcher.AssertForegroundThread();
var normalizedDocumentPath = _filePathNormalizer.Normalize(documentFilePath);
var projects = _projectSnapshotManagerAccessor.Instance.Projects;
for (var i = 0; i < projects.Count; i++)
{
if (projects[i].FilePath == _miscellaneousHostProject.FilePath)
{
// We don't resolve documents to belonging to the miscellaneous project.
continue;
}
var projectDirectory = _filePathNormalizer.GetDirectory(projects[i].FilePath);
if (normalizedDocumentPath.StartsWith(projectDirectory))
{
projectSnapshot = projects[i];
return true;
}
}
projectSnapshot = null;
return false;
}
public override ProjectSnapshot GetMiscellaneousProject()
{
_foregroundDispatcher.AssertForegroundThread();
var miscellaneousProject = _projectSnapshotManagerAccessor.Instance.GetLoadedProject(_miscellaneousHostProject.FilePath);
if (miscellaneousProject == null)
{
_projectSnapshotManagerAccessor.Instance.ProjectAdded(_miscellaneousHostProject);
miscellaneousProject = _projectSnapshotManagerAccessor.Instance.GetLoadedProject(_miscellaneousHostProject.FilePath);
}
return miscellaneousProject;
}
}
}

Просмотреть файл

@ -0,0 +1,435 @@
// 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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class DefaultRazorProjectService : RazorProjectService
{
private readonly ProjectSnapshotManagerAccessor _projectSnapshotManagerAccessor;
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly HostDocumentFactory _hostDocumentFactory;
private readonly RemoteTextLoaderFactory _remoteTextLoaderFactory;
private readonly ProjectResolver _projectResolver;
private readonly DocumentVersionCache _documentVersionCache;
private readonly FilePathNormalizer _filePathNormalizer;
private readonly DocumentResolver _documentResolver;
private readonly ILogger _logger;
public DefaultRazorProjectService(
ForegroundDispatcher foregroundDispatcher,
HostDocumentFactory hostDocumentFactory,
RemoteTextLoaderFactory remoteTextLoaderFactory,
DocumentResolver documentResolver,
ProjectResolver projectResolver,
DocumentVersionCache documentVersionCache,
FilePathNormalizer filePathNormalizer,
ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor,
ILoggerFactory loggerFactory)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (hostDocumentFactory == null)
{
throw new ArgumentNullException(nameof(hostDocumentFactory));
}
if (remoteTextLoaderFactory == null)
{
throw new ArgumentNullException(nameof(remoteTextLoaderFactory));
}
if (documentResolver == null)
{
throw new ArgumentNullException(nameof(documentResolver));
}
if (projectResolver == null)
{
throw new ArgumentNullException(nameof(projectResolver));
}
if (documentVersionCache == null)
{
throw new ArgumentNullException(nameof(documentVersionCache));
}
if (filePathNormalizer == null)
{
throw new ArgumentNullException(nameof(filePathNormalizer));
}
if (projectSnapshotManagerAccessor == null)
{
throw new ArgumentNullException(nameof(projectSnapshotManagerAccessor));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_foregroundDispatcher = foregroundDispatcher;
_hostDocumentFactory = hostDocumentFactory;
_remoteTextLoaderFactory = remoteTextLoaderFactory;
_documentResolver = documentResolver;
_projectResolver = projectResolver;
_documentVersionCache = documentVersionCache;
_filePathNormalizer = filePathNormalizer;
_projectSnapshotManagerAccessor = projectSnapshotManagerAccessor;
_logger = loggerFactory.CreateLogger<DefaultRazorProjectService>();
}
public override void AddDocument(string filePath)
{
_foregroundDispatcher.AssertForegroundThread();
var textDocumentPath = _filePathNormalizer.Normalize(filePath);
if (_documentResolver.TryResolveDocument(textDocumentPath, out var _))
{
// Document already added. This usually occurs when VSCode has already pre-initialized
// open documents and then we try to manually add all known razor documents.
return;
}
if (!_projectResolver.TryResolvePotentialProject(textDocumentPath, out var projectSnapshot))
{
projectSnapshot = _projectResolver.GetMiscellaneousProject();
}
var targetFilePath = textDocumentPath;
var projectDirectory = _filePathNormalizer.GetDirectory(projectSnapshot.FilePath);
if (targetFilePath.StartsWith(projectDirectory, FilePathComparison.Instance))
{
// Make relative
targetFilePath = textDocumentPath.Substring(projectDirectory.Length);
}
// Representing all of our host documents with a re-normalized target path to workaround GetRelatedDocument limitations.
var normalizedTargetFilePath = targetFilePath.Replace('/', '\\').TrimStart('\\');
var hostDocument = _hostDocumentFactory.Create(textDocumentPath, normalizedTargetFilePath);
var defaultProject = (DefaultProjectSnapshot)projectSnapshot;
var textLoader = _remoteTextLoaderFactory.Create(textDocumentPath);
_logger.LogInformation($"Adding document '{filePath}' to project '{projectSnapshot.FilePath}'.");
_projectSnapshotManagerAccessor.Instance.DocumentAdded(defaultProject.HostProject, hostDocument, textLoader);
}
public override void OpenDocument(string filePath, SourceText sourceText, long version)
{
_foregroundDispatcher.AssertForegroundThread();
var textDocumentPath = _filePathNormalizer.Normalize(filePath);
if (!_documentResolver.TryResolveDocument(textDocumentPath, out var _))
{
// Document hasn't been added. This usually occurs when VSCode trumps all other initialization
// processes and pre-initializes already open documents.
AddDocument(filePath);
}
if (!_projectResolver.TryResolvePotentialProject(textDocumentPath, out var projectSnapshot))
{
projectSnapshot = _projectResolver.GetMiscellaneousProject();
}
var defaultProject = (DefaultProjectSnapshot)projectSnapshot;
_logger.LogInformation($"Opening document '{textDocumentPath}' in project '{projectSnapshot.FilePath}'.");
_projectSnapshotManagerAccessor.Instance.DocumentOpened(defaultProject.HostProject.FilePath, textDocumentPath, sourceText);
TrackDocumentVersion(textDocumentPath, version);
}
public override void CloseDocument(string filePath)
{
_foregroundDispatcher.AssertForegroundThread();
var textDocumentPath = _filePathNormalizer.Normalize(filePath);
if (!_projectResolver.TryResolvePotentialProject(textDocumentPath, out var projectSnapshot))
{
projectSnapshot = _projectResolver.GetMiscellaneousProject();
}
var textLoader = _remoteTextLoaderFactory.Create(filePath);
var defaultProject = (DefaultProjectSnapshot)projectSnapshot;
_logger.LogInformation($"Closing document '{textDocumentPath}' in project '{projectSnapshot.FilePath}'.");
_projectSnapshotManagerAccessor.Instance.DocumentClosed(defaultProject.HostProject.FilePath, textDocumentPath, textLoader);
}
public override void RemoveDocument(string filePath)
{
_foregroundDispatcher.AssertForegroundThread();
var textDocumentPath = _filePathNormalizer.Normalize(filePath);
if (!_projectResolver.TryResolvePotentialProject(textDocumentPath, out var projectSnapshot))
{
projectSnapshot = _projectResolver.GetMiscellaneousProject();
}
if (!projectSnapshot.DocumentFilePaths.Contains(textDocumentPath, FilePathComparer.Instance))
{
_logger.LogInformation($"Containing project is not tracking document '{filePath}");
return;
}
var document = (DefaultDocumentSnapshot)projectSnapshot.GetDocument(textDocumentPath);
var defaultProject = (DefaultProjectSnapshot)projectSnapshot;
_logger.LogInformation($"Removing document '{textDocumentPath}' from project '{projectSnapshot.FilePath}'.");
_projectSnapshotManagerAccessor.Instance.DocumentRemoved(defaultProject.HostProject, document.State.HostDocument);
}
public override void UpdateDocument(string filePath, SourceText sourceText, long version)
{
_foregroundDispatcher.AssertForegroundThread();
var textDocumentPath = _filePathNormalizer.Normalize(filePath);
if (!_projectResolver.TryResolvePotentialProject(textDocumentPath, out var projectSnapshot))
{
projectSnapshot = _projectResolver.GetMiscellaneousProject();
}
var defaultProject = (DefaultProjectSnapshot)projectSnapshot;
_logger.LogTrace($"Updating document '{textDocumentPath}'.");
_projectSnapshotManagerAccessor.Instance.DocumentChanged(defaultProject.HostProject.FilePath, textDocumentPath, sourceText);
TrackDocumentVersion(textDocumentPath, version);
}
public override void AddProject(string filePath)
{
_foregroundDispatcher.AssertForegroundThread();
var normalizedPath = _filePathNormalizer.Normalize(filePath);
var hostProject = new HostProject(normalizedPath, RazorDefaults.Configuration, RazorDefaults.RootNamespace);
_projectSnapshotManagerAccessor.Instance.ProjectAdded(hostProject);
_logger.LogInformation($"Added project '{filePath}' to project system.");
TryMigrateMiscellaneousDocumentsToProject();
}
public override void RemoveProject(string filePath)
{
_foregroundDispatcher.AssertForegroundThread();
var normalizedPath = _filePathNormalizer.Normalize(filePath);
var project = (DefaultProjectSnapshot)_projectSnapshotManagerAccessor.Instance.GetLoadedProject(normalizedPath);
if (project == null)
{
// Never tracked the project to begin with, noop.
return;
}
_logger.LogInformation($"Removing project '{filePath}' from project system.");
_projectSnapshotManagerAccessor.Instance.ProjectRemoved(project.HostProject);
TryMigrateDocumentsFromRemovedProject(project);
}
public override void UpdateProject(
string filePath,
RazorConfiguration configuration,
string rootNamespace,
ProjectWorkspaceState projectWorkspaceState,
IReadOnlyList<DocumentSnapshotHandle> documents)
{
_foregroundDispatcher.AssertForegroundThread();
var normalizedPath = _filePathNormalizer.Normalize(filePath);
var project = (DefaultProjectSnapshot)_projectSnapshotManagerAccessor.Instance.GetLoadedProject(normalizedPath);
if (project == null)
{
// Never tracked the project to begin with, noop.
_logger.LogInformation($"Failed to update untracked project '{filePath}'.");
return;
}
UpdateProjectDocuments(documents, project);
if (!projectWorkspaceState.Equals(ProjectWorkspaceState.Default))
{
_logger.LogInformation($"Updating project '{filePath}' TagHelpers ({projectWorkspaceState.TagHelpers.Count}) and C# Language Version ({projectWorkspaceState.CSharpLanguageVersion}).");
}
_projectSnapshotManagerAccessor.Instance.ProjectWorkspaceStateChanged(project.FilePath, projectWorkspaceState);
var currentHostProject = project.HostProject;
var currentConfiguration = currentHostProject.Configuration;
if (currentConfiguration.ConfigurationName == configuration?.ConfigurationName &&
currentHostProject.RootNamespace == rootNamespace)
{
_logger.LogTrace($"Updating project '{filePath}'. The project is already using configuration '{configuration.ConfigurationName}' and root namespace '{rootNamespace}'.");
return;
}
if (configuration == null)
{
configuration = RazorDefaults.Configuration;
_logger.LogInformation($"Updating project '{filePath}' to use Razor's default configuration ('{configuration.ConfigurationName}')'.");
}
else if (currentConfiguration.ConfigurationName != configuration.ConfigurationName)
{
_logger.LogInformation($"Updating project '{filePath}' to Razor configuration '{configuration.ConfigurationName}' with language version '{configuration.LanguageVersion}'.");
}
if (currentHostProject.RootNamespace != rootNamespace)
{
_logger.LogInformation($"Updating project '{filePath}''s root namespace to '{rootNamespace}'.");
}
var hostProject = new HostProject(project.FilePath, configuration, rootNamespace);
_projectSnapshotManagerAccessor.Instance.ProjectConfigurationChanged(hostProject);
}
private void UpdateProjectDocuments(IReadOnlyList<DocumentSnapshotHandle> documents, DefaultProjectSnapshot project)
{
var currentHostProject = project.HostProject;
var projectDirectory = _filePathNormalizer.GetDirectory(project.FilePath);
var documentMap = documents.ToDictionary(document => EnsureFullPath(document.FilePath, projectDirectory), FilePathComparer.Instance);
foreach (var documentFilePath in project.DocumentFilePaths)
{
if (!documentMap.TryGetValue(documentFilePath, out var documentHandle))
{
// Document exists in the project but not in the configured documents. Chances are the project configuration is from a fallback
// configuration case (< 2.1) or the project isn't fully loaded yet.
continue;
}
var documentSnapshot = (DefaultDocumentSnapshot)project.GetDocument(documentFilePath);
var currentHostDocument = documentSnapshot.State.HostDocument;
var newFilePath = EnsureFullPath(documentHandle.FilePath, projectDirectory);
var newHostDocument = _hostDocumentFactory.Create(newFilePath, documentHandle.TargetPath, documentHandle.FileKind);
if (HostDocumentComparer.Instance.Equals(currentHostDocument, newHostDocument))
{
// Current and "new" host documents are equivalent
continue;
}
_logger.LogTrace($"Updating document '{newHostDocument.FilePath}''s file kind to '{newHostDocument.FileKind}' and target path to '{newHostDocument.TargetPath}'.");
_projectSnapshotManagerAccessor.Instance.DocumentRemoved(currentHostProject, currentHostDocument);
var remoteTextLoader = _remoteTextLoaderFactory.Create(newFilePath);
_projectSnapshotManagerAccessor.Instance.DocumentAdded(currentHostProject, newHostDocument, remoteTextLoader);
}
}
private string EnsureFullPath(string filePath, string projectDirectory)
{
var normalizedFilePath = _filePathNormalizer.Normalize(filePath);
if (!normalizedFilePath.StartsWith(projectDirectory))
{
// Remove '/' from document path
normalizedFilePath = normalizedFilePath.Substring(1);
normalizedFilePath = _filePathNormalizer.Normalize(projectDirectory + normalizedFilePath);
}
return normalizedFilePath;
}
// Internal for testing
internal void TryMigrateDocumentsFromRemovedProject(ProjectSnapshot project)
{
_foregroundDispatcher.AssertForegroundThread();
var miscellaneousProject = _projectResolver.GetMiscellaneousProject();
foreach (var documentFilePath in project.DocumentFilePaths)
{
var documentSnapshot = (DefaultDocumentSnapshot)project.GetDocument(documentFilePath);
if (!_projectResolver.TryResolvePotentialProject(documentFilePath, out var toProject))
{
// This is the common case. It'd be rare for a project to be nested but we need to protect against it anyhow.
toProject = miscellaneousProject;
}
var textLoader = new DocumentSnapshotTextLoader(documentSnapshot);
var defaultToProject = (DefaultProjectSnapshot)toProject;
var newHostDocument = _hostDocumentFactory.Create(documentSnapshot.FilePath, documentSnapshot.TargetPath);
_logger.LogInformation($"Migrating '{documentFilePath}' from the '{project.FilePath}' project to '{toProject.FilePath}' project.");
_projectSnapshotManagerAccessor.Instance.DocumentAdded(defaultToProject.HostProject, newHostDocument, textLoader);
}
}
// Internal for testing
internal void TryMigrateMiscellaneousDocumentsToProject()
{
_foregroundDispatcher.AssertForegroundThread();
var miscellaneousProject = _projectResolver.GetMiscellaneousProject();
foreach (var documentFilePath in miscellaneousProject.DocumentFilePaths)
{
if (!_projectResolver.TryResolvePotentialProject(documentFilePath, out var projectSnapshot))
{
continue;
}
var documentSnapshot = (DefaultDocumentSnapshot)miscellaneousProject.GetDocument(documentFilePath);
// Remove from miscellaneous project
var defaultMiscProject = (DefaultProjectSnapshot)miscellaneousProject;
_projectSnapshotManagerAccessor.Instance.DocumentRemoved(defaultMiscProject.HostProject, documentSnapshot.State.HostDocument);
// Add to new project
var textLoader = new DocumentSnapshotTextLoader(documentSnapshot);
var defaultProject = (DefaultProjectSnapshot)projectSnapshot;
var newHostDocument = _hostDocumentFactory.Create(documentSnapshot.FilePath, documentSnapshot.TargetPath);
_logger.LogInformation($"Migrating '{documentFilePath}' from the '{miscellaneousProject.FilePath}' project to '{projectSnapshot.FilePath}' project.");
_projectSnapshotManagerAccessor.Instance.DocumentAdded(defaultProject.HostProject, newHostDocument, textLoader);
}
}
private void TrackDocumentVersion(string textDocumentPath, long version)
{
if (!_documentResolver.TryResolveDocument(textDocumentPath, out var documentSnapshot))
{
return;
}
_documentVersionCache.TrackDocumentVersion(documentSnapshot, version);
}
private class DelegatingTextLoader : TextLoader
{
private readonly DocumentSnapshot _fromDocument;
public DelegatingTextLoader(DocumentSnapshot fromDocument)
{
_fromDocument = fromDocument ?? throw new ArgumentNullException(nameof(fromDocument));
}
public override async Task<TextAndVersion> LoadTextAndVersionAsync(
Workspace workspace,
DocumentId documentId,
CancellationToken cancellationToken)
{
var sourceText = await _fromDocument.GetTextAsync();
var version = await _fromDocument.GetTextVersionAsync();
var textAndVersion = TextAndVersion.Create(sourceText, version.GetNewerVersion());
return textAndVersion;
}
}
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal abstract class DocumentResolver
{
public abstract bool TryResolveDocument(string documentFilePath, out DocumentSnapshot document);
}
}

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using OmniSharp.Extensions.JsonRpc;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
[Parallel, Method("projects/addDocument")]
internal interface IRazorAddDocumentHandler : IJsonRpcRequestHandler<AddDocumentParams>
{
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.JsonRpc;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
[Serial, Method("projects/addProject")]
internal interface IRazorAddProjectHandler : IJsonRpcNotificationHandler<RazorAddProjectParams>
{
}
}

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using OmniSharp.Extensions.JsonRpc;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
[Parallel, Method("projects/removeDocument")]
internal interface IRazorRemoveDocumentHandler : IJsonRpcRequestHandler<RemoveDocumentParams>
{
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.JsonRpc;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
[Serial, Method("projects/removeProject")]
internal interface IRazorRemoveProjectHandler : IJsonRpcNotificationHandler<RazorRemoveProjectParams>
{
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.JsonRpc;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
[Serial, Method("projects/updateProject")]
internal interface IRazorUpdateProjectHandler : IJsonRpcNotificationHandler<RazorUpdateProjectParams>
{
}
}

Просмотреть файл

@ -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.
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal abstract class ProjectResolver
{
public abstract bool TryResolvePotentialProject(string documentFilePath, out ProjectSnapshot projectSnapshot);
public abstract ProjectSnapshot GetMiscellaneousProject();
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.Embedded.MediatR;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class RazorAddProjectParams : IRequest
{
public string FilePath { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal abstract class RazorProjectService
{
public abstract void AddDocument(string filePath);
public abstract void OpenDocument(string filePath, SourceText sourceText, long version);
public abstract void CloseDocument(string filePath);
public abstract void RemoveDocument(string filePath);
public abstract void UpdateDocument(string filePath, SourceText sourceText, long version);
public abstract void AddProject(string filePath);
public abstract void RemoveProject(string filePath);
public abstract void UpdateProject(
string filePath,
RazorConfiguration configuration,
string rootNamespace,
ProjectWorkspaceState projectWorkspaceState,
IReadOnlyList<DocumentSnapshotHandle> documents);
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.Embedded.MediatR;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class RazorRemoveProjectParams : IRequest
{
public string FilePath { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.LanguageServer.Common.Serialization;
using OmniSharp.Extensions.Embedded.MediatR;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class RazorUpdateProjectParams : IRequest
{
public FullProjectSnapshotHandle ProjectSnapshotHandle { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.Embedded.MediatR;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class RemoveDocumentParams : IRequest
{
public string FilePath { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,7 @@
// 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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

Просмотреть файл

@ -0,0 +1,22 @@
// 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;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
public static class RazorDefaults
{
public static DocumentSelector Selector { get; } = new DocumentSelector(
new DocumentFilter()
{
Pattern = "**/*.{cshtml,razor}"
});
public static RazorConfiguration Configuration { get; } = FallbackRazorConfiguration.MVC_2_1;
public static string RootNamespace { get; } = null;
}
}

Просмотреть файл

@ -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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Text;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal static class RazorDiagnosticConverter
{
public static Diagnostic Convert(RazorDiagnostic razorDiagnostic, SourceText sourceText)
{
if (razorDiagnostic == null)
{
throw new ArgumentNullException(nameof(razorDiagnostic));
}
if (sourceText == null)
{
throw new ArgumentNullException(nameof(sourceText));
}
var diagnostic = new Diagnostic()
{
Message = razorDiagnostic.GetMessage(),
Code = razorDiagnostic.Id,
Severity = ConvertSeverity(razorDiagnostic.Severity),
Range = ConvertSpanToRange(razorDiagnostic.Span, sourceText),
};
return diagnostic;
}
// Internal for testing
internal static DiagnosticSeverity ConvertSeverity(RazorDiagnosticSeverity severity)
{
switch (severity)
{
case RazorDiagnosticSeverity.Error:
return DiagnosticSeverity.Error;
default:
return DiagnosticSeverity.Information;
}
}
// Internal for testing
internal static Range ConvertSpanToRange(SourceSpan sourceSpan, SourceText sourceText)
{
if (sourceSpan == SourceSpan.Undefined)
{
return null;
}
var spanStartIndex = NormalizeIndex(sourceSpan.AbsoluteIndex);
var startPosition = sourceText.Lines.GetLinePosition(spanStartIndex);
var start = new Position()
{
Line = startPosition.Line,
Character = startPosition.Character,
};
var spanEndIndex = NormalizeIndex(sourceSpan.AbsoluteIndex + sourceSpan.Length);
var endPosition = sourceText.Lines.GetLinePosition(spanEndIndex);
var end = new Position()
{
Line = endPosition.Line,
Character = endPosition.Character,
};
var range = new Range()
{
Start = start,
End = end,
};
return range;
int NormalizeIndex(int index)
{
if (index >= sourceText.Length)
{
// Span start index is past the end of the document. Roslyn and VSCode don't support
// virtual positions that don't exist on the document; normalize to the last character.
index = sourceText.Length - 1;
}
return Math.Max(index, 0);
}
}
}
}

Просмотреть файл

@ -0,0 +1,231 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class RazorDiagnosticsPublisher : DocumentProcessedListener
{
// Internal for testing
internal readonly Dictionary<string, IReadOnlyList<RazorDiagnostic>> _publishedDiagnostics;
internal Timer _workTimer;
internal Timer _documentClosedTimer;
private static readonly TimeSpan PublishDelay = TimeSpan.FromSeconds(2);
private static readonly TimeSpan CheckForDocumentClosedDelay = TimeSpan.FromSeconds(5);
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly ILanguageServer _languageServer;
private readonly Dictionary<string, DocumentSnapshot> _work;
private readonly ILogger<RazorDiagnosticsPublisher> _logger;
private ProjectSnapshotManager _projectManager;
public RazorDiagnosticsPublisher(
ForegroundDispatcher foregroundDispatcher,
ILanguageServer languageServer,
ILoggerFactory loggerFactory)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (languageServer == null)
{
throw new ArgumentNullException(nameof(languageServer));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_foregroundDispatcher = foregroundDispatcher;
_languageServer = languageServer;
_publishedDiagnostics = new Dictionary<string, IReadOnlyList<RazorDiagnostic>>(FilePathComparer.Instance);
_work = new Dictionary<string, DocumentSnapshot>(FilePathComparer.Instance);
_logger = loggerFactory.CreateLogger<RazorDiagnosticsPublisher>();
}
public override void Initialize(ProjectSnapshotManager projectManager)
{
if (projectManager == null)
{
throw new ArgumentNullException(nameof(projectManager));
}
_projectManager = projectManager;
}
public override void DocumentProcessed(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
_foregroundDispatcher.AssertForegroundThread();
lock (_work)
{
_work[document.FilePath] = document;
StartWorkTimer();
StartDocumentClosedCheckTimer();
}
}
private void StartWorkTimer()
{
_foregroundDispatcher.AssertForegroundThread();
// Access to the timer is protected by the lock in Synchronize and in Timer_Tick
if (_workTimer == null)
{
// Timer will fire after a fixed delay, but only once.
_workTimer = new Timer(WorkTimer_Tick, null, PublishDelay, Timeout.InfiniteTimeSpan);
}
}
private void StartDocumentClosedCheckTimer()
{
_foregroundDispatcher.AssertForegroundThread();
if (_documentClosedTimer == null)
{
_documentClosedTimer = new Timer(DocumentClosedTimer_Tick, null, CheckForDocumentClosedDelay, Timeout.InfiniteTimeSpan);
}
}
private async void DocumentClosedTimer_Tick(object state)
{
await Task.Factory.StartNew(
ClearClosedDocuments,
CancellationToken.None,
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler);
}
// Internal for testing
internal void ClearClosedDocuments()
{
lock (_publishedDiagnostics)
{
var publishedDiagnostics = new Dictionary<string, IReadOnlyList<RazorDiagnostic>>(_publishedDiagnostics);
foreach (var entry in publishedDiagnostics)
{
if (!_projectManager.IsDocumentOpen(entry.Key))
{
// Document is now closed, we shouldn't track its diagnostics anymore.
_publishedDiagnostics.Remove(entry.Key);
// If the last published diagnostics for the document were > 0 then we need to clear them out so the user
// doesn't have a ton of closed document errors that they can't get rid of.
if (entry.Value.Count > 0)
{
PublishDiagnosticsForFilePath(entry.Key, Array.Empty<Diagnostic>());
}
}
}
_documentClosedTimer?.Dispose();
_documentClosedTimer = null;
if (_publishedDiagnostics.Count > 0)
{
// There's no way for us to know when a document is closed at this layer. Therefore, we need to poll every X seconds
// and check if the currently tracked documents are closed. In practice this work is super minimal.
StartDocumentClosedCheckTimer();
}
}
}
// Internal for testing
internal async Task PublishDiagnosticsAsync(DocumentSnapshot document)
{
var result = await document.GetGeneratedOutputAsync();
var diagnostics = result.GetCSharpDocument().Diagnostics;
lock (_publishedDiagnostics)
{
if (_publishedDiagnostics.TryGetValue(document.FilePath, out var previousDiagnostics) &&
diagnostics.SequenceEqual(previousDiagnostics))
{
// Diagnostics are the same as last publish
return;
}
_publishedDiagnostics[document.FilePath] = diagnostics;
}
if (!document.TryGetText(out var sourceText))
{
Debug.Fail("Document source text should already be available.");
}
var convertedDiagnostics = diagnostics.Select(razorDiagnostic => RazorDiagnosticConverter.Convert(razorDiagnostic, sourceText));
PublishDiagnosticsForFilePath(document.FilePath, convertedDiagnostics);
if (_logger.IsEnabled(LogLevel.Trace))
{
var diagnosticString = string.Join(", ", diagnostics.Select(diagnostic => diagnostic.Id));
_logger.LogTrace($"Publishing diagnostics for document '{document.FilePath}': {diagnosticString}");
}
}
private async void WorkTimer_Tick(object state)
{
DocumentSnapshot[] documents;
lock (_work)
{
documents = _work.Values.ToArray();
_work.Clear();
}
for (var i = 0; i < documents.Length; i++)
{
var document = documents[i];
await PublishDiagnosticsAsync(document);
}
lock (_work)
{
// Resetting the timer allows another batch of work to start.
_workTimer.Dispose();
_workTimer = null;
// If more work came in while we were running start the timer again.
if (_work.Count > 0)
{
StartWorkTimer();
}
}
}
private void PublishDiagnosticsForFilePath(string filePath, IEnumerable<Diagnostic> diagnostics)
{
var uriBuilder = new UriBuilder()
{
Scheme = Uri.UriSchemeFile,
Path = filePath,
Host = string.Empty,
};
_languageServer.Document.PublishDiagnostics(new PublishDiagnosticsParams()
{
Uri = uriBuilder.Uri,
Diagnostics = new Container<Diagnostic>(diagnostics),
});
}
}
}

Просмотреть файл

@ -0,0 +1,178 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class RazorDocumentSynchronizationEndpoint : ITextDocumentSyncHandler
{
private SynchronizationCapability _capability;
private readonly ILogger _logger;
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly DocumentResolver _documentResolver;
private readonly RazorProjectService _projectService;
public RazorDocumentSynchronizationEndpoint(
ForegroundDispatcher foregroundDispatcher,
DocumentResolver documentResolver,
RazorProjectService projectService,
ILoggerFactory loggerFactory)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (documentResolver == null)
{
throw new ArgumentNullException(nameof(documentResolver));
}
if (projectService == null)
{
throw new ArgumentNullException(nameof(projectService));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_foregroundDispatcher = foregroundDispatcher;
_documentResolver = documentResolver;
_projectService = projectService;
_logger = loggerFactory.CreateLogger<RazorDocumentSynchronizationEndpoint>();
}
public TextDocumentSyncKind Change { get; } = TextDocumentSyncKind.Incremental;
public void SetCapability(SynchronizationCapability capability)
{
_capability = capability;
}
public async Task<Unit> Handle(DidChangeTextDocumentParams notification, CancellationToken token)
{
_foregroundDispatcher.AssertBackgroundThread();
var document = await Task.Factory.StartNew(() =>
{
_documentResolver.TryResolveDocument(notification.TextDocument.Uri.AbsolutePath, out var documentSnapshot);
return documentSnapshot;
}, CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
var sourceText = await document.GetTextAsync();
sourceText = ApplyContentChanges(notification.ContentChanges, sourceText);
await Task.Factory.StartNew(
() => _projectService.UpdateDocument(document.FilePath, sourceText, notification.TextDocument.Version),
CancellationToken.None,
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler);
return Unit.Value;
}
public async Task<Unit> Handle(DidOpenTextDocumentParams notification, CancellationToken token)
{
_foregroundDispatcher.AssertBackgroundThread();
var sourceText = SourceText.From(notification.TextDocument.Text);
await Task.Factory.StartNew(
() => _projectService.OpenDocument(notification.TextDocument.Uri.AbsolutePath, sourceText, notification.TextDocument.Version),
CancellationToken.None,
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler);
return Unit.Value;
}
public async Task<Unit> Handle(DidCloseTextDocumentParams notification, CancellationToken token)
{
_foregroundDispatcher.AssertBackgroundThread();
await Task.Factory.StartNew(
() => _projectService.CloseDocument(notification.TextDocument.Uri.AbsolutePath),
CancellationToken.None,
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler);
return Unit.Value;
}
public Task<Unit> Handle(DidSaveTextDocumentParams notification, CancellationToken token)
{
_logger.LogInformation($"Saved Document {notification.TextDocument.Uri.AbsolutePath}");
return Unit.Task;
}
public TextDocumentAttributes GetTextDocumentAttributes(Uri uri)
{
return new TextDocumentAttributes(uri, "razor");
}
TextDocumentChangeRegistrationOptions IRegistration<TextDocumentChangeRegistrationOptions>.GetRegistrationOptions()
{
return new TextDocumentChangeRegistrationOptions()
{
DocumentSelector = RazorDefaults.Selector,
SyncKind = Change
};
}
TextDocumentRegistrationOptions IRegistration<TextDocumentRegistrationOptions>.GetRegistrationOptions()
{
return new TextDocumentRegistrationOptions()
{
DocumentSelector = RazorDefaults.Selector,
};
}
TextDocumentSaveRegistrationOptions IRegistration<TextDocumentSaveRegistrationOptions>.GetRegistrationOptions()
{
return new TextDocumentSaveRegistrationOptions()
{
DocumentSelector = RazorDefaults.Selector,
IncludeText = true
};
}
// Internal for testing
internal SourceText ApplyContentChanges(IEnumerable<TextDocumentContentChangeEvent> contentChanges, SourceText sourceText)
{
foreach (var change in contentChanges)
{
var linePosition = new LinePosition((int)change.Range.Start.Line, (int)change.Range.Start.Character);
var position = sourceText.Lines.GetPosition(linePosition);
var textSpan = new TextSpan(position, change.RangeLength);
var textChange = new TextChange(textSpan, change.Text);
_logger.LogTrace("Applying " + textChange);
// If there happens to be multiple text changes we generate a new source text for each one. Due to the
// differences in VSCode and Roslyn's representation we can't pass in all changes simultaneously because
// ordering may differ.
sourceText = sourceText.WithChanges(textChange);
}
return sourceText;
}
}
}

Просмотреть файл

@ -0,0 +1,229 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class RazorLanguageEndpoint : IRazorLanguageQueryHandler
{
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly DocumentResolver _documentResolver;
private readonly DocumentVersionCache _documentVersionCache;
private readonly ILogger _logger;
public RazorLanguageEndpoint(
ForegroundDispatcher foregroundDispatcher,
DocumentResolver documentResolver,
DocumentVersionCache documentVersionCache,
ILoggerFactory loggerFactory)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (documentResolver == null)
{
throw new ArgumentNullException(nameof(documentResolver));
}
if (documentVersionCache == null)
{
throw new ArgumentNullException(nameof(documentVersionCache));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_foregroundDispatcher = foregroundDispatcher;
_documentResolver = documentResolver;
_documentVersionCache = documentVersionCache;
_logger = loggerFactory.CreateLogger<RazorLanguageEndpoint>();
}
public async Task<RazorLanguageQueryResponse> Handle(RazorLanguageQueryParams request, CancellationToken cancellationToken)
{
long documentVersion = -1;
DocumentSnapshot documentSnapshot = null;
await Task.Factory.StartNew(() =>
{
_documentResolver.TryResolveDocument(request.Uri.AbsolutePath, out documentSnapshot);
if (!_documentVersionCache.TryGetDocumentVersion(documentSnapshot, out documentVersion))
{
Debug.Fail("Document should always be available here.");
}
return documentSnapshot;
}, CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
var codeDocument = await documentSnapshot.GetGeneratedOutputAsync();
var sourceText = await documentSnapshot.GetTextAsync();
var linePosition = new LinePosition((int)request.Position.Line, (int)request.Position.Character);
var hostDocumentIndex = sourceText.Lines.GetPosition(linePosition);
var responsePosition = request.Position;
if (codeDocument.IsUnsupported())
{
// All language queries on unsupported documents return Html. This is equivalent to what pre-VSCode Razor was capable of.
return new RazorLanguageQueryResponse()
{
Kind = RazorLanguageKind.Html,
Position = responsePosition,
PositionIndex = hostDocumentIndex,
HostDocumentVersion = documentVersion,
};
}
var syntaxTree = codeDocument.GetSyntaxTree();
var classifiedSpans = syntaxTree.GetClassifiedSpans();
var tagHelperSpans = syntaxTree.GetTagHelperSpans();
var languageKind = GetLanguageKind(classifiedSpans, tagHelperSpans, hostDocumentIndex);
var responsePositionIndex = hostDocumentIndex;
if (languageKind == RazorLanguageKind.CSharp)
{
if (TryGetCSharpProjectedPosition(codeDocument, hostDocumentIndex, out var projectedPosition, out var projectedIndex))
{
// For C# locations, we attempt to return the corresponding position
// within the projected document
responsePosition = projectedPosition;
responsePositionIndex = projectedIndex;
}
else
{
// It no longer makes sense to think of this location as C#, since it doesn't
// correspond to any position in the projected document. This should not happen
// since there should be source mappings for all the C# spans.
languageKind = RazorLanguageKind.Razor;
responsePositionIndex = hostDocumentIndex;
}
}
_logger.LogTrace($"Language query request for ({request.Position.Line}, {request.Position.Character}) = {languageKind} at ({responsePosition.Line}, {responsePosition.Character})");
return new RazorLanguageQueryResponse()
{
Kind = languageKind,
Position = responsePosition,
PositionIndex = responsePositionIndex,
HostDocumentVersion = documentVersion
};
}
// Internal for testing
internal static RazorLanguageKind GetLanguageKind(
IReadOnlyList<ClassifiedSpanInternal> classifiedSpans,
IReadOnlyList<TagHelperSpanInternal> tagHelperSpans,
int absoluteIndex)
{
for (var i = 0; i < classifiedSpans.Count; i++)
{
var classifiedSpan = classifiedSpans[i];
var span = classifiedSpan.Span;
if (span.AbsoluteIndex <= absoluteIndex)
{
var end = span.AbsoluteIndex + span.Length;
if (end >= absoluteIndex)
{
if (end == absoluteIndex)
{
// We're at an edge.
if (span.Length > 0 &&
classifiedSpan.AcceptedCharacters == AcceptedCharactersInternal.None)
{
// Non-marker spans do not own the edges after it
continue;
}
}
// Overlaps with request
switch (classifiedSpan.SpanKind)
{
case SpanKindInternal.Markup:
return RazorLanguageKind.Html;
case SpanKindInternal.Code:
return RazorLanguageKind.CSharp;
}
// Content type was non-C# or Html or we couldn't find a classified span overlapping the request position.
// All other classified span kinds default back to Razor
return RazorLanguageKind.Razor;
}
}
}
for (var i = 0; i < tagHelperSpans.Count; i++)
{
var tagHelperSpan = tagHelperSpans[i];
var span = tagHelperSpan.Span;
if (span.AbsoluteIndex <= absoluteIndex)
{
var end = span.AbsoluteIndex + span.Length;
if (end >= absoluteIndex)
{
if (end == absoluteIndex)
{
// We're at an edge. TagHelper spans never own their edge and aren't represented by marker spans
continue;
}
// Found intersection
return RazorLanguageKind.Html;
}
}
}
// Default to Razor
return RazorLanguageKind.Razor;
}
// Internal for testing
internal static bool TryGetCSharpProjectedPosition(RazorCodeDocument codeDocument, int absoluteIndex, out Position projectedPosition, out int projectedIndex)
{
var csharpDoc = codeDocument.GetCSharpDocument();
foreach (var mapping in csharpDoc.SourceMappings)
{
var originalSpan = mapping.OriginalSpan;
var originalAbsoluteIndex = originalSpan.AbsoluteIndex;
if (originalAbsoluteIndex <= absoluteIndex)
{
// Treat the mapping as owning the edge at its end (hence <= originalSpan.Length),
// otherwise we wouldn't handle the cursor being right after the final C# char
var distanceIntoOriginalSpan = absoluteIndex - originalAbsoluteIndex;
if (distanceIntoOriginalSpan <= originalSpan.Length)
{
var generatedSource = SourceText.From(csharpDoc.GeneratedCode);
projectedIndex = mapping.GeneratedSpan.AbsoluteIndex + distanceIntoOriginalSpan;
var generatedLinePosition = generatedSource.Lines.GetLinePosition(projectedIndex);
projectedPosition = new Position(generatedLinePosition.Line, generatedLinePosition.Character);
return true;
}
}
}
projectedPosition = default;
projectedIndex = default;
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
public enum RazorLanguageKind
{
CSharp = 1,
Html = 2,
Razor = 3
}
}

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class RazorLanguageQueryParams : IRequest<RazorLanguageQueryResponse>
{
public Uri Uri { get; set; }
public Position Position { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class RazorLanguageQueryResponse
{
public RazorLanguageKind Kind { get; set; }
public int PositionIndex { get; set; }
public Position Position { get; set; }
public long HostDocumentVersion { get; set; }
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше