diff --git a/.editorconfig b/.editorconfig index b1bfe93a1..0786fbe27 100644 --- a/.editorconfig +++ b/.editorconfig @@ -54,11 +54,11 @@ dotnet_diagnostic.CA1068.severity = error # CA1501: Avoid excessive inheritance dotnet_diagnostic.CA1501.severity = error # CA1502: Avoid excessive complexity -dotnet_diagnostic.CA1502.severity = warning +dotnet_diagnostic.CA1502.severity = silent # CA1505: Avoid unmaintainable code dotnet_diagnostic.CA1505.severity = error # CA1506: Avoid excessive class coupling -dotnet_diagnostic.CA1506.severity = warning +dotnet_diagnostic.CA1506.severity = silent # CA1507: Use nameof in place of string dotnet_diagnostic.CA1507.severity = error # CA1508: Avoid dead conditional code @@ -95,22 +95,22 @@ dotnet_diagnostic.RCS1210.severity = error # RCS1036: Remove unnecessary blank line dotnet_diagnostic.RCS1036.severity = error # RCS1075: Avoid empty catch clause that catches System.Exception -dotnet_diagnostic.RCS1075.severity = suggestion +dotnet_diagnostic.RCS1075.severity = error # RCS1170: Use read-only auto-implemented property dotnet_diagnostic.RCS1170.severity = error # VSTHRD002: Avoid problematic synchronous waits -dotnet_diagnostic.VSTHRD002.severity = suggestion +dotnet_diagnostic.VSTHRD002.severity = error # VSTHRD003: Avoid awaiting foreign Tasks -dotnet_diagnostic.VSTHRD003.severity = suggestion +dotnet_diagnostic.VSTHRD003.severity = error # VSTHRD105: Avoid method overloads that assume TaskScheduler.Current -dotnet_diagnostic.VSTHRD105.severity = suggestion +dotnet_diagnostic.VSTHRD105.severity = error # VSTHRD100: Avoid async void methods -dotnet_diagnostic.VSTHRD100.severity = suggestion +dotnet_diagnostic.VSTHRD100.severity = error # VSTHRD103: Call async methods when in an async method -dotnet_diagnostic.VSTHRD103.severity = suggestion +dotnet_diagnostic.VSTHRD103.severity = error # VSTHRD110: Observe result of async calls -dotnet_diagnostic.VSTHRD110.severity = suggestion +dotnet_diagnostic.VSTHRD110.severity = error # VSTHRD114: Avoid returning a null Task dotnet_diagnostic.VSTHRD114.severity = error # VSTHRD200: Use "Async" suffix for awaitable methods diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..e0c1cb705 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,9 @@ +# Enable this file in your git config: git config blame.ignoreRevsFile .git-blame-ignore-revs +# Enabled on GitHub automatically + +# Close Over APIs, was mostly reformatting +945d61634784db2e51f894c9606e785a099fd23d +# Dotnet Format Style +44387b36695607248cebb9467ad48061c19354cb +# Formatting Fixup +7233182585b63760992545c7407b17fb2965bc5c diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index e3a2fb57f..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: 2 -updates: -- package-ecosystem: nuget - directory: "/" - labels: [ ] - schedule: - interval: weekly - open-pull-requests-limit: 10 - groups: - OmniSharp: - patterns: - - "OmniSharp.Extensions.*" - xUnit: - patterns: - - "xunit" - - "xunit.*" -- package-ecosystem: github-actions - directory: "/" - labels: [ ] - schedule: - interval: weekly diff --git a/.github/release.yml b/.github/release.yml index bb7a942a4..cc5a1aa80 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -3,7 +3,7 @@ changelog: labels: - Ignore authors: - - dependabot[bot] + - dependabot categories: - title: Enhancements & Features ✨ labels: diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index fd93eb384..d4393920e 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -4,73 +4,70 @@ on: push: branches: [ main ] pull_request: - # The branches below must be a subset of the branches above branches: [ main ] - paths-ignore: [ '**/*.md' ] - merge_group: - types: [ checks_requested ] - schedule: - # 6am UTC which should be after a new daily build posts - - cron: "0 6 * * *" jobs: ci: name: dotnet strategy: + fail-fast: false matrix: os: [ windows-latest, macos-latest, ubuntu-latest ] runs-on: ${{ matrix.os }} env: DOTNET_NOLOGO: true - DOTNET_CLI_TELEMETRY_OPTOUT: true DOTNET_GENERATE_ASPNET_CERTIFICATE: false steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install dotnet - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: cache: true - cache-dependency-path: '**/packages.lock.json' - dotnet-version: | - 6.0.x - 7.0.x - 8.0.x - source-url: https://pkgs.dev.azure.com/mscodehub/PowerShellCore/_packaging/PowerShellCore_PublicPackages/nuget/v3/index.json - env: - NUGET_AUTH_TOKEN: ${{ secrets.AZURE_NUGET_TOKEN }} + cache-dependency-path: '**/*.csproj' + global-json-file: ./global.json - name: Install PSResources shell: pwsh run: ./tools/installPSResources.ps1 - - name: Download daily install script - uses: actions/checkout@v4 + - name: Download PowerShell install script + uses: actions/checkout@v6 with: repository: PowerShell/PowerShell path: pwsh sparse-checkout: tools/install-powershell.ps1 sparse-checkout-cone-mode: false - - name: Build and test + - name: Install preview shell: pwsh - run: Invoke-Build -Configuration Release ${{ github.event_name == 'merge_group' && 'TestFull' || 'Test' }} + run: ./pwsh/tools/install-powershell.ps1 -Preview -Destination ./preview - - name: Test with daily - if: ${{ github.event_name == 'schedule' }} + - name: Build and test shell: pwsh - run: ./pwsh/tools/install-powershell.ps1 -Daily && Invoke-Build -Configuration Release TestE2EDaily + run: Invoke-Build -Configuration Release TestFull + + # - name: Start VS Code Tunnel if failed or in debug mode + # if: ${{ failure() || runner.debug == '1' }} + # #v0.0.2 - Pinned for security + # uses: justingrote/vscode-action@5d495ef6156c20f1f9bb21031c15776d476c10c3 + # with: + # #All these settings are optional + # tunnel-name: 'pses-action-tunnel' + # connection-timeout: 2 + # session-timeout: 30 + # no-cache-cli-auth: true - name: Upload build artifacts if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: PowerShellEditorServices-module-${{ matrix.os }} path: module - name: Upload test results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 if: always() with: name: PowerShellEditorServices-test-results-${{ matrix.os }} diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index 8ababb3c9..7349ee838 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -11,7 +11,7 @@ jobs: stale-resolved-issues: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 name: Label resolved issues as needing fix verification with: any-of-labels: "Resolution-Answered,Resolution-Duplicate,Resolution-External,Resolution-Fixed,Resolution-Inactive" @@ -23,7 +23,7 @@ jobs: stale-fixed-issues: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 name: Close issues needing fix verification after 1 week of inactivity with: stale-issue-label: "Needs: Fix Verification" @@ -34,7 +34,7 @@ jobs: stale-feedback-issues: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 name: Close issues needing author feedback after 1 week of inactivity with: stale-issue-label: "Needs: Author Feedback" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 018eaaa77..000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: CodeQL Analysis - -on: - push: - branches: [ main ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] - paths-ignore: [ '**/*.md' ] - -jobs: - analyze: - name: analyze - strategy: - fail-fast: false - matrix: - language: [ csharp ] - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - env: - DOTNET_NOLOGO: true - DOTNET_CLI_TELEMETRY_OPTOUT: true - DOTNET_GENERATE_ASPNET_CERTIFICATE: false - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install dotnet - uses: actions/setup-dotnet@v4 - with: - cache: true - cache-dependency-path: '**/packages.lock.json' - source-url: https://pkgs.dev.azure.com/mscodehub/PowerShellCore/_packaging/PowerShellCore_PublicPackages/nuget/v3/index.json - env: - NUGET_AUTH_TOKEN: ${{ secrets.AZURE_NUGET_TOKEN }} - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - - - name: Install PSResources - shell: pwsh - run: tools/installPSResources.ps1 - - - name: Build - shell: pwsh - run: Invoke-Build Build - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: '/language:${{matrix.language}}' diff --git a/.github/workflows/emacs-test.yml b/.github/workflows/emacs-test.yml index 31f840581..32fb728a2 100644 --- a/.github/workflows/emacs-test.yml +++ b/.github/workflows/emacs-test.yml @@ -6,7 +6,6 @@ on: pull_request: # The branches below must be a subset of the branches above branches: [ main ] - paths-ignore: [ '**/*.md' ] merge_group: types: [ checks_requested ] @@ -16,20 +15,16 @@ jobs: runs-on: ubuntu-latest env: DOTNET_NOLOGO: true - DOTNET_CLI_TELEMETRY_OPTOUT: true DOTNET_GENERATE_ASPNET_CERTIFICATE: false steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install dotnet - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: cache: true - cache-dependency-path: '**/packages.lock.json' - source-url: https://pkgs.dev.azure.com/mscodehub/PowerShellCore/_packaging/PowerShellCore_PublicPackages/nuget/v3/index.json - env: - NUGET_AUTH_TOKEN: ${{ secrets.AZURE_NUGET_TOKEN }} + cache-dependency-path: '**/*.csproj' - name: Install PSResources shell: pwsh @@ -40,7 +35,7 @@ jobs: run: Invoke-Build Build - name: Install Emacs - uses: purcell/setup-emacs@master + uses: purcell/setup-emacs@v8.0 with: version: '28.2' diff --git a/.github/workflows/vim-test.yml b/.github/workflows/vim-test.yml index b434fcb45..b04d5272a 100644 --- a/.github/workflows/vim-test.yml +++ b/.github/workflows/vim-test.yml @@ -6,7 +6,6 @@ on: pull_request: # The branches below must be a subset of the branches above branches: [ main ] - paths-ignore: [ '**/*.md' ] merge_group: types: [ checks_requested ] @@ -16,20 +15,16 @@ jobs: runs-on: ubuntu-latest env: DOTNET_NOLOGO: true - DOTNET_CLI_TELEMETRY_OPTOUT: true DOTNET_GENERATE_ASPNET_CERTIFICATE: false steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install dotnet - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: cache: true - cache-dependency-path: '**/packages.lock.json' - source-url: https://pkgs.dev.azure.com/mscodehub/PowerShellCore/_packaging/PowerShellCore_PublicPackages/nuget/v3/index.json - env: - NUGET_AUTH_TOKEN: ${{ secrets.AZURE_NUGET_TOKEN }} + cache-dependency-path: '**/*.csproj' - name: Install PSResources shell: pwsh @@ -40,16 +35,19 @@ jobs: run: Invoke-Build Build - name: Install Vim + id: vim uses: rhysd/action-setup-vim@v1 + with: + version: nightly - name: Checkout vim-ps1 - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: PProvost/vim-ps1 path: vim-ps1 - name: Checkout LanguageClient-neovim - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: autozimu/LanguageClient-neovim path: LanguageClient-neovim @@ -59,7 +57,7 @@ jobs: working-directory: LanguageClient-neovim - name: Checkout Themis - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: thinca/vim-themis path: vim-themis @@ -73,3 +71,14 @@ jobs: env: THEMIS_VIM: ${{ steps.vim.outputs.executable }} run: ./vim-themis/bin/themis ./test/vim-simple-test.vim + + # - name: Start VS Code Tunnel if failed or in debug mode + # if: ${{ failure() || runner.debug == '1' }} + # #v0.0.2 - Pinned for security + # uses: justingrote/vscode-action@5d495ef6156c20f1f9bb21031c15776d476c10c3 + # with: + # #All these settings are optional + # tunnel-name: 'pses-action-tunnel' + # connection-timeout: 2 + # session-timeout: 30 + # no-cache-cli-auth: true diff --git a/.gitignore b/.gitignore index f73bd5823..d8c2d5b16 100644 --- a/.gitignore +++ b/.gitignore @@ -1,79 +1,4 @@ -_ReSharper* -[Bb]in -bin-nano -obj -objd -out/ -tmp/ -.tmp -App_Data -*.user -*.sln.cache -*.suo -TestResults -test/emacs-session.json -[Tt]humbs.db -buildd.* -release/ -*.log -*.bak -packages -OACRTemp/ -build_logs/ -lock -/public/inc/bldver.* -/public/inc/sources.ver -/data -/target -.corext/gen -registered_data.ini -.vs/ -.dotnet/ -module/Plaster -module/PSScriptAnalyzer -module/PSReadLine -docs/_site/ -docs/_repo/ -docs/metadata/ -*.zip - -# Generated build info file -src/PowerShellEditorServices.Hosting/BuildInfo.cs - -# quickbuild.exe -/VersionGeneratingLogs/ -QLogs -QLocal -QTestLogs - -# bad tlb/chm generators in nmake tree -*.tlb -*.chm - -# dumb silverlight -ClientBin/ - -# dump azure -*.build.csdef -csx/ - -# Don't include ScriptAnalyzer binaries -PowerShellEditorServices/** -PowerShellEditorServices.NoNano/** - -PowerShellEditorServices.sln.ide/edb.chk -PowerShellEditorServices.sln.ide/edbres00001.jrs -PowerShellEditorServices.sln.ide/storage.ide -*.jrs - -# Don't include PlatyPS generated MAML -module/PowerShellEditorServices/Commands/en-US/*-help.xml - -# Don't include Third Party Notices in module folder -module/PowerShellEditorServices/Third\ Party\ Notices.txt - -# Visual Studio for Mac generated file -*.userprefs - -# JetBrains generated file (Rider, intelliJ) -.idea/ +bin/ +obj/ +module/ +TestResults/ diff --git a/.pipelines/PowerShellEditorServices-Official.yml b/.pipelines/PowerShellEditorServices-Official.yml deleted file mode 100644 index f1d47c08c..000000000 --- a/.pipelines/PowerShellEditorServices-Official.yml +++ /dev/null @@ -1,182 +0,0 @@ -################################################################################# -# OneBranch Pipelines # -# This pipeline was created by EasyStart from a sample located at: # -# https://aka.ms/obpipelines/easystart/samples # -# Documentation: https://aka.ms/obpipelines # -# Yaml Schema: https://aka.ms/obpipelines/yaml/schema # -# Retail Tasks: https://aka.ms/obpipelines/tasks # -# Support: https://aka.ms/onebranchsup # -################################################################################# - -trigger: none - -parameters: -- name: debug - displayName: Enable debug output - type: boolean - default: false - -variables: - system.debug: ${{ parameters.debug }} - BuildConfiguration: Release - WindowsContainerImage: onebranch.azurecr.io/windows/ltsc2019/vse2022:latest - DOTNET_NOLOGO: true - DOTNET_CLI_TELEMETRY_OPTOUT: true - DOTNET_GENERATE_ASPNET_CERTIFICATE: false - -resources: - repositories: - - repository: templates - type: git - name: OneBranch.Pipelines/GovernedTemplates - ref: refs/heads/main - -extends: - # https://aka.ms/obpipelines/templates - template: v2/OneBranch.Official.CrossPlat.yml@templates - parameters: - globalSdl: # https://aka.ms/obpipelines/sdl - asyncSdl: - enabled: true - forStages: [build] - stages: - - stage: build - jobs: - - job: main - displayName: Build package - pool: - type: windows - variables: - ob_outputDirectory: $(Build.SourcesDirectory)/module - steps: - - pwsh: | - [xml]$xml = Get-Content PowerShellEditorServices.Common.props - $version = $xml.Project.PropertyGroup.VersionPrefix - Write-Output "##vso[task.setvariable variable=version;isOutput=true]$version" - name: package - displayName: Get version from project properties - - task: onebranch.pipeline.version@1 - displayName: Set OneBranch version - inputs: - system: Custom - customVersion: $(package.version) - - task: UseDotNet@2 - displayName: Install .NET 8.x SDK - inputs: - packageType: sdk - version: 8.x - - task: UseDotNet@2 - displayName: Install .NET 7.x runtime - inputs: - packageType: runtime - version: 7.x - - task: UseDotNet@2 - displayName: Install .NET 6.x runtime - inputs: - packageType: runtime - version: 6.x - - task: PowerShell@2 - displayName: Install PSResources - inputs: - pwsh: true - filePath: tools/installPSResources.ps1 - - task: PowerShell@2 - displayName: Build and test - inputs: - targetType: inline - pwsh: true - script: Invoke-Build TestFull -Configuration $(BuildConfiguration) - - task: PublishTestResults@2 - displayName: Publish test results - inputs: - testRunner: VSTest - testResultsFiles: '**/*.trx' - failTaskOnFailedTests: true - - task: PowerShell@2 - displayName: Assert release configuration - inputs: - targetType: inline - pwsh: true - script: | - $assembly = [Reflection.Assembly]::LoadFile("$(Build.SourcesDirectory)/module/PowerShellEditorServices/bin/Core/Microsoft.PowerShell.EditorServices.Hosting.dll") - if ($assembly.GetCustomAttributes([System.Diagnostics.DebuggableAttribute], $true).IsJITOptimizerDisabled) { - Write-Host "##vso[task.LogIssue type=error;]Was not built in release configuration!" - exit 1 - } - - task: onebranch.pipeline.signing@1 - displayName: Sign 1st-party files - inputs: - command: sign - signing_environment: external_distribution - search_root: $(Build.SourcesDirectory)/module - files_to_sign: | - **/*.ps1; - **/*.psd1; - **/*.psm1; - **/*.ps1xml; - **/Microsoft.PowerShell.EditorServices*.dll; - !Plaster/*; - - task: onebranch.pipeline.signing@1 - displayName: Sign 3rd-party files - inputs: - command: sign - signing_environment: 135020002 - search_root: $(Build.SourcesDirectory)/module - files_to_sign: | - **/MediatR.dll; - **/Nerdbank.Streams.dll; - **/Newtonsoft.Json.dll; - **/OmniSharp.Extensions*.dll; - **/Serilog*.dll; - **/System.Reactive.dll; - Plaster/**/*.ps1; - Plaster/**/*.psd1; - Plaster/**/*.psm1; - - stage: release - dependsOn: build - variables: - version: $[ stageDependencies.build.main.outputs['package.version'] ] - drop: $(Pipeline.Workspace)/drop_build_main - jobs: - - job: validation - displayName: Manual validation - pool: - type: agentless - timeoutInMinutes: 1440 - steps: - - task: ManualValidation@0 - displayName: Wait 24 hours for validation - inputs: - notifyUsers: $(Build.RequestedForEmail) - instructions: Please validate the release - timeoutInMinutes: 1440 - - job: github - dependsOn: validation - displayName: Publish draft to GitHub - pool: - type: windows - variables: - ob_outputDirectory: $(Build.SourcesDirectory)/out - steps: - - download: current - displayName: Download artifacts - - task: ArchiveFiles@2 - displayName: Zip signed artifacts - inputs: - rootFolderOrFile: $(drop) - includeRootFolder: false - archiveType: zip - archiveFile: out/PowerShellEditorServices.zip - - task: GitHubRelease@1 - displayName: Create GitHub release - inputs: - gitHubConnection: GitHub - repositoryName: PowerShell/PowerShellEditorServices - assets: out/PowerShellEditorServices.zip - tagSource: userSpecifiedTag - tag: v$(version) - isDraft: true - addChangeLog: false - releaseNotesSource: inline - releaseNotesInline: | - # TODO: Generate release notes on GitHub! diff --git a/.pipelines/PowerShellEditorServices-OneBranch.yml b/.pipelines/PowerShellEditorServices-OneBranch.yml new file mode 100644 index 000000000..980603f14 --- /dev/null +++ b/.pipelines/PowerShellEditorServices-OneBranch.yml @@ -0,0 +1,174 @@ +################################################################################# +# OneBranch Pipelines # +# This pipeline was created by EasyStart from a sample located at: # +# https://aka.ms/obpipelines/easystart/samples # +# Documentation: https://aka.ms/obpipelines # +# Yaml Schema: https://aka.ms/obpipelines/yaml/schema # +# Retail Tasks: https://aka.ms/obpipelines/tasks # +# Support: https://aka.ms/onebranchsup # +################################################################################# + +trigger: + - main + +schedules: + - cron: "35 13 * * 4" + displayName: Weekly CodeQL + branches: + include: + - main + always: true + +resources: + repositories: + - repository: templates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +parameters: + - name: debug + displayName: Enable debug output + type: boolean + default: false + - name: OfficialBuild + displayName: Use Official OneBranch template + type: boolean + default: true + - name: Release + displayName: Generate a release + type: boolean + default: false + +variables: + system.debug: ${{ parameters.debug }} + BuildConfiguration: Release + WindowsContainerImage: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest + DOTNET_NOLOGO: true + DOTNET_GENERATE_ASPNET_CERTIFICATE: false + OneBranchTemplate: ${{ iif(parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@templates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@templates') }} + +extends: + # https://aka.ms/obpipelines/templates + template: ${{ variables.OneBranchTemplate }} + parameters: + globalSdl: # https://aka.ms/obpipelines/sdl + asyncSdl: + enabled: true + forStages: [build] + featureFlags: + EnableCDPxPAT: false + WindowsHostVersion: + Version: 2022 + Network: KS3 + release: + category: NonAzure + stages: + - stage: build + jobs: + - job: main + displayName: Build package + pool: + type: windows + variables: + ob_outputDirectory: $(Build.SourcesDirectory)/out + steps: + - pwsh: | + [xml]$xml = Get-Content PowerShellEditorServices.Common.props + $version = $xml.Project.PropertyGroup.VersionPrefix + $prerelease = $xml.Project.PropertyGroup.VersionSuffix + if ($prerelease) { $version += "-$prerelease" } + Write-Output "##vso[task.setvariable variable=version;isOutput=true]$version" + Write-Output "##vso[task.setvariable variable=prerelease;isOutput=true]$(-not [string]::IsNullOrEmpty($prerelease))" + name: package + displayName: Get version from project properties + - task: onebranch.pipeline.version@1 + displayName: Set OneBranch version + inputs: + system: Custom + customVersion: $(package.version) + - task: UseDotNet@2 + displayName: Use .NET 8.x SDK + inputs: + packageType: sdk + useGlobalJson: true + - pwsh: ./tools/installPSResources.ps1 -PSRepository CFS + displayName: Install PSResources + - pwsh: Invoke-Build TestFull -Configuration $(BuildConfiguration) -PSRepository CFS + displayName: Build and test + - task: PublishTestResults@2 + displayName: Publish test results + inputs: + testRunner: VSTest + testResultsFiles: "**/*.trx" + failTaskOnFailedTests: true + - pwsh: | + $assembly = [Reflection.Assembly]::LoadFile("$(Build.SourcesDirectory)/module/PowerShellEditorServices/bin/Core/Microsoft.PowerShell.EditorServices.Hosting.dll") + if ($assembly.GetCustomAttributes([System.Diagnostics.DebuggableAttribute], $true).IsJITOptimizerDisabled) { + Write-Host "##vso[task.LogIssue type=error;]Was not built in release configuration!" + exit 1 + } + displayName: Assert release configuration + continueOnError: true + - task: onebranch.pipeline.signing@1 + displayName: Sign 1st-party files + inputs: + command: sign + signing_profile: external_distribution + search_root: $(Build.SourcesDirectory)/module + files_to_sign: | + **/*.ps1; + **/*.psd1; + **/*.psm1; + **/*.ps1xml; + **/Microsoft.PowerShell.EditorServices*.dll; + - task: onebranch.pipeline.signing@1 + displayName: Sign 3rd-party files + inputs: + command: sign + signing_profile: 135020002 + search_root: $(Build.SourcesDirectory)/module + files_to_sign: | + **/MediatR.dll; + **/Nerdbank.Streams.dll; + **/Newtonsoft.Json.dll; + **/OmniSharp.Extensions*.dll; + **/System.Reactive.dll; + - task: ArchiveFiles@2 + displayName: Zip signed artifacts + inputs: + rootFolderOrFile: $(Build.SourcesDirectory)/module + includeRootFolder: false + archiveType: zip + archiveFile: out/PowerShellEditorServices.zip + - stage: release + dependsOn: build + condition: and(succeeded(), ${{ eq(parameters.Release, true) }}) + variables: + ob_release_environment: ${{ iif(parameters.OfficialBuild, 'Production', 'Test') }} + version: $[ stageDependencies.build.main.outputs['package.version'] ] + prerelease: $[ stageDependencies.build.main.outputs['package.prerelease'] ] + jobs: + - job: github + displayName: Publish draft to GitHub + pool: + type: release + templateContext: + inputs: + - input: pipelineArtifact + artifactName: drop_build_main + steps: + - task: GitHubRelease@1 + displayName: Create GitHub release + inputs: + gitHubConnection: github.com_andyleejordan + repositoryName: PowerShell/PowerShellEditorServices + target: main + assets: $(Pipeline.Workspace)/PowerShellEditorServices.zip + tagSource: userSpecifiedTag + tag: v$(version) + isDraft: true + isPreRelease: $(prerelease) + addChangeLog: false + releaseNotesSource: inline + releaseNotesInline: "" diff --git a/ADOPTERS.md b/ADOPTERS.md deleted file mode 100644 index ff80c2205..000000000 --- a/ADOPTERS.md +++ /dev/null @@ -1,17 +0,0 @@ -# Adopters - - - -This is a list of adopters of using PowerShell Editor Services in production or in their products (in alphabetical order): - -* [vscode-powershell](https://github.com/PowerShell/vscode-powershell) - Provides rich PowerShell language support for Visual Studio Code. You can write and debug PowerShell scripts using the excellent IDE-like interface that Visual Studio Code provides. - -* [coc-powershell](https://github.com/coc-extensions/coc-powershell) - A Vim and NeoVim plugin powered by PowerShellEditorServices and [coc.nvim](https://github.com/neoclide/coc.nvim) to provide a rich PowerShell editing experience. diff --git a/CHANGELOG.md b/CHANGELOG.md index 10c90056d..548812339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,103 @@ # PowerShell Editor Services Release History +## v4.6.0 +### Friday, May 15, 2026 + +See more details at the GitHub Release for [v4.6.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.6.0). + +New rename provider and workspace document API! + +## v4.5.0 +### Wednesday, April 08, 2026 + +See more details at the GitHub Release for [v4.5.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.5.0). + +New PSSA version and lots of fixes! + +## v4.4.0 +### Thursday, September 04, 2025 + +See more details at the GitHub Release for [v4.4.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.4.0). + +Supports screen readers! + +## Unreleased + +- ✨ 📟 [PowerShellEditorServices #2239](https://github.com/PowerShell/PowerShellEditorServices/pull/2239) - Update PSReadLine to v2.4.2-beta2. + +## v4.3.0 +### Tuesday, March 18, 2025 + +See more details at the GitHub Release for [v4.3.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.3.0). + +New packages! + +## v4.2.0 +### Thursday, January 16, 2025 + +See more details at the GitHub Release for [v4.2.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.2.0). + +Support PSScriptAnalyzer config in multi-root workspaces + +## v4.1.0 +### Wednesday, December 04, 2024 + +See more details at the GitHub Release for [v4.1.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.1.0). + +Debugger optimizations and incremental build support! + +## v4.0.0 +### Monday, November 18, 2024 + +See more details at the GitHub Release for [v4.0.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.0.0). + +Drop support for PowerShell <7.4 and logging overhaul + +PowerShell 7.2 LTS and 7.3 are now past end-of-support and are now unsupported. +This is an incompatible API change so we're bumping the major version. +Please update to PowerShell 7.4 LTS going forward. + +This release contains a logging overhaul which purposely removes our +dependency on Serilog and should lead to improved stability with +PowerShell 5.1 (by avoiding a major GAC assembly conflict). + +## v3.30.0 (mistakenly released as v3.3.0) +### Friday, November 15, 2024 + +See more details at the GitHub Release for [v3.3.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v3.3.0). + +Logging updates and dropped EOL PowerShell + +## v3.21.0 +### Wednesday, October 30, 2024 + +See more details at the GitHub Release for [v3.21.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v3.21.0). + +Updates to PSScriptAnalyzer and Call-operator support + +## v3.20.1 +### Friday, May 03, 2024 + +See more details at the GitHub Release for [v3.20.1](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v3.20.1). + +Update third-party notices. + +## v3.20.0 +### Tuesday, April 16, 2024 + +See more details at the GitHub Release for [v3.20.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v3.20.0). + +Hotfix for incorrect signing certificate, sorry about that! + +Also removed Plaster integration as we were unable to correctly sign it since we no longer own it. + +## v3.19.0 +### Wednesday, April 03, 2024 + +See more details at the GitHub Release for [v3.19.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v3.19.0). + +Overhauled Terminal Shell Integration! + ## v3.18.1 ### Tuesday, March 19, 2024 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index dddfa22df..686e5e7a0 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,8 +1,10 @@ -# Code of Conduct +# Microsoft Open Source Code of Conduct -This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. -For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments. +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -[conduct-code]: http://opensource.microsoft.com/codeofconduct/ -[conduct-FAQ]: http://opensource.microsoft.com/codeofconduct/faq/ -[conduct-email]: mailto:opencode@microsoft.com +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +- Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..193280892 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + true + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 000000000..c76c2a7e3 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 000000000..aabad504f --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,3496 @@ +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. +Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, +or you may send a check or money order for US $5.00, including the product name, +the open source component name, platform, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. + +--------------------------------------------------------- + +MediatR 8.1.0 - Apache-2.0 + + +Copyright Jimmy Bogard + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Bcl.AsyncInterfaces 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Bcl.AsyncInterfaces 8.0.0 - MIT + + +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.Configuration 6.0.1 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.Configuration.Abstractions 6.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.Configuration.Binder 6.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.DependencyInjection 8.0.0 - MIT + + +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.DependencyInjection.Abstractions 8.0.0 - MIT + + +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.FileSystemGlobbing 8.0.0 - MIT + + +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.Logging 8.0.0 - MIT + + +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.Logging.Abstractions 8.0.0 - MIT + + +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.Options 8.0.0 - MIT + + +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.Options.ConfigurationExtensions 6.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.Primitives 8.0.0 - MIT + + +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Threading 17.6.40 - MIT + + +(c) Andrew Arnott +(c) 2019 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) Manuel Romer +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright 2012 the V8 project +Copyright 1995-2017 Mark Adler +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) Rackspace, US Inc. +Copyright James Newton-King 2008 +Copyright (c) 2013 Scott Kirkland +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright 2012-2017 Mehdi Khalili +Copyright (c) 2015 .NET Foundation +Copyright (c) 2015 Christian Klutz +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) Outercurve Foundation +Copyright LibGit2Sharp contributors +(c) Yoshifumi Kawai and contributors +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) James Newton-King 2008 +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2012-2014 Mehdi Khalili +Copyright (c) 2013-2014 Omar Khudeira +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) .NET Foundation xUnit.net +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) .NET Foundation 0xUnit.net +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright James Newton-King 2008 Json.NET +Copyright (c) .NET Foundation Contributors +Copyright .NET Foundation and Contributors +Copyright 1995-2017 Mark Adler +3 CScs DEFG +Copyright 2012-2016 (c) 2008 VeriSign, Inc. +Copyright (c) 2020 Mara Bos +(c) Antoine Aubry and contributors 2008 - 2019 +Copyright (c) .NET Foundation and Contributors +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright AssemblyCompany AssemblyConfiguration +Copyright (c) 2017 Yoshifumi Kawai and contributors +Copyright 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright 2012-2016 Copyright 2012-2017 Mehdi Khalili +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) .NET Foundation ,xUnit.net Runner Utility +Copyright (c) .NET Foundation 1xUnit.net Runner Utility +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) .NET Foundation xUnit.net Runner Reporters +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) Antoine Aubry and contributors 2008 - 2019 +Copyright (c) .NET Foundation .xUnit.net Runner Reporters +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS +Copyright (c) .NET Foundation and Contributors. Visual Studio 2019 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net +Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors +Copyright Tunnel Vision Laboratories, LLC 2018 1Copyright Tunnel Vision Laboratories, LLC 2018 NAn +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic Analyzer +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Threading.Analyzers 17.6.40 - MIT + + +(c) Andrew Arnott +(c) 2019 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) Manuel Romer +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright 2012 the V8 project +Copyright 1995-2017 Mark Adler +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) Rackspace, US Inc. +Copyright James Newton-King 2008 +Copyright (c) 2013 Scott Kirkland +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright 2012-2017 Mehdi Khalili +Copyright (c) 2015 .NET Foundation +Copyright (c) 2015 Christian Klutz +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) Outercurve Foundation +Copyright LibGit2Sharp contributors +(c) Yoshifumi Kawai and contributors +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) James Newton-King 2008 +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2012-2014 Mehdi Khalili +Copyright (c) 2013-2014 Omar Khudeira +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) .NET Foundation xUnit.net +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) .NET Foundation 0xUnit.net +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright James Newton-King 2008 Json.NET +Copyright (c) .NET Foundation Contributors +Copyright .NET Foundation and Contributors +Copyright 1995-2017 Mark Adler +3 CScs DEFG +Copyright 2012-2016 (c) 2008 VeriSign, Inc. +Copyright (c) 2020 Mara Bos +(c) Antoine Aubry and contributors 2008 - 2019 +Copyright (c) .NET Foundation and Contributors +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright AssemblyCompany AssemblyConfiguration +Copyright (c) 2017 Yoshifumi Kawai and contributors +Copyright 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright 2012-2016 Copyright 2012-2017 Mehdi Khalili +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) .NET Foundation ,xUnit.net Runner Utility +Copyright (c) .NET Foundation 1xUnit.net Runner Utility +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) .NET Foundation xUnit.net Runner Reporters +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) Antoine Aubry and contributors 2008 - 2019 +Copyright (c) .NET Foundation .xUnit.net Runner Reporters +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS +Copyright (c) .NET Foundation and Contributors. Visual Studio 2019 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net +Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors +Copyright Tunnel Vision Laboratories, LLC 2018 1Copyright Tunnel Vision Laboratories, LLC 2018 NAn +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic Analyzer +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Validation 17.6.11 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation +Copyright 1995-2017 Mark Adler +Copyright (c) Rackspace, US Inc. +Copyright James Newton-King 2008 +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright (c) 2015 .NET Foundation +Copyright (c) Outercurve Foundation +Copyright LibGit2Sharp contributors +Copyright (c) 2007 James Newton-King +Copyright (c) James Newton-King 2008 +Copyright (c) .NET Foundation xUnit.net +Copyright (c) .NET Foundation 0xUnit.net +Copyright James Newton-King 2008 Json.NET +Copyright 1995-2017 Mark Adler +3 CScs DEFG +Copyright (c) .NET Foundation and Contributors +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright AssemblyCompany AssemblyConfiguration +Copyright 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) .NET Foundation ,xUnit.net Runner Utility +Copyright (c) .NET Foundation 1xUnit.net Runner Utility +Copyright (c) .NET Foundation xUnit.net Runner Reporters +Copyright (c) .NET Foundation .xUnit.net Runner Reporters +(c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS +Copyright 2004-2021 Castle Project - http://www.castleproject.org +Copyright (c) .NET Foundation and Contributors. Visual Studio 2019 +Copyright (c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net +Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors +Copyright Tunnel Vision Laboratories, LLC 2018 1Copyright Tunnel Vision Laboratories, LLC 2018 NAn +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic Analyzer + +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. +Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, +or you may send a check or money order for US $5.00, including the product name, +the open source component name, platform, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. + +--------------------------------------------------------- + +Castle.Core 5.1.1 - Apache-2.0 + + +(c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright 2004-2021 Castle Project - http://www.castleproject.org +Copyright (c) 2004-2022 Castle Project - http://www.castleproject.org + +Copyright 2004-2021 Castle Project - http://www.castleproject.org/ + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit 2.4.2 - Apache-2.0 + + +Copyright (c) .NET Foundation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.abstractions 2.0.3 - Apache-2.0 + + +Copyright (c) Outercurve Foundation +Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.analyzers 1.0.0 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.assert 2.4.2 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.core 2.4.2 - Apache-2.0 + + +Copyright (c) .NET Foundation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.extensibility.core 2.4.2 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net +Copyright (c) .NET Foundation 0xUnit.net +Copyright (c) .NET Foundation xUnit.net Runner Utility + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.extensibility.execution 2.4.2 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Moq 4.18.4 - BSD-3-Clause + + +Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +CSharpIsNullAnalyzer 0.1.329 - MIT + + +Copyright (c) Andrew Arnott + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +DotNetAnalyzers.DocumentationAnalyzers 1.0.0-beta.59 - MIT + + +Copyright (c) Rackspace, US Inc. +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic Analyzer + +The MIT License (MIT) + +Copyright (c) Tunnel Vision Laboratories, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +DotNetAnalyzers.DocumentationAnalyzers.Unstable 1.0.0.59 - MIT + + +Copyright (c) Rackspace, US Inc. +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright Tunnel Vision Laboratories, LLC 2018 +1Copyright Tunnel Vision Laboratories, LLC 2018 NAn +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic Analyzer + +The MIT License (MIT) + +Copyright (c) Tunnel Vision Laboratories, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Build.Tasks.Git 1.1.1 - MIT + + +(c) Microsoft Corporation. + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.SourceLink.Common 1.1.1 - MIT + + +(c) Microsoft Corporation. + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.SourceLink.GitHub 1.1.1 - MIT + + +(c) Microsoft Corporation. + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Nerdbank.GitVersioning 3.5.119 - MIT + + +Copyright 1995-2017 Mark Adler +Copyright James Newton-King 2008 +Copyright LibGit2Sharp contributors +Copyright James Newton-King 2008 Json.NET +Copyright 1995-2017 Mark Adler +3 CScs DEFG +Copyright (c) .NET Foundation and Contributors +Copyright AssemblyCompany AssemblyConfiguration +Copyright 1995-2017 Jean-loup Gailly and Mark Adler + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Newtonsoft.Json 13.0.1 - MIT + + +Copyright James Newton-King 2008 +Copyright (c) 2007 James Newton-King +Copyright (c) James Newton-King 2008 +Copyright James Newton-King 2008 Json.NET + +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Nullable 1.3.1 - MIT + + +Copyright (c) Manuel Römer + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +StyleCop.Analyzers.Unstable 1.2.0.435 - MIT + + + +MIT License + +Copyright (c) Tunnel Vision Laboratories, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.runner.visualstudio 2.4.5 - MIT + + +Copyright (c) .NET Foundation +Copyright (c) 2015 .NET Foundation +Copyright (c) Outercurve Foundation +Copyright (c) .NET Foundation and Contributors +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) .NET Foundation ,xUnit.net Runner Utility +Copyright (c) .NET Foundation 1xUnit.net Runner Utility +Copyright (c) .NET Foundation xUnit.net Runner Reporters +Copyright (c) .NET Foundation .xUnit.net Runner Reporters +Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS +Copyright (c) .NET Foundation and Contributors. Visual Studio 2019 + +Unless otherwise noted, the source code here is covered by the following license: + + Copyright (c) .NET Foundation and Contributors + All Rights Reserved + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +----------------------- + +The code in src/xunit.runner.visualstudio/Utility/AssemblyResolution/Microsoft.DotNet.PlatformAbstractions was imported from: + https://github.com/dotnet/core-setup/tree/v2.0.1/src/managed/Microsoft.DotNet.PlatformAbstractions + +The code in src/xunit.runner.visualstudio/Utility/AssemblyResolution/Microsoft.DotNet.PlatformAbstractions was imported from: + https://github.com/dotnet/core-setup/tree/v2.0.1/src/managed/Microsoft.Extensions.DependencyModel + +Both sets of code are covered by the following license: + + The MIT License (MIT) + + Copyright (c) 2015 .NET Foundation + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Procdump 0.0.1 + + + +--------------------------------------------------------- + + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Nerdbank.Streams 2.10.69 - MIT + + +(c) Andrew Arnott +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Newtonsoft.Json 13.0.3 - MIT + + +Copyright James Newton-King 2008 +Copyright (c) 2007 James Newton-King +Copyright (c) James Newton-King 2008 +Copyright James Newton-King 2008 Json.NET + +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.DebugAdapter 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.DebugAdapter.Client 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.DebugAdapter.Server 0.19.9 - MIT + + + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.DebugAdapter.Shared 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.JsonRpc 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.JsonRpc.Generators 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.LanguageClient 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.LanguageProtocol 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.LanguageServer 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +OmniSharp.Extensions.LanguageServer.Shared 0.19.9 - MIT + + +(c) Microsoft 2023 +Copyright OmniSharp and contributors +Copyright OmniSharp and contributors (c) 2018 +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors +All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.IO.Pipelines 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.IO.Pipes.AccessControl 5.0.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Memory 4.5.5 - MIT + + +(c) 2022 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Reactive 6.0.0 - MIT + + +Copyright (c) .NET Foundation and Contributors +Copyright (c) .NET Foundation and Contributors. Rx Reactive Extensions Observable LINQ Events + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Runtime.CompilerServices.Unsafe 6.0.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.AccessControl 5.0.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Threading.Channels 6.0.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ValueTuple 4.5.0 - MIT + + +(c) 2023 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- diff --git a/PowerShellEditorServices.Common.props b/PowerShellEditorServices.Common.props index 80d45b7ba..2af5f41b1 100644 --- a/PowerShellEditorServices.Common.props +++ b/PowerShellEditorServices.Common.props @@ -1,6 +1,6 @@ - 3.18.1 + 4.6.0 Microsoft © Microsoft Corporation. @@ -11,8 +11,6 @@ git https://github.com/PowerShell/PowerShellEditorServices portable - - true true true diff --git a/PowerShellEditorServices.build.ps1 b/PowerShellEditorServices.build.ps1 index 7c0319a95..62cb53369 100644 --- a/PowerShellEditorServices.build.ps1 +++ b/PowerShellEditorServices.build.ps1 @@ -7,11 +7,7 @@ param( [switch]$LocalOmniSharp, - [string]$PsesSubmodulePath = "$PSScriptRoot/module", - - [string]$ModulesJsonPath = "$PSScriptRoot/modules.json", - - [string]$DefaultModuleRepository = "PSGallery", + [string]$PSRepository = "PSGallery", [string]$Verbosity = "minimal", @@ -22,8 +18,8 @@ param( [string[]]$TestArgs = @("--logger", "console;verbosity=minimal", "--logger", "trx") ) -#Requires -Modules @{ModuleName="InvokeBuild"; ModuleVersion="5.0.0"} -#Requires -Modules @{ModuleName="platyPS"; ModuleVersion="0.14.0"} +#Requires -Modules @{ModuleName = "InvokeBuild"; ModuleVersion = "5.0.0"} +#Requires -Modules @{ModuleName = "platyPS"; ModuleVersion = "0.14.2"} $script:dotnetBuildArgs = @( "--verbosity" @@ -40,29 +36,21 @@ $script:dotnetTestArgs = @("test") + $script:dotnetBuildArgs + $TestArgs + @( ) $script:IsNix = $IsLinux -or $IsMacOS -# For Apple M1, pwsh might be getting emulated, in which case we need to check -# for the proc_translated flag, otherwise we can check the architecture. -$script:IsAppleM1 = $IsMacOS -and ((sysctl -n sysctl.proc_translated 2> $null) -eq 1 -or - [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq "Arm64") -$script:IsArm64 = -not $script:IsNix -and @("ARM64") -contains $env:PROCESSOR_ARCHITECTURE -$script:BuildInfoPath = [System.IO.Path]::Combine($PSScriptRoot, "src", "PowerShellEditorServices.Hosting", "BuildInfo.cs") -$script:PsesCommonProps = [xml](Get-Content -Raw "$PSScriptRoot/PowerShellEditorServices.Common.props") +$script:BuildInfoPath = "src/PowerShellEditorServices.Hosting/BuildInfo.cs" $script:NetFramework = @{ PS51 = 'net462' - PS72 = 'net6.0' - PS73 = 'net7.0' PS74 = 'net8.0' Standard = 'netstandard2.0' } -$script:HostCoreOutput = "$PSScriptRoot/src/PowerShellEditorServices.Hosting/bin/$Configuration/$($script:NetFramework.PS72)/publish" -$script:HostDeskOutput = "$PSScriptRoot/src/PowerShellEditorServices.Hosting/bin/$Configuration/$($script:NetFramework.PS51)/publish" -$script:PsesOutput = "$PSScriptRoot/src/PowerShellEditorServices/bin/$Configuration/$($script:NetFramework.Standard)/publish" +$script:HostCoreOutput = "src/PowerShellEditorServices.Hosting/bin/$Configuration/$($script:NetFramework.PS74)/publish" +$script:HostDeskOutput = "src/PowerShellEditorServices.Hosting/bin/$Configuration/$($script:NetFramework.PS51)/publish" +$script:PsesOutput = "src/PowerShellEditorServices/bin/$Configuration/$($script:NetFramework.Standard)/publish" if (Get-Command git -ErrorAction SilentlyContinue) { # ignore changes to this file - git update-index --assume-unchanged "$PSScriptRoot/src/PowerShellEditorServices.Hosting/BuildInfo.cs" + git update-index --assume-unchanged $script:BuildInfoPath } Task FindDotNet { @@ -72,61 +60,36 @@ Task FindDotNet { [Version]$existingVersion, $null = (dotnet --version) -split " " -split "-" Assert ($existingVersion -ge [Version]("8.0")) ".NET SDK 8.0 or higher is required, please update it: https://aka.ms/dotnet-cli" - Write-Host "Using dotnet v$(dotnet --version) at path $((Get-Command dotnet).Source)" -ForegroundColor Green + Write-Build DarkGreen "Using dotnet v$(dotnet --version) at path $((Get-Command dotnet).Source)" } -Task BinClean { - Remove-BuildItem $PSScriptRoot\.tmp - Remove-BuildItem $PSScriptRoot\module\PowerShellEditorServices\bin -} - -Task Clean FindDotNet, BinClean, { +Task Clean FindDotNet, { + Write-Build DarkMagenta "Cleaning PowerShellEditorServices" Invoke-BuildExec { & dotnet clean --verbosity $Verbosity } - Remove-BuildItem $PSScriptRoot\src\*.nupkg - Remove-BuildItem $PSScriptRoot\PowerShellEditorServices*.zip - Remove-BuildItem $PSScriptRoot\module\PowerShellEditorServices\Commands\en-US\*-help.xml - - # Remove bundled component modules - $moduleJsonPath = "$PSScriptRoot\modules.json" - if (Test-Path $moduleJsonPath) { - Get-Content -Raw $moduleJsonPath | - ConvertFrom-Json | - ForEach-Object { $_.PSObject.Properties.Name } | - ForEach-Object { Remove-BuildItem -Path "$PSScriptRoot/module/$_" } - } + Remove-BuildItem module/PowerShellEditorServices/bin + Remove-BuildItem module/PowerShellEditorServices/Commands/en-US/*-help.xml + Remove-BuildItem module/PSReadLine + Remove-BuildItem module/PSScriptAnalyzer } Task CreateBuildInfo { - $buildVersion = "" $buildOrigin = "Development" $buildCommit = git rev-parse HEAD + [xml]$xml = Get-Content "PowerShellEditorServices.Common.props" + $buildVersion = $xml.Project.PropertyGroup.VersionPrefix + $prerelease = $xml.Project.PropertyGroup.VersionSuffix + if ($prerelease) { $buildVersion += "-$prerelease" } + # Set build info fields on build platforms - if ($env:TF_BUILD) { - if ($env:BUILD_BUILDNUMBER -like "PR-*") { - $buildOrigin = "PR" - } elseif ($env:BUILD_DEFINITIONNAME -like "*-CI") { - $buildOrigin = "CI" - } else { + if ($env:TF_BUILD) { # Azure DevOps AKA OneBranch + if ($env:BUILD_REASON -like "Manual") { $buildOrigin = "Release" + } else { + $buildOrigin = "AzureDevOps-CI" } - - $propsXml = [xml](Get-Content -Raw -LiteralPath "$PSScriptRoot/PowerShellEditorServices.Common.props") - $propsBody = $propsXml.Project.PropertyGroup - $buildVersion = $propsBody.VersionPrefix - - if ($propsBody.VersionSuffix) { - $buildVersion += '-' + $propsBody.VersionSuffix - } - } - - # Allow override of build info fields (except date) - if ($env:PSES_BUILD_VERSION) { - $buildVersion = $env:PSES_BUILD_VERSION - } - - if ($env:PSES_BUILD_ORIGIN) { - $buildOrigin = $env:PSES_BUILD_ORIGIN + } elseif ($env:GITHUB_ACTIONS) { + $buildOrigin = "GitHub-CI" } [string]$buildTime = [datetime]::Today.ToString("s", [System.Globalization.CultureInfo]::InvariantCulture) @@ -150,49 +113,183 @@ namespace Microsoft.PowerShell.EditorServices.Hosting "@ if (Compare-Object $buildInfoContents.Split([Environment]::NewLine) (Get-Content $script:BuildInfoPath)) { - Write-Host "Updating build info." + Write-Build DarkMagenta "Updating build info" Set-Content -LiteralPath $script:BuildInfoPath -Value $buildInfoContents -Force } } -Task SetupHelpForTests { - # TODO: Check if it must be updated in a compatible way! - Write-Host "Updating help for tests." - Update-Help -Module Microsoft.PowerShell.Management,Microsoft.PowerShell.Utility -Force -Scope CurrentUser -UICulture en-US +task RestorePsesModules { + # NOTE: When updating module versions, ensure they are also saved to the CFS feed + if (-not (Test-Path "module/PSScriptAnalyzer")) { + Write-Build DarkMagenta "Restoring PSScriptAnalyzer module" + Save-PSResource -Path module -Name PSScriptAnalyzer -Version "1.25.0" -Repository $PSRepository -TrustRepository -Verbose + } + if (-not (Test-Path "module/PSReadLine")) { + Write-Build DarkMagenta "Restoring PSReadLine module" + Save-PSResource -Path module -Name PSReadLine -Version "2.4.5" -Repository $PSRepository -TrustRepository -Verbose + } } -Task Build FindDotNet, CreateBuildInfo, { - Invoke-BuildExec { & dotnet publish $script:dotnetBuildArgs .\src\PowerShellEditorServices\PowerShellEditorServices.csproj -f $script:NetFramework.Standard } - Invoke-BuildExec { & dotnet publish $script:dotnetBuildArgs .\src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj -f $script:NetFramework.PS72 } +Task Build FindDotNet, CreateBuildInfo, RestorePsesModules, { + Write-Build DarkGreen 'Building PowerShellEditorServices' + Invoke-BuildExec { & dotnet publish $script:dotnetBuildArgs ./src/PowerShellEditorServices/PowerShellEditorServices.csproj -f $script:NetFramework.Standard } + Invoke-BuildExec { & dotnet publish $script:dotnetBuildArgs ./src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj -f $script:NetFramework.PS74 } if (-not $script:IsNix) { - Invoke-BuildExec { & dotnet publish $script:dotnetBuildArgs .\src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj -f $script:NetFramework.PS51 } + Invoke-BuildExec { & dotnet publish $script:dotnetBuildArgs ./src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj -f $script:NetFramework.PS51 } } +} -If { + $Null -eq $script:ChangesDetected -or $true -eq $script:ChangesDetected } -# The concise set of tests (for pull requests) -Task Test TestPS74, TestE2EPwsh, TestPS51, TestE2EPowerShell +Task AssembleModule -After Build { + Write-Build DarkGreen 'Assembling PowerShellEditorServices module' + $psesOutputPath = './module/PowerShellEditorServices' + $psesBinOutputPath = "$psesOutputPath/bin" + $psesDepsPath = "$psesBinOutputPath/Common" + $psesCoreHostPath = "$psesBinOutputPath/Core" + $psesDeskHostPath = "$psesBinOutputPath/Desktop" + + foreach ($dir in $psesDepsPath, $psesCoreHostPath, $psesDeskHostPath) { + New-Item -Force -Path $dir -ItemType Directory | Out-Null + } -# Every combination of tests (for main branch) -Task TestFull Test, TestPS73, TestPS72, TestE2EPwshCLM, TestE2EPowerShellCLM + # Copy documents to module root + foreach ($document in @('LICENSE', 'NOTICE.txt', 'README.md', 'SECURITY.md')) { + Copy-Item -Force -Path $document -Destination './module' + } -Task TestPS74 Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test\ - Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS74 } + # Assemble PSES module + $includedDlls = [System.Collections.Generic.HashSet[string]]::new() + [void]$includedDlls.Add('System.Management.Automation.dll') + + # PSES/bin/Common + foreach ($psesComponent in Get-ChildItem $script:PsesOutput) { + if ($psesComponent.Name -eq 'System.Management.Automation.dll' -or + $psesComponent.Name -eq 'System.Runtime.InteropServices.RuntimeInformation.dll') { + continue + } + + if ($psesComponent.Extension) { + [void]$includedDlls.Add($psesComponent.Name) + Copy-Item -Path $psesComponent.FullName -Destination $psesDepsPath -Force + } + } + + # PSES/bin/Core + foreach ($hostComponent in Get-ChildItem $script:HostCoreOutput) { + if (-not $includedDlls.Contains($hostComponent.Name)) { + Copy-Item -Path $hostComponent.FullName -Destination $psesCoreHostPath -Force + } + } + + # PSES/bin/Desktop + if (-not $script:IsNix) { + foreach ($hostComponent in Get-ChildItem $script:HostDeskOutput) { + if (-not $includedDlls.Contains($hostComponent.Name)) { + Copy-Item -Path $hostComponent.FullName -Destination $psesDeskHostPath -Force + } + } + } } -Task TestPS73 Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test\ - Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS73 } +Task BuildCmdletHelp -After AssembleModule { + Write-Build DarkGreen 'Building cmdlet help' + New-ExternalHelp -Path ./module/docs -OutputPath ./module/PowerShellEditorServices/Commands/en-US -Force +} + +Task SetupHelpForTests { + # Some CI do not ship with help included, and the secure devops pipeline also does not allow internet access, so we must update help from our local repository source. + + # Only commands in Microsoft.PowerShell.Archive can be tested for help so as to minimize the repository storage. + # This requires admin rights for PS5.1 + + # NOTE: You can run this task once as admin or update help separately, and continue to run tests as non-admin, if for instance developing locally. + + $installHelpScript = { + param( + [Parameter(Position = 0)][string]$helpPath + ) + $PSVersion = $PSVersionTable.PSVersion + $ErrorActionPreference = 'Stop' + $helpPath = Resolve-Path $helpPath + if ($PSEdition -ne 'Desktop') { + $helpPath = Join-Path $helpPath '7' + } + + if ((Get-Help Expand-Archive).remarks -notlike 'Get-Help cannot find the Help files*') { + Write-Host -ForegroundColor Green "PowerShell $PSVersion Archive help is already installed" + return + } + + if ($PSEdition -eq 'Desktop') { + # Cant use requires RunAsAdministrator because PS isn't smart enough to know this is a subscript. + if (-not [Security.Principal.WindowsPrincipal]::new( + [Security.Principal.WindowsIdentity]::GetCurrent() + ).IsInRole( + [Security.Principal.WindowsBuiltInRole]::Administrator + )) { + throw 'Windows PowerShell Update-Help requires admin rights. Please re-run the script in an elevated PowerShell session!' + } + } + + Write-Host -ForegroundColor Magenta "PowerShell $PSVersion Archive help is not installed, installing from $helpPath" + + $updateHelpParams = @{ + Module = 'Microsoft.PowerShell.Archive' + SourcePath = $helpPath + UICulture = 'en-US' + Force = $true + Verbose = $true + } + + # PS7+ does not require admin rights if CurrentUser is used for scope. PS5.1 does not have this option. + if ($PSEdition -ne 'Desktop') { + $updateHelpParams.'Scope' = 'CurrentUser' + } + # Update the help and capture verbose output + $updateHelpOutput = Update-Help @updateHelpParams *>&1 + + if ((Get-Help Expand-Archive).remarks -like 'Get-Help cannot find the Help files*') { + throw "Failed to install PowerShell $PSVersion Help: $updateHelpOutput" + } else { + Write-Host -ForegroundColor Green "PowerShell $PSVersion Archive help installed successfully" + } + } + + # Need this to inject the help file path since PSScriptRoot won't work inside the script + $helpPath = Resolve-Path "$PSScriptRoot\test\PowerShellEditorServices.Test.Shared\PSHelp" -ErrorAction Stop + Write-Build DarkMagenta "Runner help located at $helpPath" + + if (Get-Command powershell.exe -CommandType Application -ea 0) { + Write-Build DarkMagenta 'Checking PowerShell 5.1 help' + & powershell.exe -NoProfile -NonInteractive -Command $installHelpScript -args $helpPath + if ($LASTEXITCODE -ne 0) { + throw 'Failed to install PowerShell 5.1 help!' + } + } + + if ($PwshPreview -and (Get-Command $PwshPreview -ea 0)) { + Write-Build DarkMagenta "Checking PowerShell Preview help at $PwshPreview" + Invoke-BuildExec { & $PwshPreview -NoProfile -NonInteractive -Command $installHelpScript -args $helpPath } + if ($LASTEXITCODE -ne 0) { + throw 'Failed to install PowerShell Preview help!' + } + } + + if ($PSEdition -eq 'Core') { + Write-Build DarkMagenta "Checking this PowerShell process's help" + & $installHelpScript $helpPath + } } -Task TestPS72 Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test\ - Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS72 } +Task TestPS74 Build, SetupHelpForTests, { + Set-Location ./test/PowerShellEditorServices.Test/ + Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS74 } } Task TestPS51 -If (-not $script:IsNix) Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test\ + Set-Location ./test/PowerShellEditorServices.Test/ # TODO: See https://github.com/dotnet/sdk/issues/18353 for x64 test host # that is debuggable! If architecture is added, the assembly path gets an # additional folder, necessitating fixes to find the commands definition @@ -201,7 +298,7 @@ Task TestPS51 -If (-not $script:IsNix) Build, SetupHelpForTests, { # TODO: See https://github.com/PowerShell/vscode-powershell/issues/3886 # Inheriting the module path for powershell.exe breaks things! $originalModulePath = $env:PSModulePath - $env:PSModulePath = "" + $env:PSModulePath = '' Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS51 } } finally { $env:PSModulePath = $originalModulePath @@ -211,32 +308,33 @@ Task TestPS51 -If (-not $script:IsNix) Build, SetupHelpForTests, { # NOTE: The framework for the E2E tests applies to the mock client, and so # should just be the latest supported framework. Task TestE2EPwsh Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test.E2E\ - $env:PWSH_EXE_NAME = "pwsh" + Set-Location ./test/PowerShellEditorServices.Test.E2E/ + $env:PWSH_EXE_NAME = 'pwsh' Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS74 } } -$PwshDaily = if ($script:IsNix) { - "$HOME/.powershell-daily/pwsh" +if ($env:GITHUB_ACTIONS) { + $PwshPreview = if ($script:IsNix) { "$PSScriptRoot/preview/pwsh" } else { "$PSScriptRoot/preview/pwsh.exe" } } else { - "$env:LOCALAPPDATA\Microsoft\powershell-daily\pwsh.exe" + $PwshPreview = if ($script:IsNix) { "$HOME/.powershell-preview/pwsh" } else { "$env:LOCALAPPDATA/Microsoft/powershell-preview/pwsh.exe" } } -Task TestE2EDaily -If (Test-Path $PwshDaily) Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test.E2E\ - $env:PWSH_EXE_NAME = $PwshDaily - Write-Host "Running end-to-end tests with: $(& $PwshDaily --version)" +Task TestE2EPreview -If (-not $env:TF_BUILD) Build, SetupHelpForTests, { + Assert (Test-Path $PwshPreview) "PowerShell Preview not found at $PwshPreview, please install it: https://github.com/PowerShell/PowerShell/blob/master/tools/install-powershell.ps1" + Set-Location ./test/PowerShellEditorServices.Test.E2E/ + $env:PWSH_EXE_NAME = $PwshPreview + Write-Build DarkGreen "Running end-to-end tests with: $(& $PwshPreview --version)" Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS74 } } Task TestE2EPowerShell -If (-not $script:IsNix) Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test.E2E\ - $env:PWSH_EXE_NAME = "powershell" + Set-Location ./test/PowerShellEditorServices.Test.E2E/ + $env:PWSH_EXE_NAME = 'powershell' try { # TODO: See https://github.com/PowerShell/vscode-powershell/issues/3886 # Inheriting the module path for powershell.exe breaks things! $originalModulePath = $env:PSModulePath - $env:PSModulePath = "" + $env:PSModulePath = '' Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS74 } } finally { $env:PSModulePath = $originalModulePath @@ -244,150 +342,65 @@ Task TestE2EPowerShell -If (-not $script:IsNix) Build, SetupHelpForTests, { } Task TestE2EPwshCLM -If (-not $script:IsNix) Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test.E2E\ - $env:PWSH_EXE_NAME = "pwsh" + Set-Location ./test/PowerShellEditorServices.Test.E2E/ + $env:PWSH_EXE_NAME = 'pwsh' - if (-not [Security.Principal.WindowsIdentity]::GetCurrent().Owner.IsWellKnown("BuiltInAdministratorsSid")) { - Write-Warning "Skipping Constrained Language Mode tests as they must be ran in an elevated process." + if (-not [Security.Principal.WindowsIdentity]::GetCurrent().Owner.IsWellKnown('BuiltInAdministratorsSid')) { + Write-Build DarkRed 'Skipping Constrained Language Mode tests as they must be ran in an elevated process' return } try { - Write-Host "Running end-to-end tests in Constrained Language Mode." - [System.Environment]::SetEnvironmentVariable("__PSLockdownPolicy", "0x80000007", [System.EnvironmentVariableTarget]::Machine) + Write-Build DarkGreen 'Running end-to-end tests in Constrained Language Mode' + [System.Environment]::SetEnvironmentVariable('__PSLockdownPolicy', '0x80000007', [System.EnvironmentVariableTarget]::Machine) Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS74 } } finally { - [System.Environment]::SetEnvironmentVariable("__PSLockdownPolicy", $null, [System.EnvironmentVariableTarget]::Machine) + [System.Environment]::SetEnvironmentVariable('__PSLockdownPolicy', $null, [System.EnvironmentVariableTarget]::Machine) } } Task TestE2EPowerShellCLM -If (-not $script:IsNix) Build, SetupHelpForTests, { - Set-Location .\test\PowerShellEditorServices.Test.E2E\ - $env:PWSH_EXE_NAME = "powershell" + Set-Location ./test/PowerShellEditorServices.Test.E2E/ + $env:PWSH_EXE_NAME = 'powershell' - if (-not [Security.Principal.WindowsIdentity]::GetCurrent().Owner.IsWellKnown("BuiltInAdministratorsSid")) { - Write-Warning "Skipping Constrained Language Mode tests as they must be ran in an elevated process." + if (-not [Security.Principal.WindowsIdentity]::GetCurrent().Owner.IsWellKnown('BuiltInAdministratorsSid')) { + Write-Build DarkRed 'Skipping Constrained Language Mode tests as they must be ran in an elevated process' return } try { - Write-Host "Running end-to-end tests in Constrained Language Mode." - [System.Environment]::SetEnvironmentVariable("__PSLockdownPolicy", "0x80000007", [System.EnvironmentVariableTarget]::Machine) + Write-Build DarkGreen 'Running end-to-end tests in Constrained Language Mode' + [System.Environment]::SetEnvironmentVariable('__PSLockdownPolicy', '0x80000007', [System.EnvironmentVariableTarget]::Machine) # TODO: See https://github.com/PowerShell/vscode-powershell/issues/3886 # Inheriting the module path for powershell.exe breaks things! $originalModulePath = $env:PSModulePath - $env:PSModulePath = "" + $env:PSModulePath = '' Invoke-BuildExec { & dotnet $script:dotnetTestArgs $script:NetFramework.PS74 } } finally { - [System.Environment]::SetEnvironmentVariable("__PSLockdownPolicy", $null, [System.EnvironmentVariableTarget]::Machine) + [System.Environment]::SetEnvironmentVariable('__PSLockdownPolicy', $null, [System.EnvironmentVariableTarget]::Machine) $env:PSModulePath = $originalModulePath } } -Task LayoutModule -After Build { - $modulesDir = "$PSScriptRoot/module" - $psesOutputPath = "$modulesDir/PowerShellEditorServices" - $psesBinOutputPath = "$PSScriptRoot/module/PowerShellEditorServices/bin" - $psesDepsPath = "$psesBinOutputPath/Common" - $psesCoreHostPath = "$psesBinOutputPath/Core" - $psesDeskHostPath = "$psesBinOutputPath/Desktop" - - foreach ($dir in $psesDepsPath, $psesCoreHostPath, $psesDeskHostPath) { - New-Item -Force -Path $dir -ItemType Directory | Out-Null - } - - # Copy Third Party Notices.txt to module folder - Copy-Item -Force -Path "$PSScriptRoot\Third Party Notices.txt" -Destination $psesOutputPath - - # Assemble PSES module - - $includedDlls = [System.Collections.Generic.HashSet[string]]::new() - [void]$includedDlls.Add('System.Management.Automation.dll') - - # PSES/bin/Common - foreach ($psesComponent in Get-ChildItem $script:PsesOutput) { - if ($psesComponent.Name -eq 'System.Management.Automation.dll' -or - $psesComponent.Name -eq 'System.Runtime.InteropServices.RuntimeInformation.dll') { - continue - } - - if ($psesComponent.Extension) { - [void]$includedDlls.Add($psesComponent.Name) - Copy-Item -Path $psesComponent.FullName -Destination $psesDepsPath -Force - } - } - - # PSES/bin/Core - foreach ($hostComponent in Get-ChildItem $script:HostCoreOutput) { - if (-not $includedDlls.Contains($hostComponent.Name)) { - Copy-Item -Path $hostComponent.FullName -Destination $psesCoreHostPath -Force - } - } - - # PSES/bin/Desktop - if (-not $script:IsNix) { - foreach ($hostComponent in Get-ChildItem $script:HostDeskOutput) { - if (-not $includedDlls.Contains($hostComponent.Name)) { - Copy-Item -Path $hostComponent.FullName -Destination $psesDeskHostPath -Force - } - } - } +Task BuildIfChanged.Init -Before BuildIfChanged { + [bool]$script:ChangesDetected = $false } -task RestorePsesModules -After Build { - $submodulePath = (Resolve-Path $PsesSubmodulePath).Path + [IO.Path]::DirectorySeparatorChar - Write-Host "Restoring EditorServices modules..." - - # Read in the modules.json file as a hashtable so it can be splatted - $moduleInfos = @{} - - (Get-Content -Raw $ModulesJsonPath | ConvertFrom-Json).PSObject.Properties | ForEach-Object { - $name = $_.Name - $body = @{ - Name = $name - Version = $_.Value.Version - AllowPrerelease = $_.Value.AllowPrerelease - Repository = if ($_.Value.Repository) { $_.Value.Repository } else { $DefaultModuleRepository } - Path = $submodulePath - } - - if (-not $name) { - throw "EditorServices module listed without name in '$ModulesJsonPath'" - } - - $moduleInfos.Add($name, $body) - } - - # Save each module in the modules.json file - foreach ($moduleName in $moduleInfos.Keys) { - if (Test-Path -Path (Join-Path -Path $submodulePath -ChildPath $moduleName)) { - Write-Host "`tModule '${moduleName}' already detected, skipping!" - continue - } - - $moduleInstallDetails = $moduleInfos[$moduleName] +Task BuildIfChanged -Inputs { + $slash = [IO.Path]::DirectorySeparatorChar + Get-ChildItem ./src -Filter '*.cs' -Recurse + | Where-Object FullName -NotLike ('*' + $slash + 'obj' + $slash + '*') + | Where-Object FullName -NotLike ('*' + $slash + 'bin' + $slash + '*') +} -Outputs { + './src/PowerShellEditorServices/bin/Debug/netstandard2.0/Microsoft.PowerShell.EditorServices.dll' + './src/PowerShellEditorServices.Hosting/bin/Debug/net8.0/Microsoft.PowerShell.EditorServices.Hosting.dll' +} -Jobs { + Write-Build DarkMagenta 'Changes detected, rebuilding' + $script:ChangesDetected = $true +}, Build - $splatParameters = @{ - Name = $moduleName - RequiredVersion = $moduleInstallDetails.Version - Repository = if ($moduleInstallDetails.Repository) { $moduleInstallDetails.Repository } else { $DefaultModuleRepository } - Path = $submodulePath - } - - # There's a bug in PowerShell get where this argument isn't correctly translated when it's false. - if ($moduleInstallDetails.AllowPrerelease) { - $splatParameters["AllowPrerelease"] = $moduleInstallDetails.AllowPrerelease - } - - Write-Host "`tInstalling module: ${moduleName} with arguments $(ConvertTo-Json $splatParameters)" - - Save-Module @splatParameters - } -} +Task Test TestPS74, TestE2EPwsh, TestPS51, TestE2EPowerShell -Task BuildCmdletHelp -After LayoutModule { - New-ExternalHelp -Path $PSScriptRoot\module\docs -OutputPath $PSScriptRoot\module\PowerShellEditorServices\Commands\en-US -Force | Out-Null -} +Task TestFull Test, TestE2EPreview, TestE2EPwshCLM, TestE2EPowerShellCLM -# The default task is to run the entire CI build Task . Clean, Build, Test diff --git a/PowerShellEditorServices.sln b/PowerShellEditorServices.sln index c41d92df0..754851233 100644 --- a/PowerShellEditorServices.sln +++ b/PowerShellEditorServices.sln @@ -3,158 +3,55 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F594E7FD-1E72-4E51-A496-B019C2BA3180}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F46EF430-95AA-4386-9259-292A443AB715}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{422E561A-8118-4BE7-A54F-9309E4F03AAE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellEditorServices.Test.Shared", "test\PowerShellEditorServices.Test.Shared\PowerShellEditorServices.Test.Shared.csproj", "{9D307AF9-D1F7-4185-AE9B-2DD3F178832C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerShellEditorServices.Test", "test\PowerShellEditorServices.Test\PowerShellEditorServices.Test.csproj", "{8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellEditorServices.Test", "test\PowerShellEditorServices.Test\PowerShellEditorServices.Test.csproj", "{DFD3C9C2-F9E6-4EE3-B614-A8EA7D1E1A98}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerShellEditorServices.Test.Shared", "test\PowerShellEditorServices.Test.Shared\PowerShellEditorServices.Test.Shared.csproj", "{6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellEditorServices.Test.E2E", "test\PowerShellEditorServices.Test.E2E\PowerShellEditorServices.Test.E2E.csproj", "{AA007633-5178-4D73-A262-CCE7247BDE93}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{E2316F5C-A551-4A8D-8103-E42CA6D757E2}" - ProjectSection(SolutionItems) = preProject - scripts\AddCopyrightHeaders.ps1 = scripts\AddCopyrightHeaders.ps1 - EndProjectSection +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F40C4EC9-AE86-4A26-974F-95381888DCDC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerShellEditorServices", "src\PowerShellEditorServices\PowerShellEditorServices.csproj", "{29EEDF03-0990-45F4-846E-2616970D1FA2}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellEditorServices", "src\PowerShellEditorServices\PowerShellEditorServices.csproj", "{B4431254-9A2F-43DE-A998-12B22A1593CE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerShellEditorServices.Test.E2E", "test\PowerShellEditorServices.Test.E2E\PowerShellEditorServices.Test.E2E.csproj", "{2561F253-8F72-436A-BCC3-AA63AB82EDC0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerShellEditorServices.Hosting", "src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj", "{3CC791E7-6FC9-4DDE-B4A2-547266977E4E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellEditorServices.Hosting", "src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj", "{983D05F2-3C77-4B51-9A28-A8C6595911BA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - CoreCLR|Any CPU = CoreCLR|Any CPU - CoreCLR|x64 = CoreCLR|x64 - CoreCLR|x86 = CoreCLR|x86 Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.CoreCLR|Any CPU.ActiveCfg = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.CoreCLR|Any CPU.Build.0 = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.CoreCLR|x64.ActiveCfg = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.CoreCLR|x64.Build.0 = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.CoreCLR|x86.ActiveCfg = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.CoreCLR|x86.Build.0 = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Debug|x64.ActiveCfg = Debug|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Debug|x64.Build.0 = Debug|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Debug|x86.ActiveCfg = Debug|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Debug|x86.Build.0 = Debug|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Release|Any CPU.Build.0 = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Release|x64.ActiveCfg = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Release|x64.Build.0 = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Release|x86.ActiveCfg = Release|Any CPU - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3}.Release|x86.Build.0 = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.CoreCLR|Any CPU.ActiveCfg = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.CoreCLR|Any CPU.Build.0 = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.CoreCLR|x64.ActiveCfg = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.CoreCLR|x64.Build.0 = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.CoreCLR|x86.ActiveCfg = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.CoreCLR|x86.Build.0 = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Debug|x64.ActiveCfg = Debug|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Debug|x64.Build.0 = Debug|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Debug|x86.ActiveCfg = Debug|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Debug|x86.Build.0 = Debug|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Release|Any CPU.Build.0 = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Release|x64.ActiveCfg = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Release|x64.Build.0 = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Release|x86.ActiveCfg = Release|Any CPU - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}.Release|x86.Build.0 = Release|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.CoreCLR|Any CPU.ActiveCfg = CoreCLR|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.CoreCLR|Any CPU.Build.0 = CoreCLR|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.CoreCLR|x64.ActiveCfg = CoreCLR|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.CoreCLR|x64.Build.0 = CoreCLR|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.CoreCLR|x86.ActiveCfg = CoreCLR|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.CoreCLR|x86.Build.0 = CoreCLR|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Debug|x64.ActiveCfg = Debug|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Debug|x64.Build.0 = Debug|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Debug|x86.ActiveCfg = Debug|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Debug|x86.Build.0 = Debug|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Release|Any CPU.Build.0 = Release|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Release|x64.ActiveCfg = Release|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Release|x64.Build.0 = Release|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Release|x86.ActiveCfg = Release|Any CPU - {3B38E8DA-8BFF-4264-AF16-47929E6398A3}.Release|x86.Build.0 = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.CoreCLR|Any CPU.ActiveCfg = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.CoreCLR|Any CPU.Build.0 = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.CoreCLR|x64.ActiveCfg = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.CoreCLR|x64.Build.0 = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.CoreCLR|x86.ActiveCfg = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.CoreCLR|x86.Build.0 = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Debug|x64.ActiveCfg = Debug|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Debug|x64.Build.0 = Debug|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Debug|x86.ActiveCfg = Debug|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Debug|x86.Build.0 = Debug|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Release|Any CPU.Build.0 = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Release|x64.ActiveCfg = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Release|x64.Build.0 = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Release|x86.ActiveCfg = Release|Any CPU - {29EEDF03-0990-45F4-846E-2616970D1FA2}.Release|x86.Build.0 = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.CoreCLR|Any CPU.ActiveCfg = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.CoreCLR|Any CPU.Build.0 = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.CoreCLR|x64.ActiveCfg = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.CoreCLR|x64.Build.0 = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.CoreCLR|x86.ActiveCfg = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.CoreCLR|x86.Build.0 = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Debug|x64.ActiveCfg = Debug|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Debug|x64.Build.0 = Debug|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Debug|x86.ActiveCfg = Debug|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Debug|x86.Build.0 = Debug|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Release|Any CPU.Build.0 = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Release|x64.ActiveCfg = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Release|x64.Build.0 = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Release|x86.ActiveCfg = Release|Any CPU - {2561F253-8F72-436A-BCC3-AA63AB82EDC0}.Release|x86.Build.0 = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.CoreCLR|Any CPU.ActiveCfg = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.CoreCLR|Any CPU.Build.0 = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.CoreCLR|x64.ActiveCfg = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.CoreCLR|x64.Build.0 = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.CoreCLR|x86.ActiveCfg = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.CoreCLR|x86.Build.0 = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Debug|x64.ActiveCfg = Debug|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Debug|x64.Build.0 = Debug|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Debug|x86.ActiveCfg = Debug|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Debug|x86.Build.0 = Debug|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Release|Any CPU.Build.0 = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Release|x64.ActiveCfg = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Release|x64.Build.0 = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Release|x86.ActiveCfg = Release|Any CPU - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {8ED116F4-9DDF-4C49-AB96-AE462E3D64C3} = {422E561A-8118-4BE7-A54F-9309E4F03AAE} - {6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA} = {422E561A-8118-4BE7-A54F-9309E4F03AAE} - {29EEDF03-0990-45F4-846E-2616970D1FA2} = {F594E7FD-1E72-4E51-A496-B019C2BA3180} - {2561F253-8F72-436A-BCC3-AA63AB82EDC0} = {422E561A-8118-4BE7-A54F-9309E4F03AAE} - {3CC791E7-6FC9-4DDE-B4A2-547266977E4E} = {F594E7FD-1E72-4E51-A496-B019C2BA3180} + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9D307AF9-D1F7-4185-AE9B-2DD3F178832C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D307AF9-D1F7-4185-AE9B-2DD3F178832C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D307AF9-D1F7-4185-AE9B-2DD3F178832C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D307AF9-D1F7-4185-AE9B-2DD3F178832C}.Release|Any CPU.Build.0 = Release|Any CPU + {DFD3C9C2-F9E6-4EE3-B614-A8EA7D1E1A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFD3C9C2-F9E6-4EE3-B614-A8EA7D1E1A98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFD3C9C2-F9E6-4EE3-B614-A8EA7D1E1A98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFD3C9C2-F9E6-4EE3-B614-A8EA7D1E1A98}.Release|Any CPU.Build.0 = Release|Any CPU + {AA007633-5178-4D73-A262-CCE7247BDE93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA007633-5178-4D73-A262-CCE7247BDE93}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA007633-5178-4D73-A262-CCE7247BDE93}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA007633-5178-4D73-A262-CCE7247BDE93}.Release|Any CPU.Build.0 = Release|Any CPU + {B4431254-9A2F-43DE-A998-12B22A1593CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4431254-9A2F-43DE-A998-12B22A1593CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4431254-9A2F-43DE-A998-12B22A1593CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4431254-9A2F-43DE-A998-12B22A1593CE}.Release|Any CPU.Build.0 = Release|Any CPU + {983D05F2-3C77-4B51-9A28-A8C6595911BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {983D05F2-3C77-4B51-9A28-A8C6595911BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {983D05F2-3C77-4B51-9A28-A8C6595911BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {983D05F2-3C77-4B51-9A28-A8C6595911BA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {3B9E8987-D4AC-426B-86F6-889126243A9A} + GlobalSection(NestedProjects) = preSolution + {9D307AF9-D1F7-4185-AE9B-2DD3F178832C} = {F46EF430-95AA-4386-9259-292A443AB715} + {DFD3C9C2-F9E6-4EE3-B614-A8EA7D1E1A98} = {F46EF430-95AA-4386-9259-292A443AB715} + {AA007633-5178-4D73-A262-CCE7247BDE93} = {F46EF430-95AA-4386-9259-292A443AB715} + {B4431254-9A2F-43DE-A998-12B22A1593CE} = {F40C4EC9-AE86-4A26-974F-95381888DCDC} + {983D05F2-3C77-4B51-9A28-A8C6595911BA} = {F40C4EC9-AE86-4A26-974F-95381888DCDC} EndGlobalSection EndGlobal diff --git a/README.md b/README.md index bb672b7f9..ad35b4192 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,32 @@ [![CI Tests](https://github.com/PowerShell/PowerShellEditorServices/actions/workflows/ci-test.yml/badge.svg)](https://github.com/PowerShell/PowerShellEditorServices/actions/workflows/ci-test.yml) [![Discord](https://img.shields.io/discord/180528040881815552.svg?label=%23vscode&logo=discord&logoColor=white)](https://aka.ms/psdiscord) -[![Join the chat at https://gitter.im/PowerShell/PowerShellEditorServices](https://badges.gitter.im/PowerShell/PowerShellEditorServices.svg)](https://gitter.im/PowerShell/PowerShellEditorServices?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) **PowerShell Editor Services** is a PowerShell module that provides common functionality needed to enable a consistent and robust PowerShell development experience in almost any editor or integrated development environment (IDE). -## [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) clients using PowerShell Editor Services: +## [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) clients using PowerShell Editor Services + +- [PowerShell for Visual Studio Code](https://github.com/PowerShell/vscode-powershell) +> [!NOTE] +> PowerShell for Azure Data Studio will no longer be updated or maintained. The functionality in PowerShell Editor Services is available in the following editor extensions: +> [!WARNING] +> These clients are community maintained and may be very out of date. +It is recommended to use a generic [LSP plugin](#Usage) with your client if possible. -- [PowerShell for Visual Studio Code](https://github.com/PowerShell/vscode-powershell), also available in Azure Data Studio - [lsp-pwsh](https://github.com/emacs-lsp/lsp-mode/blob/master/clients/lsp-pwsh.el), an Emacs PowerShell plugin - [intellij-powershell](https://github.com/ant-druha/intellij-powershell), adds PowerShell language support to IntelliJ-based IDEs - [coc-powershell](https://github.com/yatli/coc-powershell), a Vim and Neovim plugin +- [powershell.nvim](https://github.com/TheLeoP/powershell.nvim) a Neovim plugin + +## Supported PowerShell Versions + +PSES runs as a PowerShell Module in [currently supported versions of PowerShell 7+](https://learn.microsoft.com/en-us/powershell/scripting/install/powershell-support-lifecycle). -Please note that other than PowerShell for Visual Studio Code, these clients are community maintained and may be very out of date. -It is recommended that you simply use an LSP plugin for your editor and configure it as demonstrated [below](#Usage). +Windows PowerShell 5.1 is supported on a best-effort basis. ## Features @@ -55,7 +64,7 @@ The start script, `Start-EditorServices.ps1`, is found in the `PowerShellEditorS The session details (which named pipes were created) will be written to the given session details path, and the client needs to point to these in order to connect. -The Visual Studio Code, Vim, and IntelliJ extensions use named pipes. +The Visual Studio Code, Vim, Neovim, and IntelliJ extensions use named pipes. ### Standard Input and Output @@ -66,7 +75,7 @@ This is because because these two features require their own IO streams and stdi Please see the [emacs-simple-test.el](test/emacs-simple-test.el), [emacs-test.el](test/emacs-test.el), -[vim-simple-test.el](test/vim-simple-test.vim) and [vim-test.vim](test/vim-test.vim) for examples of end-to-end tested configurations. +[vim-simple-test.vim](test/vim-simple-test.vim) and [vim-test.vim](test/vim-test.vim) for examples of end-to-end tested configurations. They use [eglot for Emacs](https://github.com/joaotavora/eglot) and [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim). ### Advanced Usage @@ -86,7 +95,7 @@ $command = @( "-HostName 'My Client'", "-HostProfileId 'myclient'", "-HostVersion 1.0.0", - "-LogLevel Diagnostic" + "-LogLevel Trace" ) -join " " $pwsh_arguments = "-NoLogo -NoProfile -Command $command" @@ -126,7 +135,40 @@ If you want to take advantage of debugging, your client must support the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/). Your client should use the path to the debug named pipe found in the `session.json` file talked about above. -Currently, only the Visual Studio Code extension supports debugging. +The debugging functionality in PowerShell Editor Services is available in the following editor extensions: + +- [PowerShell for Visual Studio Code](https://github.com/PowerShell/vscode-powershell) +- [nvim-dap-powershell for Neovim](https://github.com/Willem-J-an/nvim-dap-powershell) +- [powershell.nvim for Neovim](https://github.com/TheLeoP/powershell.nvim) +- [intellij-powershell](https://github.com/ant-druha/intellij-powershell) + +### Rename Disclaimer + +PowerShell is not a statically typed language. As such, the renaming of functions, parameters, and other symbols can only be done on a best effort basis. While this is sufficient for the majority of use cases, it cannot be relied upon to find all instances of a symbol and rename them across an entire code base such as in C# or TypeScript. + +There are several edge case scenarios which may exist where rename is difficult or impossible, or unable to be determined due to the dynamic scoping nature of PowerShell. + +The focus of the rename support is on quick updates to variables or functions within a self-contained script file. It is not intended for module developers to find and rename a symbol across multiple files, which is very difficult to do as the relationships are primarily only computed at runtime and not possible to be statically analyzed. + +#### 👍 [Implemented and Tested Rename Scenarios](https://github.com/PowerShell/PowerShellEditorServices/blob/main/test/PowerShellEditorServices.Test.Shared/Refactoring) <- Link to Tests + +#### 🤚 Unsupported Scenarios + +- ❌ Renaming can only be done within a single file. Renaming symbols across multiple files is not supported, even if those are dotsourced from the source file. +- ❌ Functions or variables must have a corresponding definition within their scope or above to be renamed. If we cannot find the original definition of a variable or function, the rename will not be supported. +- ❌ Dynamic Parameters are not supported +- ❌ Dynamically constructed splat parameters will not be renamed/updated (e.g. `$splat = @{};$splat.a = 5;Do-Thing @a`) +- ❌ Scoped variables (e.g. $SCRIPT:test) are not currently supported +- ❌ Renaming a variable inside of a scriptblock that is used in unscoped operations like `Foreach-Parallel` or `Start-Job` and the variable is not defined within the scriptblock is not supported and can have unexpected results. +- ❌ Scriptblocks part of an assignment are considered isolated scopes. For example `$a = 5; $x = {$a}; & $x` does not consider the two $a to be related, even though in execution this reference matches. +- ❌ Scriptblocks that are part of a parameter are assumed to not be executing in a different runspace. For example, the renaming behavior will treat `ForEach-Object -Parallel {$x}` the same as `Foreach-Object {$x}` for purposes of finding scope definitions. To avoid unexpected renaming, define/redefine all your variables in the scriptblock using a param block. +- ❌ A lot of the logic relies on the position of items, so for example, defining a variable in a `begin` block and placing it after a `process` block, while technically correct in PowerShell, will not rename as expected. +- ❌ Similarly, defining a function, and having the function rely on a variable that is assigned outside the function and after the function definition, will not find the outer variable reference. +- ❌ `Get-Variable` and `Set-Variable` are not considered and not currently searched for renames + +#### 📄 Filing a Rename Issue + +If there is a rename scenario you feel can be reasonably supported in PowerShell, please file a bug report in the PowerShellEditorServices repository with the "Expected" and "Actual" being the before and after rename. We will evaluate it and accept or reject it and give reasons why. Items that fall under the Unsupported Scenarios above will be summarily rejected, however that does not mean that they may not be supported in the future if we come up with a reasonably safe way to implement a scenario. ## API Usage @@ -138,9 +180,10 @@ Please note that we only consider the following as stable APIs that can be relie The types of PowerShell Editor Services can change at any moment and should not be linked against in a production environment. -## Development +## Development Environment -> NOTE: The easiest way to manually test changes you've made in PowerShellEditorServices is to follow the [vscode-powershell development doc](https://github.com/PowerShell/vscode-powershell/blob/main/docs/development.md) to get a local build of the VS Code extension to use your local build of PowerShellEditorServices. +> [!TIP] +> The easiest way to manually test changes you've made in PowerShellEditorServices is to follow the [vscode-powershell development doc](https://github.com/PowerShell/vscode-powershell/blob/main/docs/development.md). ### 1. Install PowerShell 7+ @@ -159,59 +202,60 @@ Install-Module InvokeBuild -Scope CurrentUser Install-Module platyPS -Scope CurrentUser ``` -### 4. Delete `NuGet.Config` +### 4. Adjust `nuget.config` if necessary + +Our NuGet configuration uses a secure feed with allow-listed third party dependency packages. If your contribution requires any changes to the included NuGet packages, you must disable this secure feed. -Our NuGet configuration points to a private feed necessary for secure builds, -and it must be committed to the repo as it is. -The easiest way to build without access to that private feed is to delete the file: +First, run this command to prevent accidentally commiting changes to this file ```powershell -Remove-Item NuGet.Config +git update-index --skip-worktree nuget.config ``` -Please be careful not to commit this change in a PR. +Then, either delete the file or remove the `packagesources` section to use nuget.org again. Your PR _will_ fail automated build checks and you _must_ inform us at the top of your PR so the appropriate packages can be added if approved. -Now you're ready to build the code. -You can do so in one of two ways: +## Build PowerShell Editor Services +Now you're ready to build the code. You can do so in one of two ways: -### Building the code from PowerShell +### PowerShell ```powershell -PS C:\path\to\PowerShellEditorServices> Invoke-Build Build +PS C:\src\PowerShellEditorServices> Invoke-Build ``` -### Building the code from Visual Studio Code +### Visual Studio Code Open the PowerShellEditorServices folder that you cloned locally and press Ctrl+Shift+B -(or Cmd+Shift+B on macOS). +(or Cmd+Shift+B on macOS) which will run the default build task. + +## Code of Conduct + +Please see our [Code of Conduct](CODE_OF_CONDUCT.md) before participating in this project. ## Contributions Welcome -We would love to incorporate community contributions into this project. If you would like to -contribute code, documentation, tests, or bug reports, please read our [Contribution Guide](https://github.com/PowerShell/PowerShellEditorServices/blob/main/CONTRIBUTING.md) to learn more. +We would love to incorporate community contributions into this project. If you would like to +contribute code, documentation, tests, or bug reports, please read our [Contribution Guide](CONTRIBUTING.md) to learn more. + +## Security Note + +For any security issues, please see [here](SECURITY.md). ## Maintainers -- [Justin Grote](https://github.com/JustinGrote) - [@JustinWGrote](https://twitter.com/justinwgrote) -- [Patrick Meinecke](https://github.com/SeeminglyScience) - [@SeeminglyScienc](http://twitter.com/SeeminglyScienc) -- [Andy Jordan](https://github.com/andschwa) - [andyleejordan.com](https://andyleejordan.com/) +- Andy Jordan - [@andyleejordan](https://github.com/andyleejordan) +- Patrick Meinecke - [@SeeminglyScience](https://github.com/SeeminglyScience) +- Sydney Smith - [@SydneyhSmith](https://github.com/SydneyhSmith) +- Justin Grote - [@JustinGrote](https://github.com/JustinGrote) ### Emeriti -- [Rob Holt](https://github.com/rjmholt) - [@rjmholt](https://twitter.com/rjmholt) -- [Tyler Leonhardt](https://github.com/TylerLeonhardt) - [tylerleonhardt.com](https://tylerleonhardt.com) -- [David Wilson](https://github.com/daviwil) - [@daviwil](https://twitter.com/daviwil) +- Rob Holt - [@rjmholt](https://github.com/rjmholt) +- Tyler Leonhardt - [@TylerLeonhardt](https://github.com/TylerLeonhardt) +- David Wilson - [@daviwil](https://github.com/daviwil) ## License -This project is [licensed under the MIT License](LICENSE). - -## [Code of Conduct][conduct-md] - -This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. -For more information, see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments. - -[conduct-code]: http://opensource.microsoft.com/codeofconduct/ -[conduct-FAQ]: http://opensource.microsoft.com/codeofconduct/faq/ -[conduct-email]: mailto:opencode@microsoft.com -[conduct-md]: https://github.com/PowerShell/PowerShellEditorServices/blob/main/CODE_OF_CONDUCT.md +This project is [licensed under the MIT License](LICENSE). Please see the +[third-party notices](NOTICE.txt) file for details on the third-party +binaries that we include with releases of this project. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..f941d308b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin) and [PowerShell](https://github.com/PowerShell). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). + + diff --git a/Third Party Notices.txt b/Third Party Notices.txt deleted file mode 100644 index 7a16849ba..000000000 --- a/Third Party Notices.txt +++ /dev/null @@ -1,75 +0,0 @@ -Do Not Translate or Localize - -This file is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party IP to you under the licensing terms for the Microsoft product. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. - ---- - -Serilog - -Copyright 2013-2015 Serilog Contributors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - ---- - -Serilog.Sinks.Async - -Copyright 2013-2015 Serilog Contributors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - ---- - -Serilog.Sinks.File - -Copyright 2013-2015 Serilog Contributors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - ---- - -Serilog.Sinks.Console - -Copyright 2013-2015 Serilog Contributors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index 913e907e4..000000000 --- a/build.ps1 +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env pwsh - -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -param( - [Parameter()] - [switch] - $Bootstrap, - - [Parameter()] - [switch] - $Clean, - - [Parameter()] - [switch] - $Test -) - -$NeededTools = @{ - OpenSsl = "openssl for macOS" - PowerShellGet = "PowerShellGet latest" - InvokeBuild = "InvokeBuild latest" -} - -if ((-not $PSVersionTable["OS"]) -or $PSVersionTable["OS"].Contains("Windows")) { - $OS = "Windows" -} elseif ($PSVersionTable["OS"].Contains("Darwin")) { - $OS = "macOS" -} else { - $OS = "Linux" -} - - -function needsOpenSsl () { - if ($OS -eq "macOS") { - try { - $opensslVersion = (openssl version) - } catch { - return $true - } - } - return $false -} - -function needsPowerShellGet () { - if (Get-Module -ListAvailable -Name PowerShellGet) { - return $false - } - return $true -} - -function needsInvokeBuild () { - if (Get-Module -ListAvailable -Name InvokeBuild) { - return $false - } - return $true -} - -function getMissingTools () { - $missingTools = @() - - if (needsOpenSsl) { - $missingTools += $NeededTools.OpenSsl - } - if (needsPowerShellGet) { - $missingTools += $NeededTools.PowerShellGet - } - if (needsInvokeBuild) { - $missingTools += $NeededTools.InvokeBuild - } - - return $missingTools -} - -function hasMissingTools () { - return ((getMissingTools).Count -gt 0) -} - -if ($Bootstrap) { - $string = "Here is what your environment is missing:`n" - $missingTools = getMissingTools - if (($missingTools).Count -eq 0) { - $string += "* nothing!`n`n Run this script without a flag to build or a -Clean to clean." - } else { - $missingTools | ForEach-Object {$string += "* $_`n"} - $string += "`nAll instructions for installing these tools can be found on PowerShell Editor Services' Github:`n" ` - + "https://github.com/powershell/PowerShellEditorServices#development" - } - Write-Host "`n$string`n" -} elseif(hasMissingTools) { - Write-Host "You are missing needed tools. Run './build.ps1 -Bootstrap' to see what they are." -} else { - if($Clean) { - Invoke-Build Clean - } - - Invoke-Build Build - - if($Test) { - Invoke-Build Test - } -} diff --git a/docs/guide/getting_started.md b/docs/guide/getting_started.md index 696e102af..ddfe78f7e 100644 --- a/docs/guide/getting_started.md +++ b/docs/guide/getting_started.md @@ -61,7 +61,7 @@ Once the basic language configurations have been installed, add this to your ```lua local on_attach = function(client, bufnr) -- Enable completion triggered by - vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc') + vim.api.nvim_set_option_value("omnifunc", "v:lua.vim.lsp.omnifunc", { buf = bufnr }) local bufopts = { noremap = true, silent = true, buffer = bufnr } vim.keymap.set('n', '', vim.lsp.buf.signature_help, bufopts) @@ -101,6 +101,15 @@ lua << EOF EOF ``` +#### Theme Troubleshooting +If you find that your colorscheme appears correctly for a second and then +changes to not having full highlighting, you'll need to disable semantic +highlighting. +Add this line to the `on_attach` function. +```lua +client.server_capabilities.semanticTokensProvider = nil +``` + #### Configure Additional Settings To further configure the server, you can supply settings to the setup table. For example, you can set the code formatting preset to one true brace style @@ -112,6 +121,8 @@ require('lspconfig')['powershell_es'].setup { settings = { powershell = { codeFormatting = { Preset = 'OTBS' } } } } ``` +For a more complete list of options have a look at this schema: +[nvim-lsp-installer powershell_es reference](https://github.com/williamboman/nvim-lsp-installer/blob/main/lua/nvim-lsp-installer/_generated/schemas/powershell_es.lua) You can also set the bundled PSScriptAnalyzer's custom rule path like so: ```lua @@ -122,3 +133,34 @@ require('lspconfig')['powershell_es'].setup { settings = { powershell = { scriptAnalysis = { settingsPath = custom_settings_path } } } } ``` + +#### Autocomplete Brackets Troubleshooting +If you're using `blink.cmp` and you're getting brackets when autocompleting +cmdlet names, you'll need to add `{ "ps1", "psm1" }` to the blocked filetypes +for both `kind_resolution` and `semantic_token_resolution` in the plugin's +config file. + +[Blink.cmp completion reference](https://cmp.saghen.dev/configuration/reference#completion-accept) + +### Indentation + +Vim/Neovim does not contain default `:h indentexpr` for filetype `ps1`. +So you might notice indentation on newline is not behaving as expected for powershell files. +Luckily powershell has similar syntax like C, so we can use `:h cindent` to fix the indentation problem. +You can use the following snippet to either callback of an autocmd or ftplugin. + +```lua +--- ./nvim/lua/ftplugin/ps1.lua + +-- disable indent from powershell treesitter parser +-- because the parse isn't mature currently +-- you can ignore this step if don't use treesitter +if pcall(require, 'nvim-treesitter') then + vim.schedule(function() vim.cmd([[TSBufDisable indent]]) end) +end + +vim.opt_local.cindent = true +vim.opt_local.cinoptions:append { 'J1', '(1s', '+0' } -- see :h cino-J, cino-(, cino-+ + +vim.opt_local.iskeyword:remove { '-' } -- OPTIONALLY consider Verb-Noun as a whole word +``` diff --git a/global.json b/global.json index 4c4c3ae5e..910363ade 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "8.0.416", "rollForward": "latestFeature", "allowPrerelease": false } diff --git a/module/PowerShellEditorServices/Commands/PowerShellEditorServices.Commands.psd1 b/module/PowerShellEditorServices/Commands/PowerShellEditorServices.Commands.psd1 index 69347beab..2ef9b51d0 100644 --- a/module/PowerShellEditorServices/Commands/PowerShellEditorServices.Commands.psd1 +++ b/module/PowerShellEditorServices/Commands/PowerShellEditorServices.Commands.psd1 @@ -79,7 +79,8 @@ FunctionsToExport = @('Register-EditorCommand', 'Test-ScriptExtent', 'Open-EditorFile', 'New-EditorFile', - 'Clear-Host') + 'Clear-Host', + 'Start-DebugAttachSession') # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @() diff --git a/module/PowerShellEditorServices/Commands/Public/Start-DebugAttachSession.ps1 b/module/PowerShellEditorServices/Commands/Public/Start-DebugAttachSession.ps1 new file mode 100644 index 000000000..2ef978070 --- /dev/null +++ b/module/PowerShellEditorServices/Commands/Public/Start-DebugAttachSession.ps1 @@ -0,0 +1,201 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +using namespace System.Collections +using namespace System.Management.Automation +using namespace System.Reflection +using namespace System.Threading +using namespace System.Threading.Tasks + +function Start-DebugAttachSession { + <# + .EXTERNALHELP ..\PowerShellEditorServices.Commands-help.xml + #> + [OutputType([System.Management.Automation.Job2])] + [CmdletBinding(DefaultParameterSetName = 'ProcessId')] + param( + [Parameter()] + [string] + $Name, + + [Parameter(ParameterSetName = 'ProcessId')] + [int] + $ProcessId, + + [Parameter(ParameterSetName = 'CustomPipeName')] + [string] + $CustomPipeName, + + [Parameter()] + [string] + $RunspaceName, + + [Parameter()] + [int] + $RunspaceId, + + [Parameter()] + [string] + $ComputerName, + + [Parameter()] + [ValidateSet('Close', 'Hide', 'Keep')] + [string] + $WindowActionOnEnd, + + [Parameter()] + [IDictionary[]] + $PathMapping, + + [Parameter()] + [switch] + $AsJob + ) + + $ErrorActionPreference = 'Stop' + + try { + if ($PSBoundParameters.ContainsKey('RunspaceId') -and $RunspaceName) { + $err = [ErrorRecord]::new( + [ArgumentException]::new("Cannot specify both RunspaceId and RunspaceName parameters"), + "InvalidRunspaceParameters", + [ErrorCategory]::InvalidArgument, + $null) + $err.ErrorDetails = [ErrorDetails]::new("") + $err.ErrorDetails.RecommendedAction = 'Specify only one of RunspaceId or RunspaceName.' + $PSCmdlet.WriteError($err) + return + } + + # Var will be set by PSES in configurationDone before launching script + $debugServer = Get-Variable -Name __psEditorServices_DebugServer -ValueOnly -ErrorAction Ignore + if (-not $debugServer) { + $err = [ErrorRecord]::new( + [Exception]::new("Cannot start a new attach debug session unless running in an existing launch debug session not in a temporary console"), + "NoDebugSession", + [ErrorCategory]::InvalidOperation, + $null) + $err.ErrorDetails = [ErrorDetails]::new("") + $err.ErrorDetails.RecommendedAction = 'Launch script with debugging to ensure the debug session is available.' + $PSCmdlet.WriteError($err) + return + } + + if ($AsJob -and -not (Get-Command -Name Start-ThreadJob -ErrorAction Ignore)) { + $err = [ErrorRecord]::new( + [Exception]::new("Cannot use the -AsJob parameter unless running on PowerShell 7+ or the ThreadJob module is present"), + "NoThreadJob", + [ErrorCategory]::InvalidArgument, + $null) + $err.ErrorDetails = [ErrorDetails]::new("") + $err.ErrorDetails.RecommendedAction = 'Install the ThreadJob module or run on PowerShell 7+.' + $PSCmdlet.WriteError($err) + return + } + + $configuration = @{ + type = 'PowerShell' + request = 'attach' + # A temp console is also needed as the current one is busy running + # this code. Failing to set this will cause a deadlock. + createTemporaryIntegratedConsole = $true + } + + if ($ProcessId) { + if ($ProcessId -eq $PID) { + $err = [ErrorRecord]::new( + [ArgumentException]::new("PSES does not support attaching to the current editor process"), + "AttachToCurrentProcess", + [ErrorCategory]::InvalidArgument, + $PID) + $err.ErrorDetails = [ErrorDetails]::new("") + $err.ErrorDetails.RecommendedAction = 'Specify a different process id.' + $PSCmdlet.WriteError($err) + return + } + + if ($Name) { + $configuration.name = $Name + } + else { + $configuration.name = "Attach Process $ProcessId" + } + $configuration.processId = $ProcessId + } + elseif ($CustomPipeName) { + if ($Name) { + $configuration.name = $Name + } + else { + $configuration.name = "Attach Pipe $CustomPipeName" + } + $configuration.customPipeName = $CustomPipeName + } + else { + $configuration.name = 'Attach Session' + } + + if ($ComputerName) { + $configuration.computerName = $ComputerName + } + + if ($PSBoundParameters.ContainsKey('RunspaceId')) { + $configuration.runspaceId = $RunspaceId + } + elseif ($RunspaceName) { + $configuration.runspaceName = $RunspaceName + } + + if ($WindowActionOnEnd) { + $configuration.temporaryConsoleWindowActionOnDebugEnd = $WindowActionOnEnd.ToLowerInvariant() + } + + if ($PathMapping) { + $configuration.pathMappings = $PathMapping + } + + # https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_StartDebugging + $resp = $debugServer.SendRequest( + 'startDebugging', + @{ + configuration = $configuration + request = 'attach' + } + ) + + # PipelineStopToken added in pwsh 7.6 + $cancelToken = if ($PSCmdlet.PipelineStopToken) { + $PSCmdlet.PipelineStopToken + } + else { + [CancellationToken]::new($false) + } + + # There is no response for a startDebugging request + $task = $resp.ReturningVoid($cancelToken) + + $waitTask = { + [CmdletBinding()] + param ([Parameter(Mandatory)][Task]$Task) + + while (-not $Task.AsyncWaitHandle.WaitOne(300)) {} + $null = $Task.GetAwaiter().GetResult() + } + + if ($AsJob) { + # Using the Ast to build the scriptblock allows the job to inherit + # the using namespace entries and include the proper line/script + # paths in any error traces that are emitted. + Start-ThreadJob -ScriptBlock { + & ($args[0]).Ast.GetScriptBlock() $args[1] + } -ArgumentList $waitTask, $task + } + else { + & $waitTask $task + } + } + catch { + $PSCmdlet.WriteError($_) + return + } +} \ No newline at end of file diff --git a/module/PowerShellEditorServices/PowerShellEditorServices.psd1 b/module/PowerShellEditorServices/PowerShellEditorServices.psd1 index ebeba42f9..4fb2ea16a 100644 --- a/module/PowerShellEditorServices/PowerShellEditorServices.psd1 +++ b/module/PowerShellEditorServices/PowerShellEditorServices.psd1 @@ -19,7 +19,7 @@ RootModule = if ($PSEdition -eq 'Core') } # Version number of this module. -ModuleVersion = '3.18.1' +ModuleVersion = '4.6.0' # ID used to uniquely identify this module GUID = '9ca15887-53a2-479a-9cda-48d26bcb6c47' diff --git a/module/PowerShellEditorServices/Start-EditorServices.ps1 b/module/PowerShellEditorServices/Start-EditorServices.ps1 index 3e445679a..c61503c65 100644 --- a/module/PowerShellEditorServices/Start-EditorServices.ps1 +++ b/module/PowerShellEditorServices/Start-EditorServices.ps1 @@ -46,7 +46,7 @@ param( [ValidateNotNullOrEmpty()] $LogPath, - [ValidateSet("Diagnostic", "Verbose", "Normal", "Warning", "Error")] + [ValidateSet("Diagnostic", "Verbose", "Normal", "Warning", "Error", "Trace", "Debug", "Information", "Critical", "None")] $LogLevel, [ValidateNotNullOrEmpty()] @@ -107,5 +107,13 @@ param( $DebugServiceOutPipeName ) +#Translate legacy PSES log levels to MEL levels +$LogLevel = switch ($LogLevel) { + 'Diagnostic' { 'Trace' } + 'Verbose' { 'Debug' } + 'Normal' { 'Information' } + default { $LogLevel } +} + Import-Module -Name "$PSScriptRoot/PowerShellEditorServices.psd1" Start-EditorServices @PSBoundParameters diff --git a/module/docs/PowerShellEditorServices.Commands.md b/module/docs/PowerShellEditorServices.Commands.md index ca417c173..017432f8c 100644 --- a/module/docs/PowerShellEditorServices.Commands.md +++ b/module/docs/PowerShellEditorServices.Commands.md @@ -46,6 +46,10 @@ The Set-ScriptExtent function can insert or replace text at a specified position You can use the Find-Ast function to easily find the desired extent. +### [Start-DebugAttachSession](Start-DebugAttachSession.md) + +The Start-DebugAttachSession function can start a new debug session that is attached to the specified PowerShell instance. + ### [Test-ScriptExtent](Test-ScriptExtent.md) The Test-ScriptExtent function can be used to determine if a ScriptExtent object is before, after, or inside another ScriptExtent object. You can also test for any combination of these with separate ScriptExtent objects to test against. diff --git a/module/docs/Start-DebugAttachSession.md b/module/docs/Start-DebugAttachSession.md new file mode 100644 index 000000000..2601cd263 --- /dev/null +++ b/module/docs/Start-DebugAttachSession.md @@ -0,0 +1,273 @@ +--- +external help file: PowerShellEditorServices.Commands-help.xml +Module Name: PowerShellEditorServices.Commands +online version: https://github.com/PowerShell/PowerShellEditorServices/tree/main/module/docs/Start-DebugAttachSession.md +schema: 2.0.0 +--- + +# Start-DebugAttachSession + +## SYNOPSIS + +Starts a new debug session attached to the specified PowerShell instance. + +## SYNTAX + +### ProcessId (Default) +``` +Start-DebugAttachSession [-Name ] [-ProcessId ] [-RunspaceName ] [-RunspaceId ] + [-ComputerName ] [-WindowActionOnEnd {Close | Hide | Keep}] [-PathMapping ] [-AsJob] + [] +``` + +### CustomPipeName +``` +Start-DebugAttachSession [-Name ] [-CustomPipeName ] [-RunspaceName ] + [-RunspaceId ] [-ComputerName ] [-WindowActionOnEnd {Close | Hide | Keep}] + [-PathMapping ] [-AsJob] [] +``` + +## DESCRIPTION + +The Start-DebugAttachSession function can be used to start a new debug session that is attached to the specified PowerShell instance. The caller must be running in an existing launched debug session, the launched session is not running in a temporary console, and the launched session is not entered into a remote PSSession. If the callers script ends before the new debug session is completed, the debug session for the child will also end. + +The function will return once the attach response was received by the debug server. For an example, an attach request will return once PowerShell has attached to the process and has called `Debug-Runspace`. If you need to return early use the `-AsJob` parameter to return a `Job` object immediately that can be used to wait for the response at a later time. + +If `-ProcessId` or `-CustomPipeName` is not specified, the debug client will prompt for process to connect to. If `-RunspaceId` or `-RunspaceName` is not specified, the debug client will prompt for which runspace to connect to. + +## EXAMPLES + +### -------------------------- EXAMPLE 1 -------------------------- + +```powershell +$pipeName = "TestPipe-$(New-Guid)" +$procParams = @{ + FilePath = 'pwsh' + ArgumentList = ('-CustomPipeName {0} -File other-script.ps1' -f $pipeName) + PassThru = $true +} +$proc = Start-Process @procParams + +Start-DebugAttachSession -CustomPipeName $pipeName -RunspaceId 1 +$proc | Wait-Process + + +<# The contents of `other-script.ps1` is #> +# Waits until PowerShell has attached +$runspaces = Get-Runspace +while ($true) { + if (Get-Runspace | Where-Object { $_.Id -notin $runspaces.Id }) { + break + } + Start-Sleep -Seconds 1 +} + +# WinPS will only have breakpoints synced once the debugger has been hit. +if ($PSVersionTable.PSVersion -lt '6.0') { + Wait-Debugger +} + +# Place breakpoint below or use Wait-Debugger +# to have the attach debug session break. +$a = 'abc' +$b = '' +Write-Host "Test $a - $PID" +``` + +Launches a new PowerShell process with a custom pipe and starts a new attach configuration that will debug the new process under a child debugging session. The caller waits until the new process ends before ending the parent session. + +### -------------------------- EXAMPLE 2 -------------------------- + +```powershell +$attachParams = @{ + ComputerName = 'remote-windows' + ProcessId = $remotePid + RunspaceId = 1 + PathMapping = @( + @{ + localRoot = 'C:\local\path\to\scripts\' + remoteRoot = 'C:\remote\path\on\remote-windows\' + } + ) +} +Start-DebugAttachSession @attachParams +``` + +Attaches to a remote PSSession through the WSMan parameter and maps the remote path running the script in the PSSession to the same copy of files locally. For example `remote-windows` is running the script `C:\remote\path\on\remote-windows\script.ps1` but the same script(s) are located locally on the current host `C:\local\path\to\scripts\script.ps1`. + +The debug client can see the remote files as local when setting breakpoints and inspecting the callstack with this mapped path. + +## PARAMETERS + +### -AsJob + +Instead of waiting for the start debugging response before returning, the `-AsJob` parameter will output a job immediately after sending the request that waits for the job. This is useful if further work is needed for a debug session to successfully attach and start debugging the target runspace. + +This is only supported when the calling script is running on PowerShell 7+ or the `ThreadJob` module is present. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ComputerName + +The computer name to which a remote session will be established before attaching to the target runspace. If specified, the temporary console will run `Enter-PSSession -ComputerName ...` to connect to a host over WSMan before attaching to the requested PowerShell instance. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CustomPipeName + +The custom pipe name of the PowerShell host process to attach to. This option is mutually exclusive with `-ProcessId`. + +```yaml +Type: String +Parameter Sets: CustomPipeName +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name + +The name of the debug session to show in the debug client. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PathMapping + +An array of dictionaries with the keys `localRoot` and `remoteRoot` that maps a local and remote path root to each other. This option is useful when attaching to a PSSession running a script that is not accessible locally but can be found under a different path. + +It is a good idea to ensure the `localRoot` and `remoteRoot` entries are either the absolute path to a script or ends with the trailing directory separator if specifying a directory. A path can also be mapped from a Windows and non-Windows path, just ensure the correct directory separators are used for each OS type. For example `/` for non-Windows and `\` for Windows. + +```yaml +Type: IDictionary[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProcessId + +The ID of the PowerShell host process that should be attached. This option is mutually exclusive with `-CustomPipeName`. + +```yaml +Type: Int32 +Parameter Sets: ProcessId +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RunspaceId + +The ID of the runspace to debug in the attached process. This option is mutually exclusive with `-RunspaceName`. + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RunspaceName + +The name of the runspace to debug in the attached process. This option is mutually exclusive with `-RunspaceId`. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WindowActionOnEnd + +Specifies the action to take on the temporary debug console created by the debug client after the attached session ends. This corresponds to the VSCode attach configuration option `temporaryConsoleWindowActionOnDebugEnd`. Setting to `Close` will close the debug console, `Hide` will move back to the last debug console before the attach session started, and `Keep` (default) will keep the active terminal as the attached session. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +You can't pipe objects to this function. + +## OUTPUTS + +### None + +By default, this function returns no output. + +### System.Management.Automation.Job2 + +When you use the `-AsJob` parameter, this function returns the `Job` object that is waiting for the response. + +## NOTES + +The function will fail if the caller is not running under a debug session or was started through an attach request. + +## RELATED LINKS diff --git a/modules.json b/modules.json deleted file mode 100644 index a21a350b0..000000000 --- a/modules.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "PSScriptAnalyzer": { - "Version": "1.22.0", - "AllowPrerelease": false - }, - "Plaster": { - "Version": "1.1.3", - "AllowPrerelease": false - }, - "PSReadLine": { - "Version": "2.4.0-beta0", - "AllowPrerelease": true - } -} diff --git a/NuGet.Config b/nuget.config similarity index 64% rename from NuGet.Config rename to nuget.config index f04dcd513..f003b0fbd 100644 --- a/NuGet.Config +++ b/nuget.config @@ -2,6 +2,6 @@ - + diff --git a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs index 0eddc622b..6f2ec8851 100644 --- a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs +++ b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs @@ -138,7 +138,7 @@ public StartEditorServicesCommand() /// The minimum log level that should be emitted. /// [Parameter] - public PsesLogLevel LogLevel { get; set; } = PsesLogLevel.Normal; + public string LogLevel { get; set; } = PsesLogLevel.Warning.ToString(); /// /// Paths to additional PowerShell modules to be imported at startup. @@ -195,6 +195,11 @@ public StartEditorServicesCommand() [Parameter] public string StartupBanner { get; set; } + /// + /// Compatibility to store the currently supported PSESLogLevel Enum Value + /// + private PsesLogLevel _psesLogLevel = PsesLogLevel.Warning; + #pragma warning disable IDE0022 protected override void BeginProcessing() { @@ -215,11 +220,10 @@ protected override void BeginProcessing() } #pragma warning restore IDE0022 - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Uses ThrowTerminatingError() instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "We have to wait here, it's the whole program.")] protected override void EndProcessing() { - _logger.Log(PsesLogLevel.Diagnostic, "Beginning EndProcessing block"); - + _logger.Log(PsesLogLevel.Trace, "Beginning EndProcessing block"); try { // First try to remove PSReadLine to decomplicate startup @@ -230,11 +234,9 @@ protected override void EndProcessing() EditorServicesConfig editorServicesConfig = CreateConfigObject(); using EditorServicesLoader psesLoader = EditorServicesLoader.Create(_logger, editorServicesConfig, SessionDetailsPath, _loggerUnsubscribers); - _logger.Log(PsesLogLevel.Verbose, "Loading EditorServices"); + _logger.Log(PsesLogLevel.Debug, "Loading EditorServices"); // Synchronously start editor services and wait here until it shuts down. -#pragma warning disable VSTHRD002 psesLoader.LoadAndRunEditorServicesAsync().GetAwaiter().GetResult(); -#pragma warning restore VSTHRD002 } catch (Exception e) { @@ -260,7 +262,25 @@ protected override void EndProcessing() private void StartLogging() { - _logger = new HostLogger(LogLevel); + bool isLegacyPsesLogLevel = false; + if (!Enum.TryParse(LogLevel, true, out _psesLogLevel)) + { + // PSES used to have log levels that didn't match MEL levels, this is an adapter for those types and may eventually be removed once people migrate their settings. + isLegacyPsesLogLevel = true; + _psesLogLevel = LogLevel switch + { + "Diagnostic" => PsesLogLevel.Trace, + "Verbose" => PsesLogLevel.Debug, + "Normal" => PsesLogLevel.Information, + _ => PsesLogLevel.Trace + }; + } + + _logger = new HostLogger(_psesLogLevel); + if (isLegacyPsesLogLevel) + { + _logger.Log(PsesLogLevel.Warning, $"The log level '{LogLevel}' is deprecated and will be removed in a future release. Please update your settings or command line options to use one of the following options: 'Trace', 'Debug', 'Information', 'Warning', 'Error', 'Critical'."); + } // We need to not write log messages to Stdio // if it's being used as a protocol transport @@ -284,7 +304,7 @@ private void StartLogging() IDisposable fileLoggerUnsubscriber = _logger.Subscribe(fileLogger); fileLogger.AddUnsubscriber(fileLoggerUnsubscriber); _loggerUnsubscribers.Add(fileLoggerUnsubscriber); - _logger.Log(PsesLogLevel.Diagnostic, "Logging started"); + _logger.Log(PsesLogLevel.Trace, "Logging started"); } // Sanitizes user input and ensures the directory is created. @@ -302,7 +322,7 @@ private string GetLogDirPath() private void RemovePSReadLineForStartup() { - _logger.Log(PsesLogLevel.Verbose, "Removing PSReadLine"); + _logger.Log(PsesLogLevel.Debug, "Removing PSReadLine"); using SMA.PowerShell pwsh = SMA.PowerShell.Create(RunspaceMode.CurrentRunspace); bool hasPSReadLine = pwsh.AddCommand(new CmdletInfo(@"Microsoft.PowerShell.Core\Get-Module", typeof(GetModuleCommand))) .AddParameter("Name", "PSReadLine") @@ -317,13 +337,13 @@ private void RemovePSReadLineForStartup() .AddParameter("Name", "PSReadLine") .AddParameter("ErrorAction", "SilentlyContinue"); - _logger.Log(PsesLogLevel.Verbose, "Removed PSReadLine"); + _logger.Log(PsesLogLevel.Debug, "Removed PSReadLine"); } } private EditorServicesConfig CreateConfigObject() { - _logger.Log(PsesLogLevel.Diagnostic, "Creating host configuration"); + _logger.Log(PsesLogLevel.Trace, "Creating host configuration"); string bundledModulesPath = BundledModulesPath; if (!Path.IsPathRooted(bundledModulesPath)) @@ -352,7 +372,7 @@ private EditorServicesConfig CreateConfigObject() LogPath) { FeatureFlags = FeatureFlags, - LogLevel = LogLevel, + LogLevel = _psesLogLevel, ConsoleRepl = GetReplKind(), UseNullPSHostUI = Stdio, // If Stdio is used we can't write anything else out AdditionalModules = AdditionalModules, @@ -402,31 +422,31 @@ private string GetProfilePathFromProfileObject(PSObject profileObject, ProfileUs // * On Linux or macOS on any version greater than or equal to 7 private ConsoleReplKind GetReplKind() { - _logger.Log(PsesLogLevel.Diagnostic, "Determining REPL kind"); + _logger.Log(PsesLogLevel.Trace, "Determining REPL kind"); if (Stdio || !EnableConsoleRepl) { - _logger.Log(PsesLogLevel.Diagnostic, "REPL configured as None"); + _logger.Log(PsesLogLevel.Trace, "REPL configured as None"); return ConsoleReplKind.None; } if (UseLegacyReadLine) { - _logger.Log(PsesLogLevel.Diagnostic, "REPL configured as Legacy"); + _logger.Log(PsesLogLevel.Trace, "REPL configured as Legacy"); return ConsoleReplKind.LegacyReadLine; } - _logger.Log(PsesLogLevel.Diagnostic, "REPL configured as PSReadLine"); + _logger.Log(PsesLogLevel.Trace, "REPL configured as PSReadLine"); return ConsoleReplKind.PSReadLine; } private ITransportConfig GetLanguageServiceTransport() { - _logger.Log(PsesLogLevel.Diagnostic, "Configuring LSP transport"); + _logger.Log(PsesLogLevel.Trace, "Configuring LSP transport"); if (DebugServiceOnly) { - _logger.Log(PsesLogLevel.Diagnostic, "No LSP transport: PSES is debug only"); + _logger.Log(PsesLogLevel.Trace, "No LSP transport: PSES is debug only"); return null; } @@ -450,11 +470,11 @@ private ITransportConfig GetLanguageServiceTransport() private ITransportConfig GetDebugServiceTransport() { - _logger.Log(PsesLogLevel.Diagnostic, "Configuring debug transport"); + _logger.Log(PsesLogLevel.Trace, "Configuring debug transport"); if (LanguageServiceOnly) { - _logger.Log(PsesLogLevel.Diagnostic, "No Debug transport: PSES is language service only"); + _logger.Log(PsesLogLevel.Trace, "No Debug transport: PSES is language service only"); return null; } @@ -465,7 +485,7 @@ private ITransportConfig GetDebugServiceTransport() return new StdioTransportConfig(_logger); } - _logger.Log(PsesLogLevel.Diagnostic, "No debug transport: Transport is Stdio with debug disabled"); + _logger.Log(PsesLogLevel.Trace, "No debug transport: Transport is Stdio with debug disabled"); return null; } diff --git a/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs b/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs index b2e683a2f..66bc7b1de 100644 --- a/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs +++ b/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs @@ -90,14 +90,14 @@ public EditorServicesConfig( public ConsoleReplKind ConsoleRepl { get; set; } = ConsoleReplKind.None; /// - /// Will suppress messages to PSHost (to prevent Stdio clobbering) + /// Will suppress messages to PSHost (to prevent Stdio clobbering) /// public bool UseNullPSHostUI { get; set; } /// - /// The minimum log level to log events with. + /// The minimum log level to log events with. Defaults to warning but is usually overriden by the startup process. /// - public PsesLogLevel LogLevel { get; set; } = PsesLogLevel.Normal; + public PsesLogLevel LogLevel { get; set; } = PsesLogLevel.Warning; /// /// Configuration for the language server protocol transport to use. diff --git a/src/PowerShellEditorServices.Hosting/Configuration/HostLogger.cs b/src/PowerShellEditorServices.Hosting/Configuration/HostLogger.cs index 7ce6f66a9..1c8617341 100644 --- a/src/PowerShellEditorServices.Hosting/Configuration/HostLogger.cs +++ b/src/PowerShellEditorServices.Hosting/Configuration/HostLogger.cs @@ -12,21 +12,51 @@ namespace Microsoft.PowerShell.EditorServices.Hosting { /// - /// User-facing log level for editor services configuration. + /// Log Level for HostLogger. This is a direct copy of LogLevel from Microsoft.Extensions.Logging, and will map to + /// MEL.LogLevel once MEL is bootstrapped, but we don't want to load any MEL assemblies until the Assembly Load + /// Context is set up. /// - /// - /// The underlying values of this enum attempt to align to both - /// and - /// . - /// public enum PsesLogLevel { - Diagnostic = 0, - Verbose = 1, - Normal = 2, + /// + /// Logs that contain the most detailed messages. These messages may contain sensitive application data. + /// These messages are disabled by default and should never be enabled in a production environment. + /// + Trace = 0, + + /// + /// Logs that are used for interactive investigation during development. These logs should primarily contain + /// information useful for debugging and have no long-term value. + /// + Debug = 1, + + /// + /// Logs that track the general flow of the application. These logs should have long-term value. + /// + Information = 2, + + /// + /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the + /// application execution to stop. + /// Warning = 3, + + /// + /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a + /// failure in the current activity, not an application-wide failure. + /// Error = 4, - None = 5 + + /// + /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires + /// immediate attention. + /// + Critical = 5, + + /// + /// Not used for writing log messages. Specifies that a logging category should not write any messages. + /// + None = 6, } /// @@ -181,16 +211,9 @@ public void LogException( /// Since it's likely that the process will end when PSES shuts down, /// there's no good reason to need objects rather than writing directly to the host. /// - internal class PSHostLogger : IObserver<(PsesLogLevel logLevel, string message)> + /// The PowerShell host user interface object to log output to. + internal class PSHostLogger(PSHostUserInterface ui) : IObserver<(PsesLogLevel logLevel, string message)> { - private readonly PSHostUserInterface _ui; - - /// - /// Create a new PowerShell host logger. - /// - /// The PowerShell host user interface object to log output to. - public PSHostLogger(PSHostUserInterface ui) => _ui = ui; - public void OnCompleted() { // No-op since there's nothing to close or dispose, @@ -201,35 +224,35 @@ public void OnCompleted() public void OnNext((PsesLogLevel logLevel, string message) value) { - switch (value.logLevel) + (PsesLogLevel logLevel, string message) = value; + switch (logLevel) { - case PsesLogLevel.Diagnostic: - _ui.WriteDebugLine(value.message); - return; - - case PsesLogLevel.Verbose: - _ui.WriteVerboseLine(value.message); - return; - - case PsesLogLevel.Normal: - _ui.WriteLine(value.message); - return; - + case PsesLogLevel.Trace: + ui.WriteDebugLine("[Trace] " + message); + break; + case PsesLogLevel.Debug: + ui.WriteDebugLine(message); + break; + case PsesLogLevel.Information: + ui.WriteVerboseLine(message); + break; case PsesLogLevel.Warning: - _ui.WriteWarningLine(value.message); - return; - + ui.WriteWarningLine(message); + break; case PsesLogLevel.Error: - _ui.WriteErrorLine(value.message); - return; - + case PsesLogLevel.Critical: + ui.WriteErrorLine(message); + break; default: - _ui.WriteLine(value.message); - return; + ui.WriteDebugLine("UNKNOWN:" + message); + break; } } } + /// + /// A simple log sink that logs to a stream, typically used to log to a file. + /// internal class StreamLogger : IObserver<(PsesLogLevel logLevel, string message)>, IDisposable { public static StreamLogger CreateWithNewFile(string path) @@ -284,9 +307,7 @@ public void OnCompleted() } _cancellationSource.Cancel(); - _writerThread.Join(); - _unsubscriber.Dispose(); _fileWriter.Flush(); _fileWriter.Close(); @@ -299,29 +320,17 @@ public void OnCompleted() public void OnNext((PsesLogLevel logLevel, string message) value) { - string message = null; - switch (value.logLevel) + string message = value.logLevel switch { - case PsesLogLevel.Diagnostic: - message = $"[DBG]: {value.message}"; - break; - - case PsesLogLevel.Verbose: - message = $"[VRB]: {value.message}"; - break; - - case PsesLogLevel.Normal: - message = $"[INF]: {value.message}"; - break; - - case PsesLogLevel.Warning: - message = $"[WRN]: {value.message}"; - break; - - case PsesLogLevel.Error: - message = $"[ERR]: {value.message}"; - break; - } + // String interpolation often considered a logging sin is OK here because our filtering happens before. + PsesLogLevel.Trace => $"[TRC]: {value.message}", + PsesLogLevel.Debug => $"[DBG]: {value.message}", + PsesLogLevel.Information => $"[INF]: {value.message}", + PsesLogLevel.Warning => $"[WRN]: {value.message}", + PsesLogLevel.Error => $"[ERR]: {value.message}", + PsesLogLevel.Critical => $"[CRT]: {value.message}", + _ => value.message, + }; _messageQueue.Add(message); } diff --git a/src/PowerShellEditorServices.Hosting/Configuration/SessionFileWriter.cs b/src/PowerShellEditorServices.Hosting/Configuration/SessionFileWriter.cs index c5f351f91..bca1acd5e 100644 --- a/src/PowerShellEditorServices.Hosting/Configuration/SessionFileWriter.cs +++ b/src/PowerShellEditorServices.Hosting/Configuration/SessionFileWriter.cs @@ -63,7 +63,7 @@ public SessionFileWriter(HostLogger logger, string sessionFilePath, Version powe /// The reason for the startup failure. public void WriteSessionFailure(string reason) { - _logger.Log(PsesLogLevel.Diagnostic, "Writing session failure"); + _logger.Log(PsesLogLevel.Trace, "Writing session failure"); Dictionary sessionObject = new() { @@ -81,7 +81,7 @@ public void WriteSessionFailure(string reason) /// The debug adapter transport configuration. public void WriteSessionStarted(ITransportConfig languageServiceTransport, ITransportConfig debugAdapterTransport) { - _logger.Log(PsesLogLevel.Diagnostic, "Writing session started"); + _logger.Log(PsesLogLevel.Trace, "Writing session started"); Dictionary sessionObject = new() { @@ -142,7 +142,7 @@ private void WriteSessionObject(Dictionary sessionObject) File.WriteAllText(_sessionFilePath, content, s_sessionFileEncoding); } - _logger.Log(PsesLogLevel.Verbose, $"Session file written to {_sessionFilePath} with content:\n{content}"); + _logger.Log(PsesLogLevel.Debug, $"Session file written to {_sessionFilePath} with content:\n{content}"); } } } diff --git a/src/PowerShellEditorServices.Hosting/Configuration/TransportConfig.cs b/src/PowerShellEditorServices.Hosting/Configuration/TransportConfig.cs index be763737c..db64a27a7 100644 --- a/src/PowerShellEditorServices.Hosting/Configuration/TransportConfig.cs +++ b/src/PowerShellEditorServices.Hosting/Configuration/TransportConfig.cs @@ -53,7 +53,7 @@ public sealed class StdioTransportConfig : ITransportConfig public Task<(Stream inStream, Stream outStream)> ConnectStreamsAsync() { - _logger.Log(PsesLogLevel.Diagnostic, "Connecting stdio streams"); + _logger.Log(PsesLogLevel.Trace, "Connecting stdio streams"); return Task.FromResult((Console.OpenStandardInput(), Console.OpenStandardOutput())); } } @@ -102,11 +102,11 @@ private DuplexNamedPipeTransportConfig(HostLogger logger, string pipeName) public async Task<(Stream inStream, Stream outStream)> ConnectStreamsAsync() { - _logger.Log(PsesLogLevel.Diagnostic, "Creating named pipe"); + _logger.Log(PsesLogLevel.Trace, "Creating named pipe"); NamedPipeServerStream namedPipe = NamedPipeUtils.CreateNamedPipe(_pipeName, PipeDirection.InOut); - _logger.Log(PsesLogLevel.Diagnostic, "Waiting for named pipe connection"); + _logger.Log(PsesLogLevel.Trace, "Waiting for named pipe connection"); await namedPipe.WaitForConnectionAsync().ConfigureAwait(false); - _logger.Log(PsesLogLevel.Diagnostic, "Named pipe connected"); + _logger.Log(PsesLogLevel.Trace, "Named pipe connected"); return (namedPipe, namedPipe); } } @@ -173,18 +173,18 @@ private SimplexNamedPipeTransportConfig(HostLogger logger, string inPipeName, st public async Task<(Stream inStream, Stream outStream)> ConnectStreamsAsync() { - _logger.Log(PsesLogLevel.Diagnostic, "Starting in pipe connection"); + _logger.Log(PsesLogLevel.Trace, "Starting in pipe connection"); NamedPipeServerStream inPipe = NamedPipeUtils.CreateNamedPipe(_inPipeName, PipeDirection.InOut); Task inPipeConnected = inPipe.WaitForConnectionAsync(); - _logger.Log(PsesLogLevel.Diagnostic, "Starting out pipe connection"); + _logger.Log(PsesLogLevel.Trace, "Starting out pipe connection"); NamedPipeServerStream outPipe = NamedPipeUtils.CreateNamedPipe(_outPipeName, PipeDirection.Out); Task outPipeConnected = outPipe.WaitForConnectionAsync(); - _logger.Log(PsesLogLevel.Diagnostic, "Wating for pipe connections"); + _logger.Log(PsesLogLevel.Trace, "Wating for pipe connections"); await Task.WhenAll(inPipeConnected, outPipeConnected).ConfigureAwait(false); - _logger.Log(PsesLogLevel.Diagnostic, "Simplex named pipe transport connected"); + _logger.Log(PsesLogLevel.Trace, "Simplex named pipe transport connected"); return (inPipe, outPipe); } } diff --git a/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs b/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs index f2da0eb4e..eea23353c 100644 --- a/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs +++ b/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs @@ -70,20 +70,20 @@ public static EditorServicesLoader Create( Version powerShellVersion = GetPSVersion(); SessionFileWriter sessionFileWriter = new(logger, sessionDetailsPath, powerShellVersion); - logger.Log(PsesLogLevel.Diagnostic, "Session file writer created"); + logger.Log(PsesLogLevel.Trace, "Session file writer created"); #if CoreCLR // In .NET Core, we add an event here to redirect dependency loading to the new AssemblyLoadContext we load PSES' dependencies into - logger.Log(PsesLogLevel.Verbose, "Adding AssemblyResolve event handler for new AssemblyLoadContext dependency loading"); + logger.Log(PsesLogLevel.Debug, "Adding AssemblyResolve event handler for new AssemblyLoadContext dependency loading"); PsesLoadContext psesLoadContext = new(s_psesDependencyDirPath); - if (hostConfig.LogLevel == PsesLogLevel.Diagnostic) + if (hostConfig.LogLevel == PsesLogLevel.Trace) { AppDomain.CurrentDomain.AssemblyLoad += (object sender, AssemblyLoadEventArgs args) => { logger.Log( - PsesLogLevel.Diagnostic, + PsesLogLevel.Trace, $"Loaded into load context {AssemblyLoadContext.GetLoadContext(args.LoadedAssembly)}: {args.LoadedAssembly}"); }; } @@ -91,9 +91,9 @@ public static EditorServicesLoader Create( AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext _, AssemblyName asmName) => { #if ASSEMBLY_LOAD_STACKTRACE - logger.Log(PsesLogLevel.Diagnostic, $"Assembly resolve event fired for {asmName}. Stacktrace:\n{new StackTrace()}"); + logger.Log(PsesLogLevel.Trace, $"Assembly resolve event fired for {asmName}. Stacktrace:\n{new StackTrace()}"); #else - logger.Log(PsesLogLevel.Diagnostic, $"Assembly resolve event fired for {asmName}"); + logger.Log(PsesLogLevel.Trace, $"Assembly resolve event fired for {asmName}"); #endif // We only want the Editor Services DLL; the new ALC will lazily load its dependencies automatically @@ -104,15 +104,15 @@ public static EditorServicesLoader Create( string asmPath = Path.Combine(s_psesDependencyDirPath, $"{asmName.Name}.dll"); - logger.Log(PsesLogLevel.Verbose, "Loading PSES DLL using new assembly load context"); + logger.Log(PsesLogLevel.Debug, "Loading PSES DLL using new assembly load context"); return psesLoadContext.LoadFromAssemblyPath(asmPath); }; #else // In .NET Framework we add an event here to redirect dependency loading in the current AppDomain for PSES' dependencies - logger.Log(PsesLogLevel.Verbose, "Adding AssemblyResolve event handler for dependency loading"); + logger.Log(PsesLogLevel.Debug, "Adding AssemblyResolve event handler for dependency loading"); - if (hostConfig.LogLevel == PsesLogLevel.Diagnostic) + if (hostConfig.LogLevel == PsesLogLevel.Trace) { AppDomain.CurrentDomain.AssemblyLoad += (object sender, AssemblyLoadEventArgs args) => { @@ -122,7 +122,7 @@ public static EditorServicesLoader Create( } logger.Log( - PsesLogLevel.Diagnostic, + PsesLogLevel.Trace, $"Loaded '{args.LoadedAssembly.GetName()}' from '{args.LoadedAssembly.Location}'"); }; } @@ -131,9 +131,9 @@ public static EditorServicesLoader Create( AppDomain.CurrentDomain.AssemblyResolve += (object sender, ResolveEventArgs args) => { #if ASSEMBLY_LOAD_STACKTRACE - logger.Log(PsesLogLevel.Diagnostic, $"Assembly resolve event fired for {args.Name}. Stacktrace:\n{new StackTrace()}"); + logger.Log(PsesLogLevel.Trace, $"Assembly resolve event fired for {args.Name}. Stacktrace:\n{new StackTrace()}"); #else - logger.Log(PsesLogLevel.Diagnostic, $"Assembly resolve event fired for {args.Name}"); + logger.Log(PsesLogLevel.Trace, $"Assembly resolve event fired for {args.Name}"); #endif AssemblyName asmName = new(args.Name); @@ -143,7 +143,7 @@ public static EditorServicesLoader Create( string baseDirAsmPath = Path.Combine(s_psesBaseDirPath, dllName); if (File.Exists(baseDirAsmPath)) { - logger.Log(PsesLogLevel.Diagnostic, $"Loading {args.Name} from PSES base dir into LoadFile context"); + logger.Log(PsesLogLevel.Trace, $"Loading {args.Name} from PSES base dir into LoadFile context"); return Assembly.LoadFile(baseDirAsmPath); } @@ -151,7 +151,7 @@ public static EditorServicesLoader Create( string asmPath = Path.Combine(s_psesDependencyDirPath, dllName); if (File.Exists(asmPath)) { - logger.Log(PsesLogLevel.Diagnostic, $"Loading {args.Name} from PSES dependency dir into LoadFile context"); + logger.Log(PsesLogLevel.Trace, $"Loading {args.Name} from PSES dependency dir into LoadFile context"); return Assembly.LoadFile(asmPath); } @@ -212,10 +212,10 @@ public Task LoadAndRunEditorServicesAsync() ValidateConfiguration(); // Method with no implementation that forces the PSES assembly to load, triggering an AssemblyResolve event - _logger.Log(PsesLogLevel.Verbose, "Loading PowerShell Editor Services"); + _logger.Log(PsesLogLevel.Information, "Loading PowerShell Editor Services Assemblies"); LoadEditorServices(); - _logger.Log(PsesLogLevel.Verbose, "Starting EditorServices"); + _logger.Log(PsesLogLevel.Information, "Starting PowerShell Editor Services"); _editorServicesRunner = new EditorServicesRunner(_logger, _hostConfig, _sessionFileWriter, _loggersToUnsubscribe); @@ -225,7 +225,7 @@ public Task LoadAndRunEditorServicesAsync() public void Dispose() { - _logger.Log(PsesLogLevel.Diagnostic, "Loader disposed"); + _logger.Log(PsesLogLevel.Trace, "Loader disposed"); _editorServicesRunner?.Dispose(); // TODO: @@ -242,7 +242,7 @@ private void CheckPowerShellVersion() { PSLanguageMode languageMode = Runspace.DefaultRunspace.SessionStateProxy.LanguageMode; - _logger.Log(PsesLogLevel.Verbose, $@" + _logger.Log(PsesLogLevel.Trace, $@" == PowerShell Details == - PowerShell version: {_powerShellVersion} - Language mode: {languageMode} @@ -261,7 +261,7 @@ private void CheckPowerShellVersion() #if !CoreCLR private void CheckDotNetVersion() { - _logger.Log(PsesLogLevel.Verbose, "Checking that .NET Framework version is at least 4.8"); + _logger.Log(PsesLogLevel.Debug, "Checking that .NET Framework version is at least 4.8"); using RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Net Framework Setup\NDP\v4\Full"); object netFxValue = key?.GetValue("Release"); if (netFxValue == null || netFxValue is not int netFxVersion) @@ -269,7 +269,7 @@ private void CheckDotNetVersion() return; } - _logger.Log(PsesLogLevel.Verbose, $".NET registry version: {netFxVersion}"); + _logger.Log(PsesLogLevel.Debug, $".NET registry version: {netFxVersion}"); if (netFxVersion < Net48Version) { @@ -283,26 +283,26 @@ private void UpdatePSModulePath() { if (string.IsNullOrEmpty(_hostConfig.BundledModulePath)) { - _logger.Log(PsesLogLevel.Diagnostic, "BundledModulePath not set, skipping"); + _logger.Log(PsesLogLevel.Trace, "BundledModulePath not set, skipping"); return; } string psModulePath = Environment.GetEnvironmentVariable("PSModulePath").TrimEnd(Path.PathSeparator); if ($"{psModulePath}{Path.PathSeparator}".Contains($"{_hostConfig.BundledModulePath}{Path.PathSeparator}")) { - _logger.Log(PsesLogLevel.Diagnostic, "BundledModulePath already set, skipping"); + _logger.Log(PsesLogLevel.Trace, "BundledModulePath already set, skipping"); return; } psModulePath = $"{psModulePath}{Path.PathSeparator}{_hostConfig.BundledModulePath}"; Environment.SetEnvironmentVariable("PSModulePath", psModulePath); - _logger.Log(PsesLogLevel.Verbose, $"Updated PSModulePath to: '{psModulePath}'"); + _logger.Log(PsesLogLevel.Trace, $"Updated PSModulePath to: '{psModulePath}'"); } private void LogHostInformation() { - _logger.Log(PsesLogLevel.Verbose, $"PID: {System.Diagnostics.Process.GetCurrentProcess().Id}"); + _logger.Log(PsesLogLevel.Trace, $"PID: {System.Diagnostics.Process.GetCurrentProcess().Id}"); - _logger.Log(PsesLogLevel.Verbose, $@" + _logger.Log(PsesLogLevel.Debug, $@" == Build Details == - Editor Services version: {BuildInfo.BuildVersion} - Build origin: {BuildInfo.BuildOrigin} @@ -310,7 +310,7 @@ private void LogHostInformation() - Build time: {BuildInfo.BuildTime} "); - _logger.Log(PsesLogLevel.Verbose, $@" + _logger.Log(PsesLogLevel.Debug, $@" == Host Startup Configuration Details == - Host name: {_hostConfig.HostInfo.Name} - Host version: {_hostConfig.HostInfo.Version} @@ -333,14 +333,14 @@ private void LogHostInformation() + CurrentUserCurrentHost: {_hostConfig.ProfilePaths.CurrentUserCurrentHost ?? ""} "); - _logger.Log(PsesLogLevel.Verbose, $@" + _logger.Log(PsesLogLevel.Debug, $@" == Console Details == - Console input encoding: {Console.InputEncoding.EncodingName} - Console output encoding: {Console.OutputEncoding.EncodingName} - PowerShell output encoding: {GetPSOutputEncoding()} "); - _logger.Log(PsesLogLevel.Verbose, $@" + _logger.Log(PsesLogLevel.Debug, $@" == Environment Details == - OS description: {RuntimeInformation.OSDescription} - OS architecture: {RuntimeInformation.OSArchitecture} @@ -359,7 +359,7 @@ private static string GetPSOutputEncoding() [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2208:Instantiate argument exceptions correctly", Justification = "Checking user-defined configuration")] private void ValidateConfiguration() { - _logger.Log(PsesLogLevel.Diagnostic, "Validating configuration"); + _logger.Log(PsesLogLevel.Debug, "Validating configuration"); bool lspUsesStdio = _hostConfig.LanguageServiceTransport is StdioTransportConfig; bool debugUsesStdio = _hostConfig.DebugServiceTransport is StdioTransportConfig; @@ -382,6 +382,7 @@ private void ValidateConfiguration() } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1825:Avoid zero-length array allocations", Justification = "Cannot use Array.Empty, since it must work in net452")] private static Version GetPSVersion() { // In order to read the $PSVersionTable variable, @@ -389,12 +390,10 @@ private static Version GetPSVersion() // which is expensive. // Rather than do that, we instead go straight to the source, // which is a static property, internal in WinPS and public in PS 6+ -#pragma warning disable CA1825 return typeof(PSObject).Assembly .GetType("System.Management.Automation.PSVersionInfo") .GetMethod("get_PSVersion", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) - .Invoke(null, new object[0] /* Cannot use Array.Empty, since it must work in net452 */) as Version; -#pragma warning restore CA1825 + .Invoke(null, new object[0]) as Version; } } } diff --git a/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs b/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs index 8f868d393..5d8c368c9 100644 --- a/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs +++ b/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs @@ -42,7 +42,7 @@ public EditorServicesRunner( _config = config; _sessionFileWriter = sessionFileWriter; // NOTE: This factory helps to isolate `Microsoft.Extensions.Logging/DependencyInjection`. - _serverFactory = EditorServicesServerFactory.Create(_config.LogPath, (int)_config.LogLevel, logger); + _serverFactory = new(logger); _alreadySubscribedDebug = false; _loggersToUnsubscribe = loggersToUnsubscribe; } @@ -60,11 +60,11 @@ public Task RunUntilShutdown() Task runAndAwaitShutdown = CreateEditorServicesAndRunUntilShutdown(); // Now write the session file - _logger.Log(PsesLogLevel.Diagnostic, "Writing session file"); + _logger.Log(PsesLogLevel.Trace, "Writing session file"); _sessionFileWriter.WriteSessionStarted(_config.LanguageServiceTransport, _config.DebugServiceTransport); // Finally, wait for Editor Services to shut down - _logger.Log(PsesLogLevel.Diagnostic, "Waiting on PSES run/shutdown"); + _logger.Log(PsesLogLevel.Debug, "Waiting on PSES run/shutdown"); return runAndAwaitShutdown; } @@ -124,7 +124,7 @@ private async Task CreateEditorServicesAndRunUntilShutdown() { try { - _logger.Log(PsesLogLevel.Diagnostic, "Creating/running editor services"); + _logger.Log(PsesLogLevel.Debug, "Creating/running editor services"); bool creatingLanguageServer = _config.LanguageServiceTransport != null; bool creatingDebugServer = _config.DebugServiceTransport != null; @@ -140,6 +140,9 @@ private async Task CreateEditorServicesAndRunUntilShutdown() return; } + _logger.Log(PsesLogLevel.Information, "PSES Startup Completed. Starting Language Server."); + _logger.Log(PsesLogLevel.Information, "Please check the LSP log file in your client for further messages. In VSCode, this is the 'PowerShell' output pane"); + // We want LSP and maybe debugging // To do that we: // - Create the LSP server @@ -149,7 +152,6 @@ private async Task CreateEditorServicesAndRunUntilShutdown() // - Wait for the LSP server to finish // Unsubscribe the host logger here so that the Extension Terminal is not polluted with input after the first prompt - _logger.Log(PsesLogLevel.Verbose, "Starting server, deregistering host logger and registering shutdown listener"); if (_loggersToUnsubscribe != null) { foreach (IDisposable loggerToUnsubscribe in _loggersToUnsubscribe) @@ -193,14 +195,15 @@ private async Task CreateEditorServicesAndRunUntilShutdown() private async Task RunTempDebugSessionAsync(HostStartupInfo hostDetails) { - _logger.Log(PsesLogLevel.Diagnostic, "Running temp debug session"); + _logger.Log(PsesLogLevel.Information, "Starting temporary debug session"); PsesDebugServer debugServer = await CreateDebugServerForTempSessionAsync(hostDetails).ConfigureAwait(false); - _logger.Log(PsesLogLevel.Verbose, "Debug server created"); + _logger.Log(PsesLogLevel.Debug, "Debug server created"); await debugServer.StartAsync().ConfigureAwait(false); - _logger.Log(PsesLogLevel.Verbose, "Debug server started"); + _logger.Log(PsesLogLevel.Debug, "Debug server started"); await debugServer.WaitForShutdown().ConfigureAwait(false); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD003:Avoid awaiting foreign Tasks", Justification = "It's a wrapper.")] private async Task StartDebugServer(Task debugServerCreation) { PsesDebugServer debugServer = await debugServerCreation.ConfigureAwait(false); @@ -209,47 +212,47 @@ private async Task StartDebugServer(Task debugServerCreation) // To do this, we set an event to allow it to create a new debug server as its session ends if (!_alreadySubscribedDebug) { - _logger.Log(PsesLogLevel.Diagnostic, "Subscribing debug server for session ended event"); + _logger.Log(PsesLogLevel.Trace, "Subscribing debug server for session ended event"); _alreadySubscribedDebug = true; debugServer.SessionEnded += DebugServer_OnSessionEnded; } - _logger.Log(PsesLogLevel.Diagnostic, "Starting debug server"); + _logger.Log(PsesLogLevel.Trace, "Starting debug server"); await debugServer.StartAsync().ConfigureAwait(false); } private Task RestartDebugServerAsync(PsesDebugServer debugServer) { - _logger.Log(PsesLogLevel.Diagnostic, "Restarting debug server"); + _logger.Log(PsesLogLevel.Debug, "Restarting debug server"); Task debugServerCreation = RecreateDebugServerAsync(debugServer); return StartDebugServer(debugServerCreation); } private async Task CreateLanguageServerAsync(HostStartupInfo hostDetails) { - _logger.Log(PsesLogLevel.Verbose, $"Creating LSP transport with endpoint {_config.LanguageServiceTransport.EndpointDetails}"); + _logger.Log(PsesLogLevel.Trace, $"Creating LSP transport with endpoint {_config.LanguageServiceTransport.EndpointDetails}"); (Stream inStream, Stream outStream) = await _config.LanguageServiceTransport.ConnectStreamsAsync().ConfigureAwait(false); - _logger.Log(PsesLogLevel.Diagnostic, "Creating language server"); + _logger.Log(PsesLogLevel.Debug, "Creating language server"); return _serverFactory.CreateLanguageServer(inStream, outStream, hostDetails); } private async Task CreateDebugServerWithLanguageServerAsync(PsesLanguageServer languageServer) { - _logger.Log(PsesLogLevel.Verbose, $"Creating debug adapter transport with endpoint {_config.DebugServiceTransport.EndpointDetails}"); + _logger.Log(PsesLogLevel.Trace, $"Creating debug adapter transport with endpoint {_config.DebugServiceTransport.EndpointDetails}"); (Stream inStream, Stream outStream) = await _config.DebugServiceTransport.ConnectStreamsAsync().ConfigureAwait(false); - _logger.Log(PsesLogLevel.Diagnostic, "Creating debug adapter"); + _logger.Log(PsesLogLevel.Debug, "Creating debug adapter"); return _serverFactory.CreateDebugServerWithLanguageServer(inStream, outStream, languageServer); } private async Task RecreateDebugServerAsync(PsesDebugServer debugServer) { - _logger.Log(PsesLogLevel.Diagnostic, "Recreating debug adapter transport"); + _logger.Log(PsesLogLevel.Debug, "Recreating debug adapter transport"); (Stream inStream, Stream outStream) = await _config.DebugServiceTransport.ConnectStreamsAsync().ConfigureAwait(false); - _logger.Log(PsesLogLevel.Diagnostic, "Recreating debug adapter"); + _logger.Log(PsesLogLevel.Debug, "Recreating debug adapter"); return _serverFactory.RecreateDebugServer(inStream, outStream, debugServer); } @@ -262,20 +265,14 @@ private async Task CreateDebugServerForTempSessionAsync(HostSta private HostStartupInfo CreateHostStartupInfo() { - _logger.Log(PsesLogLevel.Diagnostic, "Creating startup info object"); + _logger.Log(PsesLogLevel.Debug, "Creating startup info object"); - ProfilePathInfo profilePaths = null; - if (_config.ProfilePaths.AllUsersAllHosts != null - || _config.ProfilePaths.AllUsersCurrentHost != null - || _config.ProfilePaths.CurrentUserAllHosts != null - || _config.ProfilePaths.CurrentUserCurrentHost != null) - { - profilePaths = new ProfilePathInfo( - _config.ProfilePaths.CurrentUserAllHosts, - _config.ProfilePaths.CurrentUserCurrentHost, - _config.ProfilePaths.AllUsersAllHosts, - _config.ProfilePaths.AllUsersCurrentHost); - } + ProfilePathInfo profilePaths = new( + _config.ProfilePaths.CurrentUserAllHosts, + _config.ProfilePaths.CurrentUserCurrentHost, + _config.ProfilePaths.AllUsersAllHosts, + _config.ProfilePaths.AllUsersCurrentHost + ); return new HostStartupInfo( _config.HostInfo.Name, @@ -287,7 +284,7 @@ private HostStartupInfo CreateHostStartupInfo() _config.AdditionalModules, _config.InitialSessionState, _config.LogPath, - (int)_config.LogLevel, + (int)_config.LogLevel, //This maps to MEL log levels, we use int so this is easily supplied externally. consoleReplEnabled: _config.ConsoleRepl != ConsoleReplKind.None, useNullPSHostUI: _config.UseNullPSHostUI, usesLegacyReadLine: _config.ConsoleRepl == ConsoleReplKind.LegacyReadLine, @@ -304,9 +301,10 @@ private void WriteStartupBanner() _config.PSHost.UI.WriteLine(_config.StartupBanner); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "Intentionally fire and forget.")] private void DebugServer_OnSessionEnded(object sender, EventArgs args) { - _logger.Log(PsesLogLevel.Verbose, "Debug session ended, restarting debug service..."); + _logger.Log(PsesLogLevel.Debug, "Debug session ended, restarting debug service..."); PsesDebugServer oldServer = (PsesDebugServer)sender; oldServer.Dispose(); _alreadySubscribedDebug = false; diff --git a/src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj b/src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj index 5c31f0758..1c30a93df 100644 --- a/src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj +++ b/src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj @@ -1,8 +1,9 @@  - + - net6.0;net462 + net8.0;net462 Microsoft.PowerShell.EditorServices.Hosting @@ -11,10 +12,15 @@ - - - - + + + + + + + + + @@ -22,7 +28,7 @@ - + diff --git a/src/PowerShellEditorServices.Hosting/packages.lock.json b/src/PowerShellEditorServices.Hosting/packages.lock.json deleted file mode 100644 index 8be3c7711..000000000 --- a/src/PowerShellEditorServices.Hosting/packages.lock.json +++ /dev/null @@ -1,979 +0,0 @@ -{ - "version": 1, - "dependencies": { - ".NETFramework,Version=v4.6.2": { - "Microsoft.NETFramework.ReferenceAssemblies": { - "type": "Direct", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", - "dependencies": { - "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3" - } - }, - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, - "PowerShellStandard.Library": { - "type": "Direct", - "requested": "[5.1.1, )", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "System.IO.Pipes.AccessControl": { - "type": "Direct", - "requested": "[5.0.0, )", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Direct", - "requested": "[4.3.0, )", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" - }, - "System.ValueTuple": { - "type": "Direct", - "requested": "[4.5.0, )", - "resolved": "4.5.0", - "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0", - "System.ValueTuple": "4.5.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0", - "System.ValueTuple": "4.5.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0", - "System.ValueTuple": "4.5.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETFramework.ReferenceAssemblies.net462": { - "type": "Transitive", - "resolved": "1.0.3", - "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ==" - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "Serilog": { - "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "7.0.2", - "System.ValueTuple": "4.5.0" - } - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Transitive", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", - "dependencies": { - "System.Memory": "4.5.4" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==" - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "Microsoft.PowerShell.EditorServices": { - "type": "Project", - "dependencies": { - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )", - "Microsoft.Extensions.Logging": "[8.0.0, )", - "OmniSharp.Extensions.DebugAdapter.Server": "[0.19.9, )", - "OmniSharp.Extensions.LanguageServer": "[0.19.9, )", - "PowerShellStandard.Library": "[5.1.1, )", - "Serilog": "[3.1.1, )", - "Serilog.Extensions.Logging": "[8.0.0, )", - "Serilog.Sinks.Async": "[1.5.0, )", - "Serilog.Sinks.File": "[5.0.0, )", - "System.IO.Pipes.AccessControl": "[5.0.0, )", - "System.Security.Principal": "[4.3.0, )", - "System.Security.Principal.Windows": "[5.0.0, )" - } - } - }, - "net6.0": { - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, - "PowerShellStandard.Library": { - "type": "Direct", - "requested": "[5.1.1, )", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "System.IO.Pipes.AccessControl": { - "type": "Direct", - "requested": "[5.0.0, )", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Direct", - "requested": "[4.3.0, )", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "Serilog": { - "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==" - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Transitive", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==" - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==" - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "Microsoft.PowerShell.EditorServices": { - "type": "Project", - "dependencies": { - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )", - "Microsoft.Extensions.Logging": "[8.0.0, )", - "OmniSharp.Extensions.DebugAdapter.Server": "[0.19.9, )", - "OmniSharp.Extensions.LanguageServer": "[0.19.9, )", - "PowerShellStandard.Library": "[5.1.1, )", - "Serilog": "[3.1.1, )", - "Serilog.Extensions.Logging": "[8.0.0, )", - "Serilog.Sinks.Async": "[1.5.0, )", - "Serilog.Sinks.File": "[5.0.0, )", - "System.IO.Pipes.AccessControl": "[5.0.0, )", - "System.Security.Principal": "[4.3.0, )", - "System.Security.Principal.Windows": "[5.0.0, )" - } - } - } - } -} \ No newline at end of file diff --git a/src/PowerShellEditorServices/Extensions/EditorContext.cs b/src/PowerShellEditorServices/Extensions/EditorContext.cs index f2ab42e8c..e79eb8c3b 100644 --- a/src/PowerShellEditorServices/Extensions/EditorContext.cs +++ b/src/PowerShellEditorServices/Extensions/EditorContext.cs @@ -92,9 +92,8 @@ public void SetSelection( /// Sets a selection in the host editor's active buffer. /// /// The range of the selection. - #pragma warning disable VSTHRD002 + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void SetSelection(FileRange selectionRange) => editorOperations.SetSelectionAsync(selectionRange.ToBufferRange()).Wait(); - #pragma warning restore VSTHRD002 #endregion } diff --git a/src/PowerShellEditorServices/Extensions/EditorObject.cs b/src/PowerShellEditorServices/Extensions/EditorObject.cs index 17006baf2..a43ab2995 100644 --- a/src/PowerShellEditorServices/Extensions/EditorObject.cs +++ b/src/PowerShellEditorServices/Extensions/EditorObject.cs @@ -115,13 +115,12 @@ internal EditorObject( /// at the time this method is invoked. /// /// A instance of the EditorContext class. - #pragma warning disable VSTHRD002, VSTHRD104 + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public EditorContext GetEditorContext() => _editorOperations.GetEditorContextAsync().Result; - #pragma warning restore VSTHRD002, VSTHRD104 internal void SetAsStaticInstance() { - EditorObject.Instance = this; + Instance = this; s_editorObjectReady.TrySetResult(true); } } diff --git a/src/PowerShellEditorServices/Extensions/EditorWindow.cs b/src/PowerShellEditorServices/Extensions/EditorWindow.cs index 8c9048c3c..6d9d4c3ff 100644 --- a/src/PowerShellEditorServices/Extensions/EditorWindow.cs +++ b/src/PowerShellEditorServices/Extensions/EditorWindow.cs @@ -43,24 +43,28 @@ internal EditorWindow(IEditorOperations editorOperations) /// Shows an informational message to the user. /// /// The message to be shown. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void ShowInformationMessage(string message) => editorOperations.ShowInformationMessageAsync(message).Wait(); /// /// Shows an error message to the user. /// /// The message to be shown. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void ShowErrorMessage(string message) => editorOperations.ShowErrorMessageAsync(message).Wait(); /// /// Shows a warning message to the user. /// /// The message to be shown. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void ShowWarningMessage(string message) => editorOperations.ShowWarningMessageAsync(message).Wait(); /// /// Sets the status bar message in the editor UI (if applicable). /// /// The message to be shown. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void SetStatusBarMessage(string message) => editorOperations.SetStatusBarMessageAsync(message, null).Wait(); /// @@ -68,6 +72,7 @@ internal EditorWindow(IEditorOperations editorOperations) /// /// The message to be shown. /// A timeout in milliseconds for how long the message should remain visible. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void SetStatusBarMessage(string message, int timeout) => editorOperations.SetStatusBarMessageAsync(message, timeout).Wait(); #endregion diff --git a/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs b/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs index b01c6eca7..f3c40d74a 100644 --- a/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs +++ b/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs @@ -28,6 +28,11 @@ public sealed class EditorWorkspace /// public string[] Paths => editorOperations.GetWorkspacePaths(); + /// + /// Get all currently open documents in the workspace. + /// + public WorkspaceOpenDocument[] Documents => editorOperations.GetWorkspaceOpenDocuments(); + #endregion #region Constructors @@ -42,12 +47,14 @@ public sealed class EditorWorkspace /// /// Creates a new file in the editor. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void NewFile() => editorOperations.NewFileAsync(string.Empty).Wait(); /// /// Creates a new file in the editor. /// /// The content to place in the new file. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void NewFile(string content) => editorOperations.NewFileAsync(content).Wait(); /// @@ -55,6 +62,7 @@ public sealed class EditorWorkspace /// its buffer will be made active. /// /// The path to the file to be opened. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void OpenFile(string filePath) => editorOperations.OpenFileAsync(filePath).Wait(); /// @@ -64,25 +72,31 @@ public sealed class EditorWorkspace /// /// The path to the file to be opened. /// Determines wether the file is opened as a preview or as a durable editor. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void OpenFile(string filePath, bool preview) => editorOperations.OpenFileAsync(filePath, preview).Wait(); /// /// Closes a file in the workspace. /// /// The path to the file to be closed. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void CloseFile(string filePath) => editorOperations.CloseFileAsync(filePath).Wait(); + public void CloseFile(WorkspaceOpenDocument document) => CloseFile(document.Path); /// /// Saves an open file in the workspace. /// /// The path to the file to be saved. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void SaveFile(string filePath) => editorOperations.SaveFileAsync(filePath).Wait(); + public void SaveFile(WorkspaceOpenDocument document) => SaveFile(document.Path); /// /// Saves a file with a new name AKA a copy. /// /// The file to copy. /// The file to create. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void SaveFile(string oldFilePath, string newFilePath) => editorOperations.SaveFileAsync(oldFilePath, newFilePath).Wait(); #endregion diff --git a/src/PowerShellEditorServices/Extensions/FileContext.cs b/src/PowerShellEditorServices/Extensions/FileContext.cs index c8c32e58e..39a4a275e 100644 --- a/src/PowerShellEditorServices/Extensions/FileContext.cs +++ b/src/PowerShellEditorServices/Extensions/FileContext.cs @@ -208,14 +208,13 @@ public void InsertText( /// /// The text string to insert. /// The buffer range which will be replaced by the string. - #pragma warning disable VSTHRD002 + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void InsertText(string textToInsert, IFileRange insertRange) { editorOperations .InsertTextAsync(scriptFile.DocumentUri.ToString(), textToInsert, insertRange.ToBufferRange()) .Wait(); } - #pragma warning restore VSTHRD002 #endregion @@ -224,6 +223,7 @@ public void InsertText(string textToInsert, IFileRange insertRange) /// /// Saves this file. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "Supporting synchronous API.")] public void Save() => editorOperations.SaveFileAsync(scriptFile.FilePath); /// @@ -233,7 +233,7 @@ public void InsertText(string textToInsert, IFileRange insertRange) /// the path where the file should be saved, /// including the file name with extension as the leaf /// - #pragma warning disable VSTHRD002 + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] public void SaveAs(string newFilePath) { // Do some validation here so that we can provide a helpful error if the path won't work @@ -248,7 +248,6 @@ public void SaveAs(string newFilePath) editorOperations.SaveFileAsync(scriptFile.FilePath, newFilePath).Wait(); } - #pragma warning restore VSTHRD002 #endregion } diff --git a/src/PowerShellEditorServices/Extensions/IEditorOperations.cs b/src/PowerShellEditorServices/Extensions/IEditorOperations.cs index 3ec33ebc6..7fb39b22c 100644 --- a/src/PowerShellEditorServices/Extensions/IEditorOperations.cs +++ b/src/PowerShellEditorServices/Extensions/IEditorOperations.cs @@ -3,9 +3,34 @@ using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Services.TextDocument; +#nullable enable namespace Microsoft.PowerShell.EditorServices.Extensions { + public readonly struct WorkspaceOpenDocument(string path, bool saved) + { + /// + /// Gets the path or URI of the open document. + /// + public string Path { get; } = path; + + /// + /// Gets whether the document is backed by a saved file path (not in-memory). + /// + public bool Saved { get; } = saved; + + /// + /// Gets the display name of this document and unsaved status. + /// + /// The display name of this document. + public override string ToString() + { + string documentPath = Path ?? string.Empty; + string fileName = System.IO.Path.GetFileName(documentPath); + return Saved ? fileName : fileName + " [Unsaved]"; + } + } + /// /// Provides an interface that must be implemented by an editor /// host to perform operations invoked by extensions written in @@ -32,6 +57,12 @@ internal interface IEditorOperations /// string[] GetWorkspacePaths(); + /// + /// Get all open documents in the current workspace session. + /// + /// All currently open documents. + WorkspaceOpenDocument[] GetWorkspaceOpenDocuments(); + /// /// Resolves the given file path relative to the current workspace path. /// diff --git a/src/PowerShellEditorServices/GlobalSuppressions.cs b/src/PowerShellEditorServices/GlobalSuppressions.cs deleted file mode 100644 index 3c0be1568..000000000 --- a/src/PowerShellEditorServices/GlobalSuppressions.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "PSES is not localized", Scope = "module")] diff --git a/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs b/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs index a9bbab38e..b981ea450 100644 --- a/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs +++ b/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs @@ -2,81 +2,30 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.IO; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.PowerShell.EditorServices.Logging; using Microsoft.PowerShell.EditorServices.Server; -using Serilog; -using Serilog.Events; using OmniSharp.Extensions.LanguageServer.Protocol.Server; using Microsoft.PowerShell.EditorServices.Services.Extension; -#if DEBUG -using Serilog.Debugging; -#endif +// The HostLogger type isn't directly referenced from this assembly, however it uses a common IObservable interface and this alias helps make it more clear the purpose. We can use Microsoft.Extensions.Logging from this point because the ALC should be loaded, but we need to only expose the IObservable to the Hosting assembly so it doesn't try to load MEL before the ALC is ready. +using HostLogger = System.IObservable<(int logLevel, string message)>; namespace Microsoft.PowerShell.EditorServices.Hosting { /// - /// Factory class for hiding dependencies of Editor Services. + /// Factory for creating the LSP server and debug server instances. /// - /// - /// Dependency injection and logging are wrapped by factory methods on this class so that the - /// host assembly can construct the LSP and debug servers without directly depending on and . - /// internal sealed class EditorServicesServerFactory : IDisposable { + private readonly HostLogger _hostLogger; + /// - /// Create a new Editor Services factory. This method will instantiate logging. + /// Creates a loggerfactory for this instance /// - /// - /// - /// This can only be called once because it sets global state (the logger) and that call is - /// in . - /// - /// - /// TODO: Why is this a static function wrapping a constructor instead of just a - /// constructor? In the end it returns an instance (albeit a "singleton"). - /// - /// - /// The path of the log file to use. - /// The minimum log level to use. - /// The host logger? - public static EditorServicesServerFactory Create(string logDirectoryPath, int minimumLogLevel, IObservable<(int logLevel, string message)> hostLogger) - { - // NOTE: Ignore the suggestion to use Environment.ProcessId as it doesn't work for - // .NET 4.6.2 (for Windows PowerShell), and this won't be caught in CI. - int currentPID = Process.GetCurrentProcess().Id; - string logPath = Path.Combine(logDirectoryPath, $"PowerShellEditorServices-{currentPID}.log"); - Log.Logger = new LoggerConfiguration() - .Enrich.FromLogContext() - .WriteTo.Async(config => config.File(logPath)) - .MinimumLevel.Is((LogEventLevel)minimumLogLevel) - .CreateLogger(); - -#if DEBUG - SelfLog.Enable(msg => Debug.WriteLine(msg)); -#endif - - LoggerFactory loggerFactory = new(); - loggerFactory.AddSerilog(); - - // Hook up logging from the host so that its recorded in the log file - hostLogger.Subscribe(new HostLoggerAdapter(loggerFactory)); - - return new EditorServicesServerFactory(loggerFactory); - } - - // TODO: Can we somehow refactor this member so the language and debug servers can be - // instantiated using their constructors instead of tying them to this factory with `Create` - // methods? - private readonly ILoggerFactory _loggerFactory; - - private EditorServicesServerFactory(ILoggerFactory loggerFactory) => _loggerFactory = loggerFactory; + /// The hostLogger that will be provided to the language services for logging handoff + internal EditorServicesServerFactory(HostLogger hostLogger) => _hostLogger = hostLogger; /// /// Create the LSP server. @@ -92,7 +41,7 @@ public static EditorServicesServerFactory Create(string logDirectoryPath, int mi public PsesLanguageServer CreateLanguageServer( Stream inputStream, Stream outputStream, - HostStartupInfo hostStartupInfo) => new(_loggerFactory, inputStream, outputStream, hostStartupInfo); + HostStartupInfo hostStartupInfo) => new(_hostLogger, inputStream, outputStream, hostStartupInfo); /// /// Create the debug server given a language server instance. @@ -110,7 +59,7 @@ public PsesDebugServer CreateDebugServerWithLanguageServer( PsesLanguageServer languageServer) { return new PsesDebugServer( - _loggerFactory, + _hostLogger, inputStream, outputStream, languageServer.LanguageServer.Services); @@ -132,7 +81,7 @@ public PsesDebugServer RecreateDebugServer( PsesDebugServer debugServer) { return new PsesDebugServer( - _loggerFactory, + _hostLogger, inputStream, outputStream, debugServer.ServiceProvider); @@ -153,7 +102,6 @@ public PsesDebugServer CreateDebugServerForTempSession( ServiceProvider serviceProvider = new ServiceCollection() .AddLogging(builder => builder .ClearProviders() - .AddSerilog() .SetMinimumLevel(LogLevel.Trace)) // TODO: Why randomly set to trace? .AddSingleton(_ => null) // TODO: Why add these for a debug server?! @@ -171,25 +119,14 @@ public PsesDebugServer CreateDebugServerForTempSession( serviceProvider.GetService(); return new PsesDebugServer( - _loggerFactory, + _hostLogger, inputStream, outputStream, serviceProvider, isTemp: true); } - /// - /// TODO: This class probably should not be as the primary - /// intention of that interface is to provide cleanup of unmanaged resources, which the - /// logger certainly is not. Nor is this class used with a . Instead, - /// this class should call in a finalizer. This - /// could potentially even be done with by passing dispose=true. - /// - public void Dispose() - { - Log.CloseAndFlush(); - _loggerFactory.Dispose(); - } + // TODO: Clean up host logger? Shouldn't matter since we start a new process after shutdown. + public void Dispose() { } } } diff --git a/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs b/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs index 964509626..3f82990cd 100644 --- a/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs +++ b/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#nullable enable using System; using System.Collections.Generic; @@ -107,9 +108,8 @@ public sealed class HostStartupInfo /// The minimum log level of log events to be logged. /// /// - /// This is cast to all of , , and , hence it is an int. + /// This primitive maps to and /// public int LogLevel { get; } @@ -191,32 +191,13 @@ public HostStartupInfo( } /// - /// This is a strange class that is generally null or otherwise just has a single path - /// set. It is eventually parsed one-by-one when setting up the PowerShell runspace. + /// Stores profile information passed from Start-EditorServices to be used for loading profiles if configured + /// and for the $PROFILE variable in the initial session state. /// - /// - /// TODO: Simplify this as a . - /// - public sealed class ProfilePathInfo - { - public ProfilePathInfo( - string currentUserAllHosts, - string currentUserCurrentHost, - string allUsersAllHosts, - string allUsersCurrentHost) - { - CurrentUserAllHosts = currentUserAllHosts; - CurrentUserCurrentHost = currentUserCurrentHost; - AllUsersAllHosts = allUsersAllHosts; - AllUsersCurrentHost = allUsersCurrentHost; - } - - public string CurrentUserAllHosts { get; } - - public string CurrentUserCurrentHost { get; } - - public string AllUsersAllHosts { get; } - - public string AllUsersCurrentHost { get; } - } + public readonly record struct ProfilePathInfo( + string CurrentUserAllHosts, + string CurrentUserCurrentHost, + string AllUsersAllHosts, + string AllUsersCurrentHost + ); } diff --git a/src/PowerShellEditorServices/IsExternalInit.cs b/src/PowerShellEditorServices/IsExternalInit.cs index c2a9d8275..c690a1820 100644 --- a/src/PowerShellEditorServices/IsExternalInit.cs +++ b/src/PowerShellEditorServices/IsExternalInit.cs @@ -1,4 +1,6 @@ -#pragma warning disable IDE0073 +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #if NET5_0_OR_GREATER [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.CompilerServices.IsExternalInit))] #else diff --git a/src/PowerShellEditorServices/Logging/HostLoggerAdapter.cs b/src/PowerShellEditorServices/Logging/HostLoggerAdapter.cs index 3290b2b78..9ffe3c950 100644 --- a/src/PowerShellEditorServices/Logging/HostLoggerAdapter.cs +++ b/src/PowerShellEditorServices/Logging/HostLoggerAdapter.cs @@ -9,23 +9,18 @@ namespace Microsoft.PowerShell.EditorServices.Logging /// /// Adapter class to allow logging events sent by the host to be recorded by PSES' logging infrastructure. /// - internal class HostLoggerAdapter : IObserver<(int logLevel, string message)> + internal class HostLoggerAdapter(ILogger logger) : IObserver<(int logLevel, string message)> { - private readonly ILogger _logger; + public void OnError(Exception error) => logger.LogError(error, "Error in host logger"); /// - /// Create a new host logger adapter. + /// Log the message received from the host into MEL. /// - /// Factory to create logger instances with. - public HostLoggerAdapter(ILoggerFactory loggerFactory) => _logger = loggerFactory.CreateLogger("HostLogs"); + public void OnNext((int logLevel, string message) value) => logger.Log((LogLevel)value.logLevel, value.message); public void OnCompleted() { // Nothing to do; we simply don't send more log messages } - - public void OnError(Exception error) => _logger.LogError(error, "Error in host logger"); - - public void OnNext((int logLevel, string message) value) => _logger.Log((LogLevel)value.logLevel, value.message); } } diff --git a/src/PowerShellEditorServices/Logging/LanguageServerLogger.cs b/src/PowerShellEditorServices/Logging/LanguageServerLogger.cs new file mode 100644 index 000000000..5d368f912 --- /dev/null +++ b/src/PowerShellEditorServices/Logging/LanguageServerLogger.cs @@ -0,0 +1,184 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using OmniSharp.Extensions.LanguageServer.Protocol.Window; + +namespace Microsoft.PowerShell.EditorServices.Logging; + +internal class LanguageServerLogger(ILanguageServerFacade responseRouter, string categoryName) : ILogger +{ + public IDisposable? BeginScope(TState state) where TState : notnull => Disposable.Empty; + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log( + LogLevel logLevel, EventId eventId, TState state, Exception? exception, + Func formatter + ) + { + if (responseRouter is null) + { + throw new InvalidOperationException("Log received without a valid responseRouter dependency. This is a bug, please report it."); + } + // Any Omnisharp or trace logs are directly LSP protocol related and we send them to the trace channel + // TODO: Dynamically adjust if SetTrace is reported + // BUG: There is an omnisharp filter incorrectly filtering this. As a workaround we will use logMessage for now. + // https://github.com/OmniSharp/csharp-language-server-protocol/issues/1390 + // + // { + // // Everything with omnisharp goes directly to trace + // string eventMessage = string.Empty; + // string exceptionName = exception?.GetType().Name ?? string.Empty; + // if (eventId.Name is not null) + // { + // eventMessage = eventId.Id == 0 ? eventId.Name : $"{eventId.Name} [{eventId.Id}] "; + // } + + // LogTraceParams trace = new() + // { + // Message = categoryName + ": " + eventMessage + exceptionName, + // Verbose = formatter(state, exception) + // }; + // responseRouter.Client.LogTrace(trace); + // } + + // Drop all omnisharp messages to trace. This isn't a MEL filter because it's specific only to this provider. + if (categoryName.StartsWith("OmniSharp.", StringComparison.OrdinalIgnoreCase)) + { + logLevel = LogLevel.Trace; + } + + (MessageType messageType, string messagePrepend) = GetMessageInfo(logLevel); + + // The vscode-languageserver-node client doesn't support LogOutputChannel as of 2024-11-24 and also doesn't + // provide a way to middleware the incoming log messages, so our output channel has no idea what the logLevel + // is. As a workaround, we send the severity in-line with the message for the client to parse. + // BUG: https://github.com/microsoft/vscode-languageserver-node/issues/1116 + if (responseRouter.Client?.ClientSettings?.ClientInfo?.Name == "Visual Studio Code") + { + messagePrepend = logLevel switch + { + LogLevel.Critical => "CRITICAL: ", + LogLevel.Error => "", + LogLevel.Warning => "", + LogLevel.Information => "", + LogLevel.Debug => "", + LogLevel.Trace => "", + _ => string.Empty + }; + + // The vscode formatter prepends some extra stuff to Info specifically, so we drop Info to Log, but it will get logged correctly on the other side thanks to our inline indicator that our custom parser on the other side will pick up and process. + if (messageType == MessageType.Info) + { + messageType = MessageType.Log; + } + } + + LogMessageParams logMessage = new() + { + Type = messageType, + Message = messagePrepend + categoryName + ": " + formatter(state, exception) + + (exception != null ? " - " + exception : "") + " | " + + //Hopefully this isn't too expensive in the long run + FormatState(state, exception) + }; + responseRouter.Window.Log(logMessage); + } + + /// + /// Formats the state object into a string for logging. + /// + /// + /// This is copied from Omnisharp, we can probably do better. + /// + /// + /// + /// + /// + private static string FormatState(TState state, Exception? exception) + { + return state switch + { + IEnumerable> dict => string.Join(" ", dict.Where(z => z.Key != "{OriginalFormat}").Select(z => $"{z.Key}='{z.Value}'")), + _ => JsonConvert.SerializeObject(state).Replace("\"", "'") + }; + } + + /// + /// Maps MEL log levels to LSP message types + /// + private static (MessageType messageType, string messagePrepend) GetMessageInfo(LogLevel logLevel) + => logLevel switch + { + LogLevel.Critical => (MessageType.Error, "Critical: "), + LogLevel.Error => (MessageType.Error, string.Empty), + LogLevel.Warning => (MessageType.Warning, string.Empty), + LogLevel.Information => (MessageType.Info, string.Empty), + LogLevel.Debug => (MessageType.Log, string.Empty), + LogLevel.Trace => (MessageType.Log, "Trace: "), + _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null) + }; +} + +internal class LanguageServerLoggerProvider(ILanguageServerFacade languageServer) : ILoggerProvider +{ + public ILogger CreateLogger(string categoryName) => new LanguageServerLogger(languageServer, categoryName); + + public void Dispose() { } +} + +public static class LanguageServerLoggerExtensions +{ + /// + /// Adds a custom logger provider for PSES LSP, that provides more granular categorization than the default Omnisharp logger, such as separating Omnisharp and PSES messages to different channels. + /// + public static ILoggingBuilder AddPsesLanguageServerLogging(this ILoggingBuilder builder) + { + builder.Services.AddSingleton(); + return builder; + } + + public static ILoggingBuilder AddLspClientConfigurableMinimumLevel( + this ILoggingBuilder builder, + LogLevel initialLevel = LogLevel.Trace + ) + { + builder.Services.AddOptions(); + builder.Services.AddSingleton(sp => + { + IOptionsMonitor optionsMonitor = sp.GetRequiredService>(); + return new(initialLevel, optionsMonitor); + }); + builder.Services.AddSingleton>(sp => + sp.GetRequiredService()); + + return builder; + } +} + +internal class DynamicLogLevelOptions( + LogLevel initialLevel, + IOptionsMonitor optionsMonitor) : IConfigureOptions +{ + private LogLevel _currentLevel = initialLevel; + private readonly IOptionsMonitor _optionsMonitor = optionsMonitor; + + public void Configure(LoggerFilterOptions options) => options.MinLevel = _currentLevel; + + public void SetLogLevel(LogLevel level) + { + _currentLevel = level; + // Trigger reload of options to apply new log level + _optionsMonitor.CurrentValue.MinLevel = level; + } +} diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj index 8581053ff..18948aabc 100644 --- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj +++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj @@ -1,5 +1,6 @@ - + PowerShell Editor Services @@ -32,17 +33,19 @@ - - - - - - - - - - - + + + + + + + + + + + + + @@ -54,16 +57,9 @@ - - + + - - - - - - - diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index 6aca82e04..9f48a0f2d 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -5,13 +5,15 @@ using System.IO; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.PowerShell.EditorServices.Handlers; using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host; using OmniSharp.Extensions.DebugAdapter.Server; using OmniSharp.Extensions.LanguageServer.Server; +// See EditorServicesServerFactory.cs for the explanation of this alias. +using HostLogger = System.IObservable<(int logLevel, string message)>; + namespace Microsoft.PowerShell.EditorServices.Server { /// @@ -26,16 +28,17 @@ internal class PsesDebugServer : IDisposable private PsesInternalHost _psesHost; private bool _startedPses; private readonly bool _isTemp; - protected readonly ILoggerFactory _loggerFactory; + // FIXME: This was never actually used in the debug server. Since we never have a debug server without an LSP, we could probably remove this and either reuse the MEL from the LSP, or create a new one here. It is probably best to only use this for exceptions that we can't reasonably send via the DAP protocol, which should only be anything before the initialize request. + protected readonly HostLogger _hostLogger; public PsesDebugServer( - ILoggerFactory factory, + HostLogger hostLogger, Stream inputStream, Stream outputStream, IServiceProvider serviceProvider, bool isTemp = false) { - _loggerFactory = factory; + _hostLogger = hostLogger; _inputStream = inputStream; _outputStream = outputStream; ServiceProvider = serviceProvider; @@ -63,7 +66,6 @@ public async Task StartAsync() .WithOutput(_outputStream) .WithServices(serviceCollection => serviceCollection - .AddLogging() .AddOptions() .AddPsesDebugServices(ServiceProvider, this)) // TODO: Consider replacing all WithHandler with AddSingleton @@ -115,9 +117,11 @@ public async Task StartAsync() response.SupportsHitConditionalBreakpoints = true; response.SupportsLogPoints = true; response.SupportsSetVariable = true; + response.SupportsDelayedStackTraceLoading = true; return Task.CompletedTask; - }); + }) + ; }).ConfigureAwait(false); } @@ -130,7 +134,6 @@ public void Dispose() _debugAdapterServer?.Dispose(); _inputStream.Dispose(); _outputStream.Dispose(); - _loggerFactory.Dispose(); _serverStopped.SetResult(true); // TODO: If the debugger has stopped, should we clear the breakpoints? } diff --git a/src/PowerShellEditorServices/Server/PsesLanguageServer.cs b/src/PowerShellEditorServices/Server/PsesLanguageServer.cs index 74319e828..042e4e8fa 100644 --- a/src/PowerShellEditorServices/Server/PsesLanguageServer.cs +++ b/src/PowerShellEditorServices/Server/PsesLanguageServer.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -8,15 +9,18 @@ using Microsoft.Extensions.Logging; using Microsoft.PowerShell.EditorServices.Handlers; using Microsoft.PowerShell.EditorServices.Hosting; +using Microsoft.PowerShell.EditorServices.Logging; using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Services.Extension; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host; -using Microsoft.PowerShell.EditorServices.Services.Template; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.LanguageServer.Protocol.General; using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Extensions.LanguageServer.Server; -using Serilog; + +// See EditorServicesServerFactory.cs for the explanation of this alias. +using HostLogger = System.IObservable<(int logLevel, string message)>; namespace Microsoft.PowerShell.EditorServices.Server { @@ -25,7 +29,7 @@ namespace Microsoft.PowerShell.EditorServices.Server /// internal class PsesLanguageServer { - internal ILoggerFactory LoggerFactory { get; } + internal HostLogger HostLogger { get; } internal ILanguageServer LanguageServer { get; private set; } private readonly LogLevel _minimumLogLevel; private readonly Stream _inputStream; @@ -33,6 +37,7 @@ internal class PsesLanguageServer private readonly HostStartupInfo _hostDetails; private readonly TaskCompletionSource _serverStart; private PsesInternalHost _psesHost; + private IDisposable hostLoggerSubscription; /// /// Create a new language server instance. @@ -42,18 +47,18 @@ internal class PsesLanguageServer /// cref="EditorServicesServerFactory.CreateLanguageServer"/>. It is essentially a /// singleton. The factory hides the logger. /// - /// Factory to create loggers with. + /// The host logger to hand off for monitoring. /// Protocol transport input stream. /// Protocol transport output stream. /// Host configuration to instantiate the server and services /// with. public PsesLanguageServer( - ILoggerFactory factory, + HostLogger hostLogger, Stream inputStream, Stream outputStream, HostStartupInfo hostStartupInfo) { - LoggerFactory = factory; + HostLogger = hostLogger; _minimumLogLevel = (LogLevel)hostStartupInfo.LogLevel; _inputStream = inputStream; _outputStream = outputStream; @@ -70,9 +75,7 @@ public PsesLanguageServer( /// cref="PsesServiceCollectionExtensions.AddPsesLanguageServices"/>. /// /// A task that completes when the server is ready and listening. -#pragma warning disable CA1506 // Coupling complexity we don't care about public async Task StartAsync() -#pragma warning restore CA1506 { LanguageServer = await OmniSharp.Extensions.LanguageServer.Server.LanguageServer.From(options => { @@ -85,10 +88,11 @@ public async Task StartAsync() serviceCollection.AddPsesLanguageServices(_hostDetails); }) .ConfigureLogging(builder => builder - .AddSerilog(Log.Logger) // TODO: Set dispose to true? - .AddLanguageProtocolLogging() + .ClearProviders() + .AddPsesLanguageServerLogging() .SetMinimumLevel(_minimumLogLevel)) // TODO: Consider replacing all WithHandler with AddSingleton + .WithConfigurationSection("powershell.rename") .WithHandler() .WithHandler() .WithHandler() @@ -114,7 +118,6 @@ public async Task StartAsync() .WithHandler() .WithHandler() .WithHandler() - .WithHandler() .WithHandler() .WithHandler() .WithHandler() @@ -122,12 +125,19 @@ public async Task StartAsync() .WithHandler() .WithHandler() .WithHandler() + .WithHandler() + .WithHandler() // NOTE: The OnInitialize delegate gets run when we first receive the // _Initialize_ request: // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize .OnInitialize( (languageServer, initializeParams, cancellationToken) => { + // Wire up the HostLogger to the LanguageServer's logger once we are initialized, so that any messages still logged to the HostLogger get sent across the LSP channel. There is no more logging to disk at this point. + hostLoggerSubscription = HostLogger.Subscribe(new HostLoggerAdapter( + languageServer.Services.GetService>() + )); + // Set the workspace path from the parameters. WorkspaceService workspaceService = languageServer.Services.GetService(); if (initializeParams.WorkspaceFolders is not null) @@ -153,15 +163,18 @@ public async Task StartAsync() InitialWorkingDirectory = initializationOptions?.GetValue("initialWorkingDirectory")?.Value() ?? workspaceService.WorkspaceFolders.FirstOrDefault()?.Uri.GetFileSystemPath() ?? Directory.GetCurrentDirectory(), - ShellIntegrationEnabled = initializationOptions?.GetValue("shellIntegrationEnabled")?.Value() - ?? false + // If a shell integration script path is provided, that implies the feature is enabled. + ShellIntegrationScript = initializationOptions?.GetValue("shellIntegrationScript")?.Value() + ?? "", }; workspaceService.InitialWorkingDirectory = hostStartOptions.InitialWorkingDirectory; _psesHost = languageServer.Services.GetService(); return _psesHost.TryStartAsync(hostStartOptions, cancellationToken); - }); + } + ) + .OnShutdown(_ => hostLoggerSubscription.Dispose()); }).ConfigureAwait(false); _serverStart.SetResult(true); diff --git a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs index 3c02510b9..5a75ce448 100644 --- a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs +++ b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs @@ -10,13 +10,13 @@ using Microsoft.PowerShell.EditorServices.Services.PowerShell.Debugging; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Runspace; -using Microsoft.PowerShell.EditorServices.Services.Template; using OmniSharp.Extensions.LanguageServer.Protocol.Server; namespace Microsoft.PowerShell.EditorServices.Server { internal static class PsesServiceCollectionExtensions { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "Using lazy initialization.")] public static IServiceCollection AddPsesLanguageServices( this IServiceCollection collection, HostStartupInfo hostStartupInfo) @@ -33,7 +33,6 @@ public static IServiceCollection AddPsesLanguageServices( .AddSingleton() .AddSingleton( (provider) => provider.GetService().DebugContext) - .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton((provider) => @@ -48,12 +47,11 @@ public static IServiceCollection AddPsesLanguageServices( // is ready, it will be available. NOTE: We cannot await this because it // uses a lazy initialization to avoid a race with the dependency injection // framework, see the EditorObject class for that! -#pragma warning disable VSTHRD110 extensionService.InitializeAsync(); -#pragma warning restore VSTHRD110 return extensionService; }) - .AddSingleton(); + .AddSingleton() + .AddSingleton(); } public static IServiceCollection AddPsesDebugServices( diff --git a/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs b/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs index f6b25c5f7..f346cb6c9 100644 --- a/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs +++ b/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs @@ -322,7 +322,7 @@ private bool TryFindSettingsFile(out string settingsFilePath) return false; } - settingsFilePath = _workspaceService?.ResolveWorkspacePath(configuredPath); + settingsFilePath = _workspaceService?.FindFileInWorkspace(configuredPath); if (settingsFilePath is null || !File.Exists(settingsFilePath)) @@ -332,6 +332,8 @@ private bool TryFindSettingsFile(out string settingsFilePath) return false; } + _logger.LogInformation($"Found PSSA settings file at '{settingsFilePath}'"); + return true; } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/BreakpointService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/BreakpointService.cs index 748e21c6d..6d7e0c31a 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/BreakpointService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/BreakpointService.cs @@ -18,6 +18,83 @@ namespace Microsoft.PowerShell.EditorServices.Services { internal class BreakpointService { + /// + /// Code used on WinPS 5.1 to set breakpoints without Script path validation. + /// It uses reflection because the APIs were not public until 7.0 but just in + /// case something changes it has a fallback to Set-PSBreakpoint. + /// + private const string _setPSBreakpointLegacy = @" + [CmdletBinding(DefaultParameterSetName = 'Line')] + param ( + [Parameter()] + [ScriptBlock] + $Action, + + [Parameter(ParameterSetName = 'Command')] + [Parameter(ParameterSetName = 'Line', Mandatory = $true)] + [string] + $Script, + + [Parameter(ParameterSetName = 'Line')] + [int] + $Line, + + [Parameter(ParameterSetName = 'Line')] + [int] + $Column, + + [Parameter(ParameterSetName = 'Command', Mandatory = $true)] + [string] + $Command + ) + + if ($Script) { + # If using Set-PSBreakpoint we need to escape any wildcard patterns. + $PSBoundParameters['Script'] = [WildcardPattern]::Escape($Script) + } + else { + # WinPS must use null for the Script if unset. + $Script = [NullString]::Value + } + + if ($PSCmdlet.ParameterSetName -eq 'Command') { + $cmdCtor = [System.Management.Automation.CommandBreakpoint].GetConstructor( + [System.Reflection.BindingFlags]'NonPublic, Public, Instance', + $null, + [type[]]@([string], [System.Management.Automation.WildcardPattern], [string], [ScriptBlock]), + $null) + + if (-not $cmdCtor) { + Microsoft.PowerShell.Utility\Set-PSBreakpoint @PSBoundParameters + return + } + + $pattern = [System.Management.Automation.WildcardPattern]::Get( + $Command, + [System.Management.Automation.WildcardOptions]'Compiled, IgnoreCase') + $b = $cmdCtor.Invoke(@($Script, $pattern, $Command, $Action)) + } + else { + $lineCtor = [System.Management.Automation.LineBreakpoint].GetConstructor( + [System.Reflection.BindingFlags]'NonPublic, Public, Instance', + $null, + [type[]]@([string], [int], [int], [ScriptBlock]), + $null) + + if (-not $lineCtor) { + Microsoft.PowerShell.Utility\Set-PSBreakpoint @PSBoundParameters + return + } + + $b = $lineCtor.Invoke(@($Script, $Line, $Column, $Action)) + } + + [Runspace]::DefaultRunspace.Debugger.SetBreakpoints( + [System.Management.Automation.Breakpoint[]]@($b)) + + $b + "; + private readonly ILogger _logger; private readonly IInternalPowerShellExecutionService _executionService; private readonly PsesInternalHost _editorServicesHost; @@ -57,7 +134,7 @@ public async Task> GetBreakpointsAsync() .ConfigureAwait(false); } - public async Task> SetBreakpointsAsync(string escapedScriptPath, IReadOnlyList breakpoints) + public async Task> SetBreakpointsAsync(IReadOnlyList breakpoints) { if (BreakpointApiUtils.SupportsBreakpointApis(_editorServicesHost.CurrentRunspace)) { @@ -114,9 +191,11 @@ public async Task> SetBreakpointsAsync(string e psCommand.AddStatement(); } + // Don't use Set-PSBreakpoint as that will try and validate the Script + // path which may or may not exist. psCommand - .AddCommand(@"Microsoft.PowerShell.Utility\Set-PSBreakpoint") - .AddParameter("Script", escapedScriptPath) + .AddScript(_setPSBreakpointLegacy, useLocalScope: true) + .AddParameter("Script", breakpoint.MappedSource ?? breakpoint.Source) .AddParameter("Line", breakpoint.LineNumber); // Check if the user has specified the column number for the breakpoint. @@ -140,7 +219,16 @@ public async Task> SetBreakpointsAsync(string e IEnumerable setBreakpoints = await _executionService .ExecutePSCommandAsync(psCommand, CancellationToken.None) .ConfigureAwait(false); - configuredBreakpoints.AddRange(setBreakpoints.Select((breakpoint) => BreakpointDetails.Create(breakpoint))); + + int bpIdx = 0; + foreach (Breakpoint setBp in setBreakpoints) + { + BreakpointDetails setBreakpoint = BreakpointDetails.Create( + setBp, + sourceBreakpoint: breakpoints[bpIdx]); + configuredBreakpoints.Add(setBreakpoint); + bpIdx++; + } } return configuredBreakpoints; } @@ -184,7 +272,7 @@ public async Task> SetCommandBreakpoints } psCommand - .AddCommand(@"Microsoft.PowerShell.Utility\Set-PSBreakpoint") + .AddScript(_setPSBreakpointLegacy, useLocalScope: true) .AddParameter("Command", breakpoint.Name); // Check if this is a "conditional" line breakpoint. diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs index 90faceda4..a32cd48da 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation; -using System.Management.Automation.Language; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -17,7 +15,6 @@ using Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility; -using Microsoft.PowerShell.EditorServices.Services.TextDocument; using Microsoft.PowerShell.EditorServices.Utility; namespace Microsoft.PowerShell.EditorServices.Services @@ -31,6 +28,7 @@ internal class DebugService #region Fields internal const string PsesGlobalVariableNamePrefix = "__psEditorServices_"; + internal const string PsesGlobalVariableDebugServerName = $"{PsesGlobalVariableNamePrefix}DebugServer"; private const string TemporaryScriptFileName = "Script Listing.ps1"; private readonly ILogger _logger; @@ -50,7 +48,6 @@ internal class DebugService private VariableContainerDetails scriptScopeVariables; private VariableContainerDetails localScopeVariables; private StackFrameDetails[] stackFrameDetails; - private readonly PropertyInfo invocationTypeScriptPositionProperty; private readonly SemaphoreSlim debugInfoHandle = AsyncUtils.CreateSimpleLockingSemaphore(); #endregion @@ -75,6 +72,11 @@ internal class DebugService /// public DebuggerStoppedEventArgs CurrentDebuggerStoppedEventArgs { get; private set; } + /// + /// Returns a task that completes when script frames and variables have completed population + /// + public Task StackFramesAndVariablesFetched { get; private set; } + /// /// Tracks whether we are running Debug-Runspace in an out-of-process runspace. /// @@ -84,6 +86,11 @@ public bool IsDebuggingRemoteRunspace set => _debugContext.IsDebuggingRemoteRunspace = value; } + /// + /// Gets or sets an array of path mappings for the current debug session. + /// + public PathMapping[] PathMappings { get; set; } = []; + #endregion #region Constructors @@ -111,12 +118,6 @@ public DebugService( _debugContext.DebuggerResuming += OnDebuggerResuming; _debugContext.BreakpointUpdated += OnBreakpointUpdated; _remoteFileManager = remoteFileManager; - - invocationTypeScriptPositionProperty = - typeof(InvocationInfo) - .GetProperty( - "ScriptPosition", - BindingFlags.NonPublic | BindingFlags.Instance); } #endregion @@ -126,22 +127,22 @@ public DebugService( /// /// Sets the list of line breakpoints for the current debugging session. /// - /// The ScriptFile in which breakpoints will be set. + /// The path in which breakpoints will be set. /// BreakpointDetails for each breakpoint that will be set. /// If true, causes all existing breakpoints to be cleared before setting new ones. + /// If true, skips the remote file manager mapping of the script path. /// An awaitable Task that will provide details about the breakpoints that were set. public async Task> SetLineBreakpointsAsync( - ScriptFile scriptFile, + string scriptPath, IReadOnlyList breakpoints, - bool clearExisting = true) + bool clearExisting = true, + bool skipRemoteMapping = false) { DscBreakpointCapability dscBreakpoints = await _debugContext.GetDscBreakpointCapabilityAsync().ConfigureAwait(false); - string scriptPath = scriptFile.FilePath; - _psesHost.Runspace.ThrowCancelledIfUnusable(); // Make sure we're using the remote script path - if (_psesHost.CurrentRunspace.IsOnRemoteMachine && _remoteFileManager is not null) + if (!skipRemoteMapping && _psesHost.CurrentRunspace.IsOnRemoteMachine && _remoteFileManager is not null) { if (!_remoteFileManager.IsUnderRemoteTempPath(scriptPath)) { @@ -165,10 +166,10 @@ public async Task> SetLineBreakpointsAsync( { if (clearExisting) { - await _breakpointService.RemoveAllBreakpointsAsync(scriptFile.FilePath).ConfigureAwait(false); + await _breakpointService.RemoveAllBreakpointsAsync(scriptPath).ConfigureAwait(false); } - return await _breakpointService.SetBreakpointsAsync(escapedScriptPath, breakpoints).ConfigureAwait(false); + return await _breakpointService.SetBreakpointsAsync(breakpoints).ConfigureAwait(false); } return await dscBreakpoints @@ -606,6 +607,49 @@ public VariableScope[] GetVariableScopes(int stackFrameId) }; } + internal bool TryGetMappedLocalPath(string remotePath, out string localPath) + { + foreach (PathMapping mapping in PathMappings) + { + if (string.IsNullOrWhiteSpace(mapping.LocalRoot) || string.IsNullOrWhiteSpace(mapping.RemoteRoot)) + { + // If either path mapping is null, we can't map the path. + continue; + } + + if (remotePath.StartsWith(mapping.RemoteRoot, StringComparison.OrdinalIgnoreCase)) + { + localPath = mapping.LocalRoot + remotePath.Substring(mapping.RemoteRoot.Length); + return true; + } + } + + localPath = null; + return false; + } + + internal bool TryGetMappedRemotePath(string localPath, out string remotePath) + { + foreach (PathMapping mapping in PathMappings) + { + if (string.IsNullOrWhiteSpace(mapping.LocalRoot) || string.IsNullOrWhiteSpace(mapping.RemoteRoot)) + { + // If either path mapping is null, we can't map the path. + continue; + } + + if (localPath.StartsWith(mapping.LocalRoot, StringComparison.OrdinalIgnoreCase)) + { + // If the local path starts with the local path mapping, we can replace it with the remote path. + remotePath = mapping.RemoteRoot + localPath.Substring(mapping.LocalRoot.Length); + return true; + } + } + + remotePath = null; + return false; + } + #endregion #region Private Methods @@ -876,14 +920,19 @@ private async Task FetchStackFramesAsync(string scriptNameOverride) StackFrameDetails stackFrameDetailsEntry = StackFrameDetails.Create(callStackFrame, autoVariables, commandVariables); string stackFrameScriptPath = stackFrameDetailsEntry.ScriptPath; - if (scriptNameOverride is not null - && string.Equals(stackFrameScriptPath, StackFrameDetails.NoFileScriptPath)) + bool isNoScriptPath = string.Equals(stackFrameScriptPath, StackFrameDetails.NoFileScriptPath); + if (scriptNameOverride is not null && isNoScriptPath) { stackFrameDetailsEntry.ScriptPath = scriptNameOverride; } + else if (TryGetMappedLocalPath(stackFrameScriptPath, out string localMappedPath) + && !isNoScriptPath) + { + stackFrameDetailsEntry.ScriptPath = localMappedPath; + } else if (_psesHost.CurrentRunspace.IsOnRemoteMachine && _remoteFileManager is not null - && !string.Equals(stackFrameScriptPath, StackFrameDetails.NoFileScriptPath)) + && !isNoScriptPath) { stackFrameDetailsEntry.ScriptPath = _remoteFileManager.GetMappedPath(stackFrameScriptPath, _psesHost.CurrentRunspace); @@ -923,6 +972,7 @@ private static string TrimScriptListingLine(PSObject scriptLineObj, ref int pref /// public event EventHandler DebuggerStopped; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "It has to be async.")] internal async void OnDebuggerStopAsync(object sender, DebuggerStopEventArgs e) { try @@ -980,12 +1030,16 @@ await _executionService.ExecutePSCommandAsync( } } - // Get call stack and variables. - await FetchStackFramesAndVariablesAsync(noScriptName ? localScriptPath : null).ConfigureAwait(false); + // Begin call stack and variables fetch. We don't need to block here. + StackFramesAndVariablesFetched = FetchStackFramesAndVariablesAsync(noScriptName ? localScriptPath : null); + if (!noScriptName && TryGetMappedLocalPath(e.InvocationInfo.ScriptName, out string mappedLocalPath)) + { + localScriptPath = mappedLocalPath; + } // If this is a remote connection and the debugger stopped at a line // in a script file, get the file contents - if (_psesHost.CurrentRunspace.IsOnRemoteMachine + else if (_psesHost.CurrentRunspace.IsOnRemoteMachine && _remoteFileManager is not null && !noScriptName) { @@ -995,53 +1049,6 @@ await _remoteFileManager.FetchRemoteFileAsync( _psesHost.CurrentRunspace).ConfigureAwait(false); } - if (stackFrameDetails.Length > 0) - { - // Augment the top stack frame with details from the stop event - if (invocationTypeScriptPositionProperty.GetValue(e.InvocationInfo) is IScriptExtent scriptExtent) - { - StackFrameDetails targetFrame = stackFrameDetails[0]; - - // Certain context changes (like stepping into the default value expression - // of a parameter) do not create a call stack frame. In order to represent - // this context change we create a fake call stack frame. - if (!string.IsNullOrEmpty(scriptExtent.File) - && !PathUtils.IsPathEqual(scriptExtent.File, targetFrame.ScriptPath)) - { - await debugInfoHandle.WaitAsync().ConfigureAwait(false); - try - { - targetFrame = new StackFrameDetails - { - ScriptPath = scriptExtent.File, - // Just use the last frame's variables since we don't have a - // good way to get real values. - AutoVariables = targetFrame.AutoVariables, - CommandVariables = targetFrame.CommandVariables, - // Ideally we'd get a real value here but since there's no real - // call stack frame for this, we'd need to replicate a lot of - // engine code. - FunctionName = "", - }; - - StackFrameDetails[] newFrames = new StackFrameDetails[stackFrameDetails.Length + 1]; - newFrames[0] = targetFrame; - stackFrameDetails.CopyTo(newFrames, 1); - stackFrameDetails = newFrames; - } - finally - { - debugInfoHandle.Release(); - } - } - - targetFrame.StartLineNumber = scriptExtent.StartLineNumber; - targetFrame.EndLineNumber = scriptExtent.EndLineNumber; - targetFrame.StartColumnNumber = scriptExtent.StartColumnNumber; - targetFrame.EndColumnNumber = scriptExtent.EndColumnNumber; - } - } - CurrentDebuggerStoppedEventArgs = new DebuggerStoppedEventArgs( e, @@ -1083,8 +1090,12 @@ private void OnBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e) { // TODO: This could be either a path or a script block! string scriptPath = lineBreakpoint.Script; - if (_psesHost.CurrentRunspace.IsOnRemoteMachine - && _remoteFileManager is not null) + if (TryGetMappedLocalPath(scriptPath, out string mappedLocalPath)) + { + scriptPath = mappedLocalPath; + } + else if (_psesHost.CurrentRunspace.IsOnRemoteMachine + && _remoteFileManager is not null) { string mappedPath = _remoteFileManager.GetMappedPath(scriptPath, _psesHost.CurrentRunspace); diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs index f1bf3199e..9736b3e85 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs @@ -34,6 +34,8 @@ internal class DebugStateService internal bool IsUsingTempIntegratedConsole { get; set; } + internal string ExecuteMode { get; set; } + // This gets set at the end of the Launch/Attach handler which set debug state. internal TaskCompletionSource ServerStarted { get; set; } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Debugging/BreakpointApiUtils.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Debugging/BreakpointApiUtils.cs index 7884fbda5..ebb0646d2 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Debugging/BreakpointApiUtils.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Debugging/BreakpointApiUtils.cs @@ -136,7 +136,7 @@ public static Breakpoint SetBreakpoint(Debugger debugger, BreakpointDetailsBase { BreakpointDetails lineBreakpoint => SetLineBreakpointDelegate( debugger, - lineBreakpoint.Source, + lineBreakpoint.MappedSource ?? lineBreakpoint.Source, lineBreakpoint.LineNumber, lineBreakpoint.ColumnNumber ?? 0, actionScriptBlock, diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Debugging/BreakpointDetails.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Debugging/BreakpointDetails.cs index 0a1c268b9..4177b3816 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Debugging/BreakpointDetails.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Debugging/BreakpointDetails.cs @@ -24,6 +24,11 @@ internal sealed class BreakpointDetails : BreakpointDetailsBase /// public string Source { get; private set; } + /// + /// Gets the source where the breakpoint is mapped to, will be null if no mapping exists. Used only for debug purposes. + /// + public string MappedSource { get; private set; } + /// /// Gets the line number at which the breakpoint is set. /// @@ -50,6 +55,7 @@ private BreakpointDetails() /// /// /// + /// /// internal static BreakpointDetails Create( string source, @@ -57,7 +63,8 @@ internal static BreakpointDetails Create( int? column = null, string condition = null, string hitCondition = null, - string logMessage = null) + string logMessage = null, + string mappedSource = null) { Validate.IsNotNullOrEmptyString(nameof(source), source); @@ -69,7 +76,8 @@ internal static BreakpointDetails Create( ColumnNumber = column, Condition = condition, HitCondition = hitCondition, - LogMessage = logMessage + LogMessage = logMessage, + MappedSource = mappedSource }; } @@ -79,10 +87,12 @@ internal static BreakpointDetails Create( /// /// The Breakpoint instance from which details will be taken. /// The BreakpointUpdateType to determine if the breakpoint is verified. + /// /// The breakpoint source from the debug client, if any. /// A new instance of the BreakpointDetails class. internal static BreakpointDetails Create( Breakpoint breakpoint, - BreakpointUpdateType updateType = BreakpointUpdateType.Set) + BreakpointUpdateType updateType = BreakpointUpdateType.Set, + BreakpointDetails sourceBreakpoint = null) { Validate.IsNotNull(nameof(breakpoint), breakpoint); @@ -96,10 +106,11 @@ internal static BreakpointDetails Create( { Id = breakpoint.Id, Verified = updateType != BreakpointUpdateType.Disabled, - Source = lineBreakpoint.Script, + Source = sourceBreakpoint?.MappedSource is not null ? sourceBreakpoint.Source : lineBreakpoint.Script, LineNumber = lineBreakpoint.Line, ColumnNumber = lineBreakpoint.Column, - Condition = lineBreakpoint.Action?.ToString() + Condition = lineBreakpoint.Action?.ToString(), + MappedSource = sourceBreakpoint?.MappedSource, }; if (lineBreakpoint.Column > 0) diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs index 4c99ff747..1c26c48de 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs @@ -11,7 +11,6 @@ using Microsoft.PowerShell.EditorServices.Logging; using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Services.DebugAdapter; -using Microsoft.PowerShell.EditorServices.Services.PowerShell.Runspace; using Microsoft.PowerShell.EditorServices.Services.TextDocument; using Microsoft.PowerShell.EditorServices.Utility; using OmniSharp.Extensions.DebugAdapter.Protocol.Models; @@ -31,20 +30,17 @@ internal class BreakpointHandlers : ISetFunctionBreakpointsHandler, ISetBreakpoi private readonly DebugService _debugService; private readonly DebugStateService _debugStateService; private readonly WorkspaceService _workspaceService; - private readonly IRunspaceContext _runspaceContext; public BreakpointHandlers( ILoggerFactory loggerFactory, DebugService debugService, DebugStateService debugStateService, - WorkspaceService workspaceService, - IRunspaceContext runspaceContext) + WorkspaceService workspaceService) { _logger = loggerFactory.CreateLogger(); _debugService = debugService; _debugStateService = debugStateService; _workspaceService = workspaceService; - _runspaceContext = runspaceContext; } public async Task Handle(SetBreakpointsArguments request, CancellationToken cancellationToken) @@ -83,6 +79,11 @@ public async Task Handle(SetBreakpointsArguments request } // At this point, the source file has been verified as a PowerShell script. + string mappedSource = null; + if (_debugService.TryGetMappedRemotePath(scriptFile.FilePath, out string remoteMappedPath)) + { + mappedSource = remoteMappedPath; + } IReadOnlyList breakpointDetails = request.Breakpoints .Select((srcBreakpoint) => BreakpointDetails.Create( scriptFile.FilePath, @@ -90,7 +91,8 @@ public async Task Handle(SetBreakpointsArguments request srcBreakpoint.Column, srcBreakpoint.Condition, srcBreakpoint.HitCondition, - srcBreakpoint.LogMessage)).ToList(); + srcBreakpoint.LogMessage, + mappedSource: mappedSource)).ToList(); // If this is a "run without debugging (Ctrl+F5)" session ignore requests to set breakpoints. IReadOnlyList updatedBreakpointDetails = breakpointDetails; @@ -102,8 +104,9 @@ public async Task Handle(SetBreakpointsArguments request { updatedBreakpointDetails = await _debugService.SetLineBreakpointsAsync( - scriptFile, - breakpointDetails).ConfigureAwait(false); + mappedSource ?? scriptFile.FilePath, + breakpointDetails, + skipRemoteMapping: mappedSource is not null).ConfigureAwait(false); } catch (Exception e) { @@ -182,12 +185,11 @@ public Task Handle(SetExceptionBreakpointsArgum Task.FromResult(new SetExceptionBreakpointsResponse()); - private bool IsFileSupportedForBreakpoints(string requestedPath, ScriptFile resolvedScriptFile) + private static bool IsFileSupportedForBreakpoints(string requestedPath, ScriptFile resolvedScriptFile) { - // PowerShell 7 and above support breakpoints in untitled files if (ScriptFile.IsUntitledPath(requestedPath)) { - return BreakpointApiUtils.SupportsBreakpointApis(_runspaceContext.CurrentRunspace); + return true; } if (string.IsNullOrEmpty(resolvedScriptFile?.FilePath)) diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs index df8165319..146bbeae0 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs @@ -7,11 +7,9 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.PowerShell.EditorServices.Services; -using Microsoft.PowerShell.EditorServices.Services.DebugAdapter; using Microsoft.PowerShell.EditorServices.Services.PowerShell; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Debugging; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution; -using Microsoft.PowerShell.EditorServices.Services.PowerShell.Runspace; using Microsoft.PowerShell.EditorServices.Services.TextDocument; using Microsoft.PowerShell.EditorServices.Utility; using OmniSharp.Extensions.DebugAdapter.Protocol.Events; @@ -44,7 +42,6 @@ internal class ConfigurationDoneHandler : IConfigurationDoneHandler private readonly IInternalPowerShellExecutionService _executionService; private readonly WorkspaceService _workspaceService; private readonly IPowerShellDebugContext _debugContext; - private readonly IRunspaceContext _runspaceContext; // TODO: Decrease these arguments since they're a bunch of interfaces that can be simplified // (i.e., `IRunspaceContext` should just be available on `IPowerShellExecutionService`). @@ -56,8 +53,7 @@ public ConfigurationDoneHandler( DebugEventHandlerService debugEventHandlerService, IInternalPowerShellExecutionService executionService, WorkspaceService workspaceService, - IPowerShellDebugContext debugContext, - IRunspaceContext runspaceContext) + IPowerShellDebugContext debugContext) { _logger = loggerFactory.CreateLogger(); _debugAdapterServer = debugAdapterServer; @@ -67,7 +63,6 @@ public ConfigurationDoneHandler( _executionService = executionService; _workspaceService = workspaceService; _debugContext = debugContext; - _runspaceContext = runspaceContext; } public Task Handle(ConfigurationDoneArguments request, CancellationToken cancellationToken) @@ -110,20 +105,20 @@ internal async Task LaunchScriptAsync(string scriptToLaunch) PSCommand command; if (System.IO.File.Exists(scriptToLaunch)) { - // For a saved file we just execute its path (after escaping it). + // For a saved file we just execute its path (after escaping it), with the configured operator + // (which can't be called that because it's a reserved keyword in C#). + string executeMode = _debugStateService?.ExecuteMode == "Call" ? "&" : "."; command = PSCommandHelpers.BuildDotSourceCommandWithArguments( - PSCommandHelpers.EscapeScriptFilePath(scriptToLaunch), _debugStateService?.Arguments); + PSCommandHelpers.EscapeScriptFilePath(scriptToLaunch), _debugStateService?.Arguments, executeMode); } else // It's a URI to an untitled script, or a raw script. { bool isScriptFile = _workspaceService.TryGetFile(scriptToLaunch, out ScriptFile untitledScript); - if (isScriptFile && BreakpointApiUtils.SupportsBreakpointApis(_runspaceContext.CurrentRunspace)) + if (isScriptFile) { // Parse untitled files with their `Untitled:` URI as the filename which will // cache the URI and contents within the PowerShell parser. By doing this, we - // light up the ability to debug untitled files with line breakpoints. This is - // only possible with PowerShell 7's new breakpoint APIs since the old API, - // Set-PSBreakpoint, validates that the given path points to a real file. + // light up the ability to debug untitled files with line breakpoints. ScriptBlockAst ast = Parser.ParseInput( untitledScript.Contents, untitledScript.DocumentUri.ToString(), diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/DisconnectHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/DisconnectHandler.cs index fef2b107c..b042f912e 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/DisconnectHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/DisconnectHandler.cs @@ -50,13 +50,23 @@ public async Task Handle(DisconnectArguments request, Cancel // We should instead ensure that the debugger is in some valid state, lock it and then tear things down _debugEventHandlerService.UnregisterEventHandlers(); + _debugService.PathMappings = []; if (!_debugStateService.ExecutionCompleted) { _debugStateService.ExecutionCompleted = true; _debugService.Abort(); - if (_debugStateService.IsInteractiveDebugSession && _debugStateService.IsAttachSession) + if (!_debugStateService.IsAttachSession && !_debugStateService.IsUsingTempIntegratedConsole) + { + await _executionService.ExecutePSCommandAsync( + new PSCommand().AddCommand("Remove-Variable") + .AddParameter("Name", DebugService.PsesGlobalVariableDebugServerName) + .AddParameter("Force", true), + cancellationToken).ConfigureAwait(false); + } + + if (_debugStateService.IsInteractiveDebugSession && _debugStateService.IsRemoteAttach) { // Pop the sessions if (_runspaceContext.CurrentRunspace.RunspaceOrigin == RunspaceOrigin.EnteredProcess) diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs index 700c154b9..200dcc316 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs @@ -65,11 +65,22 @@ internal record PsesLaunchRequestArguments : LaunchRequestArguments /// public string[] RuntimeArgs { get; set; } + /// + /// Gets or sets the script execution mode, either "DotSource" or "Call". + /// + public string ExecuteMode { get; set; } + /// /// Gets or sets optional environment variables to pass to the debuggee. The string valued /// properties of the 'environmentVariables' are used as key/value pairs. /// public Dictionary Env { get; set; } + + /// + /// Gets or sets the path mappings for the debugging session. This is + /// only used when the current runspace is remote. + /// + public PathMapping[] PathMappings { get; set; } = []; } internal record PsesAttachRequestArguments : AttachRequestArguments @@ -83,6 +94,11 @@ internal record PsesAttachRequestArguments : AttachRequestArguments public string RunspaceName { get; set; } public string CustomPipeName { get; set; } + + /// + /// Gets or sets the path mappings for the remote debugging session. + /// + public PathMapping[] PathMappings { get; set; } = []; } internal class LaunchAndAttachHandler : ILaunchHandler, IAttachHandler, IOnDebugAdapterServerStarted @@ -123,6 +139,20 @@ public LaunchAndAttachHandler( } public async Task Handle(PsesLaunchRequestArguments request, CancellationToken cancellationToken) + { + _debugService.PathMappings = request.PathMappings; + try + { + return await HandleImpl(request, cancellationToken).ConfigureAwait(false); + } + catch + { + _debugService.PathMappings = []; + throw; + } + } + + public async Task HandleImpl(PsesLaunchRequestArguments request, CancellationToken cancellationToken) { // The debugger has officially started. We use this to later check if we should stop it. ((PsesInternalHost)_executionService).DebugContext.IsActive = true; @@ -173,6 +203,23 @@ public async Task Handle(PsesLaunchRequestArguments request, Can } _logger.LogTrace("Working dir " + (string.IsNullOrEmpty(workingDir) ? "not set." : $"set to '{workingDir}'")); + + if (!request.CreateTemporaryIntegratedConsole) + { + // Start-DebugAttachSession attaches in a new temp console + // so we cannot set this var if already running in that + // console. + PSCommand setVariableCmd = new PSCommand().AddCommand("Set-Variable") + .AddParameter("Name", DebugService.PsesGlobalVariableDebugServerName) + .AddParameter("Value", _debugAdapterServer) + .AddParameter("Description", "DO NOT USE: for internal use only.") + .AddParameter("Scope", "Global") + .AddParameter("Option", "ReadOnly"); + + await _executionService.ExecutePSCommandAsync( + setVariableCmd, + cancellationToken).ConfigureAwait(false); + } } // Prepare arguments to the script - if specified @@ -183,27 +230,10 @@ public async Task Handle(PsesLaunchRequestArguments request, Can // Store the launch parameters so that they can be used later _debugStateService.NoDebug = request.NoDebug; - _debugStateService.ScriptToLaunch = request.Script; + _debugStateService.ScriptToLaunch = GetLaunchScript(request); _debugStateService.Arguments = request.Args; _debugStateService.IsUsingTempIntegratedConsole = request.CreateTemporaryIntegratedConsole; - - if (request.CreateTemporaryIntegratedConsole - && !string.IsNullOrEmpty(request.Script) - && ScriptFile.IsUntitledPath(request.Script)) - { - throw new RpcErrorException(0, null, "Running an Untitled file in a temporary Extension Terminal is currently not supported!"); - } - - // If the current session is remote, map the script path to the remote - // machine if necessary - if (_debugStateService.ScriptToLaunch != null - && _runspaceContext.CurrentRunspace.IsOnRemoteMachine) - { - _debugStateService.ScriptToLaunch = - _remoteFileManagerService.GetMappedPath( - _debugStateService.ScriptToLaunch, - _runspaceContext.CurrentRunspace); - } + _debugStateService.ExecuteMode = request.ExecuteMode; // If no script is being launched, mark this as an interactive // debugging session @@ -227,11 +257,13 @@ public async Task Handle(PsesAttachRequestArguments request, Can _debugService.IsDebuggingRemoteRunspace = true; try { + _debugService.PathMappings = request.PathMappings; return await HandleImpl(request, cancellationToken).ConfigureAwait(false); } catch { _debugService.IsDebuggingRemoteRunspace = false; + _debugService.PathMappings = []; throw; } } @@ -295,7 +327,6 @@ void RunspaceChangedHandler(object s, RunspaceChangedEventArgs _) throw new RpcErrorException(0, null, "Invalid configuration with no process ID nor custom pipe name!"); } - // Execute the Debug-Runspace command but don't await it because it // will block the debug adapter initialization process. The // InitializedEvent will be sent as soon as the RunspaceChanged @@ -322,7 +353,6 @@ void RunspaceChangedHandler(object s, RunspaceChangedEventArgs _) if (request.RunspaceId < 1) { - throw new RpcErrorException(0, null, "A positive integer must be specified for the RunspaceId!"); } @@ -440,6 +470,7 @@ public async Task OnStarted(IDebugAdapterServer server, CancellationToken cancel // be sent to the client. await _debugStateService.ServerStarted.Task.ConfigureAwait(false); + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD003:Avoid awaiting foreign Tasks", Justification = "It's a wrapper.")] private async Task OnExecutionCompletedAsync(Task executeTask) { bool isRunspaceClosed = false; @@ -464,6 +495,7 @@ private async Task OnExecutionCompletedAsync(Task executeTask) _debugEventHandlerService.UnregisterEventHandlers(); _debugService.IsDebuggingRemoteRunspace = false; + _debugService.PathMappings = []; if (!isRunspaceClosed && _debugStateService.IsAttachSession) { @@ -493,5 +525,36 @@ await _executionService.ExecutePSCommandAsync( _debugService.IsClientAttached = false; _debugAdapterServer.SendNotification(EventNames.Terminated); } + + private string GetLaunchScript(PsesLaunchRequestArguments request) + { + string scriptToLaunch = request.Script; + if (request.CreateTemporaryIntegratedConsole + && !string.IsNullOrEmpty(scriptToLaunch) + && ScriptFile.IsUntitledPath(scriptToLaunch)) + { + throw new RpcErrorException(0, null, "Running an Untitled file in a temporary Extension Terminal is currently not supported!"); + } + + // If the current session is remote, map the script path to the remote + // machine if necessary + if (scriptToLaunch is not null && _runspaceContext.CurrentRunspace.IsOnRemoteMachine) + { + if (_debugService.TryGetMappedRemotePath(scriptToLaunch, out string remoteMappedPath)) + { + scriptToLaunch = remoteMappedPath; + } + else + { + // If the script is not mapped, we will map it to the remote path + // using the RemoteFileManagerService. + scriptToLaunch = _remoteFileManagerService.GetMappedPath( + scriptToLaunch, + _runspaceContext.CurrentRunspace); + } + } + + return scriptToLaunch; + } } } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ScopesHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ScopesHandler.cs index 04adf185e..daa3d8e60 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ScopesHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ScopesHandler.cs @@ -18,16 +18,27 @@ internal class ScopesHandler : IScopesHandler public ScopesHandler(DebugService debugService) => _debugService = debugService; + /// + /// Retrieves the variable scopes (containers) for the currently selected stack frame. Variables details are fetched via a separate request. + /// public Task Handle(ScopesArguments request, CancellationToken cancellationToken) { - VariableScope[] variableScopes = - _debugService.GetVariableScopes( - (int)request.FrameId); + // HACK: The StackTraceHandler injects an artificial label frame as the first frame as a performance optimization, so when scopes are requested by the client, we need to adjust the frame index accordingly to match the underlying PowerShell frame, so when the client clicks on the label (or hit the default breakpoint), they get variables populated from the top of the PowerShell stackframe. If the client dives deeper, we need to reflect that as well (though 90% of debug users don't actually investigate this) + // VSCode Frame 0 (Label) -> PowerShell StackFrame 0 (for convenience) + // VSCode Frame 1 (First Real PS Frame) -> Also PowerShell StackFrame 0 + // VSCode Frame 2 -> PowerShell StackFrame 1 + // VSCode Frame 3 -> PowerShell StackFrame 2 + // etc. + int powershellFrameId = request.FrameId == 0 ? 0 : (int)request.FrameId - 1; + + VariableScope[] variableScopes = _debugService.GetVariableScopes(powershellFrameId); return Task.FromResult(new ScopesResponse { - Scopes = new Container(variableScopes - .Select(LspDebugUtils.CreateScope)) + Scopes = new Container( + variableScopes + .Select(LspDebugUtils.CreateScope) + ) }); } } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/StackTraceHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/StackTraceHandler.cs index f53e094e7..735d672d1 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/StackTraceHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/StackTraceHandler.cs @@ -1,64 +1,136 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#nullable enable using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using System.Management.Automation; using Microsoft.PowerShell.EditorServices.Services; -using Microsoft.PowerShell.EditorServices.Services.DebugAdapter; -using Microsoft.PowerShell.EditorServices.Utility; using OmniSharp.Extensions.DebugAdapter.Protocol.Models; using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; +using Microsoft.PowerShell.EditorServices.Services.DebugAdapter; +using System.Linq; +using OmniSharp.Extensions.JsonRpc; + +namespace Microsoft.PowerShell.EditorServices.Handlers; -namespace Microsoft.PowerShell.EditorServices.Handlers +internal class StackTraceHandler(DebugService debugService) : IStackTraceHandler { - internal class StackTraceHandler : IStackTraceHandler + /// + /// Because we don't know the size of the stacktrace beforehand, we will tell the client that there are more frames available, this is effectively a paging size, as the client should request this many frames after the first one. + /// + private const int INITIAL_PAGE_SIZE = 20; + + public async Task Handle(StackTraceArguments request, CancellationToken cancellationToken) { - private readonly DebugService _debugService; + if (!debugService.IsDebuggerStopped) + { + throw new RpcErrorException(0, null!, "Stacktrace was requested while we are not stopped at a breakpoint. This is a violation of the DAP protocol, and is probably a bug."); + } + + // Adapting to int to let us use LINQ, realistically if you have a stacktrace larger than this that the client is requesting, you have bigger problems... + int skip = Convert.ToInt32(request.StartFrame ?? 0); + int take = Convert.ToInt32(request.Levels ?? 0); - public StackTraceHandler(DebugService debugService) => _debugService = debugService; + // We generate a label for the breakpoint and can return that immediately if the client is supporting DelayedStackTraceLoading. + InvocationInfo invocationInfo = debugService.CurrentDebuggerStoppedEventArgs?.OriginalEvent?.InvocationInfo + ?? throw new RpcErrorException(0, null!, "InvocationInfo was not available on CurrentDebuggerStoppedEvent args. This is a bug and you should report it."); - public async Task Handle(StackTraceArguments request, CancellationToken cancellationToken) + string? scriptNameOverride = null; + if (debugService.TryGetMappedLocalPath(invocationInfo.ScriptName, out string mappedLocalPath)) { - StackFrameDetails[] stackFrameDetails = await _debugService.GetStackFramesAsync(cancellationToken).ConfigureAwait(false); + scriptNameOverride = mappedLocalPath; + } + StackFrame breakpointLabel = CreateBreakpointLabel(invocationInfo, scriptNameOverride: scriptNameOverride); - // Handle a rare race condition where the adapter requests stack frames before they've - // begun building. - if (stackFrameDetails is null) - { - return new StackTraceResponse - { - StackFrames = Array.Empty(), - TotalFrames = 0 - }; - } - - List newStackFrames = new(); - - long startFrameIndex = request.StartFrame ?? 0; - long maxFrameCount = stackFrameDetails.Length; - - // If the number of requested levels == 0 (or null), that means get all stack frames - // after the specified startFrame index. Otherwise get all the stack frames. - long requestedFrameCount = request.Levels ?? 0; - if (requestedFrameCount > 0) + if (skip == 0 && take == 1) // This indicates the client is doing an initial fetch, so we want to return quickly to unblock the UI and wait on the remaining stack frames for the subsequent requests. + { + return new StackTraceResponse() { - maxFrameCount = Math.Min(maxFrameCount, startFrameIndex + requestedFrameCount); - } + StackFrames = new StackFrame[] { breakpointLabel }, + TotalFrames = INITIAL_PAGE_SIZE //Indicate to the client that there are more frames available + }; + } - for (long i = startFrameIndex; i < maxFrameCount; i++) - { - // Create the new StackFrame object with an ID that can - // be referenced back to the current list of stack frames - newStackFrames.Add(LspDebugUtils.CreateStackFrame(stackFrameDetails[i], id: i)); - } + // Wait until the stack frames and variables have been fetched. + await debugService.StackFramesAndVariablesFetched.ConfigureAwait(false); + StackFrameDetails[] stackFrameDetails = await debugService.GetStackFramesAsync(cancellationToken) + .ConfigureAwait(false); + + // Handle a rare race condition where the adapter requests stack frames before they've + // begun building. + if (stackFrameDetails is null) + { return new StackTraceResponse { - StackFrames = newStackFrames, - TotalFrames = newStackFrames.Count + StackFrames = Array.Empty(), + TotalFrames = 0 + }; + } + + List newStackFrames = new(); + if (skip == 0) + { + newStackFrames.Add(breakpointLabel); + } + + newStackFrames.AddRange( + stackFrameDetails + .Skip(skip != 0 ? skip - 1 : skip) + .Take(take != 0 ? take - 1 : take) + .Select((frame, index) => CreateStackFrame(frame, index + 1)) + ); + + return new StackTraceResponse + { + StackFrames = newStackFrames, + TotalFrames = newStackFrames.Count + }; + } + + public static StackFrame CreateStackFrame(StackFrameDetails stackFrame, long id) + { + SourcePresentationHint sourcePresentationHint = + stackFrame.IsExternalCode ? SourcePresentationHint.Deemphasize : SourcePresentationHint.Normal; + + // When debugging an interactive session, the ScriptPath is which is not a valid source file. + // We need to make sure the user can't open the file associated with this stack frame. + // It will generate a VSCode error in this case. + Source? source = null; + if (!stackFrame.ScriptPath.Contains("")) + { + source = new Source + { + Path = stackFrame.ScriptPath, + PresentationHint = sourcePresentationHint }; } + + return new StackFrame + { + Id = id, + Name = (source is not null) ? stackFrame.FunctionName : "Interactive Session", + Line = (source is not null) ? stackFrame.StartLineNumber : 0, + EndLine = stackFrame.EndLineNumber, + Column = (source is not null) ? stackFrame.StartColumnNumber : 0, + EndColumn = stackFrame.EndColumnNumber, + Source = source + }; } + + public static StackFrame CreateBreakpointLabel(InvocationInfo invocationInfo, int id = 0, string? scriptNameOverride = null) => new() + { + Name = "", + Id = id, + Source = new() + { + Path = scriptNameOverride ?? invocationInfo.ScriptName + }, + Line = invocationInfo.ScriptLineNumber, + Column = invocationInfo.OffsetInLine, + PresentationHint = StackFramePresentationHint.Label + }; } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/VariablesHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/VariablesHandler.cs index 58fb4d5ef..55c5f85ff 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/VariablesHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/VariablesHandler.cs @@ -34,10 +34,12 @@ public async Task Handle(VariablesArguments request, Cancella .ToArray() }; } + #pragma warning disable RCS1075 catch (Exception) { // TODO: This shouldn't be so broad } + #pragma warning restore RCS1075 return variablesResponse; } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/PathMapping.cs b/src/PowerShellEditorServices/Services/DebugAdapter/PathMapping.cs new file mode 100644 index 000000000..6bf6a6ad9 --- /dev/null +++ b/src/PowerShellEditorServices/Services/DebugAdapter/PathMapping.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +namespace Microsoft.PowerShell.EditorServices.Services; + +/// +/// Used for attach requests to map a local and remote path together. +/// +internal record PathMapping +{ + /// + /// Gets or sets the local root of this mapping entry. + /// + public string? LocalRoot { get; set; } + + /// + /// Gets or sets the remote root of this mapping entry. + /// + public string? RemoteRoot { get; set; } +} + +#nullable disable diff --git a/src/PowerShellEditorServices/Services/Extension/EditorOperationsService.cs b/src/PowerShellEditorServices/Services/Extension/EditorOperationsService.cs index 607d38720..9c3e0c4ff 100644 --- a/src/PowerShellEditorServices/Services/Extension/EditorOperationsService.cs +++ b/src/PowerShellEditorServices/Services/Extension/EditorOperationsService.cs @@ -198,6 +198,14 @@ public async Task SaveFileAsync(string currentPath, string newSavePath) public string[] GetWorkspacePaths() => _workspaceService.WorkspacePaths.ToArray(); + public WorkspaceOpenDocument[] GetWorkspaceOpenDocuments() + => [.. + _workspaceService + .GetOpenedFiles() + .Where(static scriptFile => scriptFile.IsOpen) + .Select(static scriptFile => new WorkspaceOpenDocument(scriptFile.FilePath, !scriptFile.IsInMemory)) + ]; + public string GetWorkspaceRelativePath(ScriptFile scriptFile) => _workspaceService.GetRelativePath(scriptFile); public async Task ShowInformationMessageAsync(string message) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs b/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs index 3c5e7a1d2..28d29baa1 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs @@ -35,9 +35,7 @@ public LegacyReadLine( _onIdleAction = onIdleAction; } -#pragma warning disable CA1502 // Cyclomatic complexity we don't care about public override string ReadLine(CancellationToken cancellationToken) -#pragma warning restore CA1502 { string inputBeforeCompletion = null; string inputAfterCompletion = null; @@ -394,6 +392,7 @@ protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken) } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "This is a legacy implementation.")] private ConsoleKeyInfo ReadKeyWithIdleSupport(CancellationToken cancellationToken) { // We run the readkey function on another thread so we can run an idle handler diff --git a/src/PowerShellEditorServices/Services/PowerShell/Debugging/DscBreakpointCapability.cs b/src/PowerShellEditorServices/Services/PowerShell/Debugging/DscBreakpointCapability.cs index 763094be4..344b798f7 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Debugging/DscBreakpointCapability.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Debugging/DscBreakpointCapability.cs @@ -91,10 +91,12 @@ public static async Task GetDscCapabilityAsync( .AddCommand(@"Microsoft.PowerShell.Core\Import-Module") .AddParameter("Name", "PSDesiredStateConfiguration") .AddParameter("PassThru") - .AddParameter("ErrorAction", ActionPreference.Ignore); + .AddParameter("ErrorAction", ActionPreference.Ignore) + .AddCommand(@"Microsoft.PowerShell.Utility\Select-Object") + .AddParameter("ExpandProperty", "Name"); - IReadOnlyList dscModule = - await executionService.ExecutePSCommandAsync( + IReadOnlyList dscModule = + await executionService.ExecutePSCommandAsync( psCommand, CancellationToken.None, new PowerShellExecutionOptions { ThrowOnError = false }) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Handlers/HandlerErrorException.cs b/src/PowerShellEditorServices/Services/PowerShell/Handlers/HandlerErrorException.cs new file mode 100644 index 000000000..af6cda2ec --- /dev/null +++ b/src/PowerShellEditorServices/Services/PowerShell/Handlers/HandlerErrorException.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace Microsoft.PowerShell.EditorServices.Handlers; + +/// +/// A convenience exception for handlers to throw when a request fails for a normal reason, +/// and to communicate that reason to the user without a full internal stacktrace. +/// +/// The message describing the reason for the request failure. +/// Additional details to be logged regarding the failure. It should be serializable to JSON. +/// The severity level of the message. This is only shown in internal logging. +internal class HandlerErrorException +( + string message, + object logDetails = null, + MessageType severity = MessageType.Error +) : RpcErrorException(0, logDetails!, message) +{ + internal MessageType Severity { get; } = severity; +} diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/HostStartOptions.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/HostStartOptions.cs index f483b76b4..7de16fddf 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/HostStartOptions.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/HostStartOptions.cs @@ -9,6 +9,6 @@ internal struct HostStartOptions public string InitialWorkingDirectory { get; set; } - public bool ShellIntegrationEnabled { get; set; } + public string ShellIntegrationScript { get; set; } } } diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs index c921de81b..48ba1c27c 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs @@ -29,9 +29,7 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Host using Microsoft.PowerShell.EditorServices.Server; using OmniSharp.Extensions.DebugAdapter.Protocol.Server; -#pragma warning disable CA1506 // Coupling complexity we don't care about internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRunspaceContext, IInternalPowerShellExecutionService -#pragma warning restore CA1506 { internal const string DefaultPrompt = "> "; @@ -308,23 +306,44 @@ public async Task TryStartAsync(HostStartOptions startOptions, Cancellatio _logger.LogDebug("InitialWorkingDirectory set!"); } + _logger.LogDebug("Setting profile variable..."); + await SetProfileVariableAsync(cancellationToken).ConfigureAwait(false); + _logger.LogDebug("Profile variable set!"); + if (startOptions.LoadProfiles) { _logger.LogDebug("Loading profiles..."); await LoadHostProfilesAsync(cancellationToken).ConfigureAwait(false); _logger.LogDebug("Profiles loaded!"); } + else + { + _logger.LogDebug("Profile loading skipped per configuration!"); + } - if (startOptions.ShellIntegrationEnabled) + if (!string.IsNullOrEmpty(startOptions.ShellIntegrationScript)) { - _logger.LogDebug("Enabling shell integration..."); + _logger.LogDebug("Enabling Terminal Shell Integration..."); _shellIntegrationEnabled = true; - await EnableShellIntegrationAsync(cancellationToken).ConfigureAwait(false); + string sourceMethod = startOptions.ShellIntegrationScript.EndsWith(".ps1") ? "." : "Import-Module"; + // TODO: Make the __psEditorServices prefix shared (it's used elsewhere too). + string setupShellIntegration = $$""" + # Setup Terminal Shell Integration. + + # Define a fake PSConsoleHostReadLine so the integration script's wrapper + # can execute it to get the user's input. + $global:__psEditorServices_userInput = ""; + function global:PSConsoleHostReadLine { $global:__psEditorServices_userInput } + + # Execute the provided shell integration script. + try { {{sourceMethod}} '{{startOptions.ShellIntegrationScript}}' } catch {} + """; + await EnableShellIntegrationAsync(setupShellIntegration, cancellationToken).ConfigureAwait(false); _logger.LogDebug("Shell integration enabled!"); } else { - _logger.LogDebug("Shell integration not enabled!"); + _logger.LogDebug("Terminal Shell Integration not enabled!"); } await _started.Task.ConfigureAwait(false); @@ -495,6 +514,7 @@ public Task ExecuteDelegateAsync( new SynchronousDelegateTask(_logger, representation, executionOptions, action, cancellationToken)); } + // TODO: One day fix these so the cancellation token is last. public Task> ExecutePSCommandAsync( PSCommand psCommand, CancellationToken cancellationToken, @@ -571,219 +591,44 @@ internal void DisableTranscribeOnly() } } + internal Task SetProfileVariableAsync(CancellationToken cancellationToken) + { + // If the CurrentUserCurrentHost profile is null then we cannot create the profile variable + if (_hostInfo.ProfilePaths.CurrentUserCurrentHost is null) + { + return Task.CompletedTask; + } + + // NOTE: This is a special task run on startup! + return ExecuteDelegateAsync( + "SetProfileVariable", + executionOptions: null, + (pwsh, _) => pwsh.SetProfileVariable(_hostInfo.ProfilePaths), + cancellationToken); + } + internal Task LoadHostProfilesAsync(CancellationToken cancellationToken) { + // If the CurrentUserCurrentHost profile is null then we cannot instantiate + if (_hostInfo.ProfilePaths.CurrentUserCurrentHost is null) + { + return Task.CompletedTask; + } + // NOTE: This is a special task run on startup! return ExecuteDelegateAsync( "LoadProfiles", executionOptions: null, - (pwsh, _) => pwsh.LoadProfiles(_hostInfo.ProfilePaths), + (pwsh, _) => pwsh.LoadProfileScripts(_hostInfo.ProfilePaths), cancellationToken); } - private Task EnableShellIntegrationAsync(CancellationToken cancellationToken) + private Task EnableShellIntegrationAsync(string shellIntegrationScript, CancellationToken cancellationToken) { - // Imported on 01/03/24 from - // https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 - // with quotes escaped, `__VSCodeOriginalPSConsoleHostReadLine` removed (as it's done - // in our own ReadLine function), and `[Console]::Write` replaced with `Write-Host`. - const string shellIntegrationScript = @" -# Prevent installing more than once per session -if (Test-Path variable:global:__VSCodeOriginalPrompt) { - return; -} - -# Disable shell integration when the language mode is restricted -if ($ExecutionContext.SessionState.LanguageMode -ne ""FullLanguage"") { - return; -} - -$Global:__VSCodeOriginalPrompt = $function:Prompt - -$Global:__LastHistoryId = -1 - -# Store the nonce in script scope and unset the global -$Nonce = $env:VSCODE_NONCE -$env:VSCODE_NONCE = $null - -if ($env:VSCODE_ENV_REPLACE) { - $Split = $env:VSCODE_ENV_REPLACE.Split("":"") - foreach ($Item in $Split) { - $Inner = $Item.Split('=') - [Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':')) - } - $env:VSCODE_ENV_REPLACE = $null -} -if ($env:VSCODE_ENV_PREPEND) { - $Split = $env:VSCODE_ENV_PREPEND.Split("":"") - foreach ($Item in $Split) { - $Inner = $Item.Split('=') - [Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':') + [Environment]::GetEnvironmentVariable($Inner[0])) - } - $env:VSCODE_ENV_PREPEND = $null -} -if ($env:VSCODE_ENV_APPEND) { - $Split = $env:VSCODE_ENV_APPEND.Split("":"") - foreach ($Item in $Split) { - $Inner = $Item.Split('=') - [Environment]::SetEnvironmentVariable($Inner[0], [Environment]::GetEnvironmentVariable($Inner[0]) + $Inner[1].Replace('\x3a', ':')) - } - $env:VSCODE_ENV_APPEND = $null -} - -function Global:__VSCode-Escape-Value([string]$value) { - # NOTE: In PowerShell v6.1+, this can be written `$value -replace '…', { … }` instead of `[regex]::Replace`. - # Replace any non-alphanumeric characters. - [regex]::Replace($value, '[\\\n;]', { param($match) - # Encode the (ascii) matches as `\x` - -Join ( - [System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ } - ) - }) -} - -function Global:Prompt() { - $FakeCode = [int]!$global:? - # NOTE: We disable strict mode for the scope of this function because it unhelpfully throws an - # error when $LastHistoryEntry is null, and is not otherwise useful. - Set-StrictMode -Off - $LastHistoryEntry = Get-History -Count 1 - # Skip finishing the command if the first command has not yet started - if ($Global:__LastHistoryId -ne -1) { - if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) { - # Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command) - $Result = ""$([char]0x1b)]633;E`a"" - $Result += ""$([char]0x1b)]633;D`a"" - } - else { - # Command finished command line - # OSC 633 ; E ; ; ST - $Result = ""$([char]0x1b)]633;E;"" - # Sanitize the command line to ensure it can get transferred to the terminal and can be parsed - # correctly. This isn't entirely safe but good for most cases, it's important for the Pt parameter - # to only be composed of _printable_ characters as per the spec. - if ($LastHistoryEntry.CommandLine) { - $CommandLine = $LastHistoryEntry.CommandLine - } - else { - $CommandLine = """" - } - $Result += $(__VSCode-Escape-Value $CommandLine) - $Result += "";$Nonce"" - $Result += ""`a"" - # Command finished exit code - # OSC 633 ; D [; ] ST - $Result += ""$([char]0x1b)]633;D;$FakeCode`a"" - } - } - # Prompt started - # OSC 633 ; A ST - $Result += ""$([char]0x1b)]633;A`a"" - # Current working directory - # OSC 633 ; = ST - $Result += if ($pwd.Provider.Name -eq 'FileSystem') { ""$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a"" } - # Before running the original prompt, put $? back to what it was: - if ($FakeCode -ne 0) { - Write-Error ""failure"" -ea ignore - } - # Run the original prompt - $Result += $Global:__VSCodeOriginalPrompt.Invoke() - # Write command started - $Result += ""$([char]0x1b)]633;B`a"" - $Global:__LastHistoryId = $LastHistoryEntry.Id - return $Result -} - -# Set IsWindows property -if ($PSVersionTable.PSVersion -lt ""6.0"") { - # Windows PowerShell is only available on Windows - Write-Host -NoNewLine ""$([char]0x1b)]633;P;IsWindows=$true`a"" -} -else { - Write-Host -NoNewLine ""$([char]0x1b)]633;P;IsWindows=$IsWindows`a"" -} - -# Set always on key handlers which map to default VS Code keybindings -function Set-MappedKeyHandler { - param ([string[]] $Chord, [string[]]$Sequence) - try { - $Handler = Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1 - } - catch [System.Management.Automation.ParameterBindingException] { - # PowerShell 5.1 ships with PSReadLine 2.0.0 which does not have -Chord, - # so we check what's bound and filter it. - $Handler = Get-PSReadLineKeyHandler -Bound | Where-Object -FilterScript { $_.Key -eq $Chord } | Select-Object -First 1 - } - if ($Handler) { - Set-PSReadLineKeyHandler -Chord $Sequence -Function $Handler.Function - } -} - -$Global:__VSCodeHaltCompletions = $false -function Set-MappedKeyHandlers { - Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a' - Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b' - Set-MappedKeyHandler -Chord Shift+Enter -Sequence 'F12,c' - Set-MappedKeyHandler -Chord Shift+End -Sequence 'F12,d' - - # Conditionally enable suggestions - if ($env:VSCODE_SUGGEST -eq '1') { - Remove-Item Env:VSCODE_SUGGEST - - # VS Code send completions request (may override Ctrl+Spacebar) - Set-PSReadLineKeyHandler -Chord 'F12,e' -ScriptBlock { - Send-Completions - } - - # Suggest trigger characters - Set-PSReadLineKeyHandler -Chord ""-"" -ScriptBlock { - [Microsoft.PowerShell.PSConsoleReadLine]::Insert(""-"") - if (!$Global:__VSCodeHaltCompletions) { - Send-Completions - } - } - - Set-PSReadLineKeyHandler -Chord 'F12,y' -ScriptBlock { - $Global:__VSCodeHaltCompletions = $true - } - - Set-PSReadLineKeyHandler -Chord 'F12,z' -ScriptBlock { - $Global:__VSCodeHaltCompletions = $false - } - } -} - -function Send-Completions { - $commandLine = """" - $cursorIndex = 0 - # TODO: Since fuzzy matching exists, should completions be provided only for character after the - # last space and then filter on the client side? That would let you trigger ctrl+space - # anywhere on a word and have full completions available - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex) - $completionPrefix = $commandLine - - # Get completions - $result = ""`e]633;Completions"" - if ($completionPrefix.Length -gt 0) { - # Get and send completions - $completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex - if ($null -ne $completions.CompletionMatches) { - $result += "";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);"" - $result += $completions.CompletionMatches | ConvertTo-Json -Compress - } - } - $result += ""`a"" - - Write-Host -NoNewLine $result -} - -# Register key handlers if PSReadLine is available -if (Get-Module -Name PSReadLine) { - Set-MappedKeyHandlers -} - "; - - return ExecutePSCommandAsync(new PSCommand().AddScript(shellIntegrationScript), cancellationToken); + return ExecutePSCommandAsync( + new PSCommand().AddScript(shellIntegrationScript), + cancellationToken, + new PowerShellExecutionOptions { AddToHistory = false, ThrowOnError = false }); } public Task SetInitialWorkingDirectoryAsync(string path, CancellationToken cancellationToken) @@ -997,8 +842,9 @@ private void RunTopLevelExecutionLoop() { // Make sure we execute any startup tasks first. These should be, in order: // 1. Delegate to register psEditor variable - // 2. LoadProfiles delegate - // 3. Delegate to import PSEditModule + // 2. SetProfileVariable delegate + // 3. Optional LoadProfiles delegate + // 4. Delegate to import PSEditModule while (_taskQueue.TryTake(out ISynchronousTask task)) { task.ExecuteSynchronously(CancellationToken.None); @@ -1262,16 +1108,34 @@ private void InvokeInput(string input, CancellationToken cancellationToken) try { - // For VS Code's shell integration feature, this replaces their - // PSConsoleHostReadLine function wrapper, as that global function is not available - // to users of PSES, since we already wrap ReadLine ourselves. + // For the terminal shell integration feature, we call PSConsoleHostReadLine specially as it's been wrapped. + // Normally it would not be available (since we wrap ReadLine ourselves), + // but in this case we've made the original just emit the user's input so that the wrapper works as intended. if (_shellIntegrationEnabled) { - System.Console.Write("\x1b]633;C\a"); + // Save the user's input to our special global variable so PSConsoleHostReadLine can read it. + InvokePSCommand( + new PSCommand().AddScript("$global:__psEditorServices_userInput = $args[0]").AddArgument(input), + new PowerShellExecutionOptions { ThrowOnError = false, WriteOutputToHost = false }, + cancellationToken); + + // Invoke the PSConsoleHostReadLine wrapper. We don't write the output because it + // returns the command line (user input) which would then be duplicate noise. Fortunately + // it writes the shell integration sequences directly using [Console]::Write. + InvokePSCommand( + new PSCommand().AddScript("PSConsoleHostReadLine"), + new PowerShellExecutionOptions { ThrowOnError = false, WriteOutputToHost = false }, + cancellationToken); + + // Reset our global variable. + InvokePSCommand( + new PSCommand().AddScript("$global:__psEditorServices_userInput = \"\""), + new PowerShellExecutionOptions { ThrowOnError = false, WriteOutputToHost = false }, + cancellationToken); } InvokePSCommand( - new PSCommand().AddScript(input, useLocalScope: false), + new PSCommand().AddScript(input), new PowerShellExecutionOptions { AddToHistory = true, diff --git a/src/PowerShellEditorServices/Services/PowerShell/Refactoring/Exceptions.cs b/src/PowerShellEditorServices/Services/PowerShell/Refactoring/Exceptions.cs new file mode 100644 index 000000000..c286963a6 --- /dev/null +++ b/src/PowerShellEditorServices/Services/PowerShell/Refactoring/Exceptions.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +namespace Microsoft.PowerShell.EditorServices.Refactoring +{ + internal class TargetSymbolNotFoundException : Exception + { + public TargetSymbolNotFoundException() + { + } + + public TargetSymbolNotFoundException(string message) + : base(message) + { + } + + public TargetSymbolNotFoundException(string message, Exception inner) + : base(message, inner) + { + } + } + + internal class FunctionDefinitionNotFoundException : Exception + { + public FunctionDefinitionNotFoundException() + { + } + + public FunctionDefinitionNotFoundException(string message) + : base(message) + { + } + + public FunctionDefinitionNotFoundException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs b/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs index f7ef51ab9..f24ae98bc 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. - +#nullable enable using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -69,7 +69,7 @@ and not PSInvocationState.Failed pwsh.InvocationStateChanged += handler; } - public static Collection InvokeAndClear(this PowerShell pwsh, PSInvocationSettings invocationSettings = null) + public static Collection InvokeAndClear(this PowerShell pwsh, PSInvocationSettings? invocationSettings = null) { try { @@ -81,7 +81,7 @@ public static Collection InvokeAndClear(this PowerShell pwsh, } } - public static void InvokeAndClear(this PowerShell pwsh, PSInvocationSettings invocationSettings = null) + public static void InvokeAndClear(this PowerShell pwsh, PSInvocationSettings? invocationSettings = null) { try { @@ -93,13 +93,13 @@ public static void InvokeAndClear(this PowerShell pwsh, PSInvocationSettings inv } } - public static Collection InvokeCommand(this PowerShell pwsh, PSCommand psCommand, PSInvocationSettings invocationSettings = null) + public static Collection InvokeCommand(this PowerShell pwsh, PSCommand psCommand, PSInvocationSettings? invocationSettings = null) { pwsh.Commands = psCommand; return pwsh.InvokeAndClear(invocationSettings); } - public static void InvokeCommand(this PowerShell pwsh, PSCommand psCommand, PSInvocationSettings invocationSettings = null) + public static void InvokeCommand(this PowerShell pwsh, PSCommand psCommand, PSInvocationSettings? invocationSettings = null) { pwsh.Commands = psCommand; pwsh.InvokeAndClear(invocationSettings); @@ -193,7 +193,7 @@ public static void SetCorrectExecutionPolicy(this PowerShell pwsh, ILogger logge } } - public static void LoadProfiles(this PowerShell pwsh, ProfilePathInfo profilePaths) + public static void SetProfileVariable(this PowerShell pwsh, ProfilePathInfo profilePaths) { // Per the documentation, "the `$PROFILE` variable stores the path to the 'Current User, // Current Host' profile. The other profiles are saved in note properties of the @@ -202,15 +202,24 @@ public static void LoadProfiles(this PowerShell pwsh, ProfilePathInfo profilePat // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.1#the-profile-variable PSObject profileVariable = PSObject.AsPSObject(profilePaths.CurrentUserCurrentHost); + profileVariable.Members.Add(new PSNoteProperty(nameof(profilePaths.AllUsersAllHosts), profilePaths.AllUsersAllHosts)); + profileVariable.Members.Add(new PSNoteProperty(nameof(profilePaths.AllUsersCurrentHost), profilePaths.AllUsersCurrentHost)); + profileVariable.Members.Add(new PSNoteProperty(nameof(profilePaths.CurrentUserAllHosts), profilePaths.CurrentUserAllHosts)); + profileVariable.Members.Add(new PSNoteProperty(nameof(profilePaths.CurrentUserCurrentHost), profilePaths.CurrentUserCurrentHost)); + + pwsh.Runspace.SessionStateProxy.SetVariable("PROFILE", profileVariable); + } + + public static void LoadProfileScripts(this PowerShell pwsh, ProfilePathInfo profilePaths) + { + PSObject profileVariable = PSObject.AsPSObject(profilePaths.CurrentUserCurrentHost); + PSCommand psCommand = new PSCommand() .AddProfileLoadIfExists(profileVariable, nameof(profilePaths.AllUsersAllHosts), profilePaths.AllUsersAllHosts) .AddProfileLoadIfExists(profileVariable, nameof(profilePaths.AllUsersCurrentHost), profilePaths.AllUsersCurrentHost) .AddProfileLoadIfExists(profileVariable, nameof(profilePaths.CurrentUserAllHosts), profilePaths.CurrentUserAllHosts) .AddProfileLoadIfExists(profileVariable, nameof(profilePaths.CurrentUserCurrentHost), profilePaths.CurrentUserCurrentHost); - // NOTE: This must be set before the profiles are loaded. - pwsh.Runspace.SessionStateProxy.SetVariable("PROFILE", profileVariable); - // NOTE: Because it's possible there are no profiles defined, we might have an empty // command. Since this is being executed directly, we can't rely on `ThrowOnError = // false` to avoid an exception here. Instead, we must just not execute it. @@ -253,7 +262,7 @@ private static StringBuilder AddErrorString(this StringBuilder sb, ErrorRecord e .AppendLine("Exception:") .Append(" ").Append(error.Exception.ToString() ?? ""); - Exception innerException = error.Exception?.InnerException; + Exception? innerException = error.Exception?.InnerException; while (innerException != null) { sb.AppendLine("InnerException:") diff --git a/src/PowerShellEditorServices/Services/Template/Handlers/ITemplateHandlers.cs b/src/PowerShellEditorServices/Services/Template/Handlers/ITemplateHandlers.cs deleted file mode 100644 index 88ae40789..000000000 --- a/src/PowerShellEditorServices/Services/Template/Handlers/ITemplateHandlers.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using MediatR; -using OmniSharp.Extensions.JsonRpc; - -namespace Microsoft.PowerShell.EditorServices.Services.Template -{ - [Serial] - [Method("powerShell/getProjectTemplates", Direction.ClientToServer)] - internal interface IGetProjectTemplatesHandler : IJsonRpcRequestHandler { } - - [Serial] - [Method("powerShell/newProjectFromTemplate", Direction.ClientToServer)] - internal interface INewProjectFromTemplateHandler : IJsonRpcRequestHandler { } - - internal class GetProjectTemplatesRequest : IRequest - { - public bool IncludeInstalledModules { get; set; } - } - - internal class GetProjectTemplatesResponse - { - public bool NeedsModuleInstall { get; set; } - - public TemplateDetails[] Templates { get; set; } - } - - /// - /// Provides details about a file or project template. - /// - internal class TemplateDetails - { - /// - /// Gets or sets the title of the template. - /// - public string Title { get; set; } - - /// - /// Gets or sets the author of the template. - /// - public string Author { get; set; } - - /// - /// Gets or sets the version of the template. - /// - public string Version { get; set; } - - /// - /// Gets or sets the description of the template. - /// - public string Description { get; set; } - - /// - /// Gets or sets the template's comma-delimited string of tags. - /// - public string Tags { get; set; } - - /// - /// Gets or sets the template's folder path. - /// - public string TemplatePath { get; set; } - } - - internal class NewProjectFromTemplateRequest : IRequest - { - public string DestinationPath { get; set; } - - public string TemplatePath { get; set; } - } - - internal class NewProjectFromTemplateResponse - { - public bool CreationSuccessful { get; set; } - } -} diff --git a/src/PowerShellEditorServices/Services/Template/Handlers/TemplateHandlers.cs b/src/PowerShellEditorServices/Services/Template/Handlers/TemplateHandlers.cs deleted file mode 100644 index 685fda762..000000000 --- a/src/PowerShellEditorServices/Services/Template/Handlers/TemplateHandlers.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.PowerShell.EditorServices.Logging; - -namespace Microsoft.PowerShell.EditorServices.Services.Template -{ - internal class TemplateHandlers : IGetProjectTemplatesHandler, INewProjectFromTemplateHandler - { - private readonly ILogger _logger; - private readonly TemplateService _templateService; - - public TemplateHandlers( - ILoggerFactory factory, - TemplateService templateService) - { - _logger = factory.CreateLogger(); - _templateService = templateService; - } - - public async Task Handle(GetProjectTemplatesRequest request, CancellationToken cancellationToken) - { - bool plasterInstalled = await _templateService.ImportPlasterIfInstalledAsync().ConfigureAwait(false); - - if (plasterInstalled) - { - TemplateDetails[] availableTemplates = - await _templateService.GetAvailableTemplatesAsync( - request.IncludeInstalledModules).ConfigureAwait(false); - - return new GetProjectTemplatesResponse - { - Templates = availableTemplates - }; - } - - return new GetProjectTemplatesResponse - { - NeedsModuleInstall = true, - Templates = Array.Empty() - }; - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Want to log any exception")] - public async Task Handle(NewProjectFromTemplateRequest request, CancellationToken cancellationToken) - { - bool creationSuccessful; - try - { - await _templateService.CreateFromTemplateAsync(request.TemplatePath, request.DestinationPath).ConfigureAwait(false); - creationSuccessful = true; - } - catch (Exception e) - { - // We don't really care if this worked or not but we report status. - _logger.LogException("New plaster template failed.", e); - creationSuccessful = false; - } - - return new NewProjectFromTemplateResponse - { - CreationSuccessful = creationSuccessful - }; - } - } -} diff --git a/src/PowerShellEditorServices/Services/Template/TemplateService.cs b/src/PowerShellEditorServices/Services/Template/TemplateService.cs deleted file mode 100644 index e7295ec8c..000000000 --- a/src/PowerShellEditorServices/Services/Template/TemplateService.cs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Microsoft.Extensions.Logging; -using Microsoft.PowerShell.EditorServices.Services.PowerShell; -using Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Management.Automation; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.PowerShell.EditorServices.Services.Template -{ - /// - /// Provides a service for listing PowerShell project templates and creating - /// new projects from those templates. This service leverages the Plaster - /// module for creating projects from templates. - /// - internal class TemplateService - { - #region Private Fields - - private readonly ILogger _logger; - private bool isPlasterLoaded; - private bool? isPlasterInstalled; - private readonly IInternalPowerShellExecutionService _executionService; - - #endregion - - #region Constructors - - /// - /// Creates a new instance of the TemplateService class. - /// - /// The PowerShellContext to use for this service. - /// An ILoggerFactory implementation used for writing log messages. - public TemplateService(IInternalPowerShellExecutionService executionService, ILoggerFactory factory) - { - _logger = factory.CreateLogger(); - _executionService = executionService; - } - - #endregion - - #region Public Methods - - /// - /// Checks if Plaster is installed on the user's machine. - /// - /// A Task that can be awaited until the check is complete. The result will be true if Plaster is installed. - public async Task ImportPlasterIfInstalledAsync() - { - if (!isPlasterInstalled.HasValue) - { - PSCommand psCommand = new(); - - psCommand - .AddCommand("Get-Module") - .AddParameter("ListAvailable") - .AddParameter("Name", "Plaster"); - - psCommand - .AddCommand("Sort-Object") - .AddParameter("Descending") - .AddParameter("Property", "Version"); - - psCommand - .AddCommand("Select-Object") - .AddParameter("First", 1); - - _logger.LogTrace("Checking if Plaster is installed..."); - - IReadOnlyList moduleObject = - await _executionService.ExecutePSCommandAsync( - psCommand, - CancellationToken.None, - new PowerShellExecutionOptions { ThrowOnError = false }) - .ConfigureAwait(false); - - isPlasterInstalled = moduleObject.Count > 0; - _logger.LogTrace("Plaster installed: " + isPlasterInstalled.Value); - - // Attempt to load plaster - if (isPlasterInstalled.Value && !isPlasterLoaded) - { - _logger.LogTrace("Loading Plaster..."); - - psCommand = new PSCommand(); - psCommand - .AddCommand(@"Microsoft.PowerShell.Core\Import-Module") - .AddParameter("ModuleInfo", (PSModuleInfo)moduleObject[0].ImmediateBaseObject) - .AddParameter("PassThru") - .AddParameter("ErrorAction", ActionPreference.Ignore); - - IReadOnlyList plasterModule = - await _executionService.ExecutePSCommandAsync( - psCommand, - CancellationToken.None, - new PowerShellExecutionOptions { ThrowOnError = false }) - .ConfigureAwait(false); - - isPlasterLoaded = plasterModule.Count > 0; - _logger.LogTrace("Plaster loaded: " + isPlasterLoaded); - } - } - - return isPlasterInstalled.Value; - } - - /// - /// Gets the available file or project templates on the user's - /// machine. - /// - /// - /// If true, searches the user's installed PowerShell modules for - /// included templates. - /// - /// A Task which can be awaited for the TemplateDetails list to be returned. - public async Task GetAvailableTemplatesAsync( - bool includeInstalledModules) - { - if (!isPlasterLoaded) - { - throw new InvalidOperationException("Plaster is not loaded, templates cannot be accessed."); - } - - PSCommand psCommand = new(); - psCommand.AddCommand("Get-PlasterTemplate"); - - if (includeInstalledModules) - { - psCommand.AddParameter("IncludeModules"); - } - - IReadOnlyList templateObjects = await _executionService.ExecutePSCommandAsync( - psCommand, - CancellationToken.None).ConfigureAwait(false); - - _logger.LogTrace($"Found {templateObjects.Count} Plaster templates"); - - return - templateObjects - .Select(CreateTemplateDetails) - .ToArray(); - } - - /// - /// Creates a new file or project from a specified template and - /// places it in the destination path. This ultimately calls - /// Invoke-Plaster in PowerShell. - /// - /// The folder path containing the template. - /// The folder path where the files will be created. - /// A boolean-returning Task which communicates success or failure. - public async Task CreateFromTemplateAsync( - string templatePath, - string destinationPath) - { - _logger.LogTrace( - $"Invoking Plaster...\n\n TemplatePath: {templatePath}\n DestinationPath: {destinationPath}"); - - PSCommand command = new PSCommand() - .AddCommand("Invoke-Plaster") - .AddParameter("TemplatePath", templatePath) - .AddParameter("DestinationPath", destinationPath); - - // This command is interactive so it requires the foreground. - await _executionService.ExecutePSCommandAsync( - command, - CancellationToken.None, - new PowerShellExecutionOptions - { - RequiresForeground = true, - WriteOutputToHost = true, - ThrowOnError = false - }).ConfigureAwait(false); - - // If any errors were written out, creation was not successful - return true; - } - - #endregion - - #region Private Methods - - private static TemplateDetails CreateTemplateDetails(PSObject psObject) - { - return new TemplateDetails - { - Title = psObject.Members["Title"].Value as string, - Author = psObject.Members["Author"].Value as string, - Version = psObject.Members["Version"].Value.ToString(), - Description = psObject.Members["Description"].Value as string, - TemplatePath = psObject.Members["TemplatePath"].Value as string, - Tags = - psObject.Members["Tags"].Value is object[] tags - ? string.Join(", ", tags) - : string.Empty - }; - } - - #endregion - } -} diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs index 29e36ce25..8dbb8f798 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs @@ -327,15 +327,15 @@ CompletionResultType.ProviderItem or CompletionResultType.ProviderContainer Data = item.Detail, Detail = SupportsMarkdown ? null : item.Detail, }, - CompletionResultType.ParameterName => TryExtractType(detail, out string type) - ? item with { Kind = CompletionItemKind.Variable, Detail = type } + CompletionResultType.ParameterName => TryExtractType(detail, item.Label, out string type, out string documentation) + ? item with { Kind = CompletionItemKind.Variable, Detail = type, Documentation = documentation } // The comparison operators (-eq, -not, -gt, etc) unfortunately come across as // ParameterName types but they don't have a type associated to them, so we can // deduce it is an operator. : item with { Kind = CompletionItemKind.Operator }, CompletionResultType.ParameterValue => item with { Kind = CompletionItemKind.Value }, - CompletionResultType.Variable => TryExtractType(detail, out string type) - ? item with { Kind = CompletionItemKind.Variable, Detail = type } + CompletionResultType.Variable => TryExtractType(detail, "$" + item.Label, out string type, out string documentation) + ? item with { Kind = CompletionItemKind.Variable, Detail = type, Documentation = documentation } : item with { Kind = CompletionItemKind.Variable }, CompletionResultType.Namespace => item with { Kind = CompletionItemKind.Module }, CompletionResultType.Type => detail.StartsWith("Class ", StringComparison.CurrentCulture) @@ -366,7 +366,7 @@ private CompletionItem CreateProviderItemCompletion( if (textToBeReplaced.IndexOf(PSScriptRootVariable, StringComparison.OrdinalIgnoreCase) is int variableIndex and not -1 && System.IO.Path.GetDirectoryName(scriptFile.FilePath) is string scriptFolder and not "" && completionText.IndexOf(scriptFolder, StringComparison.OrdinalIgnoreCase) is int pathIndex and not -1 - && !scriptFile.IsInMemory) + && !scriptFile.IsUntitled) { completionText = completionText .Remove(pathIndex, scriptFolder.Length) @@ -450,16 +450,45 @@ private static string GetTypeFilterText(string textToBeReplaced, string completi /// type names in [] to be consistent with PowerShell syntax and how the debugger displays /// type names. /// - /// - /// + /// The tooltip text to parse + /// The extracted type string, if found + /// The remaining text after the type, if any + /// The label to check for in the documentation prefix /// Whether or not the type was found. - private static bool TryExtractType(string toolTipText, out string type) + internal static bool TryExtractType(string toolTipText, string label, out string type, out string documentation) { MatchCollection matches = s_typeRegex.Matches(toolTipText); type = string.Empty; + documentation = null; //We use null instead of String.Empty to indicate no documentation was found. + if ((matches.Count > 0) && (matches[0].Groups.Count > 1)) { type = matches[0].Groups[1].Value; + + // Extract the description as everything after the type + if (matches[0].Length < toolTipText.Length) + { + documentation = toolTipText.Substring(matches[0].Length).Trim(); + + if (documentation is not null) + { + // If the substring is the same as the label, documentation should remain blank + if (documentation.Equals(label, StringComparison.OrdinalIgnoreCase)) + { + documentation = null; + } + // If the documentation starts with "label - ", remove this prefix + else if (documentation.StartsWith(label + " - ", StringComparison.OrdinalIgnoreCase)) + { + documentation = documentation.Substring((label + " - ").Length).Trim(); + } + } + if (string.IsNullOrWhiteSpace(documentation)) + { + documentation = null; + } + } + return true; } return false; diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/DidChangeWatchedFilesHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/DidChangeWatchedFilesHandler.cs index edbe9b0ad..aa4c0a969 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/DidChangeWatchedFilesHandler.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/DidChangeWatchedFilesHandler.cs @@ -41,6 +41,7 @@ public DidChangeWatchedFilesHandler( public DidChangeWatchedFilesRegistrationOptions GetRegistrationOptions( DidChangeWatchedFilesCapability capability, ClientCapabilities clientCapabilities) +#pragma warning disable CS8601 // Possible null reference assignment (it's from the library). => new() { Watchers = new[] @@ -52,6 +53,7 @@ public DidChangeWatchedFilesRegistrationOptions GetRegistrationOptions( }, }, }; +#pragma warning restore CS8601 // Possible null reference assignment. public Task Handle(DidChangeWatchedFilesParams request, CancellationToken cancellationToken) { diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs index 64ccb3156..bf5f99d0f 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs @@ -90,7 +90,7 @@ public override async Task Handle(DocumentFormattingParams re return s_emptyTextEditContainer; } - return new TextEditContainer(new TextEdit + return new TextEditContainer(new OmniSharp.Extensions.LanguageServer.Protocol.Models.TextEdit { NewText = formattedScript, Range = editRange @@ -184,7 +184,7 @@ public override async Task Handle(DocumentRangeFormattingPara return s_emptyTextEditContainer; } - return new TextEditContainer(new TextEdit + return new TextEditContainer(new OmniSharp.Extensions.LanguageServer.Protocol.Models.TextEdit { NewText = formattedScript, Range = editRange diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/PsesSemanticTokensHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/PsesSemanticTokensHandler.cs index e5e69e60f..494843bb5 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/PsesSemanticTokensHandler.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/PsesSemanticTokensHandler.cs @@ -108,6 +108,11 @@ private static SemanticTokenType MapSemanticTokenType(Token token) return SemanticTokenType.Operator; } + if ((token.TokenFlags & TokenFlags.AttributeName) != 0) + { + return SemanticTokenType.Decorator; + } + if ((token.TokenFlags & TokenFlags.TypeName) != 0) { return SemanticTokenType.Type; @@ -142,8 +147,8 @@ private static SemanticTokenType MapSemanticTokenType(Token token) case TokenKind.Number: return SemanticTokenType.Number; - case TokenKind.Generic: - return SemanticTokenType.Function; + case TokenKind.Label: + return SemanticTokenType.Label; } return null; diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/RenameHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/RenameHandler.cs new file mode 100644 index 000000000..83e2b5ea1 --- /dev/null +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/RenameHandler.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Services; + +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; + +namespace Microsoft.PowerShell.EditorServices.Handlers; + +/// +/// A handler for textDocument/prepareRename +/// +internal class PrepareRenameHandler +( + RenameService renameService +) : IPrepareRenameHandler +{ + public RenameRegistrationOptions GetRegistrationOptions(RenameCapability capability, ClientCapabilities clientCapabilities) => capability.PrepareSupport ? new() { PrepareProvider = true } : new(); + + public async Task Handle(PrepareRenameParams request, CancellationToken cancellationToken) + => await renameService.PrepareRenameSymbol(request, cancellationToken).ConfigureAwait(false); +} + +/// +/// A handler for textDocument/rename +/// +internal class RenameHandler( + RenameService renameService +) : IRenameHandler +{ + // RenameOptions may only be specified if the client states that it supports prepareSupport in its initial initialize request. + public RenameRegistrationOptions GetRegistrationOptions(RenameCapability capability, ClientCapabilities clientCapabilities) => capability.PrepareSupport ? new() { PrepareProvider = true } : new(); + + public async Task Handle(RenameParams request, CancellationToken cancellationToken) + => await renameService.RenameSymbol(request, cancellationToken).ConfigureAwait(false); +} diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/TextDocumentHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/TextDocumentHandler.cs index a02e7b884..ba2e9242b 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/TextDocumentHandler.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/TextDocumentHandler.cs @@ -111,10 +111,11 @@ public override Task Handle(DidCloseTextDocumentParams notification, Cance { fileToClose.IsOpen = false; - // If the file watcher is supported, only close in-memory files when this + // If the file watcher is supported, only close non-file-backed documents when this // notification is triggered. This lets us keep workspace files open so we can scan // for references. When a file is deleted, the file watcher will close the file. - if (!_isFileWatcherSupported || fileToClose.IsInMemory) + bool isBackedByFile = !fileToClose.IsUntitled; + if (!_isFileWatcherSupported || !isBackedByFile) { _workspaceService.CloseFile(fileToClose); } @@ -132,6 +133,9 @@ public override async Task Handle(DidSaveTextDocumentParams notification, if (savedFile != null) { + // On a save, untitled files will remain in memory, so this won't change for those + savedFile.IsInMemory = savedFile.IsUntitled; + if (_remoteFileManagerService.IsUnderRemoteTempPath(savedFile.FilePath)) { await _remoteFileManagerService.SaveRemoteFileAsync(savedFile.FilePath).ConfigureAwait(false); diff --git a/src/PowerShellEditorServices/Services/TextDocument/RenameService.cs b/src/PowerShellEditorServices/Services/TextDocument/RenameService.cs new file mode 100644 index 000000000..74802cf40 --- /dev/null +++ b/src/PowerShellEditorServices/Services/TextDocument/RenameService.cs @@ -0,0 +1,617 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation.Language; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Microsoft.PowerShell.EditorServices.Handlers; +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Refactoring; +using Microsoft.PowerShell.EditorServices.Services.TextDocument; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; + +namespace Microsoft.PowerShell.EditorServices.Services; + +/// +/// Used with Configuration Bind to sync the settings to what is set on the client. +/// +internal class RenameServiceOptions +{ + public bool createParameterAlias { get; set; } + public bool acceptDisclaimer { get; set; } +} + +internal interface IRenameService +{ + /// + /// Implementation of textDocument/prepareRename + /// + internal Task PrepareRenameSymbol(PrepareRenameParams prepareRenameParams, CancellationToken cancellationToken); + + /// + /// Implementation of textDocument/rename + /// + internal Task RenameSymbol(RenameParams renameParams, CancellationToken cancellationToken); +} + +/// +/// Providers service for renaming supported symbols such as functions and variables. +/// +internal class RenameService( + WorkspaceService workspaceService, + ILanguageServerFacade lsp, + ILanguageServerConfiguration config +) : IRenameService +{ + internal bool DisclaimerAcceptedForSession; //This is exposed to allow testing non-interactively + private bool DisclaimerDeclinedForSession; + private const string ConfigSection = "powershell.rename"; + + public async Task PrepareRenameSymbol(PrepareRenameParams request, CancellationToken cancellationToken) + { + RenameParams renameRequest = new() + { + NewName = "PREPARE_RENAME_UNUSED", //A placeholder just to gather edits + Position = request.Position, + TextDocument = request.TextDocument + }; + + // TODO: As a performance optimization, should we cache these results and just fetch them on the actual rename, and move the bulk to an implementation method? Seems pretty fast right now but may slow down on large documents. Need to add a large document test example. + WorkspaceEdit? renameResponse = await RenameSymbol(renameRequest, cancellationToken).ConfigureAwait(false); + + // Since LSP 3.16 we can simply basically return a DefaultBehavior true or null to signal to the client that the position is valid for rename and it should use its default selection criteria (which is probably the language semantic highlighting or grammar). For the current scope of the rename provider, this should be fine, but we have the option to supply the specific range in the future for special cases. + RangeOrPlaceholderRange renameSupported = new(new RenameDefaultBehavior() { DefaultBehavior = true }); + + return (renameResponse?.Changes?[request.TextDocument.Uri].ToArray().Length > 0) + ? renameSupported + : null; + } + + public async Task RenameSymbol(RenameParams request, CancellationToken cancellationToken) + { + // We want scoped settings because a workspace setting might be relevant here. + RenameServiceOptions options = await GetScopedSettings(request.TextDocument.Uri, cancellationToken).ConfigureAwait(false); + + if (!await AcceptRenameDisclaimer(options.acceptDisclaimer, cancellationToken).ConfigureAwait(false)) { return null; } + + ScriptFile scriptFile = workspaceService.GetFile(request.TextDocument.Uri); + ScriptPositionAdapter position = request.Position; + + Ast? tokenToRename = FindRenamableSymbol(scriptFile, position); + if (tokenToRename is null) { return null; } + + TextEdit[] changes = tokenToRename switch + { + FunctionDefinitionAst + or CommandAst + => RenameFunction(tokenToRename, scriptFile.ScriptAst, request), + + VariableExpressionAst + or CommandParameterAst + or StringConstantExpressionAst + => RenameVariable(tokenToRename, scriptFile.ScriptAst, request, options.createParameterAlias), + + _ => throw new InvalidOperationException("This should not happen as PrepareRename should have already checked for viability. File an issue if you see this.") + }; + + return new WorkspaceEdit + { + Changes = new Dictionary> + { + [request.TextDocument.Uri] = changes + } + }; + } + + private static TextEdit[] RenameFunction(Ast target, Ast scriptAst, RenameParams renameParams) + { + RenameFunctionVisitor visitor = new(target, renameParams.NewName); + return visitor.VisitAndGetEdits(scriptAst); + } + + private static TextEdit[] RenameVariable(Ast symbol, Ast scriptAst, RenameParams requestParams, bool createParameterAlias) + { + RenameVariableVisitor visitor = new( + symbol, requestParams.NewName, createParameterAlias: createParameterAlias + ); + return visitor.VisitAndGetEdits(scriptAst); + } + + /// + /// Finds the most specific renamable symbol at the given position + /// + /// Ast of the token or null if no renamable symbol was found + internal static Ast? FindRenamableSymbol(ScriptFile scriptFile, IScriptPosition position) + { + List renameableAstTypes = [ + // Functions + typeof(FunctionDefinitionAst), + typeof(CommandAst), + + // Variables + typeof(VariableExpressionAst), + typeof(CommandParameterAst), + typeof(StringConstantExpressionAst) + ]; + Ast? ast = scriptFile.ScriptAst.FindClosest(position, renameableAstTypes.ToArray()); + + if (ast is StringConstantExpressionAst stringAst) + { + // Only splat string parameters should be considered for evaluation. + if (stringAst.FindSplatParameterReference() is not null) { return stringAst; } + // Otherwise redo the search without stringConstant, so the most specific is a command, etc. + renameableAstTypes.Remove(typeof(StringConstantExpressionAst)); + ast = scriptFile.ScriptAst.FindClosest(position, renameableAstTypes.ToArray()); + } + + // Performance optimizations + + // Only the function name is valid for rename, not other components + if (ast is FunctionDefinitionAst funcDefAst) + { + if (!funcDefAst.GetFunctionNameExtent().Contains(position)) + { + return null; + } + } + + // Only the command name (function call) portion is renamable + if (ast is CommandAst command) + { + if (command.CommandElements[0] is not StringConstantExpressionAst name) + { + return null; + } + + if (!new ScriptExtentAdapter(name.Extent).Contains(position)) + { + return null; + } + } + + return ast; + } + + /// + /// Prompts the user to accept the rename disclaimer. + /// + /// true if accepted, false if rejected + private async Task AcceptRenameDisclaimer(bool acceptDisclaimerOption, CancellationToken cancellationToken) + { + const string disclaimerDeclinedMessage = "PowerShell rename has been disabled for this session as the disclaimer message was declined. Please restart the extension if you wish to use rename and accept the disclaimer."; + + if (DisclaimerDeclinedForSession) { throw new HandlerErrorException(disclaimerDeclinedMessage); } + if (acceptDisclaimerOption || DisclaimerAcceptedForSession) { return true; } + + const string renameDisclaimer = "PowerShell rename functionality is only supported in a limited set of circumstances. [Please review the notice](https://aka.ms/powershell-rename-disclaimer) and accept the limitations and risks."; + const string acceptAnswer = "I Accept"; + const string declineAnswer = "Decline"; + + ShowMessageRequestParams reqParams = new() + { + Type = MessageType.Warning, + Message = renameDisclaimer, + Actions = new MessageActionItem[] { + new() { Title = acceptAnswer }, + new() { Title = declineAnswer } + } + }; + + MessageActionItem? result = await lsp.SendRequest(reqParams, cancellationToken).ConfigureAwait(false); + + // null happens if the user closes the dialog rather than making a selection. + if (result is null || result.Title == declineAnswer) + { + const string renameDisabledNotice = "PowerShell Rename functionality will be disabled for this session and you will not be prompted again until restart."; + + ShowMessageParams msgParams = new() + { + Message = renameDisabledNotice, + Type = MessageType.Info + }; + lsp.SendNotification(msgParams); + DisclaimerDeclinedForSession = true; + throw new HandlerErrorException(disclaimerDeclinedMessage); + } + if (result.Title == acceptAnswer) + { + // Unfortunately the LSP spec has no spec for the server to change a client setting, so we have a suboptimal experience to tell the user to change the setting rather than doing it for them without implementing custom client behavior. + const string acceptDisclaimerNotice = "PowerShell rename functionality has been enabled for this session. To avoid this prompt in the future, set the powershell.rename.acceptDisclaimer to true in your settings."; + ShowMessageParams msgParams = new() + { + Message = acceptDisclaimerNotice, + Type = MessageType.Info + }; + lsp.SendNotification(msgParams); + + DisclaimerAcceptedForSession = true; + return DisclaimerAcceptedForSession; + } + + throw new InvalidOperationException("Unknown Disclaimer Response received. This is a bug and you should report it."); + } + + private async Task GetScopedSettings(DocumentUri uri, CancellationToken cancellationToken = default) + { + IScopedConfiguration scopedConfig = await config.GetScopedConfiguration(uri, cancellationToken).ConfigureAwait(false); + return scopedConfig.GetSection(ConfigSection).Get() ?? new RenameServiceOptions(); + } +} + +internal abstract class RenameVisitorBase : AstVisitor +{ + internal List Edits { get; } = new(); + internal Ast? CurrentDocument { get; set; } + + /// + /// A convenience method to get text edits from a specified AST. + /// + internal virtual TextEdit[] VisitAndGetEdits(Ast ast) + { + ast.Visit(this); + return Edits.ToArray(); + } +} + +/// +/// A visitor that generates a list of TextEdits to a TextDocument to rename a PowerShell function +/// You should use a new instance for each rename operation. +/// Skipverify can be used as a performance optimization when you are sure you are in scope. +/// +internal class RenameFunctionVisitor(Ast target, string newName, bool skipVerify = false) : RenameVisitorBase +{ + private FunctionDefinitionAst? FunctionToRename; + + // Wire up our visitor to the relevant AST types we are potentially renaming + public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst ast) => Visit(ast); + public override AstVisitAction VisitCommand(CommandAst ast) => Visit(ast); + + internal AstVisitAction Visit(Ast ast) + { + // If this is our first run, we need to verify we are in scope and gather our rename operation info + if (!skipVerify && CurrentDocument is null) + { + CurrentDocument = ast.GetHighestParent(); + if (CurrentDocument.Find(ast => ast == target, true) is null) + { + throw new TargetSymbolNotFoundException("The target this visitor would rename is not present in the AST. This is a bug and you should file an issue"); + } + + FunctionToRename = target switch + { + FunctionDefinitionAst f => f, + CommandAst command => CurrentDocument.FindFunctionDefinition(command) + ?? throw new HandlerErrorException("The command to rename does not have a function definition. Renaming a function is only supported when the function is defined within an accessible scope"), + _ => throw new Exception($"Unsupported AST type {target.GetType()} encountered") + }; + }; + + if (CurrentDocument != ast.GetHighestParent()) + { + throw new TargetSymbolNotFoundException("The visitor should not be reused to rename a different document. It should be created new for each rename operation. This is a bug and you should file an issue"); + } + + if (ShouldRename(ast)) + { + Edits.Add(GetRenameFunctionEdit(ast)); + } + return AstVisitAction.Continue; + + // TODO: Is there a way we can know we are fully outside where the function might be referenced, and if so, call a AstVisitAction Abort as a perf optimization? + } + + internal bool ShouldRename(Ast candidate) + { + // Rename our original function definition. There may be duplicate definitions of the same name + if (candidate is FunctionDefinitionAst funcDef) + { + return funcDef == FunctionToRename; + } + + // Should only be CommandAst (function calls) from this point forward in the visit. + if (candidate is not CommandAst command) + { + throw new InvalidOperationException($"ShouldRename for a function had an Unexpected Ast Type {candidate.GetType()}. This is a bug and you should file an issue."); + } + + if (CurrentDocument is null) + { + throw new InvalidOperationException("CurrentDoc should always be set by now from first Visit. This is a bug and you should file an issue."); + } + + // Match up the command to its function definition + return CurrentDocument.FindFunctionDefinition(command) == FunctionToRename; + } + + private TextEdit GetRenameFunctionEdit(Ast candidate) + { + if (candidate is FunctionDefinitionAst funcDef) + { + if (funcDef != FunctionToRename) + { + throw new InvalidOperationException("GetRenameFunctionEdit was called on an Ast that was not the target. This is a bug and you should file an issue."); + } + + if (!IsValidFunctionName(newName)) + { + throw new HandlerErrorException($"{newName} is not a valid function name."); + } + + ScriptExtentAdapter functionNameExtent = funcDef.GetFunctionNameExtent(); + + return new TextEdit() + { + NewText = newName, + Range = functionNameExtent + }; + } + + // Should be CommandAst past this point. + if (candidate is not CommandAst command) + { + throw new InvalidOperationException($"Expected a command but got {candidate.GetType()}"); + } + + if (command.CommandElements[0] is not StringConstantExpressionAst funcName) + { + throw new InvalidOperationException("Command element should always have a string expression as its first item. This is a bug and you should report it."); + } + + return new TextEdit() + { + NewText = newName, + Range = new ScriptExtentAdapter(funcName.Extent) + }; + } + + internal static bool IsValidFunctionName(string name) + { + // Allows us to supply function:varname or varname and get a proper result + string candidate = "function " + name.TrimStart('$').TrimStart('-') + " {}"; + Ast ast = Parser.ParseInput(candidate, out _, out ParseError[] errors); + if (errors.Length > 0) + { + return false; + } + + return (ast.Find(a => a is FunctionDefinitionAst, false) as FunctionDefinitionAst)? + .Name is not null; + } +} + +internal class RenameVariableVisitor(Ast target, string newName, bool skipVerify = false, bool createParameterAlias = false) : RenameVisitorBase +{ + // Used to store the original definition of the variable to use as a reference. + internal Ast? VariableDefinition; + + // Validate and cleanup the newName definition. User may have left off the $ + private readonly string NewName = newName.TrimStart('$').TrimStart('-'); + + // Wire up our visitor to the relevant AST types we are potentially renaming + public override AstVisitAction VisitVariableExpression(VariableExpressionAst ast) => Visit(ast); + public override AstVisitAction VisitCommandParameter(CommandParameterAst ast) => Visit(ast); + public override AstVisitAction VisitStringConstantExpression(StringConstantExpressionAst ast) => Visit(ast); + + internal AstVisitAction Visit(Ast ast) + { + // If this is our first visit, we need to initialize and verify the scope, otherwise verify we are still on the same document. + if ((!skipVerify && CurrentDocument is null) || VariableDefinition is null) + { + CurrentDocument = ast.GetHighestParent(); + if (CurrentDocument.Find(ast => ast == target, true) is null) + { + throw new TargetSymbolNotFoundException("The target this visitor would rename is not present in the AST. This is a bug and you should file an issue"); + } + + // Get the original assignment of our variable, this makes finding rename targets easier in subsequent visits as well as allows us to short-circuit quickly. + VariableDefinition = target.GetTopVariableAssignment(); + if (VariableDefinition is null) + { + throw new HandlerErrorException("The variable element to rename does not have a definition. Renaming an element is only supported when the variable element is defined within an accessible scope"); + } + } + else if (CurrentDocument != ast.GetHighestParent()) + { + throw new TargetSymbolNotFoundException("The visitor should not be reused to rename a different document. It should be created new for each rename operation. This is a bug and you should file an issue"); + } + + if (ShouldRename(ast)) + { + if ( + createParameterAlias + && ast == VariableDefinition + && VariableDefinition is not null and VariableExpressionAst varDefAst + && varDefAst.Parent is ParameterAst paramAst + ) + { + Edits.Add(new TextEdit + { + NewText = $"[Alias('{varDefAst.VariablePath.UserPath}')]", + Range = new Range() + { + Start = new ScriptPositionAdapter(paramAst.Extent.StartScriptPosition), + End = new ScriptPositionAdapter(paramAst.Extent.StartScriptPosition) + } + }); + } + + Edits.Add(GetRenameVariableEdit(ast)); + } + + return AstVisitAction.Continue; + } + + private bool ShouldRename(Ast candidate) + { + if (VariableDefinition is null) + { + throw new InvalidOperationException("VariableDefinition should always be set by now from first Visit. This is a bug and you should file an issue."); + } + + if (candidate == VariableDefinition) { return true; } + // Performance optimization + if (VariableDefinition.IsAfter(candidate)) { return false; } + + if (candidate.GetTopVariableAssignment() == VariableDefinition) { return true; } + + return false; + } + + private TextEdit GetRenameVariableEdit(Ast ast) + { + return ast switch + { + VariableExpressionAst var => !IsValidVariableName(NewName) + ? throw new HandlerErrorException($"${NewName} is not a valid variable name.") + : new TextEdit + { + NewText = '$' + NewName, + Range = new ScriptExtentAdapter(var.Extent) + }, + StringConstantExpressionAst stringAst => !IsValidVariableName(NewName) + ? throw new HandlerErrorException($"{NewName} is not a valid variable name.") + : new TextEdit + { + NewText = NewName, + Range = new ScriptExtentAdapter(stringAst.Extent) + }, + CommandParameterAst param => !IsValidCommandParameterName(NewName) + ? throw new HandlerErrorException($"-{NewName} is not a valid command parameter name.") + : new TextEdit + { + NewText = '-' + NewName, + Range = new ScriptExtentAdapter(param.Extent) + }, + _ => throw new InvalidOperationException($"GetRenameVariableEdit was called on an Ast that was not the target. This is a bug and you should file an issue.") + }; + } + + internal static bool IsValidVariableName(string name) + { + // Allows us to supply $varname or varname and get a proper result + string candidate = '$' + name.TrimStart('$').TrimStart('-'); + Parser.ParseInput(candidate, out Token[] tokens, out _); + return tokens.Length is 2 + && tokens[0].Kind == TokenKind.Variable + && tokens[1].Kind == TokenKind.EndOfInput; + } + + internal static bool IsValidCommandParameterName(string name) + { + // Allows us to supply -varname or varname and get a proper result + string candidate = "Command -" + name.TrimStart('$').TrimStart('-'); + Parser.ParseInput(candidate, out Token[] tokens, out _); + return tokens.Length == 3 + && tokens[0].Kind == TokenKind.Command + && tokens[1].Kind == TokenKind.Parameter + && tokens[2].Kind == TokenKind.EndOfInput; + } +} + +/// +/// Represents a position in a script file that adapts and implicitly converts based on context. PowerShell script lines/columns start at 1, but LSP textdocument lines/columns start at 0. The default line/column constructor is 1-based. +/// +internal record ScriptPositionAdapter(IScriptPosition position) : IScriptPosition, IComparable, IComparable, IComparable +{ + public int Line => position.LineNumber; + public int LineNumber => position.LineNumber; + public int Column => position.ColumnNumber; + public int ColumnNumber => position.ColumnNumber; + public int Character => position.ColumnNumber; + + public string File => position.File; + string IScriptPosition.Line => position.Line; + public int Offset => position.Offset; + + public ScriptPositionAdapter(int Line, int Column) : this(new ScriptPosition(null, Line, Column, null)) { } + public ScriptPositionAdapter(ScriptPosition position) : this((IScriptPosition)position) { } + + public ScriptPositionAdapter(Position position) : this(position.Line + 1, position.Character + 1) { } + + public static implicit operator ScriptPositionAdapter(Position position) => new(position); + public static implicit operator Position(ScriptPositionAdapter scriptPosition) => new( + scriptPosition.position.LineNumber - 1, scriptPosition.position.ColumnNumber - 1 + ); + + public static implicit operator ScriptPositionAdapter(ScriptPosition position) => new(position); + public static implicit operator ScriptPosition(ScriptPositionAdapter position) => + position.position is ScriptPosition scriptPosition + ? scriptPosition + : new ScriptPosition( + position.position.File, + position.position.LineNumber, + position.position.ColumnNumber, + position.position.Line + ); + + internal ScriptPositionAdapter Delta(int LineAdjust, int ColumnAdjust) => new( + position.LineNumber + LineAdjust, + position.ColumnNumber + ColumnAdjust + ); + + public int CompareTo(ScriptPositionAdapter other) + { + if (position.LineNumber == other.position.LineNumber) + { + return position.ColumnNumber.CompareTo(other.position.ColumnNumber); + } + return position.LineNumber.CompareTo(other.position.LineNumber); + } + public int CompareTo(Position other) => CompareTo((ScriptPositionAdapter)other); + public int CompareTo(ScriptPosition other) => CompareTo((ScriptPositionAdapter)other); + + // Required for interface implementation but not used. + public string GetFullScript() => throw new NotImplementedException(); +} + +/// +/// Represents a range in a script file that adapts and implicitly converts based on context. PowerShell script lines/columns start at 1, but LSP textdocument lines/columns start at 0. The default ScriptExtent constructor is 1-based +/// +/// +internal record ScriptExtentAdapter(IScriptExtent extent) : IScriptExtent +{ + internal ScriptPositionAdapter Start = new(extent.StartScriptPosition); + internal ScriptPositionAdapter End = new(extent.EndScriptPosition); + + public static implicit operator ScriptExtentAdapter(ScriptExtent extent) => new(extent); + + public static implicit operator ScriptExtentAdapter(Range range) => new(new ScriptExtent( + // Will get shifted to 1-based + new ScriptPositionAdapter(range.Start), + new ScriptPositionAdapter(range.End) + )); + public static implicit operator Range(ScriptExtentAdapter adapter) => new() + { + // Will get shifted to 0-based + Start = adapter.Start, + End = adapter.End + }; + + public static implicit operator ScriptExtent(ScriptExtentAdapter adapter) => + adapter.extent is ScriptExtent scriptExtent + ? scriptExtent + : new ScriptExtent(adapter.Start, adapter.End); + + public static implicit operator RangeOrPlaceholderRange(ScriptExtentAdapter adapter) => new((Range)adapter) + { + DefaultBehavior = new() { DefaultBehavior = false } + }; + + public IScriptPosition StartScriptPosition => Start; + public IScriptPosition EndScriptPosition => End; + public int EndColumnNumber => End.ColumnNumber; + public int EndLineNumber => End.LineNumber; + public int StartOffset => extent.StartOffset; + public int EndOffset => extent.EndOffset; + public string File => extent.File; + public int StartColumnNumber => extent.StartColumnNumber; + public int StartLineNumber => extent.StartLineNumber; + public string Text => extent.Text; +} diff --git a/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs b/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs index fb6772d2b..9cff31d41 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs @@ -51,9 +51,15 @@ internal sealed class ScriptFile /// /// Gets a boolean that determines whether this file is - /// in-memory or not (either unsaved or non-file content). + /// in-memory or not (either unsaved or non-file content) aka "dirty" /// - public bool IsInMemory { get; } + public bool IsInMemory { get; internal set; } + + /// + /// Gets a value indicating whether the document URI is not a file:// URI + /// (for example, an untitled: URI). + /// + public bool IsUntitled => !DocumentUri.ToUri().IsFile; /// /// Gets a string containing the full contents of the file. @@ -105,6 +111,9 @@ public Token[] ScriptTokens internal ReferenceTable References { get; } + /// + /// Indicates whether the file is currently open in the editor. PSES may open files for analysis that aren't actually visible in the editor. + /// internal bool IsOpen { get; set; } #endregion @@ -127,11 +136,15 @@ internal ScriptFile( // so that other operations know it's untitled/in-memory // and don't think that it's a relative path // on the file system. - IsInMemory = !docUri.ToUri().IsFile; + DocumentUri = docUri; + + // Initial state of document. Untitled files are in memory by definition, otherwise files start non-dirty on a filesystem + IsInMemory = IsUntitled; + FilePath = IsInMemory ? docUri.ToString() : docUri.GetFileSystemPath(); - DocumentUri = docUri; + IsAnalysisEnabled = true; this.powerShellVersion = powerShellVersion; @@ -365,6 +378,9 @@ public void ApplyChange(FileChange fileChange) // Parse the script again to be up-to-date ParseFileContents(); References.TagAsChanged(); + + // Flag the script as modified + IsInMemory = true; } /// diff --git a/src/PowerShellEditorServices/Services/TextDocument/TokenOperations.cs b/src/PowerShellEditorServices/Services/TextDocument/TokenOperations.cs index 6a6f789ff..9ed65db20 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/TokenOperations.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/TokenOperations.cs @@ -25,9 +25,7 @@ internal static class TokenOperations /// /// Extracts all of the unique foldable regions in a script given the list tokens /// -#pragma warning disable CA1502 // Cyclomatic complexity we don't care about internal static FoldingReferenceList FoldableReferences(Token[] tokens) -#pragma warning restore CA1502 { FoldingReferenceList refList = new(); diff --git a/src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs b/src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs index a7f36aab1..2cfb57cd4 100644 --- a/src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs +++ b/src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs @@ -100,7 +100,6 @@ public override async Task Handle(DidChangeConfigurationParams request, Ca return await Unit.Task.ConfigureAwait(false); } - public event EventHandler ConfigurationUpdated; } } diff --git a/src/PowerShellEditorServices/Services/Workspace/LanguageServerSettings.cs b/src/PowerShellEditorServices/Services/Workspace/LanguageServerSettings.cs index e417d7b92..d310903ae 100644 --- a/src/PowerShellEditorServices/Services/Workspace/LanguageServerSettings.cs +++ b/src/PowerShellEditorServices/Services/Workspace/LanguageServerSettings.cs @@ -207,6 +207,7 @@ public CodeFormattingSettings(CodeFormattingSettings codeFormattingSettings) public bool WhitespaceInsideBrace { get; set; } public bool IgnoreOneLineBlock { get; set; } public bool AlignPropertyValuePairs { get; set; } + public bool AlignEnumMemberValues { get; set; } public bool UseCorrectCasing { get; set; } /// @@ -298,7 +299,8 @@ private Hashtable GetCustomPSSASettingsHashtable(int tabSize, bool insertSpaces) "PSAlignAssignmentStatement", new Hashtable { { "Enable", true }, - { "CheckHashtable", AlignPropertyValuePairs } + { "CheckHashtable", AlignPropertyValuePairs }, + { "CheckEnums", AlignEnumMemberValues }, } }, { diff --git a/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs b/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs index a3e46f4f1..961338023 100644 --- a/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs +++ b/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs @@ -252,6 +252,7 @@ function New-EditorFile { /// /// The IEditorOperations instance to use for opening/closing files in the editor. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "Intentionally fire and forget.")] public RemoteFileManagerService( ILoggerFactory factory, IRunspaceContext runspaceContext, @@ -275,7 +276,6 @@ public RemoteFileManagerService( // Delete existing temporary file cache path if it already exists TryDeleteTemporaryPath(); - // TODO: Do this somewhere other than the constructor and make it async // Register the psedit function in the current runspace RegisterPSEditFunctionAsync().HandleErrorsAsync(logger); } @@ -519,6 +519,8 @@ private RemotePathMappings GetPathMappings(IRunspaceInfo runspaceInfo) return remotePathMappings; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Supporting synchronous API.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "Intentionally fire and forget.")] private void HandleRunspaceChanged(object sender, RunspaceChangedEventArgs e) { if (e.ChangeAction == RunspaceChangeAction.Enter) @@ -572,6 +574,7 @@ private static bool ShouldTearDownRemoteFiles(RunspaceChangedEventArgs runspaceC StringComparison.CurrentCultureIgnoreCase); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "It has to be async.")] private async void HandlePSEventReceivedAsync(object sender, PSEventArgs args) { if (!string.Equals(RemoteSessionOpenFile, args.SourceIdentifier, StringComparison.CurrentCultureIgnoreCase)) diff --git a/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs b/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs index 941fcf736..9b721387a 100644 --- a/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs +++ b/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs @@ -302,7 +302,14 @@ public void CloseFile(ScriptFile scriptFile) Validate.IsNotNull(nameof(scriptFile), scriptFile); string keyName = GetFileKey(scriptFile.DocumentUri); - workspaceFiles.TryRemove(keyName, out ScriptFile _); + if (workspaceFiles.TryRemove(keyName, out ScriptFile _)) + { + logger.LogDebug("Closed file: " + scriptFile.DocumentUri); + } + else + { + logger.LogWarning("Tried to close file that was not open: " + scriptFile.DocumentUri); + } } /// @@ -312,7 +319,7 @@ public void CloseFile(ScriptFile scriptFile) public string GetRelativePath(ScriptFile scriptFile) { Uri fileUri = scriptFile.DocumentUri.ToUri(); - if (!scriptFile.IsInMemory) + if (!scriptFile.IsUntitled) { // Support calculating out-of-workspace relative paths in the common case of a // single workspace folder. Otherwise try to get the matching folder. @@ -335,6 +342,35 @@ public string GetRelativePath(ScriptFile scriptFile) return fileUri.ToString(); } + /// + /// Finds a file in the first workspace folder where it exists, if possible. + /// Used as a backwards-compatible way to find files in the workspace. + /// + /// + /// Best possible path. + public string FindFileInWorkspace(string filePath) + { + // If the file path is already an absolute path, just return it. + if (Path.IsPathRooted(filePath)) + { + return filePath; + } + + // If the file path is relative, try to find it in the workspace folders. + foreach (WorkspaceFolder workspaceFolder in WorkspaceFolders) + { + string folderPath = workspaceFolder.Uri.GetFileSystemPath(); + string combinedPath = Path.Combine(folderPath, filePath); + if (File.Exists(combinedPath)) + { + return combinedPath; + } + } + + // If the file path is not found in the workspace folders, return the original path. + return filePath; + } + /// /// Enumerate all the PowerShell (ps1, psm1, psd1) files in the workspace in a recursive manner, using default values. /// @@ -409,57 +445,6 @@ internal static string ReadFileContents(DocumentUri uri) return reader.ReadToEnd(); } - internal string ResolveWorkspacePath(string path) => ResolveRelativeScriptPath(InitialWorkingDirectory, path); - - internal string ResolveRelativeScriptPath(string baseFilePath, string relativePath) - { - // TODO: Sometimes the `baseFilePath` (even when its `WorkspacePath`) is null. - string combinedPath = null; - Exception resolveException = null; - - try - { - // If the path is already absolute there's no need to resolve it relatively - // to the baseFilePath. - if (Path.IsPathRooted(relativePath)) - { - return relativePath; - } - - // Get the directory of the original script file, combine it - // with the given path and then resolve the absolute file path. - combinedPath = - Path.GetFullPath( - Path.Combine( - baseFilePath, - relativePath)); - } - catch (NotSupportedException e) - { - // Occurs if the path is incorrectly formatted for any reason. One - // instance where this occurred is when a user had curly double-quote - // characters in their source instead of normal double-quotes. - resolveException = e; - } - catch (ArgumentException e) - { - // Occurs if the path contains invalid characters, specifically those - // listed in System.IO.Path.InvalidPathChars. - resolveException = e; - } - - if (resolveException != null) - { - logger.LogError( - "Could not resolve relative script path\r\n" + - $" baseFilePath = {baseFilePath}\r\n " + - $" relativePath = {relativePath}\r\n\r\n" + - $"{resolveException}"); - } - - return combinedPath; - } - /// /// Returns a normalized string for a given documentUri to be used as key name. /// Case-sensitive uri on Linux and lowercase for other platforms. diff --git a/src/PowerShellEditorServices/Utility/AstExtensions.cs b/src/PowerShellEditorServices/Utility/AstExtensions.cs new file mode 100644 index 000000000..49e39f696 --- /dev/null +++ b/src/PowerShellEditorServices/Utility/AstExtensions.cs @@ -0,0 +1,696 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation.Language; +using Microsoft.PowerShell.EditorServices.Services; + +namespace Microsoft.PowerShell.EditorServices.Language; + +// NOTE: A lot of this is reimplementation of https://github.com/PowerShell/PowerShell/blob/2d5d702273060b416aea9601e939ff63bb5679c9/src/System.Management.Automation/engine/parser/Position.cs which is internal and sealed. + +internal static class AstExtensions +{ + private const int IS_BEFORE = -1; + private const int IS_AFTER = 1; + private const int IS_EQUAL = 0; + internal static int CompareTo(this IScriptPosition position, IScriptPosition other) + { + if (position.LineNumber < other.LineNumber) + { + return IS_BEFORE; + } + else if (position.LineNumber > other.LineNumber) + { + return IS_AFTER; + } + else //Lines are equal + { + if (position.ColumnNumber < other.ColumnNumber) + { + return IS_BEFORE; + } + else if (position.ColumnNumber > other.ColumnNumber) + { + return IS_AFTER; + } + else //Columns are equal + { + return IS_EQUAL; + } + } + } + + internal static bool IsEqual(this IScriptPosition position, IScriptPosition other) + => position.CompareTo(other) == IS_EQUAL; + + internal static bool IsBefore(this IScriptPosition position, IScriptPosition other) + => position.CompareTo(other) == IS_BEFORE; + + internal static bool IsAfter(this IScriptPosition position, IScriptPosition other) + => position.CompareTo(other) == IS_AFTER; + + internal static bool Contains(this IScriptExtent extent, IScriptPosition position) + => extent.StartScriptPosition.IsEqual(position) + || extent.EndScriptPosition.IsEqual(position) + || (extent.StartScriptPosition.IsBefore(position) && extent.EndScriptPosition.IsAfter(position)); + + internal static bool Contains(this IScriptExtent extent, IScriptExtent other) + => extent.Contains(other.StartScriptPosition) && extent.Contains(other.EndScriptPosition); + + internal static bool StartsBefore(this IScriptExtent extent, IScriptExtent other) + => extent.StartScriptPosition.IsBefore(other.StartScriptPosition); + + internal static bool StartsBefore(this IScriptExtent extent, IScriptPosition other) + => extent.StartScriptPosition.IsBefore(other); + + internal static bool StartsAfter(this IScriptExtent extent, IScriptExtent other) + => extent.StartScriptPosition.IsAfter(other.StartScriptPosition); + + internal static bool StartsAfter(this IScriptExtent extent, IScriptPosition other) + => extent.StartScriptPosition.IsAfter(other); + + internal static bool IsBefore(this IScriptExtent extent, IScriptExtent other) + => !other.Contains(extent) + && !extent.Contains(other) + && extent.StartScriptPosition.IsBefore(other.StartScriptPosition); + + internal static bool IsAfter(this IScriptExtent extent, IScriptExtent other) + => !other.Contains(extent) + && !extent.Contains(other) + && extent.StartScriptPosition.IsAfter(other.StartScriptPosition); + + internal static bool Contains(this Ast ast, Ast other) + => ast.Extent.Contains(other.Extent); + + internal static bool Contains(this Ast ast, IScriptPosition position) + => ast.Extent.Contains(position); + + internal static bool Contains(this Ast ast, IScriptExtent position) + => ast.Extent.Contains(position); + + internal static bool IsBefore(this Ast ast, Ast other) + => ast.Extent.IsBefore(other.Extent); + + internal static bool IsAfter(this Ast ast, Ast other) + => ast.Extent.IsAfter(other.Extent); + + internal static bool StartsBefore(this Ast ast, Ast other) + => ast.Extent.StartsBefore(other.Extent); + + internal static bool StartsBefore(this Ast ast, IScriptExtent other) + => ast.Extent.StartsBefore(other); + + internal static bool StartsBefore(this Ast ast, IScriptPosition other) + => ast.Extent.StartsBefore(other); + + internal static bool StartsAfter(this Ast ast, Ast other) + => ast.Extent.StartsAfter(other.Extent); + + internal static bool StartsAfter(this Ast ast, IScriptExtent other) + => ast.Extent.StartsAfter(other); + + internal static bool StartsAfter(this Ast ast, IScriptPosition other) + => ast.Extent.StartsAfter(other); + + /// + /// Finds the outermost Ast that starts before the target and matches the predicate within the scope. + /// Returns null if none found. Useful for finding definitions of variable/function references. + /// + /// The target Ast to search from + /// The predicate to match the Ast against + /// If true, the search will continue until the topmost scope boundary is found. + /// Searches scriptblocks within the parent at each level. This can be helpful to find "side" scopes but affects performance + internal static Ast? FindStartsBefore(this Ast target, Func predicate, bool crossScopeBoundaries = false, bool searchNestedScriptBlocks = false) + { + Ast? scope = target.GetScopeBoundary(); + do + { + Ast? result = scope?.Find(ast => + ast.StartsBefore(target) + && predicate(ast) + , searchNestedScriptBlocks); + + if (result is not null) + { + return result; + } + + scope = scope?.GetScopeBoundary(); + } while (crossScopeBoundaries && scope is not null); + + return null; + } + + internal static T? FindStartsBefore(this Ast target, Func predicate, bool crossScopeBoundaries = false, bool searchNestedScriptBlocks = false) where T : Ast + => target.FindStartsBefore + ( + ast => ast is T type && predicate(type), crossScopeBoundaries, searchNestedScriptBlocks + ) as T; + + /// + /// Finds all AST items that start before the target and match the predicate within the scope. Items are returned in order from closest to furthest. Returns an empty list if none found. Useful for finding definitions of variable/function references + /// + internal static IEnumerable FindAllStartsBefore(this Ast target, Func predicate, bool crossScopeBoundaries = false) + { + Ast? scope = target.GetScopeBoundary(); + do + { + IEnumerable results = scope?.FindAll(ast => ast.StartsBefore(target) && predicate(ast) + , searchNestedScriptBlocks: false) ?? []; + + foreach (Ast result in results.Reverse()) + { + yield return result; + } + scope = scope?.GetScopeBoundary(); + } while (crossScopeBoundaries && scope is not null); + } + + internal static Ast? FindStartsAfter(this Ast target, Func predicate, bool searchNestedScriptBlocks = false) + => target.Parent?.Find(ast => ast.StartsAfter(target) && predicate(ast), searchNestedScriptBlocks); + + internal static IEnumerable FindAllStartsAfter(this Ast target, Func predicate, bool searchNestedScriptBlocks = false) + => target.Parent?.FindAll(ast => ast.StartsAfter(target) && predicate(ast), searchNestedScriptBlocks) ?? []; + + /// + /// Finds the most specific Ast at the given script position, or returns null if none found.
+ /// For example, if the position is on a variable expression within a function definition, + /// the variable will be returned even if the function definition is found first, unless variable definitions are not in the list of allowed types + ///
+ internal static Ast? FindClosest(this Ast ast, IScriptPosition position, Type[]? allowedTypes) + { + // Short circuit quickly if the position is not in the provided ast, no need to traverse if not + if (!ast.Contains(position)) { return null; } + + Ast? mostSpecificAst = null; + Ast? currentAst = ast; + do + { + currentAst = currentAst.Find(thisAst => + { + // Always starts with the current item, we can skip it + if (thisAst == mostSpecificAst) { return false; } + + if (allowedTypes is not null && !allowedTypes.Contains(thisAst.GetType())) { return false; } + + if (thisAst.Contains(position)) + { + mostSpecificAst = thisAst; + return true; //Restart the search within the more specific AST + } + + return false; + }, true); + } while (currentAst is not null); + + return mostSpecificAst; + } + + public static bool TryFindFunctionDefinition(this Ast ast, CommandAst command, out FunctionDefinitionAst? functionDefinition) + { + functionDefinition = ast.FindFunctionDefinition(command); + return functionDefinition is not null; + } + + public static FunctionDefinitionAst? FindFunctionDefinition(this Ast ast, CommandAst command) + { + if (!ast.Contains(command)) { return null; } // Short circuit if the command is not in the ast + + string? name = command.GetCommandName()?.ToLower(); + if (name is null) { return null; } + + // NOTE: There should only be one match most of the time, the only other cases is when a function is defined multiple times (bad practice). If there are multiple definitions, the candidate "closest" to the command, which would be the last one found, is the appropriate one + return command.FindAllStartsBefore(ast => + { + if (ast is not FunctionDefinitionAst funcDef) { return false; } + + if (!funcDef.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)) { return false; } + + // If the function is recursive (calls itself), its parent is a match unless a more specific in-scope function definition comes next (this is a "bad practice" edge case) + if (command.HasParent(funcDef)) { return true; } + + return command.HasParent(funcDef.Parent); // The command is in the same scope as the function definition + }, true).FirstOrDefault() as FunctionDefinitionAst; + } + + public static string GetUnqualifiedName(this VariableExpressionAst ast) + => ast.VariablePath.IsUnqualified + ? ast.VariablePath.ToString() + : ast.VariablePath.ToString().Split(':').Last(); + + public static Ast GetHighestParent(this Ast ast) + => ast.Parent is null ? ast : ast.Parent.GetHighestParent(); + + public static Ast GetHighestParent(this Ast ast, params Type[] type) + => FindParents(ast, type).LastOrDefault() ?? ast; + + /// + /// Gets the closest parent that matches the specified type or null if none found. + /// + public static T? FindParent(this Ast ast) where T : Ast + => ast.FindParent(typeof(T)) as T; + + /// + /// Gets the closest parent that matches the specified type or null if none found. + /// + public static Ast? FindParent(this Ast ast, params Type[] types) + => FindParents(ast, types).FirstOrDefault(); + + /// + /// Returns an enumerable of parents, in order of closest to furthest, that match the specified types. + /// + public static IEnumerable FindParents(this Ast ast, params Type[] types) + { + Ast? parent = ast.Parent; + while (parent is not null) + { + if (types.Contains(parent.GetType())) + { + yield return parent; + } + parent = parent.Parent; + } + } + + /// + /// Gets the closest scope boundary of the ast. + /// + public static Ast? GetScopeBoundary(this Ast ast) + => ast.FindParent + ( + typeof(ScriptBlockAst), + typeof(FunctionDefinitionAst), + typeof(ForEachStatementAst), + typeof(ForStatementAst) + ); + + public static VariableExpressionAst? FindClosestParameterInFunction(this Ast target, string functionName, string parameterName) + { + Ast? scope = target.GetScopeBoundary(); + while (scope is not null) + { + FunctionDefinitionAst? funcDef = scope.FindAll + ( + ast => + { + if (ast is not FunctionDefinitionAst funcDef + || !funcDef.StartsBefore(target) + || !funcDef.Name.Equals(functionName, StringComparison.CurrentCultureIgnoreCase)) + { + return false; + } + + IReadOnlyList? parameters = funcDef.Parameters ?? funcDef.Body?.ParamBlock?.Parameters; + if (parameters is null || parameters.Count == 0) + { + return false; + } + + return parameters.SingleOrDefault( + param => param.Name.GetUnqualifiedName().Equals(parameterName, StringComparison.CurrentCultureIgnoreCase) + ) is not null; + } + , false + ).LastOrDefault() as FunctionDefinitionAst; + + if (funcDef is not null) + { + IReadOnlyList? parameters = funcDef.Parameters ?? funcDef.Body?.ParamBlock?.Parameters; + if (parameters is null || parameters.Count == 0) + { + return null; + } + + return parameters + .SingleOrDefault + ( + param => param.Name.GetUnqualifiedName().Equals(parameterName, StringComparison.CurrentCultureIgnoreCase) + )?.Name; // Should not be null at this point + } + + scope = scope.GetScopeBoundary(); + } + return null; + } + + /// + /// Returns true if the Expression is part of a variable assignment + /// + public static bool IsVariableAssignment(this VariableExpressionAst var) + => var.Parent is AssignmentStatementAst or ParameterAst; + + public static bool IsOperatorAssignment(this VariableExpressionAst var) + { + if (var.Parent is AssignmentStatementAst assignast) + { + return assignast.Operator != TokenKind.Equals; + } + else + { + return true; + } + } + + /// + /// Returns true if the Ast is a potential variable reference + /// + public static bool IsPotentialVariableReference(this Ast ast) + => ast is VariableExpressionAst or CommandParameterAst or StringConstantExpressionAst; + + /// + /// Determines if a variable assignment is a scoped variable assignment, meaning that it can be considered the top assignment within the current scope. This does not include Variable assignments within the body of a scope which may or may not be the top only if one of these do not exist above it in the same scope. + /// + public static bool IsScopedVariableAssignment(this VariableExpressionAst var) + { + // foreach ($x in $y) { } + if (var.Parent is ForEachStatementAst forEachAst && forEachAst.Variable == var) + { + return true; + } + + // for ($x = 1; $x -lt 10; $x++) { } + if (var.Parent is ForStatementAst forAst && forAst.Initializer is AssignmentStatementAst assignAst && assignAst.Left == var) + { + return true; + } + + // param($x = 1) + if (var.Parent is ParameterAst paramAst && paramAst.Name == var) + { + return true; + } + + return false; + } + + /// + /// For a given string constant, determine if it is a splat, and there is at least one splat reference. If so, return the location of the splat assignment. + /// + public static VariableExpressionAst? FindSplatParameterReference(this StringConstantExpressionAst stringConstantAst) + { + if (stringConstantAst.Parent is not HashtableAst hashtableAst) { return null; } + if (hashtableAst.Parent is not CommandExpressionAst commandAst) { return null; } + if (commandAst.Parent is not AssignmentStatementAst assignmentAst) { return null; } + if (assignmentAst.Left is not VariableExpressionAst leftAssignVarAst) { return null; } + return assignmentAst.FindStartsAfter(ast => + ast is VariableExpressionAst var + && var.Splatted + && var.GetUnqualifiedName().Equals(leftAssignVarAst.GetUnqualifiedName(), StringComparison.CurrentCultureIgnoreCase) + , true) as VariableExpressionAst; + } + + /// + /// For a given splat reference, find its source splat assignment. If the reference is not a splat, an exception will be thrown. If no assignment is found, null will be returned. + /// + public static StringConstantExpressionAst? FindSplatAssignmentReference(this VariableExpressionAst varAst) + { + if (!varAst.Splatted) { throw new InvalidOperationException("The provided variable reference is not a splat and cannot be used with FindSplatVariableAssignment"); } + + return varAst.FindStartsBefore(ast => + ast is StringConstantExpressionAst stringAst + && stringAst.Value == varAst.GetUnqualifiedName() + && stringAst.FindSplatParameterReference() == varAst, + crossScopeBoundaries: true) as StringConstantExpressionAst; + } + + /// + /// Returns the function a parameter is defined in. Returns null if it is an anonymous function such as a scriptblock + /// + public static bool TryGetFunction(this ParameterAst ast, out FunctionDefinitionAst? function) + { + if (ast.Parent is FunctionDefinitionAst funcDef) { function = funcDef; return true; } + if (ast.Parent.Parent is FunctionDefinitionAst paramBlockFuncDef) { function = paramBlockFuncDef; return true; } + function = null; + return false; + } + + /// + /// Finds the highest variable expression within a variable assignment within the current scope of the provided variable reference. Returns the original object if it is the highest assignment or null if no assignment was found. It is assumed the reference is part of a larger Ast. + /// + /// A variable reference that is either a VariableExpression or a StringConstantExpression (splatting reference) + public static Ast? GetTopVariableAssignment(this Ast reference) + { + if (!reference.IsPotentialVariableReference()) + { + throw new NotSupportedException("The provided reference is not a variable reference type."); + } + + // Splats are special, we will treat them as a top variable assignment and search both above for a parameter assignment and below for a splat reference, but we don't require a command definition within the same scope for the splat. + if (reference is StringConstantExpressionAst stringConstant) + { + VariableExpressionAst? splat = stringConstant.FindSplatParameterReference(); + if (splat is null) { return null; } + // Find the function associated with the splat parameter reference + string? commandName = (splat.Parent as CommandAst)?.GetCommandName().ToLower(); + if (commandName is null) { return null; } + VariableExpressionAst? splatParamReference = splat.FindClosestParameterInFunction(commandName, stringConstant.Value); + + if (splatParamReference is not null) + { + return splatParamReference; + } + } + + // If nothing found, search parent scopes for a variable assignment until we hit the top of the document + string name = reference switch + { + VariableExpressionAst varExpression => varExpression.GetUnqualifiedName(), + CommandParameterAst param => param.ParameterName, + StringConstantExpressionAst stringConstantExpressionAst => stringConstantExpressionAst.Value, + _ => throw new NotSupportedException("The provided reference is not a variable reference type.") + }; + VariableExpressionAst? varAssignment = null; + Ast? scope = reference; + + while (scope is not null) + { + // Check if the reference is a parameter in the current scope. This saves us from having to do a nested search later on. + IEnumerable? parameters = scope switch + { + // Covers both function test() { param($x) } and function param($x) + FunctionDefinitionAst f => f.Body?.ParamBlock?.Parameters ?? f.Parameters, + ScriptBlockAst s => s.ParamBlock?.Parameters, + _ => null + }; + ParameterAst? matchParam = parameters?.SingleOrDefault( + param => param.Name.GetUnqualifiedName().Equals(name, StringComparison.CurrentCultureIgnoreCase) + ); + if (matchParam is not null) + { + return matchParam.Name; + } + + // Find any top level function definitions in the currentscope that might match the parameter + if (reference is CommandParameterAst parameterAst) + { + string? commandName = (parameterAst.Parent as CommandAst)?.GetCommandName()?.ToLower(); + + if (commandName is not null) + { + VariableExpressionAst? paramDefinition = parameterAst.FindClosestParameterInFunction(commandName, parameterAst.ParameterName); + if (paramDefinition is not null) + { + return paramDefinition; + } + } + } + + // Will find the outermost assignment within the scope that matches the reference. + varAssignment = reference switch + { + VariableExpressionAst => scope.FindStartsBefore(var => + var.GetUnqualifiedName().Equals(name, StringComparison.CurrentCultureIgnoreCase) + && ( + (var.IsVariableAssignment() && !var.IsOperatorAssignment()) + || var.IsScopedVariableAssignment() + ) + , crossScopeBoundaries: false, searchNestedScriptBlocks: false + ), + + CommandParameterAst param => scope.FindStartsBefore(var => + var.GetUnqualifiedName().Equals(name, StringComparison.CurrentCultureIgnoreCase) + && var.Parent is ParameterAst paramAst + && paramAst.TryGetFunction(out FunctionDefinitionAst? foundFunction) + && foundFunction?.Name.ToLower() + == (param.Parent as CommandAst)?.GetCommandName()?.ToLower() + && foundFunction?.Parent?.Parent == scope + ), + + _ => null + }; + + if (varAssignment is not null) + { + return varAssignment; + } + + if (reference is VariableExpressionAst varAst + && + ( + varAst.IsScopedVariableAssignment() + || (varAst.IsVariableAssignment() && !varAst.IsOperatorAssignment()) + ) + ) + { + // The current variable reference is the top level assignment because we didn't find any other assignments above it + return reference; + } + + // Get the next highest scope + scope = scope.GetScopeBoundary(); + } + + // If we make it this far we didn't find any references. + + // An operator assignment can be a definition only as long as there are no assignments above it in all scopes. + if (reference is VariableExpressionAst variableAst + && variableAst.IsVariableAssignment() + && variableAst.IsOperatorAssignment()) + { + return reference; + } + + return null; + } + + public static bool HasParent(this Ast ast, Ast parent) + { + Ast? current = ast; + while (current is not null) + { + if (current == parent) + { + return true; + } + current = current.Parent; + } + return false; + } + + /// + /// Return an extent that only contains the position of the name of the function, for Client highlighting purposes. + /// + internal static ScriptExtentAdapter GetFunctionNameExtent(this FunctionDefinitionAst ast) + { + ScriptExtentAdapter funcExtent = new(ast.Extent); + string? extentText = ast.Extent.Text; + + if (!string.IsNullOrEmpty(extentText)) + { + (int nameStartOffset, int nameEndOffset) = GetFunctionNameOffsets(extentText, ast.Name); + ScriptPosition originalStart = funcExtent.Start; + funcExtent.Start = GetPositionAtOffset(originalStart, extentText, nameStartOffset); + funcExtent.End = GetPositionAtOffset(originalStart, extentText, nameEndOffset); + return funcExtent; + } + + // Fallback for unexpected input: preserve the previous behavior as closely as possible. + string name = ast.Name; + int funcLength = "function ".Length; + funcExtent.Start = funcExtent.Start.Delta(0, funcLength); + funcExtent.End = funcExtent.Start.Delta(0, name.Length); + + return funcExtent; + } + + private static ScriptPosition GetPositionAtOffset( + ScriptPosition basePosition, + string text, + int offset) + { + int lineDelta = 0; + int columnDelta = 0; + + for (int i = 0; i < offset; i++) + { + char c = text[i]; + if (c == '\r') + { + if (i + 1 < offset && text[i + 1] == '\n') + { + i++; + } + + lineDelta++; + columnDelta = 0; + } + else if (c == '\n') + { + lineDelta++; + columnDelta = 0; + } + else + { + columnDelta++; + } + } + + return AdjustPosition(basePosition, lineDelta, columnDelta); + } + + private static ScriptPosition AdjustPosition( + ScriptPosition basePosition, + int lineDelta, + int columnDelta) + => new( + basePosition.File, + basePosition.LineNumber + lineDelta, + basePosition.ColumnNumber + columnDelta, + basePosition.Line); + + internal static (int NameStartOffset, int NameEndOffset) GetFunctionNameOffsets( + string text, + string functionName) + { + if (string.IsNullOrEmpty(text)) + { + throw new ArgumentException("Function definition text cannot be null or empty.", nameof(text)); + } + + if (string.IsNullOrEmpty(functionName)) + { + throw new ArgumentException("Function name cannot be null or empty.", nameof(functionName)); + } + + int keywordStart = 0; + while (keywordStart < text.Length && char.IsWhiteSpace(text[keywordStart])) + { + keywordStart++; + } + + if (keywordStart >= text.Length) + { + throw new InvalidOperationException("Function definition text does not contain a declaration keyword."); + } + + int keywordEnd = keywordStart; + while (keywordEnd < text.Length && !char.IsWhiteSpace(text[keywordEnd])) + { + keywordEnd++; + } + + string keyword = text.Substring(keywordStart, keywordEnd - keywordStart); + bool isKnownKeyword = keyword.Equals("function", StringComparison.OrdinalIgnoreCase) + || keyword.Equals("filter", StringComparison.OrdinalIgnoreCase) + || keyword.Equals("workflow", StringComparison.OrdinalIgnoreCase); + + if (!isKnownKeyword) + { + throw new InvalidOperationException($"Unexpected function definition keyword '{keyword}'."); + } + + int nameStartOffset = text.IndexOf(functionName, keywordEnd, StringComparison.Ordinal); + if (nameStartOffset < 0) + { + throw new InvalidOperationException($"Could not find function name '{functionName}' in function definition text."); + } + + int nameEndOffset = nameStartOffset + functionName.Length; + + return (nameStartOffset, nameEndOffset); + } +} diff --git a/src/PowerShellEditorServices/Utility/AsyncUtils.cs b/src/PowerShellEditorServices/Utility/AsyncUtils.cs index c3f146bb7..c94023a7e 100644 --- a/src/PowerShellEditorServices/Utility/AsyncUtils.cs +++ b/src/PowerShellEditorServices/Utility/AsyncUtils.cs @@ -33,6 +33,7 @@ internal static Task HandleErrorsAsync( : LogTaskErrors(task, logger, callerName, callerSourceFile, callerLineNumber); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD003:Avoid awaiting foreign Tasks", Justification = "It's a wrapper.")] private static async Task LogTaskErrors(Task task, ILogger logger, string callerName, string callerSourceFile, int callerLineNumber) { try diff --git a/src/PowerShellEditorServices/Utility/LspDebugUtils.cs b/src/PowerShellEditorServices/Utility/LspDebugUtils.cs index 115689586..36677743c 100644 --- a/src/PowerShellEditorServices/Utility/LspDebugUtils.cs +++ b/src/PowerShellEditorServices/Utility/LspDebugUtils.cs @@ -56,38 +56,6 @@ public static Breakpoint CreateBreakpoint( }; } - public static StackFrame CreateStackFrame( - StackFrameDetails stackFrame, - long id) - { - SourcePresentationHint sourcePresentationHint = - stackFrame.IsExternalCode ? SourcePresentationHint.Deemphasize : SourcePresentationHint.Normal; - - // When debugging an interactive session, the ScriptPath is which is not a valid source file. - // We need to make sure the user can't open the file associated with this stack frame. - // It will generate a VSCode error in this case. - Source source = null; - if (!stackFrame.ScriptPath.Contains("<")) - { - source = new Source - { - Path = stackFrame.ScriptPath, - PresentationHint = sourcePresentationHint - }; - } - - return new StackFrame - { - Id = id, - Name = (source != null) ? stackFrame.FunctionName : "Interactive Session", - Line = (source != null) ? stackFrame.StartLineNumber : 0, - EndLine = stackFrame.EndLineNumber, - Column = (source != null) ? stackFrame.StartColumnNumber : 0, - EndColumn = stackFrame.EndColumnNumber, - Source = source - }; - } - public static Scope CreateScope(VariableScope scope) { return new Scope diff --git a/src/PowerShellEditorServices/Utility/PSCommandExtensions.cs b/src/PowerShellEditorServices/Utility/PSCommandExtensions.cs index b16129af9..4fe298424 100644 --- a/src/PowerShellEditorServices/Utility/PSCommandExtensions.cs +++ b/src/PowerShellEditorServices/Utility/PSCommandExtensions.cs @@ -94,10 +94,12 @@ private static StringBuilder AddCommandText(this StringBuilder sb, Command comma public static string EscapeScriptFilePath(string f) => string.Concat("'", f.Replace("'", "''"), "'"); - public static PSCommand BuildDotSourceCommandWithArguments(string command, IEnumerable arguments) + // Operator defaults to dot-source but could also be call (ampersand). + // It can't be called that because it's a reserved keyword in C#. + public static PSCommand BuildDotSourceCommandWithArguments(string command, IEnumerable arguments, string executeMode = ".") { string args = string.Join(" ", arguments ?? Array.Empty()); - string script = string.Concat(". ", command, string.IsNullOrEmpty(args) ? "" : " ", args); + string script = string.Concat(executeMode, " ", command, string.IsNullOrEmpty(args) ? "" : " ", args); // HACK: We use AddScript instead of AddArgument/AddParameter to reuse Powershell parameter binding logic. return new PSCommand().AddScript(script); } diff --git a/src/PowerShellEditorServices/packages.lock.json b/src/PowerShellEditorServices/packages.lock.json deleted file mode 100644 index a41fcb2aa..000000000 --- a/src/PowerShellEditorServices/packages.lock.json +++ /dev/null @@ -1,446 +0,0 @@ -{ - "version": 1, - "dependencies": { - ".NETStandard,Version=v2.0": { - "Microsoft.CSharp": { - "type": "Direct", - "requested": "[4.7.0, )", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0" - } - }, - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Direct", - "requested": "[0.19.9, )", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Direct", - "requested": "[0.19.9, )", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "PowerShellStandard.Library": { - "type": "Direct", - "requested": "[5.1.1, )", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "Serilog": { - "type": "Direct", - "requested": "[3.1.1, )", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "7.0.2" - } - }, - "Serilog.Extensions.Logging": { - "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Direct", - "requested": "[1.5.0, )", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Direct", - "requested": "[5.0.0, )", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.IO.Pipes.AccessControl": { - "type": "Direct", - "requested": "[5.0.0, )", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Principal": { - "type": "Direct", - "requested": "[4.3.0, )", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Direct", - "requested": "[5.0.0, )", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0", - "System.ComponentModel.Annotations": "5.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", - "dependencies": { - "System.Memory": "4.5.4" - } - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - } - } - } -} \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterClientExtensions.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterClientExtensions.cs deleted file mode 100644 index bdf785ede..000000000 --- a/test/PowerShellEditorServices.Test.E2E/DebugAdapterClientExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Threading.Tasks; -using Microsoft.PowerShell.EditorServices.Handlers; -using OmniSharp.Extensions.DebugAdapter.Client; -using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; - -namespace PowerShellEditorServices.Test.E2E -{ - public static class DebugAdapterClientExtensions - { - public static async Task LaunchScript(this DebugAdapterClient debugAdapterClient, string script, TaskCompletionSource started) - { - LaunchResponse launchResponse = await debugAdapterClient.Launch( - new PsesLaunchRequestArguments - { - NoDebug = false, - Script = script, - Cwd = "", - CreateTemporaryIntegratedConsole = false - }); - - if (launchResponse is null) - { - throw new Exception("Launch response was null."); - } - - // This will check to see if we received the Initialized event from the server. - await started.Task; - } - } -} diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs index bc1ac3435..332005d39 100644 --- a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs @@ -2,127 +2,181 @@ // Licensed under the MIT License. using System; +using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; +using System.Xml.Linq; +using Microsoft.PowerShell.EditorServices.Handlers; +using Nerdbank.Streams; using OmniSharp.Extensions.DebugAdapter.Client; +using OmniSharp.Extensions.DebugAdapter.Protocol.Client; +using OmniSharp.Extensions.DebugAdapter.Protocol.Events; using OmniSharp.Extensions.DebugAdapter.Protocol.Models; using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; +using OmniSharp.Extensions.JsonRpc.Server; using Xunit; using Xunit.Abstractions; +using DapStackFrame = OmniSharp.Extensions.DebugAdapter.Protocol.Models.StackFrame; namespace PowerShellEditorServices.Test.E2E { [Trait("Category", "DAP")] - public class DebugAdapterProtocolMessageTests : IAsyncLifetime, IDisposable + // ITestOutputHelper is injected by XUnit + // https://xunit.net/docs/capturing-output + public class DebugAdapterProtocolMessageTests(ITestOutputHelper output) : IAsyncLifetime { - private const string TestOutputFileName = "__dapTestOutputFile.txt"; + // After initialization, use this client to send messages for E2E tests and check results + private IDebugAdapterClient client; + private static readonly bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - private static readonly string s_binDir = - Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - private static readonly string s_testOutputPath = Path.Combine(s_binDir, TestOutputFileName); - private readonly ITestOutputHelper _output; - private DebugAdapterClient PsesDebugAdapterClient; - private PsesStdioProcess _psesProcess; + /// + /// Test scripts output here, where the output can be read to verify script progress against breakpointing + /// + private readonly string testScriptLogPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - public TaskCompletionSource Started { get; } = new TaskCompletionSource(); + private readonly PsesStdioLanguageServerProcessHost psesHost = new(isDebugAdapter: true); - public DebugAdapterProtocolMessageTests(ITestOutputHelper output) => _output = output; + private readonly TaskCompletionSource initializedLanguageClientTcs = new(); + /// + /// This task is useful for waiting until the client is initialized (but before Server Initialized is sent) + /// + private Task initializedLanguageClient => initializedLanguageClientTcs.Task; - public async Task InitializeAsync() - { - LoggerFactory factory = new(); - _psesProcess = new PsesStdioProcess(factory, true); - await _psesProcess.Start(); + /// + /// Is used to read the script log file to verify script progress against breakpointing. + private StreamReader scriptLogReader; + + private TaskCompletionSource nextStoppedTcs = new(); + /// + /// This task is useful for waiting until a breakpoint is hit in a test. + /// + private Task nextStopped => nextStoppedTcs.Task; + + /// + /// This task is useful for waiting until a StartDebuggingAttachRequest is received. + /// + private readonly TaskCompletionSource startDebuggingAttachRequestTcs = new(); - TaskCompletionSource initialized = new(); + /// + /// This task is useful for waiting until the debug session has terminated. + /// + private readonly TaskCompletionSource terminatedTcs = new(); - _psesProcess.ProcessExited += (sender, args) => + public async Task InitializeAsync() + { + // Cleanup testScriptLogPath if it exists due to an interrupted previous run + if (File.Exists(testScriptLogPath)) { - initialized.TrySetException(new ProcessExitedException("Initialization failed due to process failure", args.ExitCode, args.ErrorMessage)); - Started.TrySetException(new ProcessExitedException("Startup failed due to process failure", args.ExitCode, args.ErrorMessage)); - }; + File.Delete(testScriptLogPath); + } - PsesDebugAdapterClient = DebugAdapterClient.Create(options => + (StreamReader stdout, StreamWriter stdin) = await psesHost.Start(); + + // Splice the streams together and enable debug logging of all messages sent and received + DebugOutputStream psesStream = new( + FullDuplexStream.Splice(stdout.BaseStream, stdin.BaseStream) + ); + + /* + PSES follows the following DAP flow: + Receive a Initialize request + Run Initialize handler and send response back + Receive a Launch/Attach request + Run Launch/Attach handler and send response back + PSES sends the initialized event at the end of the Launch/Attach handler + + This is to spec, but the omnisharp client has a flaw where it does not complete the await until after + Server Initialized has been received, when it should in fact return once the Client Initialize (aka + capabilities) response is received. Per the DAP spec, we can send Launch/Attach before Server Initialized + and PSES relies on this behavior, but if we await the standard client initialization From method, it would + deadlock the test because it won't return until Server Initialized is received from PSES, which it won't + send until a launch is sent. + + HACK: To get around this, we abuse the OnInitialized handler to return the client "early" via the + `InitializedLanguageClient` once the Client Initialize response has been received. + see https://github.com/OmniSharp/csharp-language-server-protocol/issues/1408 + */ + Task dapClientInitializeTask = DebugAdapterClient.From(options => { options - .WithInput(_psesProcess.OutputStream) - .WithOutput(_psesProcess.InputStream) - // The OnStarted delegate gets run when we receive the _Initialized_ event from the server: - // https://microsoft.github.io/debug-adapter-protocol/specification#Events_Initialized - .OnStarted((_, _) => + .WithInput(psesStream) + .WithOutput(psesStream) + // The "early" return mentioned above + .OnInitialized((dapClient, _, _, _) => { - Started.SetResult(true); + initializedLanguageClientTcs.SetResult(dapClient); return Task.CompletedTask; }) - // The OnInitialized delegate gets run when we first receive the _Initialize_ response: - // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize - .OnInitialized((_, _, _, _) => + // This TCS is useful to wait for a breakpoint to be hit + .OnStopped((StoppedEvent e) => { - initialized.SetResult(true); - return Task.CompletedTask; - }); + TaskCompletionSource currentStoppedTcs = nextStoppedTcs; + nextStoppedTcs = new(); - options.OnUnhandledException = (exception) => - { - initialized.SetException(exception); - Started.SetException(exception); - }; + currentStoppedTcs.SetResult(e); + }) + .OnRequest("startDebugging", (StartDebuggingAttachRequestArguments request) => + { + startDebuggingAttachRequestTcs.SetResult(request); + return Task.CompletedTask; + }) + .OnTerminated((TerminatedEvent e) => + { + terminatedTcs.SetResult(e); + return Task.CompletedTask; + }) + ; }); - // PSES follows the following flow: - // Receive a Initialize request - // Run Initialize handler and send response back - // Receive a Launch/Attach request - // Run Launch/Attach handler and send response back - // PSES sends the initialized event at the end of the Launch/Attach handler - - // The way that the Omnisharp client works is that this Initialize method doesn't return until - // after OnStarted is run... which only happens when Initialized is received from the server. - // so if we would await this task, it would deadlock. - // To get around this, we run the Initialize() without await but use a `TaskCompletionSource` - // that gets completed when we receive the response to Initialize - // This tells us that we are ready to send messages to PSES... but are not stuck waiting for - // Initialized. -#pragma warning disable CS4014 - PsesDebugAdapterClient.Initialize(CancellationToken.None); -#pragma warning restore CS4014 - await initialized.Task; + // This ensures any unhandled exceptions get addressed if it fails to start before our early return completes. + // Under normal operation the initializedLanguageClient will always return first. + await Task.WhenAny( + initializedLanguageClient, + dapClientInitializeTask + ); + + client = await initializedLanguageClient; } public async Task DisposeAsync() { - await PsesDebugAdapterClient.RequestDisconnect(new DisconnectArguments + await client.RequestDisconnect(new DisconnectArguments { Restart = false, TerminateDebuggee = true }); - await _psesProcess.Stop(); - } + client?.Dispose(); + psesHost.Stop(); - public void Dispose() - { - GC.SuppressFinalize(this); - PsesDebugAdapterClient?.Dispose(); - _psesProcess?.Dispose(); + scriptLogReader?.Dispose(); //Also disposes the underlying filestream + if (File.Exists(testScriptLogPath)) + { + File.Delete(testScriptLogPath); + } } private static string NewTestFile(string script, bool isPester = false) { string fileExt = isPester ? ".Tests.ps1" : ".ps1"; - string filePath = Path.Combine(s_binDir, Path.GetRandomFileName() + fileExt); + string filePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + fileExt); File.WriteAllText(filePath, script); return filePath; } - private string GenerateScriptFromLoggingStatements(params string[] logStatements) + /// + /// Given an array of strings, generate a PowerShell script that writes each string to our test script log path + /// so it can be read back later to verify script progress against breakpointing. + /// + /// A list of statements that for which a script will be generated to write each statement to a testing log that can be read by . The strings are double quoted in Powershell, so variables such as $($PSScriptRoot) etc. can be used + /// A script string that should be written to disk and instructed by PSES to execute + /// + private string GenerateLoggingScript(params string[] logStatements) { if (logStatements.Length == 0) { @@ -130,9 +184,9 @@ private string GenerateScriptFromLoggingStatements(params string[] logStatements } // Clean up side effects from other test runs. - if (File.Exists(s_testOutputPath)) + if (File.Exists(testScriptLogPath)) { - File.Delete(s_testOutputPath); + File.Delete(testScriptLogPath); } // Have script create file first with `>` (but don't rely on overwriting). @@ -141,7 +195,7 @@ private string GenerateScriptFromLoggingStatements(params string[] logStatements .Append("Write-Output \"") .Append(logStatements[0]) .Append("\" > '") - .Append(s_testOutputPath) + .Append(testScriptLogPath) .AppendLine("'"); for (int i = 1; i < logStatements.Length; i++) @@ -151,77 +205,114 @@ private string GenerateScriptFromLoggingStatements(params string[] logStatements .Append("Write-Output \"") .Append(logStatements[i]) .Append("\" >> '") - .Append(s_testOutputPath) + .Append(testScriptLogPath) .AppendLine("'"); } - _output.WriteLine("Script is:"); - _output.WriteLine(builder.ToString()); + output.WriteLine("Script is:"); + output.WriteLine(builder.ToString()); return builder.ToString(); } - private static async Task GetLog() + /// + /// Reads the next output line from the test script log file. Useful in assertions to verify script progress against breakpointing. + /// + private async Task ReadScriptLogLineAsync() { - for (int i = 0; !File.Exists(s_testOutputPath) && i < 60; i++) + while (scriptLogReader is null) { - await Task.Delay(1000); + try + { + scriptLogReader = new StreamReader( + new FileStream( + testScriptLogPath, + FileMode.OpenOrCreate, + FileAccess.Read, // Because we use append, its OK to create the file ahead of the script + FileShare.ReadWrite + ) + ); + } + catch (IOException) //Sadly there does not appear to be a xplat way to wait for file availability, but luckily this does not appear to fire often. + { + await Task.Delay(500); + } + } + + // return valid lines only + string nextLine = string.Empty; + while (nextLine is null || nextLine.Length == 0) + { + nextLine = await scriptLogReader.ReadLineAsync(); //Might return null if at EOF because we created it above but the script hasn't written to it yet } - // Sleep one more time after the file exists so whatever is writing can finish. - await Task.Delay(1000); - return File.ReadLines(s_testOutputPath).ToArray(); + return nextLine; } [Fact] public void CanInitializeWithCorrectServerSettings() { - Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsConditionalBreakpoints); - Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsConfigurationDoneRequest); - Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsFunctionBreakpoints); - Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsHitConditionalBreakpoints); - Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsLogPoints); - Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsSetVariable); + Assert.True(client.ServerSettings.SupportsConditionalBreakpoints); + Assert.True(client.ServerSettings.SupportsConfigurationDoneRequest); + Assert.True(client.ServerSettings.SupportsFunctionBreakpoints); + Assert.True(client.ServerSettings.SupportsHitConditionalBreakpoints); + Assert.True(client.ServerSettings.SupportsLogPoints); + Assert.True(client.ServerSettings.SupportsSetVariable); + Assert.True(client.ServerSettings.SupportsDelayedStackTraceLoading); } [Fact] public async Task UsesDotSourceOperatorAndQuotesAsync() { - string filePath = NewTestFile(GenerateScriptFromLoggingStatements("$($MyInvocation.Line)")); - await PsesDebugAdapterClient.LaunchScript(filePath, Started); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()); + string filePath = NewTestFile(GenerateLoggingScript("$($MyInvocation.Line)")); + await client.LaunchScript(filePath); + ConfigurationDoneResponse configDoneResponse = await client.RequestConfigurationDone(new ConfigurationDoneArguments()); + Assert.NotNull(configDoneResponse); + + string actual = await ReadScriptLogLineAsync(); + Assert.StartsWith(". '", actual); + } + + [Fact] + public async Task UsesCallOperatorWithSettingAsync() + { + string filePath = NewTestFile(GenerateLoggingScript("$($MyInvocation.Line)")); + await client.LaunchScript(filePath, executeMode: "Call"); + ConfigurationDoneResponse configDoneResponse = await client.RequestConfigurationDone(new ConfigurationDoneArguments()); Assert.NotNull(configDoneResponse); - Assert.Collection(await GetLog(), - (i) => Assert.StartsWith(". '", i)); + + string actual = await ReadScriptLogLineAsync(); + Assert.StartsWith("& '", actual); } [Fact] public async Task CanLaunchScriptWithNoBreakpointsAsync() { - string filePath = NewTestFile(GenerateScriptFromLoggingStatements("works")); + string filePath = NewTestFile(GenerateLoggingScript("works")); - await PsesDebugAdapterClient.LaunchScript(filePath, Started); + await client.LaunchScript(filePath); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()); + ConfigurationDoneResponse configDoneResponse = await client.RequestConfigurationDone(new ConfigurationDoneArguments()); Assert.NotNull(configDoneResponse); - Assert.Collection(await GetLog(), - (i) => Assert.Equal("works", i)); + + string actual = await ReadScriptLogLineAsync(); + Assert.Equal("works", actual); } [SkippableFact] public async Task CanSetBreakpointsAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode, "Breakpoints can't be set in Constrained Language Mode."); - string filePath = NewTestFile(GenerateScriptFromLoggingStatements( + string filePath = NewTestFile(GenerateLoggingScript( "before breakpoint", "at breakpoint", "after breakpoint" )); - await PsesDebugAdapterClient.LaunchScript(filePath, Started); + await client.LaunchScript(filePath); // {"command":"setBreakpoints","arguments":{"source":{"name":"dfsdfg.ps1","path":"/Users/tyleonha/Code/PowerShell/Misc/foo/dfsdfg.ps1"},"lines":[2],"breakpoints":[{"line":2}],"sourceModified":false},"type":"request","seq":3} - SetBreakpointsResponse setBreakpointsResponse = await PsesDebugAdapterClient.SetBreakpoints(new SetBreakpointsArguments + SetBreakpointsResponse setBreakpointsResponse = await client.SetBreakpoints(new SetBreakpointsArguments { Source = new Source { Name = Path.GetFileName(filePath), Path = filePath }, Breakpoints = new SourceBreakpoint[] { new SourceBreakpoint { Line = 2 } }, @@ -233,19 +324,106 @@ public async Task CanSetBreakpointsAsync() Assert.Equal(filePath, breakpoint.Source.Path, ignoreCase: s_isWindows); Assert.Equal(2, breakpoint.Line); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()); + ConfigurationDoneResponse configDoneResponse = await client.RequestConfigurationDone(new ConfigurationDoneArguments()); Assert.NotNull(configDoneResponse); - Assert.Collection(await GetLog(), - (i) => Assert.Equal("before breakpoint", i)); - File.Delete(s_testOutputPath); - ContinueResponse continueResponse = await PsesDebugAdapterClient.RequestContinue( - new ContinueArguments { ThreadId = 1 }); + // Wait until we hit the breakpoint + StoppedEvent stoppedEvent = await nextStopped; + Assert.Equal("breakpoint", stoppedEvent.Reason); + + // The code before the breakpoint should have already run + Assert.Equal("before breakpoint", await ReadScriptLogLineAsync()); - Assert.NotNull(continueResponse); - Assert.Collection(await GetLog(), - (i) => Assert.Equal("at breakpoint", i), - (i) => Assert.Equal("after breakpoint", i)); + // Assert that the stopped breakpoint is the one we set + StackTraceResponse stackTraceResponse = await client.RequestStackTrace(new StackTraceArguments { ThreadId = 1 }); + DapStackFrame stoppedTopFrame = stackTraceResponse.StackFrames.First(); + Assert.Equal(2, stoppedTopFrame.Line); + + _ = await client.RequestContinue(new ContinueArguments { ThreadId = 1 }); + + string atBreakpointActual = await ReadScriptLogLineAsync(); + Assert.Equal("at breakpoint", atBreakpointActual); + + string afterBreakpointActual = await ReadScriptLogLineAsync(); + Assert.Equal("after breakpoint", afterBreakpointActual); + } + + [SkippableFact] + public async Task FailsIfStacktraceRequestedWhenNotPaused() + { + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode, + "Breakpoints can't be set in Constrained Language Mode."); + + // We want a long running script that never hits the next breakpoint + string filePath = NewTestFile(GenerateLoggingScript( + "$(sleep 10)", + "Should fail before we get here" + )); + + await client.SetBreakpoints( + new SetBreakpointsArguments + { + Source = new Source { Name = Path.GetFileName(filePath), Path = filePath }, + Breakpoints = new SourceBreakpoint[] { new SourceBreakpoint { Line = 1 } }, + SourceModified = false, + } + ); + + // Signal to start the script + await client.RequestConfigurationDone(new ConfigurationDoneArguments()); + await client.LaunchScript(filePath); + + // Try to get the stacktrace, which should throw as we are not currently at a breakpoint. + await Assert.ThrowsAsync(() => client.RequestStackTrace( + new StackTraceArguments { } + )); + } + + [SkippableFact] + public async Task SendsInitialLabelBreakpointForPerformanceReasons() + { + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode, + "Breakpoints can't be set in Constrained Language Mode."); + string filePath = NewTestFile(GenerateLoggingScript( + "before breakpoint", + "label breakpoint" + )); + + // Request a launch. Note that per DAP spec, launch doesn't actually begin until ConfigDone finishes. + await client.LaunchScript(filePath); + + SetBreakpointsResponse setBreakpointsResponse = await client.SetBreakpoints(new SetBreakpointsArguments + { + Source = new Source { Name = Path.GetFileName(filePath), Path = filePath }, + Breakpoints = new SourceBreakpoint[] { new SourceBreakpoint { Line = 2 } }, + SourceModified = false, + }); + + Breakpoint breakpoint = setBreakpointsResponse.Breakpoints.First(); + Assert.True(breakpoint.Verified); + Assert.Equal(filePath, breakpoint.Source.Path, ignoreCase: s_isWindows); + Assert.Equal(2, breakpoint.Line); + + _ = client.RequestConfigurationDone(new ConfigurationDoneArguments()); + + // Wait for the breakpoint to be hit + StoppedEvent stoppedEvent = await nextStopped; + Assert.Equal("breakpoint", stoppedEvent.Reason); + + // The code before the breakpoint should have already run + Assert.Equal("before breakpoint", await ReadScriptLogLineAsync()); + + // Get the stacktrace for the breakpoint + StackTraceResponse stackTraceResponse = await client.RequestStackTrace( + new StackTraceArguments { ThreadId = 1 } + ); + DapStackFrame firstFrame = stackTraceResponse.StackFrames.First(); + + // Our synthetic label breakpoint should be present + Assert.Equal( + StackFramePresentationHint.Label, + firstFrame.PresentationHint + ); } // This is a regression test for a bug where user code causes a new synchronization context @@ -262,21 +440,21 @@ public async Task CanSetBreakpointsAsync() [SkippableFact] public async Task CanStepPastSystemWindowsForms() { - Skip.IfNot(PsesStdioProcess.IsWindowsPowerShell, + Skip.IfNot(PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, "Windows Forms requires Windows PowerShell."); - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode, "Breakpoints can't be set in Constrained Language Mode."); string filePath = NewTestFile(string.Join(Environment.NewLine, new[] { - "Add-Type -AssemblyName System.Windows.Forms", - "$global:form = New-Object System.Windows.Forms.Form", - "Write-Host $form" - })); + "Add-Type -AssemblyName System.Windows.Forms", + "$global:form = New-Object System.Windows.Forms.Form", + "Write-Host $form" + })); - await PsesDebugAdapterClient.LaunchScript(filePath, Started); + await client.LaunchScript(filePath); - SetFunctionBreakpointsResponse setBreakpointsResponse = await PsesDebugAdapterClient.SetFunctionBreakpoints( + SetFunctionBreakpointsResponse setBreakpointsResponse = await client.SetFunctionBreakpoints( new SetFunctionBreakpointsArguments { Breakpoints = new FunctionBreakpoint[] @@ -286,11 +464,11 @@ public async Task CanStepPastSystemWindowsForms() Breakpoint breakpoint = setBreakpointsResponse.Breakpoints.First(); Assert.True(breakpoint.Verified); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()); + ConfigurationDoneResponse configDoneResponse = await client.RequestConfigurationDone(new ConfigurationDoneArguments()); Assert.NotNull(configDoneResponse); await Task.Delay(5000); - VariablesResponse variablesResponse = await PsesDebugAdapterClient.RequestVariables( + VariablesResponse variablesResponse = await client.RequestVariables( new VariablesArguments { VariablesReference = 1 }); Variable form = variablesResponse.Variables.FirstOrDefault(v => v.Name == "$form"); @@ -305,30 +483,31 @@ public async Task CanStepPastSystemWindowsForms() [Fact] public async Task CanLaunchScriptWithCommentedLastLineAsync() { - string script = GenerateScriptFromLoggingStatements("$($MyInvocation.Line)") + "# a comment at the end"; + string script = GenerateLoggingScript("$($MyInvocation.Line)", "$(1+1)") + "# a comment at the end"; Assert.EndsWith(Environment.NewLine + "# a comment at the end", script); // NOTE: This is horribly complicated, but the "script" parameter here is assigned to // PsesLaunchRequestArguments.Script, which is then assigned to // DebugStateService.ScriptToLaunch in that handler, and finally used by the // ConfigurationDoneHandler in LaunchScriptAsync. - await PsesDebugAdapterClient.LaunchScript(script, Started); + await client.LaunchScript(script); + + _ = await client.RequestConfigurationDone(new ConfigurationDoneArguments()); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()); - Assert.NotNull(configDoneResponse); // We can check that the script was invoked as expected, which is to dot-source a script // block with the contents surrounded by newlines. While we can't check that the last // line was a curly brace by itself, we did check that the contents ended with a // comment, so if this output exists then the bug did not recur. - Assert.Collection(await GetLog(), - (i) => Assert.Equal(". {", i), - (i) => Assert.Equal("", i)); + Assert.Equal(". {", await ReadScriptLogLineAsync()); + + // Verifies that the script did run and the body was evaluated + Assert.Equal("2", await ReadScriptLogLineAsync()); } [SkippableFact] public async Task CanRunPesterTestFile() { - Skip.If(s_isWindows, "Windows CI Pester is broken."); + Skip.If(true, "Pester test is broken."); /* TODO: Get this to work on Windows. string pesterLog = Path.Combine(s_binDir, Path.GetRandomFileName() + ".log"); @@ -345,24 +524,369 @@ public async Task CanRunPesterTestFile() await Task.Delay(1000); } await Task.Delay(15000); - _output.WriteLine(File.ReadAllText(pesterLog)); + output.WriteLine(File.ReadAllText(pesterLog)); */ string pesterTest = NewTestFile(@" - Describe 'A' { - Context 'B' { - It 'C' { - { throw 'error' } | Should -Throw - } - It 'D' { - " + GenerateScriptFromLoggingStatements("pester") + @" + Describe 'A' { + Context 'B' { + It 'C' { + { throw 'error' } | Should -Throw + } + It 'D' { + " + GenerateLoggingScript("pester") + @" + } + } + }", isPester: true); + + await client.LaunchScript($"Invoke-Pester -Script '{pesterTest}'"); + await client.RequestConfigurationDone(new ConfigurationDoneArguments()); + Assert.Equal("pester", await ReadScriptLogLineAsync()); + } + +#nullable enable + [InlineData("", null, null, 0, 0, null)] + [InlineData("-ProcessId 1234 -RunspaceId 5678", null, null, 1234, 5678, null)] + [InlineData("-ProcessId 1234 -RunspaceId 5678 -ComputerName comp", "comp", null, 1234, 5678, null)] + [InlineData("-CustomPipeName testpipe -RunspaceName rs-name", null, "testpipe", 0, 0, "rs-name")] + [SkippableTheory] + public async Task CanLaunchScriptWithNewChildAttachSession( + string paramString, + string? expectedComputerName, + string? expectedPipeName, + int expectedProcessId, + int expectedRunspaceId, + string? expectedRunspaceName) + { + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode, + "PowerShellEditorServices.Command is not signed to run FLM in Constrained Language Mode."); + + string script = NewTestFile($"Start-DebugAttachSession {paramString}"); + + using CancellationTokenSource timeoutCts = new(30000); + using CancellationTokenRegistration _ = timeoutCts.Token.Register(() => + { + startDebuggingAttachRequestTcs.TrySetCanceled(); + }); + using CancellationTokenRegistration _2 = timeoutCts.Token.Register(() => + { + terminatedTcs.TrySetCanceled(); + }); + + await client.LaunchScript(script); + await client.RequestConfigurationDone(new ConfigurationDoneArguments()); + + StartDebuggingAttachRequestArguments attachRequest = await startDebuggingAttachRequestTcs.Task; + Assert.Equal("attach", attachRequest.Request); + Assert.Equal(expectedComputerName, attachRequest.Configuration.ComputerName); + Assert.Equal(expectedPipeName, attachRequest.Configuration.CustomPipeName); + Assert.Equal(expectedProcessId, attachRequest.Configuration.ProcessId); + Assert.Equal(expectedRunspaceId, attachRequest.Configuration.RunspaceId); + Assert.Equal(expectedRunspaceName, attachRequest.Configuration.RunspaceName); + + await terminatedTcs.Task; + } + + [SkippableFact] + public async Task CanLaunchScriptWithNewChildAttachSessionAsJob() + { + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode, + "PowerShellEditorServices.Command is not signed to run FLM in Constrained Language Mode."); + Skip.If(PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, + "WinPS does not have ThreadJob, needed by -AsJob, present by default."); + + string script = NewTestFile("Start-DebugAttachSession -AsJob | Receive-Job -Wait -AutoRemoveJob"); + + using CancellationTokenSource timeoutCts = new(30000); + using CancellationTokenRegistration _1 = timeoutCts.Token.Register(() => + { + startDebuggingAttachRequestTcs.TrySetCanceled(); + }); + using CancellationTokenRegistration _2 = timeoutCts.Token.Register(() => + { + terminatedTcs.TrySetCanceled(); + }); + + await client.LaunchScript(script); + await client.RequestConfigurationDone(new ConfigurationDoneArguments()); + + StartDebuggingAttachRequestArguments attachRequest = await startDebuggingAttachRequestTcs.Task; + Assert.Equal("attach", attachRequest.Request); + Assert.Null(attachRequest.Configuration.ComputerName); + Assert.Null(attachRequest.Configuration.CustomPipeName); + Assert.Equal(0, attachRequest.Configuration.ProcessId); + Assert.Equal(0, attachRequest.Configuration.RunspaceId); + Assert.Null(attachRequest.Configuration.RunspaceName); + + await terminatedTcs.Task; + } + + [SkippableFact(Timeout = 15000)] + public async Task CanAttachScriptWithPathMappings() + { + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode, + "Breakpoints can't be set in Constrained Language Mode."); + + string[] logStatements = ["$PSCommandPath", "after breakpoint"]; + + await RunWithAttachableProcess(logStatements, async (filePath, processId, runspaceId) => + { + string localParent = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + string localScriptPath = Path.Combine(localParent, Path.GetFileName(filePath)); + Directory.CreateDirectory(localParent); + File.Copy(filePath, localScriptPath); + + Task nextStoppedTask = nextStopped; + + AttachResponse attachResponse = await client.Attach( + new PsesAttachRequestArguments + { + ProcessId = processId, + RunspaceId = runspaceId, + PathMappings = [ + new() + { + LocalRoot = localParent + Path.DirectorySeparatorChar, + RemoteRoot = Path.GetDirectoryName(filePath) + Path.DirectorySeparatorChar + } + ] + }) ?? throw new Exception("Attach response was null."); + Assert.NotNull(attachResponse); + + SetBreakpointsResponse setBreakpointsResponse = await client.SetBreakpoints(new SetBreakpointsArguments + { + Source = new Source { Name = Path.GetFileName(localScriptPath), Path = localScriptPath }, + Breakpoints = new SourceBreakpoint[] { new SourceBreakpoint { Line = 2 } }, + SourceModified = false, + }); + + Breakpoint breakpoint = setBreakpointsResponse.Breakpoints.First(); + Assert.True(breakpoint.Verified); + Assert.NotNull(breakpoint.Source); + Assert.Equal(localScriptPath, breakpoint.Source.Path, ignoreCase: s_isWindows); + Assert.Equal(2, breakpoint.Line); + + ConfigurationDoneResponse configDoneResponse = await client.RequestConfigurationDone(new ConfigurationDoneArguments()); + Assert.NotNull(configDoneResponse); + + // Wait-Debugger stop + StoppedEvent stoppedEvent = await nextStoppedTask; + Assert.Equal("step", stoppedEvent.Reason); + Assert.NotNull(stoppedEvent.ThreadId); + + nextStoppedTask = nextStopped; + + // It is important we wait for the stack trace before continue. + // The stopped event starts to get the stack trace info in the + // background and requesting the stack trace is the only way to + // ensure it is done and won't conflict with the continue request. + await client.RequestStackTrace(new StackTraceArguments { ThreadId = (int)stoppedEvent.ThreadId }); + await client.RequestContinue(new ContinueArguments { ThreadId = (int)stoppedEvent.ThreadId }); + + // Wait until we hit the breakpoint + stoppedEvent = await nextStoppedTask; + Assert.Equal("breakpoint", stoppedEvent.Reason); + Assert.NotNull(stoppedEvent.ThreadId); + + // The code before the breakpoint should have already run + // It will contain the actual script being run + string beforeBreakpointActual = await ReadScriptLogLineAsync(); + Assert.Equal(filePath, beforeBreakpointActual); + + // Assert that the stopped breakpoint is the one we set + StackTraceResponse stackTraceResponse = await client.RequestStackTrace(new StackTraceArguments { ThreadId = (int)stoppedEvent.ThreadId }); + DapStackFrame? stoppedTopFrame = stackTraceResponse.StackFrames?.First(); + + // The top frame should have a source path of our local script. + Assert.NotNull(stoppedTopFrame); + Assert.Equal(2, stoppedTopFrame.Line); + Assert.NotNull(stoppedTopFrame.Source); + Assert.Equal(localScriptPath, stoppedTopFrame.Source.Path, ignoreCase: s_isWindows); + + await client.RequestContinue(new ContinueArguments { ThreadId = 1 }); + + string afterBreakpointActual = await ReadScriptLogLineAsync(); + Assert.Equal("after breakpoint", afterBreakpointActual); + }); + } + + private async Task RunWithAttachableProcess(string[] logStatements, Func action) + { + /* + There is no public API in pwsh to wait for an attach event. We + use reflection to wait until the AvailabilityChanged event is + subscribed to by Debug-Runspace as a marker that it is ready to + continue. + + We also run the test script in another runspace as WinPS' + Debug-Runspace will break on the first statement after the + attach and we want that to be the Wait-Debugger call. + + We can use https://github.com/PowerShell/PowerShell/pull/25788 + once that is merged and we are running against that version but + WinPS will always need this. + */ + string scriptEntrypoint = @" + param([string]$TestScript) + + $debugRunspaceCmd = Get-Command Debug-Runspace -Module Microsoft.PowerShell.Utility + $runspaceBase = [PSObject].Assembly.GetType( + 'System.Management.Automation.Runspaces.RunspaceBase') + $availabilityChangedField = $runspaceBase.GetField( + 'AvailabilityChanged', + [System.Reflection.BindingFlags]'NonPublic, Instance') + if (-not $availabilityChangedField) { + throw 'Failed to get AvailabilityChanged event field' + } + + $ps = [PowerShell]::Create() + $runspace = $ps.Runspace + + # Wait-Debugger is needed in WinPS to sync breakpoints before + # running the script. + $null = $ps.AddCommand('Wait-Debugger').AddStatement() + $null = $ps.AddCommand($TestScript) + + # Let the runner know what Runspace to attach to and that it + # is ready to run. + 'RID: {0}' -f $runspace.Id + + $start = Get-Date + while ($true) { + $subscribed = $availabilityChangedField.GetValue($runspace) | + Where-Object Target -is $debugRunspaceCmd.ImplementingType + if ($subscribed) { + break + } + + if (((Get-Date) - $start).TotalSeconds -gt 10) { + throw 'Timeout waiting for Debug-Runspace to be subscribed.' + } + } + + $ps.Invoke() + foreach ($e in $ps.Streams.Error) { + Write-Error -ErrorRecord $e + } + + # Keep running until the runner has deleted the test script to + # ensure the process doesn't finish before the test does in + # normal circumstances. + while (Test-Path -LiteralPath $TestScript) { + Start-Sleep -Seconds 1 + } + "; + + string filePath = NewTestFile(GenerateLoggingScript(logStatements)); + string encArgs = CreatePwshEncodedArgs(filePath); + string encCommand = Convert.ToBase64String(Encoding.Unicode.GetBytes(scriptEntrypoint)); + + ProcessStartInfo psi = new ProcessStartInfo + { + FileName = PsesStdioLanguageServerProcessHost.PwshExe, + Arguments = $"-NoLogo -NoProfile -EncodedCommand {encCommand} -EncodedArguments {encArgs}", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + psi.EnvironmentVariables["TERM"] = "dumb"; // Avoids color/VT sequences in test output. + + TaskCompletionSource ridOutput = new(); + + // Task shouldn't take longer than 30 seconds to complete. + using CancellationTokenSource debugTaskCts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + using CancellationTokenRegistration _ = debugTaskCts.Token.Register(ridOutput.SetCanceled); + using Process? psProc = Process.Start(psi); + try + { + Assert.NotNull(psProc); + psProc.OutputDataReceived += (sender, args) => + { + if (!string.IsNullOrEmpty(args.Data)) + { + if (args.Data.StartsWith("RID: ")) + { + int rid = int.Parse(args.Data.Substring(5)); + ridOutput.SetResult(rid); } + + output.WriteLine("STDOUT: {0}", args.Data); } - }", isPester: true); + }; + psProc.ErrorDataReceived += (sender, args) => + { + if (!string.IsNullOrEmpty(args.Data)) + { + output.WriteLine("STDERR: {0}", args.Data); + } + }; + psProc.EnableRaisingEvents = true; + psProc.BeginOutputReadLine(); + psProc.BeginErrorReadLine(); + + Task procExited = psProc.WaitForExitAsync(debugTaskCts.Token); + Task waitRid = ridOutput.Task; + + // Wait for the process to fail or the script to start. + Task finishedTask = await Task.WhenAny(waitRid, procExited); + if (finishedTask == procExited) + { + await procExited; + Assert.Fail("The attached process exited before the PowerShell entrypoint could start."); + } + int rid = await waitRid; + + Task debugTask = action(filePath, psProc.Id, rid); + finishedTask = await Task.WhenAny(procExited, debugTask); + if (finishedTask == procExited) + { + await procExited; + Assert.Fail("Attached process exited before the script could start."); + } + + await debugTask; - await PsesDebugAdapterClient.LaunchScript($"Invoke-Pester -Script '{pesterTest}'", Started); - await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()); - Assert.Collection(await GetLog(), (i) => Assert.Equal("pester", i)); + File.Delete(filePath); + psProc.Kill(); + await procExited; + } + catch + { + if (psProc is not null && !psProc.HasExited) + { + psProc.Kill(); + } + + throw; + } } + + private static string CreatePwshEncodedArgs(params string[] args) + { + // Only way to pass args to -EncodedCommand is to use CLIXML with + // -EncodedArguments. Not pretty but the structure isn't too + // complex and saves us trying to embed/escape strings in a script. + string clixmlNamespace = "http://schemas.microsoft.com/powershell/2004/04"; + string clixml = new XDocument( + new XDeclaration("1.0", "utf-16", "yes"), + new XElement(XName.Get("Objs", clixmlNamespace), + new XAttribute("Version", "1.1.0.1"), + new XElement(XName.Get("Obj", clixmlNamespace), + new XAttribute("RefId", "0"), + new XElement(XName.Get("TN", clixmlNamespace), + new XAttribute("RefId", "0"), + new XElement(XName.Get("T", clixmlNamespace), "System.Collections.ArrayList"), + new XElement(XName.Get("T", clixmlNamespace), "System.Object") + ), + new XElement(XName.Get("LST", clixmlNamespace), + args.Select(s => new XElement(XName.Get("S", clixmlNamespace), s)) + ) + ))).ToString(SaveOptions.DisableFormatting); + + return Convert.ToBase64String(Encoding.Unicode.GetBytes(clixml)); + } + + private record StartDebuggingAttachRequestArguments(PsesAttachRequestArguments Configuration, string Request); } } diff --git a/test/PowerShellEditorServices.Test.E2E/Hosts/DebugOutputStream.cs b/test/PowerShellEditorServices.Test.E2E/Hosts/DebugOutputStream.cs new file mode 100644 index 000000000..70ffdbced --- /dev/null +++ b/test/PowerShellEditorServices.Test.E2E/Hosts/DebugOutputStream.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable + +using System.Diagnostics; +using System.IO; +using System.Text; +using Nerdbank.Streams; + +namespace PowerShellEditorServices.Test.E2E; + +/// +/// A stream that logs all data read and written to the debug stream which is visible in the debug console when a +/// debugger is attached. +/// +internal class DebugOutputStream : MonitoringStream +{ + public DebugOutputStream(Stream? underlyingStream) + : base(underlyingStream ?? new MemoryStream()) + { + DidRead += (_, segment) => + { + if (segment.Array is null) { return; } + LogData("⬅️", segment.Array, segment.Offset, segment.Count); + }; + + DidWrite += (_, segment) => + { + if (segment.Array is null) { return; } + LogData("➡️", segment.Array, segment.Offset, segment.Count); + }; + } + + private static void LogData(string header, byte[] buffer, int offset, int count) + { + // If debugging, the raw traffic will be visible in the debug console + if (Debugger.IsAttached) + { + string data = Encoding.UTF8.GetString(buffer, offset, count); + Debug.WriteLine($"{header} {data}"); + } + } +} diff --git a/test/PowerShellEditorServices.Test.E2E/Hosts/IAsyncLanguageServerHost.cs b/test/PowerShellEditorServices.Test.E2E/Hosts/IAsyncLanguageServerHost.cs new file mode 100644 index 000000000..11ea57ef0 --- /dev/null +++ b/test/PowerShellEditorServices.Test.E2E/Hosts/IAsyncLanguageServerHost.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace PowerShellEditorServices.Test.E2E; + +/// +/// Represents a debug adapter server host that can be started and stopped and provides streams for communication. +/// +public interface IAsyncLanguageServerHost : IAsyncDisposable +{ + // Start the host and return when the host is ready to communicate. It should return a tuple of a stream Reader and stream Writer for communication with the LSP. The underlying streams can be retrieved via baseStream propertyif needed. + Task<(StreamReader, StreamWriter)> Start(CancellationToken token = default); + // Stops the host and returns when the host has fully stopped. It should be idempotent, such that if called while the host is already stopping/stopped, it will have the same result + Task Stop(CancellationToken token = default); + + // Optional to implement if more is required than a simple stop + async ValueTask IAsyncDisposable.DisposeAsync() => await Stop(); +} diff --git a/test/PowerShellEditorServices.Test.E2E/Hosts/IDebugAdapterClientExtensions.cs b/test/PowerShellEditorServices.Test.E2E/Hosts/IDebugAdapterClientExtensions.cs new file mode 100644 index 000000000..e37be268d --- /dev/null +++ b/test/PowerShellEditorServices.Test.E2E/Hosts/IDebugAdapterClientExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Handlers; +using OmniSharp.Extensions.DebugAdapter.Protocol.Client; +using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; + +namespace PowerShellEditorServices.Test.E2E +{ + public static class IDebugAdapterClientExtensions + { + public static async Task LaunchScript(this IDebugAdapterClient debugAdapterClient, string script, string executeMode = "DotSource") + { + _ = await debugAdapterClient.Launch( + new PsesLaunchRequestArguments + { + NoDebug = false, + Script = script, + Cwd = "", + CreateTemporaryIntegratedConsole = false, + ExecuteMode = executeMode, + }) ?? throw new Exception("Launch response was null."); + } + } +} diff --git a/test/PowerShellEditorServices.Test.E2E/Hosts/PsesStdioLanguageServerProcessHost.cs b/test/PowerShellEditorServices.Test.E2E/Hosts/PsesStdioLanguageServerProcessHost.cs new file mode 100644 index 000000000..3c58e6778 --- /dev/null +++ b/test/PowerShellEditorServices.Test.E2E/Hosts/PsesStdioLanguageServerProcessHost.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace PowerShellEditorServices.Test.E2E; + +/// +/// A is responsible for launching or attaching to a language server, providing access to its input and output streams, and tracking its lifetime. +/// +internal class PsesStdioLanguageServerProcessHost(bool isDebugAdapter) +: StdioLanguageServerProcessHost(PwshExe, GeneratePsesArguments(isDebugAdapter)) +{ + protected static readonly string s_binDir = + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + private static readonly string s_bundledModulePath = new FileInfo(Path.Combine( + s_binDir, "..", "..", "..", "..", "..", "module")).FullName; + + private static readonly string s_sessionDetailsPath = Path.Combine( + s_binDir, $"pses_test_sessiondetails_{Path.GetRandomFileName()}"); + + private static readonly string s_logPath = Path.Combine( + s_binDir, $"pses_test_logs_{Path.GetRandomFileName()}"); + + private const string s_logLevel = "Diagnostic"; + private static readonly string[] s_featureFlags = { "PSReadLine" }; + private const string s_hostName = "TestHost"; + private const string s_hostProfileId = "TestHost"; + private const string s_hostVersion = "1.0.0"; + + // Adjust the environment variable if wanting to test with 5.1 or a specific pwsh path + public static string PwshExe { get; } = Environment.GetEnvironmentVariable("PWSH_EXE_NAME") ?? "pwsh"; + public static bool IsWindowsPowerShell { get; } = PwshExe.EndsWith("powershell"); + public static bool RunningInConstrainedLanguageMode { get; } = + Environment.GetEnvironmentVariable("__PSLockdownPolicy", EnvironmentVariableTarget.Machine) != null; + + private static string[] GeneratePsesArguments(bool isDebugAdapter) + { + List args = new() + { + "&", + SingleQuoteEscape(Path.Combine(s_bundledModulePath, "PowerShellEditorServices", "Start-EditorServices.ps1")), + "-LogPath", + SingleQuoteEscape(s_logPath), + "-LogLevel", + s_logLevel, + "-SessionDetailsPath", + SingleQuoteEscape(s_sessionDetailsPath), + "-FeatureFlags", + string.Join(',', s_featureFlags), + "-HostName", + s_hostName, + "-HostProfileId", + s_hostProfileId, + "-HostVersion", + s_hostVersion, + "-BundledModulesPath", + SingleQuoteEscape(s_bundledModulePath), + "-Stdio" + }; + + if (isDebugAdapter) + { + args.Add("-DebugServiceOnly"); + } + + string base64Str = Convert.ToBase64String( + System.Text.Encoding.Unicode.GetBytes(string.Join(' ', args))); + + return + [ + "-NoLogo", + "-NoProfile", + "-EncodedCommand", + base64Str + ]; + } + + private static string SingleQuoteEscape(string str) => $"'{str.Replace("'", "''")}'"; +} diff --git a/test/PowerShellEditorServices.Test.E2E/Hosts/StdioLanguageServerProcessHost.cs b/test/PowerShellEditorServices.Test.E2E/Hosts/StdioLanguageServerProcessHost.cs new file mode 100644 index 000000000..447ae9b44 --- /dev/null +++ b/test/PowerShellEditorServices.Test.E2E/Hosts/StdioLanguageServerProcessHost.cs @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace PowerShellEditorServices.Test.E2E; + +/// +/// Hosts a language server process that communicates over stdio +/// +internal class StdioLanguageServerProcessHost(string fileName, IEnumerable argumentList) : IAsyncLanguageServerHost +{ + // The PSES process that will be started and managed + private readonly Process process = new() + { + EnableRaisingEvents = true, + StartInfo = new ProcessStartInfo(fileName, argumentList) + { + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + // Track the state of the startup + private TaskCompletionSource<(StreamReader, StreamWriter)>? startTcs; + private TaskCompletionSource? stopTcs; + + // Starts the process. Returns when the process has started and streams are available. + public async Task<(StreamReader, StreamWriter)> Start(CancellationToken token = default) + { + // Runs this once upon process exit to clean up the state. + EventHandler? exitHandler = null; + exitHandler = (sender, e) => + { + // Complete the stopTcs task when the process finally exits, allowing stop to complete + stopTcs?.TrySetResult(true); + stopTcs = null; + startTcs = null; + process.Exited -= exitHandler; + }; + process.Exited += exitHandler; + + if (stopTcs is not null) + { + throw new InvalidOperationException("The process is currently stopping and cannot be started."); + } + + // Await the existing task if we have already started, making this operation idempotent + if (startTcs is not null) + { + return await startTcs.Task; + } + + // Initiate a new startTcs to track the startup + startTcs = new(); + + token.ThrowIfCancellationRequested(); + + // Should throw if there are any startup problems such as invalid path, etc. + process.Start(); + + // According to the source the streams should be allocated synchronously after the process has started, however it's not super clear so we will put this here in case there is an explicit race condition. + if (process.StandardInput.BaseStream is null || process.StandardOutput.BaseStream is null) + { + throw new InvalidOperationException("The process has started but the StandardInput or StandardOutput streams are not available. This should never happen and is probably a race condition, please report it to PowerShellEditorServices."); + } + + startTcs.SetResult(( + process.StandardOutput, + process.StandardInput + )); + + // Return the result of the completion task + return await startTcs.Task; + } + + public async Task WaitForExit(CancellationToken token = default) + { + AssertStarting(); + await process.WaitForExitAsync(token); + } + + /// + /// Determines if the process is in the starting state and throws if not. + /// + private void AssertStarting() + { + if (startTcs is null) + { + throw new InvalidOperationException("The process is not starting/started, use Start() first."); + } + } + + public async Task Stop(CancellationToken token = default) + { + AssertStarting(); + if (stopTcs is not null) + { + return await stopTcs.Task; + } + stopTcs = new(); + token.ThrowIfCancellationRequested(); + process.Kill(); + await process.WaitForExitAsync(token); + return true; + } +} diff --git a/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/LSPTestsFixtures.cs similarity index 86% rename from test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs rename to test/PowerShellEditorServices.Test.E2E/LSPTestsFixtures.cs index 9017eab4f..6eb8e4569 100644 --- a/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs +++ b/test/PowerShellEditorServices.Test.E2E/LSPTestsFixtures.cs @@ -8,9 +8,9 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; using Microsoft.PowerShell.EditorServices.Logging; using Microsoft.PowerShell.EditorServices.Services.Configuration; +using Nerdbank.Streams; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Client; using OmniSharp.Extensions.LanguageServer.Protocol; @@ -38,14 +38,16 @@ public class LSPTestsFixture : IAsyncLifetime internal List TelemetryEvents = new(); public ITestOutputHelper Output { get; set; } - protected PsesStdioProcess _psesProcess; - public int ProcessId => _psesProcess.Id; + internal PsesStdioLanguageServerProcessHost _psesHost = new(IsDebugAdapterTests); public async Task InitializeAsync() { - LoggerFactory factory = new(); - _psesProcess = new PsesStdioProcess(factory, IsDebugAdapterTests); - await _psesProcess.Start(); + (StreamReader stdout, StreamWriter stdin) = await _psesHost.Start(); + + // Splice the streams together and enable debug logging of all messages sent and received + DebugOutputStream psesStream = new( + FullDuplexStream.Splice(stdout.BaseStream, stdin.BaseStream) + ); DirectoryInfo testDir = Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName())); @@ -53,12 +55,13 @@ public async Task InitializeAsync() PsesLanguageClient = LanguageClient.PreInit(options => { options - .WithInput(_psesProcess.OutputStream) - .WithOutput(_psesProcess.InputStream) + .WithInput(psesStream) + .WithOutput(psesStream) .WithWorkspaceFolder(DocumentUri.FromFileSystemPath(testDir.FullName), "testdir") .WithInitializationOptions(new { EnableProfileLoading = false }) .OnPublishDiagnostics(diagnosticParams => Diagnostics.AddRange(diagnosticParams.Diagnostics.Where(d => d != null))) - .OnLogMessage(logMessageParams => { + .OnLogMessage(logMessageParams => + { Output?.WriteLine($"{logMessageParams.Type}: {logMessageParams.Message}"); Messages.Add(logMessageParams); }) @@ -98,7 +101,7 @@ public async Task InitializeAsync() public async Task DisposeAsync() { await PsesLanguageClient.Shutdown(); - await _psesProcess.Stop(); + await _psesHost.Stop(); PsesLanguageClient?.Dispose(); } } diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index 3c49ca805..155c23a3a 100644 --- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs @@ -7,14 +7,12 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Handlers; using Microsoft.PowerShell.EditorServices.Services.Configuration; using Microsoft.PowerShell.EditorServices.Services.PowerShell; -using Microsoft.PowerShell.EditorServices.Services.Template; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Client; @@ -30,11 +28,10 @@ namespace PowerShellEditorServices.Test.E2E [Trait("Category", "LSP")] public class LanguageServerProtocolMessageTests : IClassFixture, IDisposable { - // Borrowed from `VersionUtils` which can't be used here due to an initialization problem. - private static bool IsLinux { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - private static readonly string s_binDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + private const string testCommand = "Expand-Archive"; + private const string testDescription = "Extracts files from a specified archive (zipped) file."; private readonly ILanguageClient PsesLanguageClient; private readonly List Messages; @@ -49,7 +46,7 @@ public LanguageServerProtocolMessageTests(ITestOutputHelper output, LSPTestsFixt Messages.Clear(); Diagnostics = data.Diagnostics; Diagnostics.Clear(); - PwshExe = PsesStdioProcess.PwshExe; + PwshExe = PsesStdioLanguageServerProcessHost.PwshExe; } public void Dispose() @@ -86,9 +83,9 @@ private async Task WaitForDiagnosticsAsync() // Wait for PSSA to finish. for (int i = 0; Diagnostics.Count == 0; i++) { - if (i >= 30) + if (i >= 120) { - throw new InvalidDataException("No diagnostics showed up after 20s."); + throw new InvalidDataException("No diagnostics showed up after 2 minutes."); } await Task.Delay(1000); @@ -140,7 +137,7 @@ function CanSendWorkspaceSymbolRequest { [SkippableFact] public async Task CanReceiveDiagnosticsFromFileOpenAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode && PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); NewTestFile("$a = 4"); @@ -162,7 +159,7 @@ public async Task WontReceiveDiagnosticsFromFileOpenThatIsNotPowerShellAsync() [SkippableFact] public async Task CanReceiveDiagnosticsFromFileChangedAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode && PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string filePath = NewTestFile("$a = 4"); @@ -213,7 +210,7 @@ public async Task CanReceiveDiagnosticsFromFileChangedAsync() [SkippableFact] public async Task CanReceiveDiagnosticsFromConfigurationChangeAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode && PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); PsesLanguageClient.SendNotification("workspace/didChangeConfiguration", @@ -313,7 +310,7 @@ await PsesLanguageClient [SkippableFact] public async Task CanSendFormattingRequestAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode && PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string scriptPath = NewTestFile(@" @@ -349,7 +346,7 @@ public async Task CanSendFormattingRequestAsync() [SkippableFact] public async Task CanSendRangeFormattingRequestAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode && PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string scriptPath = NewTestFile(@" @@ -978,7 +975,7 @@ enum MyEnum { [SkippableFact] public async Task CanSendCodeActionRequestAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode && PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string filePath = NewTestFile("gci"); @@ -1031,111 +1028,80 @@ await PsesLanguageClient }); } - [SkippableFact] + [Fact] public async Task CanSendCompletionAndCompletionResolveRequestAsync() { - Skip.If(IsLinux, "This depends on the help system, which is flaky on Linux."); - Skip.If(PsesStdioProcess.IsWindowsPowerShell, "This help system isn't updated in CI."); - string filePath = NewTestFile("Write-H"); - CompletionList completionItems = await PsesLanguageClient.TextDocument.RequestCompletion( new CompletionParams { TextDocument = new TextDocumentIdentifier { - Uri = DocumentUri.FromFileSystemPath(filePath) + Uri = DocumentUri.FromFileSystemPath(NewTestFile(testCommand)) }, Position = new Position(line: 0, character: 7) }); CompletionItem completionItem = Assert.Single(completionItems, - completionItem1 => completionItem1.FilterText == "Write-Host"); + completionItem1 => completionItem1.FilterText == testCommand); CompletionItem updatedCompletionItem = await PsesLanguageClient .SendRequest("completionItem/resolve", completionItem) .Returning(CancellationToken.None); - Assert.Contains("Writes customized output to a host", updatedCompletionItem.Documentation.String); + Assert.Contains(testDescription, updatedCompletionItem.Documentation.String); } - // Regression test for https://github.com/PowerShell/PowerShellEditorServices/issues/1926 - [SkippableFact] - public async Task CanRequestCompletionsAndHandleExceptions() + [Fact] + public async Task CanSendCompletionResolveWithModulePrefixRequestAsync() { - PowerShellVersion details - = await PsesLanguageClient - .SendRequest("powerShell/getVersion", new GetVersionParams()) - .Returning(CancellationToken.None); - - Skip.IfNot(details.Version.StartsWith("7.2") || details.Version.StartsWith("7.3"), - "This is a bug in PowerShell 7.2 and 7.3, fixed in 7.4"); - - string filePath = NewTestFile(@" -@() | ForEach-Object { - if ($false) { - return - } + await PsesLanguageClient + .SendRequest( + "evaluate", + new EvaluateRequestArguments + { + Expression = "Import-Module Microsoft.PowerShell.Archive -Prefix Test" + }) + .ReturningVoid(CancellationToken.None); - @{key=$} - }"); + try + { + const string command = "Expand-TestArchive"; - Messages.Clear(); // On some systems there's a warning message about configuration items too. - CompletionList completionItems = await PsesLanguageClient.TextDocument.RequestCompletion( + CompletionList completionItems = await PsesLanguageClient.TextDocument.RequestCompletion( new CompletionParams { TextDocument = new TextDocumentIdentifier { - Uri = DocumentUri.FromFileSystemPath(filePath) + Uri = DocumentUri.FromFileSystemPath(NewTestFile(command)) }, - Position = new Position(line: 6, character: 11) + Position = new Position(line: 0, character: 12) }); - Assert.Empty(completionItems); - Assert.Collection(Messages, - (message) => Assert.Contains("Error Occurred in TabExpansion2", message.Message), - (message) => Assert.Contains("Exception occurred while running handling completion request", message.Message)); - } + CompletionItem completionItem = Assert.Single(completionItems, + completionItem1 => completionItem1.Label == command); - [SkippableFact(Skip = "Completion for Expand-SlowArchive is flaky.")] - public async Task CanSendCompletionResolveWithModulePrefixRequestAsync() - { - await PsesLanguageClient + CompletionItem updatedCompletionItem = await PsesLanguageClient.ResolveCompletion(completionItem); + + Assert.Contains(testDescription, updatedCompletionItem.Documentation.String); + } + finally + { + // Reset the Archive module to the non-prefixed version + await PsesLanguageClient .SendRequest( "evaluate", new EvaluateRequestArguments { - Expression = "Import-Module Microsoft.PowerShell.Archive -Prefix Slow" + Expression = "Remove-Module Microsoft.PowerShell.Archive;Import-Module Microsoft.PowerShell.Archive -Force" }) .ReturningVoid(CancellationToken.None); - - string filePath = NewTestFile("Expand-SlowArch"); - - CompletionList completionItems = await PsesLanguageClient.TextDocument.RequestCompletion( - new CompletionParams - { - TextDocument = new TextDocumentIdentifier - { - Uri = DocumentUri.FromFileSystemPath(filePath) - }, - Position = new Position(line: 0, character: 15) - }); - - CompletionItem completionItem = Assert.Single(completionItems, - completionItem1 => completionItem1.Label == "Expand-SlowArchive"); - - CompletionItem updatedCompletionItem = await PsesLanguageClient - .SendRequest("completionItem/resolve", completionItem) - .Returning(CancellationToken.None); - - Assert.Contains("Extracts files from a specified archive", updatedCompletionItem.Documentation.String); + } } - [SkippableFact] + [Fact] public async Task CanSendHoverRequestAsync() { - Skip.If(IsLinux, "This depends on the help system, which is flaky on Linux."); - Skip.If(PsesStdioProcess.IsWindowsPowerShell, "This help system isn't updated in CI."); - string filePath = NewTestFile("Write-Host"); + string filePath = NewTestFile(testCommand); Hover hover = await PsesLanguageClient.TextDocument.RequestHover( new HoverParams @@ -1149,37 +1115,36 @@ public async Task CanSendHoverRequestAsync() Assert.True(hover.Contents.HasMarkedStrings); Assert.Collection(hover.Contents.MarkedStrings, - str1 => Assert.Equal("Write-Host", str1.Value), + str1 => Assert.Equal(testCommand, str1.Value), str2 => { Assert.Equal("markdown", str2.Language); - Assert.Equal("Writes customized output to a host.", str2.Value); + Assert.Equal(testDescription, str2.Value); }); } [Fact] public async Task CanSendSignatureHelpRequestAsync() { - string filePath = NewTestFile("Get-Date -"); + string filePath = NewTestFile($"{testCommand} -"); - SignatureHelp signatureHelp = await PsesLanguageClient - .SendRequest( - "textDocument/signatureHelp", - new SignatureHelpParams + SignatureHelp signatureHelp = await PsesLanguageClient.RequestSignatureHelp + ( + new SignatureHelpParams + { + TextDocument = new TextDocumentIdentifier { - TextDocument = new TextDocumentIdentifier - { - Uri = new Uri(filePath) - }, - Position = new Position - { - Line = 0, - Character = 10 - } - }) - .Returning(CancellationToken.None); + Uri = new Uri(filePath) + }, + Position = new Position + { + Line = 0, + Character = 10 + } + } + ); - Assert.Contains("Get-Date", signatureHelp.Signatures.First().Label); + Assert.Contains(testCommand, signatureHelp.Signatures.First().Label); } [Fact] @@ -1213,30 +1178,10 @@ await PsesLanguageClient Assert.Equal(33, locationOrLocationLink.Location.Range.End.Character); } - [SkippableFact] - public async Task CanSendGetProjectTemplatesRequestAsync() - { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, - "Plaster doesn't work in Constrained Language Mode."); - - GetProjectTemplatesResponse getProjectTemplatesResponse = - await PsesLanguageClient - .SendRequest( - "powerShell/getProjectTemplates", - new GetProjectTemplatesRequest - { - IncludeInstalledModules = true - }) - .Returning(CancellationToken.None); - - Assert.Contains(getProjectTemplatesResponse.Templates, t => t.Title is "AddPSScriptAnalyzerSettings"); - Assert.Contains(getProjectTemplatesResponse.Templates, t => t.Title is "New PowerShell Manifest Module"); - } - [SkippableFact] public async Task CanSendGetCommentHelpRequestAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode && PsesStdioLanguageServerProcessHost.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string scriptPath = NewTestFile(@" @@ -1290,9 +1235,16 @@ await PsesLanguageClient Assert.Equal(0, evaluateResponseBody.VariablesReference); } - [Fact] + // getCommand gets all the commands in the system, and is not optimized and can take forever on CI systems + [SkippableFact(Timeout = 120000)] public async Task CanSendGetCommandRequestAsync() { + Skip.If( + Environment.GetEnvironmentVariable("TF_BUILD") is not null || + Environment.GetEnvironmentVariable("GITHUB_ACTIONS") is not null || + Environment.GetEnvironmentVariable("CI") is not null, + "This test is too slow in CI."); + List pSCommandMessages = await PsesLanguageClient .SendRequest("powerShell/getCommand", new GetCommandParams()) @@ -1306,7 +1258,7 @@ await PsesLanguageClient [SkippableFact] public async Task CanSendExpandAliasRequestAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, + Skip.If(PsesStdioLanguageServerProcessHost.RunningInConstrainedLanguageMode, "The expand alias request doesn't work in Constrained Language Mode."); ExpandAliasResult expandAliasResult = diff --git a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj index 77eacbd87..82c0899f5 100644 --- a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj +++ b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj @@ -1,5 +1,6 @@ - + net8.0 @@ -7,24 +8,27 @@ - - - - - + + + + + + - - + + - - + + diff --git a/test/PowerShellEditorServices.Test.E2E/Processes/LoggingStream.cs b/test/PowerShellEditorServices.Test.E2E/Processes/LoggingStream.cs deleted file mode 100644 index 9a29452b0..000000000 --- a/test/PowerShellEditorServices.Test.E2E/Processes/LoggingStream.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Diagnostics; -using System.IO; -using System.Text; - -namespace PowerShellEditorServices.Test.E2E -{ - internal class LoggingStream : Stream - { - private static readonly string s_banner = new('=', 20); - - private readonly Stream _underlyingStream; - - public LoggingStream(Stream underlyingStream) => _underlyingStream = underlyingStream; - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - _underlyingStream.Dispose(); - } - } - - public override bool CanRead => _underlyingStream.CanRead; - - public override bool CanSeek => _underlyingStream.CanSeek; - - public override bool CanWrite => _underlyingStream.CanWrite; - - public override long Length => _underlyingStream.Length; - - public override long Position { get => _underlyingStream.Position; set => _underlyingStream.Position = value; } - - public override void Flush() => _underlyingStream.Flush(); - - public override int Read(byte[] buffer, int offset, int count) - { - int actualCount = _underlyingStream.Read(buffer, offset, count); - LogData("READ", buffer, offset, actualCount); - return actualCount; - } - - public override long Seek(long offset, SeekOrigin origin) => _underlyingStream.Seek(offset, origin); - - public override void SetLength(long value) => _underlyingStream.SetLength(value); - - public override void Write(byte[] buffer, int offset, int count) - { - LogData("WRITE", buffer, offset, count); - _underlyingStream.Write(buffer, offset, count); - } - - private static void LogData(string header, byte[] buffer, int offset, int count) - { - Debug.WriteLine($"{header} |{s_banner.Substring(0, Math.Max(s_banner.Length - header.Length - 2, 0))}"); - string data = Encoding.UTF8.GetString(buffer, offset, count); - Debug.WriteLine(data); - Debug.WriteLine(s_banner); - Debug.WriteLine("\n"); - } - } -} diff --git a/test/PowerShellEditorServices.Test.E2E/Processes/PsesStdioProcess.cs b/test/PowerShellEditorServices.Test.E2E/Processes/PsesStdioProcess.cs deleted file mode 100644 index 0aa651205..000000000 --- a/test/PowerShellEditorServices.Test.E2E/Processes/PsesStdioProcess.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using Microsoft.Extensions.Logging; - -namespace PowerShellEditorServices.Test.E2E -{ - /// - /// A is responsible for launching or attaching to a language server, providing access to its input and output streams, and tracking its lifetime. - /// - public class PsesStdioProcess : StdioServerProcess - { - protected static readonly string s_binDir = - Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - - #region private static or constants members - - private static readonly string s_bundledModulePath = new FileInfo(Path.Combine( - s_binDir, "..", "..", "..", "..", "..", "module")).FullName; - - private static readonly string s_sessionDetailsPath = Path.Combine( - s_binDir, $"pses_test_sessiondetails_{Path.GetRandomFileName()}"); - - private static readonly string s_logPath = Path.Combine( - s_binDir, $"pses_test_logs_{Path.GetRandomFileName()}"); - - private const string s_logLevel = "Diagnostic"; - private static readonly string[] s_featureFlags = { "PSReadLine" }; - private const string s_hostName = "TestHost"; - private const string s_hostProfileId = "TestHost"; - private const string s_hostVersion = "1.0.0"; - - #endregion - - #region public static properties - - // NOTE: Just hard-code this to "powershell" when testing with the code lens. - public static string PwshExe { get; } = Environment.GetEnvironmentVariable("PWSH_EXE_NAME") ?? "pwsh"; - public static bool IsWindowsPowerShell { get; } = PwshExe.EndsWith("powershell"); - public static bool RunningInConstrainedLanguageMode { get; } = - Environment.GetEnvironmentVariable("__PSLockdownPolicy", EnvironmentVariableTarget.Machine) != null; - - #endregion - - #region ctor - - public PsesStdioProcess(ILoggerFactory loggerFactory, bool isDebugAdapter) : base(loggerFactory, GeneratePsesStartInfo(isDebugAdapter)) - { - } - - #endregion - - #region helper private methods - - private static ProcessStartInfo GeneratePsesStartInfo(bool isDebugAdapter) - { - ProcessStartInfo processStartInfo = new() - { - FileName = PwshExe - }; - - foreach (string arg in GeneratePsesArguments(isDebugAdapter)) - { - processStartInfo.ArgumentList.Add(arg); - } - - return processStartInfo; - } - - private static string[] GeneratePsesArguments(bool isDebugAdapter) - { - List args = new() - { - "&", - SingleQuoteEscape(Path.Combine(s_bundledModulePath, "PowerShellEditorServices", "Start-EditorServices.ps1")), - "-LogPath", - SingleQuoteEscape(s_logPath), - "-LogLevel", - s_logLevel, - "-SessionDetailsPath", - SingleQuoteEscape(s_sessionDetailsPath), - "-FeatureFlags", - string.Join(',', s_featureFlags), - "-HostName", - s_hostName, - "-HostProfileId", - s_hostProfileId, - "-HostVersion", - s_hostVersion, - "-BundledModulesPath", - SingleQuoteEscape(s_bundledModulePath), - "-Stdio" - }; - - if (isDebugAdapter) - { - args.Add("-DebugServiceOnly"); - } - - string base64Str = Convert.ToBase64String( - System.Text.Encoding.Unicode.GetBytes(string.Join(' ', args))); - - return new string[] - { - "-NoLogo", - "-NoProfile", - "-EncodedCommand", - base64Str - }; - } - - private static string SingleQuoteEscape(string str) => $"'{str.Replace("'", "''")}'"; - - #endregion - } -} diff --git a/test/PowerShellEditorServices.Test.E2E/Processes/ServerProcess.cs b/test/PowerShellEditorServices.Test.E2E/Processes/ServerProcess.cs deleted file mode 100644 index 8d744dc11..000000000 --- a/test/PowerShellEditorServices.Test.E2E/Processes/ServerProcess.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Reactive.Subjects; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; - -namespace PowerShellEditorServices.Test.E2E -{ - /// - /// A is responsible for launching or attaching to a language server, providing access to its input and output streams, and tracking its lifetime. - /// - public abstract class ServerProcess : IDisposable - { - private readonly ISubject _exitedSubject; - - private readonly Lazy _inStreamLazy; - - private readonly Lazy _outStreamLazy; - - /// - /// Create a new . - /// - /// - /// The factory for loggers used by the process and its components. - /// - protected ServerProcess(ILoggerFactory loggerFactory) - { - LoggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - Log = LoggerFactory.CreateLogger(categoryName: GetType().FullName); - - ServerStartCompletion = new TaskCompletionSource(); - - ServerExitCompletion = new TaskCompletionSource(); - ServerExitCompletion.SetResult(null); // Start out as if the server has already exited. - - Exited = _exitedSubject = new AsyncSubject(); - - _inStreamLazy = new Lazy(() => new LoggingStream(GetInputStream())); - _outStreamLazy = new Lazy(() => new LoggingStream(GetOutputStream())); - } - - /// - /// Finalizer for . - /// - ~ServerProcess() - { - Dispose(false); - } - - /// - /// Dispose of resources being used by the launcher. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose of resources being used by the launcher. - /// - /// - /// Explicit disposal? - /// - protected virtual void Dispose(bool disposing) - { - } - - /// - /// The factory for loggers used by the process and its components. - /// - protected ILoggerFactory LoggerFactory { get; } - - /// - /// The process's logger. - /// - protected ILogger Log { get; } - - /// - /// The used to signal server startup. - /// - protected TaskCompletionSource ServerStartCompletion { get; set; } - - /// - /// The used to signal server exit. - /// - protected TaskCompletionSource ServerExitCompletion { get; set; } - - /// - /// Event raised when the server has exited. - /// - public IObservable Exited { get; } - - /// - /// Is the server running? - /// - public abstract bool IsRunning { get; } - - /// - /// A that completes when the server has started. - /// - public Task HasStarted => ServerStartCompletion.Task; - - /// - /// A that completes when the server has exited. - /// - public Task HasExited => ServerExitCompletion.Task; - - protected abstract Stream GetInputStream(); - - protected abstract Stream GetOutputStream(); - - /// - /// The server's input stream. - /// - /// - /// The connection will write to the server's input stream, and read from its output stream. - /// - public Stream InputStream => _inStreamLazy.Value; - - /// - /// The server's output stream. - /// - /// - /// The connection will read from the server's output stream, and write to its input stream. - /// - public Stream OutputStream => _outStreamLazy.Value; - - /// - /// Start or connect to the server. - /// - public abstract Task Start(); - - /// - /// Stop or disconnect from the server. - /// - public abstract Task Stop(); - - /// - /// Raise the event. - /// - protected virtual void OnExited() - { - _exitedSubject.OnNext(System.Reactive.Unit.Default); - _exitedSubject.OnCompleted(); - } - } -} diff --git a/test/PowerShellEditorServices.Test.E2E/Processes/StdioServerProcess.cs b/test/PowerShellEditorServices.Test.E2E/Processes/StdioServerProcess.cs deleted file mode 100644 index 0cfc516ec..000000000 --- a/test/PowerShellEditorServices.Test.E2E/Processes/StdioServerProcess.cs +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; - -namespace PowerShellEditorServices.Test.E2E -{ - /// - /// A is a that launches its server as an external process and communicates with it over STDIN / STDOUT. - /// - public class StdioServerProcess : ServerProcess - { - /// - /// A that describes how to start the server. - /// - private readonly ProcessStartInfo _serverStartInfo; - - /// - /// The current server process (if any). - /// -#pragma warning disable CA2213 - private Process _serverProcess; -#pragma warning restore CA2213 - - /// - /// Create a new . - /// - /// - /// The factory for loggers used by the process and its components. - /// - /// - /// A that describes how to start the server. - /// - public StdioServerProcess(ILoggerFactory loggerFactory, ProcessStartInfo serverStartInfo) - : base(loggerFactory) => _serverStartInfo = serverStartInfo ?? throw new ArgumentNullException(nameof(serverStartInfo)); - - public int ProcessId => _serverProcess.Id; - - /// - /// The process ID of the server process, useful for attaching a debugger. - /// - public int Id => _serverProcess.Id; - - /// - /// Dispose of resources being used by the launcher. - /// - /// - /// Explicit disposal? - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - Process serverProcess = Interlocked.Exchange(ref _serverProcess, null); - if (serverProcess is not null) - { - if (!serverProcess.HasExited) - { - serverProcess.Kill(); - } - - serverProcess.Dispose(); - } - } - base.Dispose(disposing); - } - - /// - /// Is the server running? - /// - public override bool IsRunning => !ServerExitCompletion.Task.IsCompleted; - - /// - /// The server's input stream. - /// - protected override Stream GetInputStream() => _serverProcess?.StandardInput?.BaseStream; - - /// - /// The server's output stream. - /// - protected override Stream GetOutputStream() => _serverProcess?.StandardOutput?.BaseStream; - - /// - /// Start or connect to the server. - /// - public override Task Start() - { - ServerExitCompletion = new TaskCompletionSource(); - - _serverStartInfo.CreateNoWindow = true; - _serverStartInfo.UseShellExecute = false; - _serverStartInfo.RedirectStandardInput = true; - _serverStartInfo.RedirectStandardOutput = true; - _serverStartInfo.RedirectStandardError = true; - - Process serverProcess = _serverProcess = new Process - { - StartInfo = _serverStartInfo, - EnableRaisingEvents = true - }; - serverProcess.Exited += ServerProcess_Exit; - - if (!serverProcess.Start()) - { - throw new InvalidOperationException("Failed to launch language server ."); - } - - ServerStartCompletion.TrySetResult(null); - - return Task.CompletedTask; - } - - /// - /// Stop or disconnect from the server. - /// - public override Task Stop() - { - Process serverProcess = Interlocked.Exchange(ref _serverProcess, null); - ServerExitCompletion.TrySetResult(null); - if (serverProcess?.HasExited == false) - { - serverProcess.Kill(); - } - return ServerExitCompletion.Task; - } - - public event EventHandler ProcessExited; - - /// - /// Called when the server process has exited. - /// - /// - /// The event sender. - /// - /// - /// The event arguments. - /// - private void ServerProcess_Exit(object sender, EventArgs args) - { - Log.LogDebug("Server process has exited."); - - Process serverProcess = (Process)sender; - - int exitCode = serverProcess.ExitCode; - string errorMsg = serverProcess.StandardError.ReadToEnd(); - - OnExited(); - ProcessExited?.Invoke(this, new ProcessExitedEventArgs(exitCode, errorMsg)); - if (exitCode != 0) - { - ServerExitCompletion.TrySetException(new ProcessExitedException("Stdio server process exited unexpectedly", exitCode, errorMsg)); - } - else - { - ServerExitCompletion.TrySetResult(null); - } - ServerStartCompletion = new TaskCompletionSource(); - } - } - - public class ProcessExitedException : Exception - { - public ProcessExitedException(string message, int exitCode, string errorMessage) - : base(message) - { - ExitCode = exitCode; - ErrorMessage = errorMessage; - } - - public int ExitCode { get; init; } - - public string ErrorMessage { get; init; } - } - - public class ProcessExitedEventArgs : EventArgs - { - public ProcessExitedEventArgs(int exitCode, string errorMessage) - { - ExitCode = exitCode; - ErrorMessage = errorMessage; - } - - public int ExitCode { get; init; } - - public string ErrorMessage { get; init; } - } -} diff --git a/test/PowerShellEditorServices.Test.E2E/packages.lock.json b/test/PowerShellEditorServices.Test.E2E/packages.lock.json deleted file mode 100644 index 59dfb28f8..000000000 --- a/test/PowerShellEditorServices.Test.E2E/packages.lock.json +++ /dev/null @@ -1,500 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net8.0": { - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.9.0", - "Microsoft.TestPlatform.TestHost": "17.9.0" - } - }, - "Newtonsoft.Json": { - "type": "Direct", - "requested": "[13.0.3, )", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "OmniSharp.Extensions.DebugAdapter.Client": { - "type": "Direct", - "requested": "[0.19.9, )", - "resolved": "0.19.9", - "contentHash": "wxjisQyg/+IL6PAdcEUdQ5PQKgwi5C5h8TJ+Lq2LHwzCH4q75dd2KtCgMeLfGv2zYX+iejkvrTKJla/NI5yjVw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageClient": { - "type": "Direct", - "requested": "[0.19.9, )", - "resolved": "0.19.9", - "contentHash": "WI+oxw30IYVykAxnZjf8fdBP2qiO4qjk3KvMtav7u3VytbDQ+wqjAGV83/os61Lc3EhF+QC8kCGKv8vQtfJ+aA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "xunit": { - "type": "Direct", - "requested": "[2.6.6, )", - "resolved": "2.6.6", - "contentHash": "MAbOOMtZIKyn2lrAmMlvhX0BhDOX/smyrTB+8WTXnSKkrmTGBS2fm8g1PZtHBPj91Dc5DJA7fY+/81TJ/yUFZw==", - "dependencies": { - "xunit.analyzers": "1.10.0", - "xunit.assert": "2.6.6", - "xunit.core": "[2.6.6]" - } - }, - "xunit.runner.visualstudio": { - "type": "Direct", - "requested": "[2.5.7, )", - "resolved": "2.5.7", - "contentHash": "31Rl7dBJriX0DNwZfDp8gqFOPsiM0c9kqpcH/HvNi9vDp+K7Ydf42H7mVIvYT918Ywzn1ymLg1c4DDC6iU754w==" - }, - "Xunit.SkippableFact": { - "type": "Direct", - "requested": "[1.4.13, )", - "resolved": "1.4.13", - "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", - "dependencies": { - "Validation": "2.4.18", - "xunit.extensibility.execution": "2.4.0" - } - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "Spmg7Wx49Ya3SxBjyeAR+nQpjMTKZwTwpZ7KyeOTIqI/WHNPnBU4HUvl5kuHPQAwGWqMy4FGZja1HvEwvoaDiA==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.9.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "PowerShellStandard.Library": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "Serilog": { - "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==" - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Transitive", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==" - }, - "System.IO.Pipes.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "Validation": { - "type": "Transitive", - "resolved": "2.4.18", - "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" - }, - "xunit.abstractions": { - "type": "Transitive", - "resolved": "2.0.3", - "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" - }, - "xunit.analyzers": { - "type": "Transitive", - "resolved": "1.10.0", - "contentHash": "Lw8CiDy5NaAWcO6keqD7iZHYUTIuCOcoFrUHw5Sv84ITZ9gFeDybdkVdH0Y2maSlP9fUjtENyiykT44zwFQIHA==" - }, - "xunit.assert": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "74Cm9lAZOk5TKCz2MvCBCByKsS23yryOKDIMxH3XRDHXmfGM02jKZWzRA7g4mGB41GnBnv/pcWP3vUYkrCtEcg==" - }, - "xunit.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "tqi7RfaNBqM7t8zx6QHryuBPzmotsZXKGaWnopQG2Ez5UV7JoWuyoNdT6gLpDIcKdGYey6YTXJdSr9IXDMKwjg==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]", - "xunit.extensibility.execution": "[2.6.6]" - } - }, - "xunit.extensibility.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "ty6VKByzbx4Toj4/VGJLEnlmOawqZiMv0in/tLju+ftA+lbWuAWDERM+E52Jfhj4ZYHrAYVa14KHK5T+dq0XxA==", - "dependencies": { - "xunit.abstractions": "2.0.3" - } - }, - "xunit.extensibility.execution": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "UDjIVGj2TepVKN3n32/qXIdb3U6STwTb9L6YEwoQO2A8OxiJS5QAVv2l1aT6tDwwv/9WBmm8Khh/LyHALipcng==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]" - } - }, - "Microsoft.PowerShell.EditorServices": { - "type": "Project", - "dependencies": { - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )", - "Microsoft.Extensions.Logging": "[8.0.0, )", - "OmniSharp.Extensions.DebugAdapter.Server": "[0.19.9, )", - "OmniSharp.Extensions.LanguageServer": "[0.19.9, )", - "PowerShellStandard.Library": "[5.1.1, )", - "Serilog": "[3.1.1, )", - "Serilog.Extensions.Logging": "[8.0.0, )", - "Serilog.Sinks.Async": "[1.5.0, )", - "Serilog.Sinks.File": "[5.0.0, )", - "System.IO.Pipes.AccessControl": "[5.0.0, )", - "System.Security.Principal": "[4.3.0, )", - "System.Security.Principal.Windows": "[5.0.0, )" - } - } - } - } -} \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.E2E/xunit.runner.json b/test/PowerShellEditorServices.Test.E2E/xunit.runner.json index 2719fd14a..f1c3b37cd 100644 --- a/test/PowerShellEditorServices.Test.E2E/xunit.runner.json +++ b/test/PowerShellEditorServices.Test.E2E/xunit.runner.json @@ -1,8 +1,10 @@ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", "appDomain": "denied", - "parallelizeTestCollections": false, + "parallelizeTestCollections": true, + "parallelAlgorithm": "conservative", "methodDisplay": "method", "diagnosticMessages": true, - "longRunningTestSeconds": 60 + "longRunningTestSeconds": 10, + "showLiveOutput": true } diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandInFile.cs b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandInFile.cs index 80a1cc906..f84a44a11 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandInFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandInFile.cs @@ -12,7 +12,7 @@ internal static class CompleteCommandInFile file: TestUtilities.NormalizePath("Completion/CompletionExamples.psm1"), text: string.Empty, startLineNumber: 8, - startColumnNumber: 7, + startColumnNumber: 10, startOffset: 0, endLineNumber: 0, endColumnNumber: 0, @@ -22,17 +22,17 @@ internal static class CompleteCommandInFile { Kind = CompletionItemKind.Function, Detail = "", - FilterText = "Get-Something", - InsertText = "Get-Something", - Label = "Get-Something", - SortText = "0001Get-Something", + FilterText = "Get-XYZSomething", + InsertText = "Get-XYZSomething", + Label = "Get-XYZSomething", + SortText = "0001Get-XYZSomething", TextEdit = new TextEdit { - NewText = "Get-Something", + NewText = "Get-XYZSomething", Range = new Range { Start = new Position { Line = 7, Character = 0 }, - End = new Position { Line = 7, Character = 6 } + End = new Position { Line = 7, Character = 9 } } } }; diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1 b/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1 index a4ce41222..840ec9fdf 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1 +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1 @@ -1,11 +1,11 @@ -function Get-Something +function Get-XYZSomething { $testVar2 = "Shouldn't find this variable" } $testVar1 = "Should find this variable" -Get-So +Get-XYZSo $testVar @@ -13,7 +13,7 @@ Import-Module PowerShellGet Get-Rand function Test-Completion { - param([Parameter(Mandatory, Value)]) + param([Parameter(Mandatory, Value)]$test) } Get-ChildItem / diff --git a/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_HelpInfo.xml b/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_HelpInfo.xml new file mode 100644 index 000000000..80a0b57ae --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_HelpInfo.xml @@ -0,0 +1,10 @@ + + + https://aka.ms/powershell75-help + + + en-US + 7.5.0.0 + + + \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.cab b/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.cab new file mode 100644 index 000000000..9be01fd7f Binary files /dev/null and b/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.cab differ diff --git a/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.zip b/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.zip new file mode 100644 index 000000000..8d98199a7 Binary files /dev/null and b/test/PowerShellEditorServices.Test.Shared/PSHelp/7/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.zip differ diff --git a/test/PowerShellEditorServices.Test.Shared/PSHelp/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_HelpInfo.xml b/test/PowerShellEditorServices.Test.Shared/PSHelp/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_HelpInfo.xml new file mode 100644 index 000000000..1b6473db8 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/PSHelp/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_HelpInfo.xml @@ -0,0 +1,10 @@ + + + https://aka.ms/powershell51-help + + + en-US + 5.2.0.0 + + + \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.Shared/PSHelp/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.cab b/test/PowerShellEditorServices.Test.Shared/PSHelp/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.cab new file mode 100644 index 000000000..15ab9f029 Binary files /dev/null and b/test/PowerShellEditorServices.Test.Shared/PSHelp/Microsoft.PowerShell.Archive_eb74e8da-9ae2-482a-a648-e96550fb8733_en-US_HelpContent.cab differ diff --git a/test/PowerShellEditorServices.Test.Shared/PSHelp/README b/test/PowerShellEditorServices.Test.Shared/PSHelp/README new file mode 100644 index 000000000..0feba607f --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/PSHelp/README @@ -0,0 +1,3 @@ +Several CI platforms do not ship with PowerShell help. The build script updates help offline using this information. +As some of the CI platforms do not have internet access. +Linux servers use the zip file, while windows uses the cab files. diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCallWIthinStringExpression.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCallWIthinStringExpression.ps1 new file mode 100644 index 000000000..944e6d5df --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCallWIthinStringExpression.ps1 @@ -0,0 +1,3 @@ +function foo { + write-host "This will do recursion ... $(foo)" +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCallWIthinStringExpressionRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCallWIthinStringExpressionRenamed.ps1 new file mode 100644 index 000000000..44d843c5a --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCallWIthinStringExpressionRenamed.ps1 @@ -0,0 +1,3 @@ +function Renamed { + write-host "This will do recursion ... $(Renamed)" +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCmdlet.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCmdlet.ps1 new file mode 100644 index 000000000..1614b63a9 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCmdlet.ps1 @@ -0,0 +1,21 @@ +function Testing-Foo { + [CmdletBinding(SupportsShouldProcess)] + param ( + $Text, + $Param + ) + + begin { + if ($PSCmdlet.ShouldProcess("Target", "Operation")) { + Testing-Foo -Text "Param" -Param [1,2,3] + } + } + + process { + Testing-Foo -Text "Param" -Param [1,2,3] + } + + end { + Testing-Foo -Text "Param" -Param [1,2,3] + } +} \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCmdletRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCmdletRenamed.ps1 new file mode 100644 index 000000000..ee14a9fb7 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionCmdletRenamed.ps1 @@ -0,0 +1,21 @@ +function Renamed { + [CmdletBinding(SupportsShouldProcess)] + param ( + $Text, + $Param + ) + + begin { + if ($PSCmdlet.ShouldProcess("Target", "Operation")) { + Renamed -Text "Param" -Param [1,2,3] + } + } + + process { + Renamed -Text "Param" -Param [1,2,3] + } + + end { + Renamed -Text "Param" -Param [1,2,3] + } +} \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionFilter.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionFilter.ps1 new file mode 100644 index 000000000..4c2c5499d --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionFilter.ps1 @@ -0,0 +1,5 @@ +filter AFilter { + $_ +} + +1..3 | AFilter diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionFilterRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionFilterRenamed.ps1 new file mode 100644 index 000000000..0eaf1eb85 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionFilterRenamed.ps1 @@ -0,0 +1,5 @@ +filter Renamed { + $_ +} + +1..3 | Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeach.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeach.ps1 new file mode 100644 index 000000000..2454effe6 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeach.ps1 @@ -0,0 +1,17 @@ +$x = 1..10 + +function testing_files { + param ( + $x + ) + write-host "Printing $x" +} + +foreach ($number in $x) { + testing_files $number + + function testing_files { + write-host "------------------" + } +} +testing_files "99" diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachObject.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachObject.ps1 new file mode 100644 index 000000000..107c50223 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachObject.ps1 @@ -0,0 +1,17 @@ +$x = 1..10 + +function testing_files { + param ( + $x + ) + write-host "Printing $x" +} + +$x | ForEach-Object { + testing_files $_ + + function testing_files { + write-host "------------------" + } +} +testing_files "99" diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachObjectRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachObjectRenamed.ps1 new file mode 100644 index 000000000..80073c640 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachObjectRenamed.ps1 @@ -0,0 +1,17 @@ +$x = 1..10 + +function Renamed { + param ( + $x + ) + write-host "Printing $x" +} + +$x | ForEach-Object { + Renamed $_ + + function testing_files { + write-host "------------------" + } +} +Renamed "99" diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachRenamed.ps1 new file mode 100644 index 000000000..cd0dcb424 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionForeachRenamed.ps1 @@ -0,0 +1,17 @@ +$x = 1..10 + +function Renamed { + param ( + $x + ) + write-host "Printing $x" +} + +foreach ($number in $x) { + Renamed $number + + function testing_files { + write-host "------------------" + } +} +Renamed "99" diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionInnerIsNested.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionInnerIsNested.ps1 new file mode 100644 index 000000000..fe67c234d --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionInnerIsNested.ps1 @@ -0,0 +1,13 @@ +function outer { + function foo { + Write-Host 'Inside nested foo' + } + foo +} + +function foo { + Write-Host 'Inside top-level foo' +} + +outer +foo diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionInnerIsNestedRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionInnerIsNestedRenamed.ps1 new file mode 100644 index 000000000..8e698a3f1 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionInnerIsNestedRenamed.ps1 @@ -0,0 +1,13 @@ +function outer { + function Renamed { + Write-Host 'Inside nested foo' + } + Renamed +} + +function foo { + Write-Host 'Inside top-level foo' +} + +outer +foo diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionLoop.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionLoop.ps1 new file mode 100644 index 000000000..6973855a7 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionLoop.ps1 @@ -0,0 +1,6 @@ +for ($i = 0; $i -lt 2; $i++) { + function FunctionInLoop { + Write-Host "Function inside a loop" + } + FunctionInLoop +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionLoopRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionLoopRenamed.ps1 new file mode 100644 index 000000000..6e7632c46 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionLoopRenamed.ps1 @@ -0,0 +1,6 @@ +for ($i = 0; $i -lt 2; $i++) { + function Renamed { + Write-Host "Function inside a loop" + } + Renamed +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionMultipleOccurrences.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionMultipleOccurrences.ps1 new file mode 100644 index 000000000..76aeced88 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionMultipleOccurrences.ps1 @@ -0,0 +1,6 @@ +function foo { + Write-Host "Inside foo" +} + +foo +foo diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionMultipleOccurrencesRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionMultipleOccurrencesRenamed.ps1 new file mode 100644 index 000000000..cb78322be --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionMultipleOccurrencesRenamed.ps1 @@ -0,0 +1,6 @@ +function Renamed { + Write-Host "Inside foo" +} + +Renamed +Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionNestedRedefinition.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionNestedRedefinition.ps1 new file mode 100644 index 000000000..2454effe6 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionNestedRedefinition.ps1 @@ -0,0 +1,17 @@ +$x = 1..10 + +function testing_files { + param ( + $x + ) + write-host "Printing $x" +} + +foreach ($number in $x) { + testing_files $number + + function testing_files { + write-host "------------------" + } +} +testing_files "99" diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionNestedRedefinitionRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionNestedRedefinitionRenamed.ps1 new file mode 100644 index 000000000..304a97c87 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionNestedRedefinitionRenamed.ps1 @@ -0,0 +1,17 @@ +$x = 1..10 + +function testing_files { + param ( + $x + ) + write-host "Printing $x" +} + +foreach ($number in $x) { + testing_files $number + + function Renamed { + write-host "------------------" + } +} +testing_files "99" diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionOuterHasNestedFunction.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionOuterHasNestedFunction.ps1 new file mode 100644 index 000000000..966fdccb7 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionOuterHasNestedFunction.ps1 @@ -0,0 +1,7 @@ +function OuterFunction { + function NewInnerFunction { + Write-Host "This is the inner function" + } + NewInnerFunction +} +OuterFunction diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionOuterHasNestedFunctionRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionOuterHasNestedFunctionRenamed.ps1 new file mode 100644 index 000000000..98f89d16f --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionOuterHasNestedFunctionRenamed.ps1 @@ -0,0 +1,7 @@ +function Renamed { + function NewInnerFunction { + Write-Host "This is the inner function" + } + NewInnerFunction +} +Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSameName.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSameName.ps1 new file mode 100644 index 000000000..9849ee15a --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSameName.ps1 @@ -0,0 +1,8 @@ +function SameNameFunction { + Write-Host 'This is the outer function' + function SameNameFunction { + Write-Host 'This is the inner function' + } + SameNameFunction +} +SameNameFunction diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSameNameRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSameNameRenamed.ps1 new file mode 100644 index 000000000..e32595a64 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSameNameRenamed.ps1 @@ -0,0 +1,8 @@ +function SameNameFunction { + Write-Host 'This is the outer function' + function Renamed { + Write-Host 'This is the inner function' + } + Renamed +} +SameNameFunction diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionScriptblock.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionScriptblock.ps1 new file mode 100644 index 000000000..de0fd1737 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionScriptblock.ps1 @@ -0,0 +1,7 @@ +$scriptBlock = { + function FunctionInScriptBlock { + Write-Host "Inside a script block" + } + FunctionInScriptBlock +} +& $scriptBlock diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionScriptblockRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionScriptblockRenamed.ps1 new file mode 100644 index 000000000..727ca6f58 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionScriptblockRenamed.ps1 @@ -0,0 +1,7 @@ +$scriptBlock = { + function Renamed { + Write-Host "Inside a script block" + } + Renamed +} +& $scriptBlock diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSimple.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSimple.ps1 new file mode 100644 index 000000000..e13582550 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSimple.ps1 @@ -0,0 +1,5 @@ +function foo { + Write-Host "Inside foo" +} + +foo diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSimpleRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSimpleRenamed.ps1 new file mode 100644 index 000000000..26ffe37f9 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionSimpleRenamed.ps1 @@ -0,0 +1,5 @@ +function Renamed { + Write-Host "Inside foo" +} + +Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInnerFunction.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInnerFunction.ps1 new file mode 100644 index 000000000..1e77268e4 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInnerFunction.ps1 @@ -0,0 +1,7 @@ +function OuterFunction { + function NewInnerFunction { + Write-Host 'This is the inner function' + } + NewInnerFunction +} +OuterFunction diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInnerFunctionRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInnerFunctionRenamed.ps1 new file mode 100644 index 000000000..177d5940b --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInnerFunctionRenamed.ps1 @@ -0,0 +1,7 @@ +function OuterFunction { + function Renamed { + Write-Host 'This is the inner function' + } + Renamed +} +OuterFunction diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInternalCalls.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInternalCalls.ps1 new file mode 100644 index 000000000..eae1f3a19 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInternalCalls.ps1 @@ -0,0 +1,5 @@ +function FunctionWithInternalCalls { + Write-Host "This function calls itself" + FunctionWithInternalCalls +} +FunctionWithInternalCalls diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInternalCallsRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInternalCallsRenamed.ps1 new file mode 100644 index 000000000..4926dffb9 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWithInternalCallsRenamed.ps1 @@ -0,0 +1,5 @@ +function Renamed { + Write-Host "This function calls itself" + Renamed +} +Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWorkflow.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWorkflow.ps1 new file mode 100644 index 000000000..a214b934f --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWorkflow.ps1 @@ -0,0 +1,5 @@ +workflow AWorkflow { + "workflow" +} + +AWorkflow diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWorkflowRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWorkflowRenamed.ps1 new file mode 100644 index 000000000..9db546ede --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/FunctionWorkflowRenamed.ps1 @@ -0,0 +1,5 @@ +workflow Renamed { + "workflow" +} + +Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/_RefactorFunctionTestCases.cs b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/_RefactorFunctionTestCases.cs new file mode 100644 index 000000000..b58f6cc06 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Functions/_RefactorFunctionTestCases.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace PowerShellEditorServices.Test.Shared.Refactoring; + +public static class RefactorFunctionTestCases +{ + /// + /// Defines where functions should be renamed. These numbers are 1-based. + /// + public static readonly RenameTestTarget[] TestCases = + [ + new("FunctionSimple.ps1", Line: 1, Column: 11 ), + new("FunctionSimple.ps1", Line: 1, Column: 1, NoResult: true ), + new("FunctionSimple.ps1", Line: 2, Column: 4, NoResult: true ), + new("FunctionSimple.ps1", Line: 1, Column: 11, NewName: "Bad Name", ShouldThrow: true ), + new("FunctionCallWIthinStringExpression.ps1", Line: 1, Column: 10 ), + new("FunctionCmdlet.ps1", Line: 1, Column: 10 ), + new("FunctionFilter.ps1", Line: 1, Column: 8 ), + new("FunctionForeach.ps1", Line: 11, Column: 5 ), + new("FunctionForeachObject.ps1", Line: 11, Column: 5 ), + new("FunctionInnerIsNested.ps1", Line: 5, Column: 5 ), + new("FunctionLoop.ps1", Line: 5, Column: 5 ), + new("FunctionMultipleOccurrences.ps1", Line: 5, Column: 3 ), + new("FunctionNestedRedefinition.ps1", Line: 13, Column: 15 ), + new("FunctionOuterHasNestedFunction.ps1", Line: 1, Column: 10 ), + new("FunctionSameName.ps1", Line: 3, Column: 14 ), + new("FunctionScriptblock.ps1", Line: 5, Column: 5 ), + new("FunctionWithInnerFunction.ps1", Line: 5, Column: 5 ), + new("FunctionWithInternalCalls.ps1", Line: 3, Column: 6 ), + new("FunctionWorkflow.ps1", Line: 1, Column: 10 ), + ]; +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/RenameTestTarget.cs b/test/PowerShellEditorServices.Test.Shared/Refactoring/RenameTestTarget.cs new file mode 100644 index 000000000..f3e6277e6 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/RenameTestTarget.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +namespace PowerShellEditorServices.Test.Shared.Refactoring; + +/// +/// Describes a test case for renaming a symbol within a file. +/// +public class RenameTestTarget +{ + /// + /// The test case file name e.g. testScript.ps1 + /// + public string FileName { get; set; } = "UNKNOWN"; + /// + /// The line where the cursor should be positioned for the rename + /// + public int Line { get; set; } = -1; + /// + /// The column/character indent where the cursor should be positioned for the rename. + /// + public int Column { get; set; } = -1; + /// + /// What the target symbol represented by the line and column should be renamed to. Defaults to "Renamed" if not specified + /// + public string NewName = "Renamed"; + + public bool ShouldFail; + public bool ShouldThrow; + + /// The test case file name e.g. testScript.ps1 + /// The line where the cursor should be positioned for the rename + /// The column/character indent where the cursor should be positioned for the rename + /// What the target symbol represented by the line and column should be renamed to. Defaults to "Renamed" if not specified + /// This test case should return null (cannot be renamed) + /// This test case should throw a HandlerErrorException meaning user needs to be alerted in a custom way + public RenameTestTarget(string FileName, int Line, int Column, string NewName = "Renamed", bool NoResult = false, bool ShouldThrow = false) + { + this.FileName = FileName; + this.Line = Line; + this.Column = Column; + this.NewName = NewName; + this.ShouldFail = NoResult; + this.ShouldThrow = ShouldThrow; + } + public RenameTestTarget() { } + + public override string ToString() => $"{FileName.Substring(0, FileName.Length - 4)} {Line}:{Column} N:{NewName} F:{ShouldFail} T:{ShouldThrow}"; +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/RefactorUtilitiesData.cs b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/RefactorUtilitiesData.cs new file mode 100644 index 000000000..a2452620e --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/RefactorUtilitiesData.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace PowerShellEditorServices.Test.Shared.Refactoring +{ + internal static class RenameUtilitiesData + { + public static readonly RenameTestTarget GetVariableExpressionAst = new() + { + Column = 11, + Line = 15, + NewName = "Renamed", + FileName = "TestDetection.ps1" + }; + public static readonly RenameTestTarget GetVariableExpressionStartAst = new() + { + Column = 1, + Line = 15, + NewName = "Renamed", + FileName = "TestDetection.ps1" + }; + public static readonly RenameTestTarget GetVariableWithinParameterAst = new() + { + Column = 21, + Line = 3, + NewName = "Renamed", + FileName = "TestDetection.ps1" + }; + public static readonly RenameTestTarget GetHashTableKey = new() + { + Column = 9, + Line = 16, + NewName = "Renamed", + FileName = "TestDetection.ps1" + }; + public static readonly RenameTestTarget GetVariableWithinCommandAst = new() + { + Column = 29, + Line = 6, + NewName = "Renamed", + FileName = "TestDetection.ps1" + }; + public static readonly RenameTestTarget GetCommandParameterAst = new() + { + Column = 12, + Line = 21, + NewName = "Renamed", + FileName = "TestDetection.ps1" + }; + public static readonly RenameTestTarget GetFunctionDefinitionAst = new() + { + Column = 12, + Line = 1, + NewName = "Renamed", + FileName = "TestDetection.ps1" + }; + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDetection.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDetection.ps1 new file mode 100644 index 000000000..d12a8652f --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDetection.ps1 @@ -0,0 +1,21 @@ +function New-User { + param ( + [string]$Username, + [string]$password + ) + write-host $username + $password + + $splat= @{ + Username = "JohnDeer" + Password = "SomePassword" + } + New-User @splat +} + +$UserDetailsSplat= @{ + Username = "JohnDoe" + Password = "SomePassword" +} +New-User @UserDetailsSplat + +New-User -Username "JohnDoe" -Password "SomePassword" diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDetectionUnderFunctionDef.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDetectionUnderFunctionDef.ps1 new file mode 100644 index 000000000..6ef6e2652 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDetectionUnderFunctionDef.ps1 @@ -0,0 +1,15 @@ +function Write-Item($itemCount) { + $i = 1 + + while ($i -le $itemCount) { + $str = "Output $i" + Write-Output $str + + # In the gutter on the left, right click and select "Add Conditional Breakpoint" + # on the next line. Use the condition: $i -eq 25 + $i = $i + 1 + + # Slow down execution a bit so user can test the "Pause debugger" feature. + Start-Sleep -Milliseconds $DelayMilliseconds + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDotSourcingFalse.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDotSourcingFalse.ps1 new file mode 100644 index 000000000..d12a8652f --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDotSourcingFalse.ps1 @@ -0,0 +1,21 @@ +function New-User { + param ( + [string]$Username, + [string]$password + ) + write-host $username + $password + + $splat= @{ + Username = "JohnDeer" + Password = "SomePassword" + } + New-User @splat +} + +$UserDetailsSplat= @{ + Username = "JohnDoe" + Password = "SomePassword" +} +New-User @UserDetailsSplat + +New-User -Username "JohnDoe" -Password "SomePassword" diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDotSourcingTrue.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDotSourcingTrue.ps1 new file mode 100644 index 000000000..b1cd25e65 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Utilities/TestDotSourcingTrue.ps1 @@ -0,0 +1,7 @@ +$sb = { $var = 30 } +$shouldDotSource = Get-Random -Minimum 0 -Maximum 2 +if ($shouldDotSource) { + . $sb +} else { + & $sb +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/ParameterUndefinedFunction.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/ParameterUndefinedFunction.ps1 new file mode 100644 index 000000000..a07d73e79 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/ParameterUndefinedFunction.ps1 @@ -0,0 +1 @@ +FunctionThatIsNotDefinedInThisScope -TestParameter 'test' diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameter.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameter.ps1 new file mode 100644 index 000000000..49ca3a191 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameter.ps1 @@ -0,0 +1,10 @@ +function Get-foo { + param ( + [string]$string, + [int]$pos + ) + + return $string[$pos] + +} +Get-foo -string 'Hello' -pos -1 diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterRenamed.ps1 new file mode 100644 index 000000000..a3cd4fed5 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterRenamed.ps1 @@ -0,0 +1,10 @@ +function Get-foo { + param ( + [string]$Renamed, + [int]$pos + ) + + return $Renamed[$pos] + +} +Get-foo -Renamed 'Hello' -pos -1 diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterSplatted.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterSplatted.ps1 new file mode 100644 index 000000000..79dc6e7ee --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterSplatted.ps1 @@ -0,0 +1,21 @@ +function New-User { + param ( + [string]$Username, + [string]$password + ) + Write-Host $username + $password + + $splat = @{ + Username = 'JohnDeer' + Password = 'SomePassword' + } + New-User @splat +} + +$UserDetailsSplat = @{ + Username = 'JohnDoe' + Password = 'SomePassword' +} +New-User @UserDetailsSplat + +New-User -Username 'JohnDoe' -Password 'SomePassword' diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterSplattedRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterSplattedRenamed.ps1 new file mode 100644 index 000000000..176f51023 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableCommandParameterSplattedRenamed.ps1 @@ -0,0 +1,21 @@ +function New-User { + param ( + [string]$Renamed, + [string]$password + ) + Write-Host $Renamed + $password + + $splat = @{ + Renamed = 'JohnDeer' + Password = 'SomePassword' + } + New-User @splat +} + +$UserDetailsSplat = @{ + Renamed = 'JohnDoe' + Password = 'SomePassword' +} +New-User @UserDetailsSplat + +New-User -Renamed 'JohnDoe' -Password 'SomePassword' diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDefinedInParamBlock.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDefinedInParamBlock.ps1 new file mode 100644 index 000000000..737974e68 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDefinedInParamBlock.ps1 @@ -0,0 +1,12 @@ +$x = 1 +function test { + begin { + $x = 5 + } + process { + $x + } + end { + $x + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDefinedInParamBlockRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDefinedInParamBlockRenamed.ps1 new file mode 100644 index 000000000..abc8c54c8 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDefinedInParamBlockRenamed.ps1 @@ -0,0 +1,12 @@ +$x = 1 +function test { + begin { + $Renamed = 5 + } + process { + $Renamed + } + end { + $Renamed + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDotNotationFromInnerFunction.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDotNotationFromInnerFunction.ps1 new file mode 100644 index 000000000..126a2745d --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDotNotationFromInnerFunction.ps1 @@ -0,0 +1,21 @@ +$NeededTools = @{ + OpenSsl = 'openssl for macOS' + PowerShellGet = 'PowerShellGet latest' + InvokeBuild = 'InvokeBuild latest' +} + +function getMissingTools () { + $missingTools = @() + + if (needsOpenSsl) { + $missingTools += $NeededTools.OpenSsl + } + if (needsPowerShellGet) { + $missingTools += $NeededTools.PowerShellGet + } + if (needsInvokeBuild) { + $missingTools += $NeededTools.InvokeBuild + } + + return $missingTools +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDotNotationFromInnerFunctionRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDotNotationFromInnerFunctionRenamed.ps1 new file mode 100644 index 000000000..d8c478ec6 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableDotNotationFromInnerFunctionRenamed.ps1 @@ -0,0 +1,21 @@ +$Renamed = @{ + OpenSsl = 'openssl for macOS' + PowerShellGet = 'PowerShellGet latest' + InvokeBuild = 'InvokeBuild latest' +} + +function getMissingTools () { + $missingTools = @() + + if (needsOpenSsl) { + $missingTools += $Renamed.OpenSsl + } + if (needsPowerShellGet) { + $missingTools += $Renamed.PowerShellGet + } + if (needsInvokeBuild) { + $missingTools += $Renamed.InvokeBuild + } + + return $missingTools +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForeachDuplicateAssignment.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForeachDuplicateAssignment.ps1 new file mode 100644 index 000000000..ba03d8eb3 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForeachDuplicateAssignment.ps1 @@ -0,0 +1,14 @@ + +$a = 1..5 +$b = 6..10 +function test { + process { + foreach ($testvar in $a) { + $testvar + } + + foreach ($testvar in $b) { + $testvar + } + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForeachDuplicateAssignmentRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForeachDuplicateAssignmentRenamed.ps1 new file mode 100644 index 000000000..4467e88cb --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForeachDuplicateAssignmentRenamed.ps1 @@ -0,0 +1,14 @@ + +$a = 1..5 +$b = 6..10 +function test { + process { + foreach ($Renamed in $a) { + $Renamed + } + + foreach ($testvar in $b) { + $testvar + } + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForloopDuplicateAssignment.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForloopDuplicateAssignment.ps1 new file mode 100644 index 000000000..66844c960 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForloopDuplicateAssignment.ps1 @@ -0,0 +1,17 @@ + +$a = 1..5 +$b = 6..10 +function test { + process { + + $i=10 + + for ($i = 0; $i -lt $a.Count; $i++) { + $i + } + + for ($i = 0; $i -lt $a.Count; $i++) { + $i + } + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForloopDuplicateAssignmentRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForloopDuplicateAssignmentRenamed.ps1 new file mode 100644 index 000000000..ff61eb4f6 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInForloopDuplicateAssignmentRenamed.ps1 @@ -0,0 +1,17 @@ + +$a = 1..5 +$b = 6..10 +function test { + process { + + $i=10 + + for ($Renamed = 0; $Renamed -lt $a.Count; $Renamed++) { + $Renamed + } + + for ($i = 0; $i -lt $a.Count; $i++) { + $i + } + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInLoop.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInLoop.ps1 new file mode 100644 index 000000000..bf5af6be8 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInLoop.ps1 @@ -0,0 +1,4 @@ +$var = 10 +for ($i = 0; $i -lt $var; $i++) { + Write-Output "Count: $i" +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInLoopRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInLoopRenamed.ps1 new file mode 100644 index 000000000..cfc98f0d5 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInLoopRenamed.ps1 @@ -0,0 +1,4 @@ +$Renamed = 10 +for ($i = 0; $i -lt $Renamed; $i++) { + Write-Output "Count: $i" +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInParam.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInParam.ps1 new file mode 100644 index 000000000..436c6fbc8 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInParam.ps1 @@ -0,0 +1,28 @@ +param([int]$Count = 50, [int]$DelayMilliseconds = 200) + +function Write-Item($itemCount) { + $i = 1 + + while ($i -le $itemCount) { + $str = "Output $i" + Write-Output $str + + # In the gutter on the left, right click and select "Add Conditional Breakpoint" + # on the next line. Use the condition: $i -eq 25 + $i = $i + 1 + + # Slow down execution a bit so user can test the "Pause debugger" feature. + Start-Sleep -Milliseconds $DelayMilliseconds + } +} + +# Do-Work will be underlined in green if you haven't disable script analysis. +# Hover over the function name below to see the PSScriptAnalyzer warning that "Do-Work" +# doesn't use an approved verb. +function Do-Work($workCount) { + Write-Output 'Doing work...' + Write-Item $workcount + Write-Host 'Done!' +} + +Do-Work $Count diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInParamRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInParamRenamed.ps1 new file mode 100644 index 000000000..8127b6ced --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInParamRenamed.ps1 @@ -0,0 +1,28 @@ +param([int]$Count = 50, [int]$DelayMilliseconds = 200) + +function Write-Item($itemCount) { + $i = 1 + + while ($i -le $itemCount) { + $str = "Output $i" + Write-Output $str + + # In the gutter on the left, right click and select "Add Conditional Breakpoint" + # on the next line. Use the condition: $i -eq 25 + $i = $i + 1 + + # Slow down execution a bit so user can test the "Pause debugger" feature. + Start-Sleep -Milliseconds $DelayMilliseconds + } +} + +# Do-Work will be underlined in green if you haven't disable script analysis. +# Hover over the function name below to see the PSScriptAnalyzer warning that "Do-Work" +# doesn't use an approved verb. +function Do-Work($Renamed) { + Write-Output 'Doing work...' + Write-Item $Renamed + Write-Host 'Done!' +} + +Do-Work $Count diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInPipeline.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInPipeline.ps1 new file mode 100644 index 000000000..220a984b7 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInPipeline.ps1 @@ -0,0 +1,4 @@ +$oldVarName = 5 +1..10 | +Where-Object { $_ -le $oldVarName } | +Write-Output diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInPipelineRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInPipelineRenamed.ps1 new file mode 100644 index 000000000..dea826fbf --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInPipelineRenamed.ps1 @@ -0,0 +1,4 @@ +$Renamed = 5 +1..10 | +Where-Object { $_ -le $Renamed } | +Write-Output diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblock.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblock.ps1 new file mode 100644 index 000000000..3ddce4ece --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblock.ps1 @@ -0,0 +1,3 @@ +$var = 'Hello' +$action = { Write-Output $var } +&$action diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockRenamed.ps1 new file mode 100644 index 000000000..35ac2282a --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockRenamed.ps1 @@ -0,0 +1,3 @@ +$Renamed = 'Hello' +$action = { Write-Output $Renamed } +&$action diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockScoped.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockScoped.ps1 new file mode 100644 index 000000000..c37f20f5d --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockScoped.ps1 @@ -0,0 +1,3 @@ +$var = 'Hello' +$action = { $var = 'No'; Write-Output $var } +&$action diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockScopedRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockScopedRenamed.ps1 new file mode 100644 index 000000000..06e0db7a6 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableInScriptblockScopedRenamed.ps1 @@ -0,0 +1,3 @@ +$var = 'Hello' +$action = { $Renamed = 'No'; Write-Output $Renamed } +&$action diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedFunctionScriptblock.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedFunctionScriptblock.ps1 new file mode 100644 index 000000000..32efd9617 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedFunctionScriptblock.ps1 @@ -0,0 +1,9 @@ +function Sample{ + $var = 'Hello' + $sb = { + Write-Host $var + } + & $sb + $var +} +Sample diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedFunctionScriptblockRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedFunctionScriptblockRenamed.ps1 new file mode 100644 index 000000000..3d8fb1184 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedFunctionScriptblockRenamed.ps1 @@ -0,0 +1,9 @@ +function Sample{ + $Renamed = 'Hello' + $sb = { + Write-Host $Renamed + } + & $sb + $Renamed +} +Sample diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunction.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunction.ps1 new file mode 100644 index 000000000..3c6c22651 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunction.ps1 @@ -0,0 +1,7 @@ +$var = 10 +function TestFunction { + $var = 20 + Write-Output $var +} +TestFunction +Write-Output $var diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRefactorInner.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRefactorInner.ps1 new file mode 100644 index 000000000..3c6c22651 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRefactorInner.ps1 @@ -0,0 +1,7 @@ +$var = 10 +function TestFunction { + $var = 20 + Write-Output $var +} +TestFunction +Write-Output $var diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRefactorInnerRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRefactorInnerRenamed.ps1 new file mode 100644 index 000000000..d943f509a --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRefactorInnerRenamed.ps1 @@ -0,0 +1,7 @@ +$var = 10 +function TestFunction { + $Renamed = 20 + Write-Output $Renamed +} +TestFunction +Write-Output $var diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRenamed.ps1 new file mode 100644 index 000000000..3886cf867 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNestedScopeFunctionRenamed.ps1 @@ -0,0 +1,7 @@ +$Renamed = 10 +function TestFunction { + $var = 20 + Write-Output $var +} +TestFunction +Write-Output $Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNonParam.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNonParam.ps1 new file mode 100644 index 000000000..eaf921681 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNonParam.ps1 @@ -0,0 +1,8 @@ +$params = @{ + HtmlBodyContent = 'Testing JavaScript and CSS paths...' + JavaScriptPaths = '.\Assets\script.js' + StyleSheetPaths = '.\Assets\style.css' +} + +$view = New-VSCodeHtmlContentView -Title 'Test View' -ShowInColumn Two +Set-VSCodeHtmlContentView -View $view @params diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNonParamRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNonParamRenamed.ps1 new file mode 100644 index 000000000..31740427f --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableNonParamRenamed.ps1 @@ -0,0 +1,8 @@ +$params = @{ + HtmlBodyContent = 'Testing JavaScript and CSS paths...' + JavaScriptPaths = '.\Assets\script.js' + StyleSheetPaths = '.\Assets\style.css' +} + +$Renamed = New-VSCodeHtmlContentView -Title 'Test View' -ShowInColumn Two +Set-VSCodeHtmlContentView -View $Renamed @params diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableParameterCommandWithSameName.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableParameterCommandWithSameName.ps1 new file mode 100644 index 000000000..88d091f84 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableParameterCommandWithSameName.ps1 @@ -0,0 +1,56 @@ +function Test-AADConnected { + + param ( + [Parameter(Mandatory = $false)][String]$UserPrincipalName + ) + Begin {} + Process { + [HashTable]$ConnectAADSplat = @{} + if ($UserPrincipalName) { + $ConnectAADSplat = @{ + AccountId = $UserPrincipalName + ErrorAction = 'Stop' + } + } + } +} + +function Set-MSolUMFA{ + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$UserPrincipalName, + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][ValidateSet('Enabled', 'Disabled', 'Enforced')][String]$StrongAuthenticationRequiremets + ) + begin{ + # Check if connected to Msol Session already + if (!(Test-MSolConnected)) { + Write-Verbose('No existing Msol session detected') + try { + Write-Verbose('Initiating connection to Msol') + Connect-MsolService -ErrorAction Stop + Write-Verbose('Connected to Msol successfully') + } catch{ + return Write-Error($_.Exception.Message) + } + } + if (!(Get-MsolUser -MaxResults 1 -ErrorAction Stop)){ + return Write-Error('Insufficient permissions to set MFA') + } + } + Process{ + # Get the time and calc 2 min to the future + $TimeStart = Get-Date + $TimeEnd = $timeStart.addminutes(1) + $Finished = $false + #Loop to check if the user exists already + if ($PSCmdlet.ShouldProcess($UserPrincipalName, 'StrongAuthenticationRequiremets = ' + $StrongAuthenticationRequiremets)) { + } + } + End{} +} + +Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements $sta -ErrorAction Stop +$UserPrincipalName = 'Bob' +if ($UserPrincipalName) { + $SplatTestAADConnected.Add('UserPrincipalName', $UserPrincipalName) +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableParameterCommandWithSameNameRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableParameterCommandWithSameNameRenamed.ps1 new file mode 100644 index 000000000..fb21baa6e --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableParameterCommandWithSameNameRenamed.ps1 @@ -0,0 +1,56 @@ +function Test-AADConnected { + + param ( + [Parameter(Mandatory = $false)][String]$Renamed + ) + Begin {} + Process { + [HashTable]$ConnectAADSplat = @{} + if ($Renamed) { + $ConnectAADSplat = @{ + AccountId = $Renamed + ErrorAction = 'Stop' + } + } + } +} + +function Set-MSolUMFA{ + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$UserPrincipalName, + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][ValidateSet('Enabled', 'Disabled', 'Enforced')][String]$StrongAuthenticationRequiremets + ) + begin{ + # Check if connected to Msol Session already + if (!(Test-MSolConnected)) { + Write-Verbose('No existing Msol session detected') + try { + Write-Verbose('Initiating connection to Msol') + Connect-MsolService -ErrorAction Stop + Write-Verbose('Connected to Msol successfully') + } catch{ + return Write-Error($_.Exception.Message) + } + } + if (!(Get-MsolUser -MaxResults 1 -ErrorAction Stop)){ + return Write-Error('Insufficient permissions to set MFA') + } + } + Process{ + # Get the time and calc 2 min to the future + $TimeStart = Get-Date + $TimeEnd = $timeStart.addminutes(1) + $Finished = $false + #Loop to check if the user exists already + if ($PSCmdlet.ShouldProcess($UserPrincipalName, 'StrongAuthenticationRequiremets = ' + $StrongAuthenticationRequiremets)) { + } + } + End{} +} + +Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements $sta -ErrorAction Stop +$UserPrincipalName = 'Bob' +if ($UserPrincipalName) { + $SplatTestAADConnected.Add('UserPrincipalName', $UserPrincipalName) +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableRedefinition.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableRedefinition.ps1 new file mode 100644 index 000000000..1063dc887 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableRedefinition.ps1 @@ -0,0 +1,3 @@ +$var = 10 +$var = 20 +Write-Output $var diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableRedefinitionRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableRedefinitionRenamed.ps1 new file mode 100644 index 000000000..29f3f87c7 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableRedefinitionRenamed.ps1 @@ -0,0 +1,3 @@ +$Renamed = 10 +$Renamed = 20 +Write-Output $Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableScriptWithParamBlock.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableScriptWithParamBlock.ps1 new file mode 100644 index 000000000..1a14d2d8b --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableScriptWithParamBlock.ps1 @@ -0,0 +1,28 @@ +param([int]$Count = 50, [int]$DelayMilliSeconds = 200) + +function Write-Item($itemCount) { + $i = 1 + + while ($i -le $itemCount) { + $str = "Output $i" + Write-Output $str + + # In the gutter on the left, right click and select "Add Conditional Breakpoint" + # on the next line. Use the condition: $i -eq 25 + $i = $i + 1 + + # Slow down execution a bit so user can test the "Pause debugger" feature. + Start-Sleep -Milliseconds $DelayMilliSeconds + } +} + +# Do-Work will be underlined in green if you haven't disable script analysis. +# Hover over the function name below to see the PSScriptAnalyzer warning that "Do-Work" +# doesn't use an approved verb. +function Do-Work($workCount) { + Write-Output 'Doing work...' + Write-Item $workcount + Write-Host 'Done!' +} + +Do-Work $Count diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableScriptWithParamBlockRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableScriptWithParamBlockRenamed.ps1 new file mode 100644 index 000000000..aa9e325d0 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableScriptWithParamBlockRenamed.ps1 @@ -0,0 +1,28 @@ +param([int]$Count = 50, [int]$Renamed = 200) + +function Write-Item($itemCount) { + $i = 1 + + while ($i -le $itemCount) { + $str = "Output $i" + Write-Output $str + + # In the gutter on the left, right click and select "Add Conditional Breakpoint" + # on the next line. Use the condition: $i -eq 25 + $i = $i + 1 + + # Slow down execution a bit so user can test the "Pause debugger" feature. + Start-Sleep -Milliseconds $Renamed + } +} + +# Do-Work will be underlined in green if you haven't disable script analysis. +# Hover over the function name below to see the PSScriptAnalyzer warning that "Do-Work" +# doesn't use an approved verb. +function Do-Work($workCount) { + Write-Output 'Doing work...' + Write-Item $workcount + Write-Host 'Done!' +} + +Do-Work $Count diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleAssignment.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleAssignment.ps1 new file mode 100644 index 000000000..6097d4154 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleAssignment.ps1 @@ -0,0 +1,2 @@ +$var = 10 +Write-Output $var diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleAssignmentRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleAssignmentRenamed.ps1 new file mode 100644 index 000000000..3962ce503 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleAssignmentRenamed.ps1 @@ -0,0 +1,2 @@ +$Renamed = 10 +Write-Output $Renamed diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleFunctionParameter.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleFunctionParameter.ps1 new file mode 100644 index 000000000..ca370b580 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleFunctionParameter.ps1 @@ -0,0 +1,18 @@ +$x = 1..10 + +function testing_files { + + param ( + $x + ) + Write-Host "Printing $x" +} + +foreach ($number in $x) { + testing_files $number + + function testing_files { + Write-Host '------------------' + } +} +testing_files '99' diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleFunctionParameterRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleFunctionParameterRenamed.ps1 new file mode 100644 index 000000000..0e022321f --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableSimpleFunctionParameterRenamed.ps1 @@ -0,0 +1,18 @@ +$x = 1..10 + +function testing_files { + + param ( + $Renamed + ) + Write-Host "Printing $Renamed" +} + +foreach ($number in $x) { + testing_files $number + + function testing_files { + Write-Host '------------------' + } +} +testing_files '99' diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinCommandAstScriptBlock.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinCommandAstScriptBlock.ps1 new file mode 100644 index 000000000..4d2f47f74 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinCommandAstScriptBlock.ps1 @@ -0,0 +1,3 @@ +# Not same +$var = 10 +Get-ChildItem | Rename-Item -NewName { $var = $_.FullName + (Get-Random); $var } diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinCommandAstScriptBlockRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinCommandAstScriptBlockRenamed.ps1 new file mode 100644 index 000000000..56c4b4965 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinCommandAstScriptBlockRenamed.ps1 @@ -0,0 +1,3 @@ +# Not same +$var = 10 +Get-ChildItem | Rename-Item -NewName { $Renamed = $_.FullName + (Get-Random); $Renamed } diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinForeachObject.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinForeachObject.ps1 new file mode 100644 index 000000000..89ab6ca1d --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinForeachObject.ps1 @@ -0,0 +1,5 @@ +# Same +$var = 10 +0..10 | ForEach-Object { + $var += 5 +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinForeachObjectRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinForeachObjectRenamed.ps1 new file mode 100644 index 000000000..12f936b61 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinForeachObjectRenamed.ps1 @@ -0,0 +1,5 @@ +# Same +$Renamed = 10 +0..10 | ForEach-Object { + $Renamed += 5 +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinHashtableExpression.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinHashtableExpression.ps1 new file mode 100644 index 000000000..cb3f58b1c --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinHashtableExpression.ps1 @@ -0,0 +1,3 @@ +# Not same +$var = 10 +0..10 | Select-Object @{n='SomeProperty';e={ $var = 30 * $_; $var }} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinHashtableExpressionRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinHashtableExpressionRenamed.ps1 new file mode 100644 index 000000000..0ee85fa2d --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableWithinHashtableExpressionRenamed.ps1 @@ -0,0 +1,3 @@ +# Not same +$var = 10 +0..10 | Select-Object @{n='SomeProperty';e={ $Renamed = 30 * $_; $Renamed }} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableusedInWhileLoop.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableusedInWhileLoop.ps1 new file mode 100644 index 000000000..6ef6e2652 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableusedInWhileLoop.ps1 @@ -0,0 +1,15 @@ +function Write-Item($itemCount) { + $i = 1 + + while ($i -le $itemCount) { + $str = "Output $i" + Write-Output $str + + # In the gutter on the left, right click and select "Add Conditional Breakpoint" + # on the next line. Use the condition: $i -eq 25 + $i = $i + 1 + + # Slow down execution a bit so user can test the "Pause debugger" feature. + Start-Sleep -Milliseconds $DelayMilliseconds + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableusedInWhileLoopRenamed.ps1 b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableusedInWhileLoopRenamed.ps1 new file mode 100644 index 000000000..7a5a46479 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/VariableusedInWhileLoopRenamed.ps1 @@ -0,0 +1,15 @@ +function Write-Item($itemCount) { + $Renamed = 1 + + while ($Renamed -le $itemCount) { + $str = "Output $Renamed" + Write-Output $str + + # In the gutter on the left, right click and select "Add Conditional Breakpoint" + # on the next line. Use the condition: $i -eq 25 + $Renamed = $Renamed + 1 + + # Slow down execution a bit so user can test the "Pause debugger" feature. + Start-Sleep -Milliseconds $DelayMilliseconds + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/_RefactorVariableTestCases.cs b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/_RefactorVariableTestCases.cs new file mode 100644 index 000000000..2f54ed758 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Refactoring/Variables/_RefactorVariableTestCases.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +namespace PowerShellEditorServices.Test.Shared.Refactoring; +public static class RefactorVariableTestCases +{ + public static readonly RenameTestTarget[] TestCases = + [ + new ("VariableSimpleAssignment.ps1", Line: 1, Column: 1), + new ("VariableSimpleAssignment.ps1", Line: 1, Column: 1, NewName: "$Renamed"), + new ("VariableSimpleAssignment.ps1", Line: 1, Column: 1, NewName: "$Bad Name", ShouldThrow: true), + new ("VariableSimpleAssignment.ps1", Line: 1, Column: 1, NewName: "Bad Name", ShouldThrow: true), + new ("VariableSimpleAssignment.ps1", Line: 1, Column: 6, NoResult: true), + new ("VariableCommandParameter.ps1", Line: 3, Column: 17), + new ("VariableCommandParameter.ps1", Line: 3, Column: 17, NewName: "-Renamed"), + new ("VariableCommandParameter.ps1", Line: 10, Column: 10), + new ("VariableCommandParameterSplatted.ps1", Line: 3, Column: 19 ), + new ("VariableCommandParameterSplatted.ps1", Line: 21, Column: 12), + new ("VariableDefinedInParamBlock.ps1", Line: 10, Column: 9), + new ("VariableDotNotationFromInnerFunction.ps1", Line: 1, Column: 1), + new ("VariableDotNotationFromInnerFunction.ps1", Line: 11, Column: 26), + new ("VariableInForeachDuplicateAssignment.ps1", Line: 6, Column: 18), + new ("VariableInForloopDuplicateAssignment.ps1", Line: 9, Column: 14), + new ("VariableInLoop.ps1", Line: 1, Column: 1), + new ("VariableInParam.ps1", Line: 24, Column: 16), + new ("VariableInPipeline.ps1", Line: 3, Column: 23), + new ("VariableInScriptblockScoped.ps1", Line: 2, Column: 16), + new ("VariableNestedFunctionScriptblock.ps1", Line: 4, Column: 20), + new ("VariableNestedScopeFunction.ps1", Line: 1, Column: 1), + new ("VariableNestedScopeFunctionRefactorInner.ps1", Line: 3, Column: 5), + new ("VariableNonParam.ps1", Line: 7, Column: 1), + new ("VariableParameterCommandWithSameName.ps1", Line: 9, Column: 13), + new ("VariableRedefinition.ps1", Line: 1, Column: 1), + new ("VariableScriptWithParamBlock.ps1", Line: 1, Column: 30), + new ("VariableSimpleFunctionParameter.ps1", Line: 6, Column: 9), + new ("VariableusedInWhileLoop.ps1", Line: 2, Column: 5), + new ("VariableWithinCommandAstScriptBlock.ps1", Line: 3, Column: 75), + new ("VariableWithinForeachObject.ps1", Line: 2, Column: 1), + new ("VariableWithinHashtableExpression.ps1", Line: 3, Column: 46), + ]; +} diff --git a/test/PowerShellEditorServices.Test.Shared/SymbolDetails/SymbolDetails.ps1 b/test/PowerShellEditorServices.Test.Shared/SymbolDetails/SymbolDetails.ps1 index 31882d033..3143ede56 100644 --- a/test/PowerShellEditorServices.Test.Shared/SymbolDetails/SymbolDetails.ps1 +++ b/test/PowerShellEditorServices.Test.Shared/SymbolDetails/SymbolDetails.ps1 @@ -1,4 +1,6 @@ -Get-Process -ComputerName "test-computer" +Expand-Archive -Path $TEMP +# References Test uses this one +Get-Process -Name 'powershell*' <# .Synopsis @@ -10,19 +12,18 @@ .EXAMPLE Another example of how to use this cmdlet #> -function Get-Thing -{ +function Get-Thing { [Alias()] [OutputType([int])] Param ( # Param1 help description - [Parameter(Mandatory=$true, - ValueFromPipelineByPropertyName=$true, - Position=0)] + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] $Name ) - + Begin { } diff --git a/test/PowerShellEditorServices.Test.Shared/packages.lock.json b/test/PowerShellEditorServices.Test.Shared/packages.lock.json deleted file mode 100644 index c6efa4b81..000000000 --- a/test/PowerShellEditorServices.Test.Shared/packages.lock.json +++ /dev/null @@ -1,451 +0,0 @@ -{ - "version": 1, - "dependencies": { - ".NETStandard,Version=v2.0": { - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0", - "System.ComponentModel.Annotations": "5.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "PowerShellStandard.Library": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "Serilog": { - "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "7.0.2" - } - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Transitive", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", - "dependencies": { - "System.Memory": "4.5.4" - } - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.IO.Pipes.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "Microsoft.PowerShell.EditorServices": { - "type": "Project", - "dependencies": { - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )", - "Microsoft.Extensions.Logging": "[8.0.0, )", - "OmniSharp.Extensions.DebugAdapter.Server": "[0.19.9, )", - "OmniSharp.Extensions.LanguageServer": "[0.19.9, )", - "PowerShellStandard.Library": "[5.1.1, )", - "Serilog": "[3.1.1, )", - "Serilog.Extensions.Logging": "[8.0.0, )", - "Serilog.Sinks.Async": "[1.5.0, )", - "Serilog.Sinks.File": "[5.0.0, )", - "System.IO.Pipes.AccessControl": "[5.0.0, )", - "System.Security.Principal": "[4.3.0, )", - "System.Security.Principal.Windows": "[5.0.0, )" - } - } - } - } -} \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index 30b020c30..3ba16008d 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -33,21 +33,21 @@ internal class TestReadLine : IReadLine } [Trait("Category", "DebugService")] - public class DebugServiceTests : IDisposable + public class DebugServiceTests : IAsyncLifetime { - private readonly PsesInternalHost psesHost; - private readonly BreakpointService breakpointService; - private readonly DebugService debugService; + private PsesInternalHost psesHost; + private BreakpointService breakpointService; + private DebugService debugService; private readonly BlockingCollection debuggerStoppedQueue = new(); - private readonly WorkspaceService workspace; - private readonly ScriptFile debugScriptFile; - private readonly ScriptFile oddPathScriptFile; - private readonly ScriptFile variableScriptFile; + private WorkspaceService workspace; + private ScriptFile debugScriptFile; + private ScriptFile oddPathScriptFile; + private ScriptFile variableScriptFile; private readonly TestReadLine testReadLine = new(); - public DebugServiceTests() + public async Task InitializeAsync() { - psesHost = PsesHostFactory.Create(NullLoggerFactory.Instance); + psesHost = await PsesHostFactory.Create(NullLoggerFactory.Instance); // This is required for remote debugging, but we call it here to end up in the same // state as the usual startup path. psesHost.DebugContext.EnableDebugMode(); @@ -76,26 +76,23 @@ public DebugServiceTests() variableScriptFile = GetDebugScript("VariableTest.ps1"); } - public void Dispose() + public async Task DisposeAsync() { debugService.Abort(); + await Task.Run(psesHost.StopAsync); debuggerStoppedQueue.Dispose(); -#pragma warning disable VSTHRD002 - psesHost.StopAsync().Wait(); -#pragma warning restore VSTHRD002 - GC.SuppressFinalize(this); } /// - /// This event handler lets us test that the debugger stopped or paused as expected. It will - /// deadlock if called in the PSES Pipeline Thread, which can easily happen in this test - /// code when methods on are called. Hence we treat this test - /// code like UI code and use 'ConfigureAwait(true)' or 'Task.Run(...)' to ensure we stay - /// OFF the pipeline thread. + /// This event handler lets us test that the debugger stopped or paused + /// as expected. It will deadlock if called in the PSES Pipeline Thread. + /// Hence we use 'Task.Run(...)' when accessing the queue to ensure we + /// stay OFF the pipeline thread. /// /// /// - private void OnDebuggerStopped(object sender, DebuggerStoppedEventArgs e) => debuggerStoppedQueue.Add(e); + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "This intentionally fires and forgets on another thread.")] + private void OnDebuggerStopped(object sender, DebuggerStoppedEventArgs e) => Task.Run(() => debuggerStoppedQueue.Add(e)); private ScriptFile GetDebugScript(string fileName) => workspace.GetFile(TestUtilities.GetSharedPath(Path.Combine("Debugging", fileName))); @@ -118,20 +115,20 @@ private Task ExecuteScriptFileAsync(string scriptFilePath, params string[] args) private Task ExecuteVariableScriptFileAsync() => ExecuteScriptFileAsync(variableScriptFile.FilePath); - private void AssertDebuggerPaused() + private async Task AssertDebuggerPaused() { using CancellationTokenSource cts = new(60000); - DebuggerStoppedEventArgs eventArgs = debuggerStoppedQueue.Take(cts.Token); + DebuggerStoppedEventArgs eventArgs = await Task.Run(() => debuggerStoppedQueue.Take(cts.Token)); Assert.Empty(eventArgs.OriginalEvent.Breakpoints); } - private void AssertDebuggerStopped( + private async Task AssertDebuggerStopped( string scriptPath = "", int lineNumber = -1, CommandBreakpointDetails commandBreakpointDetails = default) { - using CancellationTokenSource cts = new(60000); - DebuggerStoppedEventArgs eventArgs = debuggerStoppedQueue.Take(cts.Token); + using CancellationTokenSource cts = new(30000); + DebuggerStoppedEventArgs eventArgs = await Task.Run(() => debuggerStoppedQueue.Take(cts.Token)); Assert.True(psesHost.DebugContext.IsStopped); @@ -176,8 +173,8 @@ await debugService.SetCommandBreakpointsAsync( Task> executeTask = psesHost.ExecutePSCommandAsync( new PSCommand().AddScript("Get-Random -SetSeed 42 -Maximum 100"), CancellationToken.None); - AssertDebuggerStopped("", 1); - await Task.Run(debugService.Continue); + await AssertDebuggerStopped("", 1); + debugService.Continue(); Assert.Equal(17, (await executeTask)[0]); StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync(); @@ -204,7 +201,7 @@ await debugService.SetCommandBreakpointsAsync( public async Task DebuggerAcceptsScriptArgs(string[] args) { IReadOnlyList breakpoints = await debugService.SetLineBreakpointsAsync( - oddPathScriptFile, + oddPathScriptFile.FilePath, new[] { BreakpointDetails.Create(oddPathScriptFile.FilePath, 3) }); Assert.Single(breakpoints); @@ -218,7 +215,7 @@ public async Task DebuggerAcceptsScriptArgs(string[] args) Task _ = ExecuteScriptFileAsync(oddPathScriptFile.FilePath, args); - AssertDebuggerStopped(oddPathScriptFile.FilePath, 3); + await AssertDebuggerStopped(oddPathScriptFile.FilePath, 3); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -285,7 +282,7 @@ public async Task DebuggerStopsOnFunctionBreakpoints() new[] { CommandBreakpointDetails.Create("Write-Host") }); Task _ = ExecuteDebugFileAsync(); - AssertDebuggerStopped(debugScriptFile.FilePath, 6); + await AssertDebuggerStopped(debugScriptFile.FilePath, 6); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -296,8 +293,8 @@ public async Task DebuggerStopsOnFunctionBreakpoints() Assert.Equal("1", i.ValueString); // The function breakpoint should fire the next time through the loop. - await Task.Run(debugService.Continue); - AssertDebuggerStopped(debugScriptFile.FilePath, 6); + debugService.Continue(); + await AssertDebuggerStopped(debugScriptFile.FilePath, 6); variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -313,7 +310,7 @@ public async Task DebuggerSetsAndClearsLineBreakpoints() { IReadOnlyList breakpoints = await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 5), BreakpointDetails.Create(debugScriptFile.FilePath, 10) @@ -326,7 +323,7 @@ await debugService.SetLineBreakpointsAsync( Assert.Equal(10, breakpoints[1].LineNumber); breakpoints = await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 2) }); confirmedBreakpoints = await GetConfirmedBreakpoints(debugScriptFile); @@ -334,7 +331,7 @@ await debugService.SetLineBreakpointsAsync( Assert.Equal(2, breakpoints[0].LineNumber); await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, Array.Empty()); IReadOnlyList remainingBreakpoints = await GetConfirmedBreakpoints(debugScriptFile); @@ -345,16 +342,16 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerStopsOnLineBreakpoints() { await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 5), BreakpointDetails.Create(debugScriptFile.FilePath, 7) }); Task _ = ExecuteDebugFileAsync(); - AssertDebuggerStopped(debugScriptFile.FilePath, 5); - await Task.Run(debugService.Continue); - AssertDebuggerStopped(debugScriptFile.FilePath, 7); + await AssertDebuggerStopped(debugScriptFile.FilePath, 5); + debugService.Continue(); + await AssertDebuggerStopped(debugScriptFile.FilePath, 7); } [Fact] @@ -364,13 +361,13 @@ public async Task DebuggerStopsOnConditionalBreakpoints() const int breakpointValue2 = 20; await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 7, null, $"$i -eq {breakpointValue1} -or $i -eq {breakpointValue2}"), }); Task _ = ExecuteDebugFileAsync(); - AssertDebuggerStopped(debugScriptFile.FilePath, 7); + await AssertDebuggerStopped(debugScriptFile.FilePath, 7); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -382,8 +379,8 @@ await debugService.SetLineBreakpointsAsync( // The conditional breakpoint should not fire again, until the value of // i reaches breakpointValue2. - await Task.Run(debugService.Continue); - AssertDebuggerStopped(debugScriptFile.FilePath, 7); + debugService.Continue(); + await AssertDebuggerStopped(debugScriptFile.FilePath, 7); variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -400,13 +397,13 @@ public async Task DebuggerStopsOnHitConditionBreakpoint() const int hitCount = 5; await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 6, null, null, $"{hitCount}"), }); Task _ = ExecuteDebugFileAsync(); - AssertDebuggerStopped(debugScriptFile.FilePath, 6); + await AssertDebuggerStopped(debugScriptFile.FilePath, 6); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -423,11 +420,11 @@ public async Task DebuggerStopsOnConditionalAndHitConditionBreakpoint() const int hitCount = 5; await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 6, null, "$i % 2 -eq 0", $"{hitCount}") }); Task _ = ExecuteDebugFileAsync(); - AssertDebuggerStopped(debugScriptFile.FilePath, 6); + await AssertDebuggerStopped(debugScriptFile.FilePath, 6); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -444,7 +441,7 @@ public async Task DebuggerProvidesMessageForInvalidConditionalBreakpoint() { IReadOnlyList breakpoints = await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, new[] { // TODO: Add this breakpoint back when it stops moving around?! The ordering // of these two breakpoints seems to do with which framework executes the @@ -472,7 +469,7 @@ public async Task DebuggerFindsParsableButInvalidSimpleBreakpointConditions() { IReadOnlyList breakpoints = await debugService.SetLineBreakpointsAsync( - debugScriptFile, + debugScriptFile.FilePath, new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 5, column: null, condition: "$i == 100"), BreakpointDetails.Create(debugScriptFile.FilePath, 7, column: null, condition: "$i > 100") @@ -495,18 +492,16 @@ public async Task DebuggerBreaksWhenRequested() IReadOnlyList confirmedBreakpoints = await GetConfirmedBreakpoints(debugScriptFile); Assert.Empty(confirmedBreakpoints); Task _ = ExecuteDebugFileAsync(); - // NOTE: This must be run on a separate thread so the async event handlers can fire. - await Task.Run(debugService.Break); - AssertDebuggerPaused(); + debugService.Break(); + await AssertDebuggerPaused(); } [Fact] public async Task DebuggerRunsCommandsWhileStopped() { Task _ = ExecuteDebugFileAsync(); - // NOTE: This must be run on a separate thread so the async event handlers can fire. - await Task.Run(debugService.Break); - AssertDebuggerPaused(); + debugService.Break(); + await AssertDebuggerPaused(); // Try running a command from outside the pipeline thread Task> executeTask = psesHost.ExecutePSCommandAsync( @@ -526,16 +521,17 @@ await debugService.SetCommandBreakpointsAsync( ScriptFile testScript = GetDebugScript("PSDebugContextTest.ps1"); Task _ = ExecuteScriptFileAsync(testScript.FilePath); - AssertDebuggerStopped(testScript.FilePath, 11); + await AssertDebuggerStopped(testScript.FilePath, 11); VariableDetails prompt = await debugService.EvaluateExpressionAsync("prompt", false, CancellationToken.None); Assert.Equal("True > ", prompt.ValueString); } - [SkippableFact] - public async Task DebuggerBreaksInUntitledScript() + [Theory] + [InlineData("Command")] + [InlineData("Line")] + public async Task DebuggerBreaksInUntitledScript(string breakpointType) { - Skip.IfNot(VersionUtils.PSEdition == "Core", "Untitled script breakpoints only supported in PowerShell Core"); const string contents = "Write-Output $($MyInvocation.Line)"; const string scriptPath = "untitled:Untitled-1"; Assert.True(ScriptFile.IsUntitledPath(scriptPath)); @@ -544,14 +540,23 @@ public async Task DebuggerBreaksInUntitledScript() Assert.Equal(contents, scriptFile.Contents); Assert.True(workspace.TryGetFile(scriptPath, out ScriptFile _)); - await debugService.SetCommandBreakpointsAsync( - new[] { CommandBreakpointDetails.Create("Write-Output") }); + if (breakpointType == "Command") + { + await debugService.SetCommandBreakpointsAsync( + new[] { CommandBreakpointDetails.Create("Write-Output") }); + } + else + { + await debugService.SetLineBreakpointsAsync( + scriptFile.FilePath, + new[] { BreakpointDetails.Create(scriptPath, 1) }); + } ConfigurationDoneHandler configurationDoneHandler = new( - NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null, psesHost); + NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null); Task _ = configurationDoneHandler.LaunchScriptAsync(scriptPath); - AssertDebuggerStopped(scriptPath, 1); + await AssertDebuggerStopped(scriptPath, 1); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.CommandVariablesName); VariableDetailsBase myInvocation = Array.Find(variables, v => v.Name == "$MyInvocation"); @@ -570,7 +575,7 @@ await debugService.SetCommandBreakpointsAsync( public async Task RecordsF5CommandInPowerShellHistory() { ConfigurationDoneHandler configurationDoneHandler = new( - NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null, psesHost); + NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null); await configurationDoneHandler.LaunchScriptAsync(debugScriptFile.FilePath); IReadOnlyList historyResult = await psesHost.ExecutePSCommandAsync( @@ -610,7 +615,7 @@ public async Task RecordsF8CommandInHistory() public async Task OddFilePathsLaunchCorrectly() { ConfigurationDoneHandler configurationDoneHandler = new( - NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null, psesHost); + NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null); await configurationDoneHandler.LaunchScriptAsync(oddPathScriptFile.FilePath); IReadOnlyList historyResult = await psesHost.ExecutePSCommandAsync( @@ -625,11 +630,11 @@ public async Task OddFilePathsLaunchCorrectly() public async Task DebuggerVariableStringDisplaysCorrectly() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 8) }); Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -643,11 +648,11 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerGetsVariables() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 21) }); Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -693,11 +698,11 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerSetsVariablesNoConversion() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 14) }); Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); VariableScope[] scopes = debugService.GetVariableScopes(0); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -723,8 +728,8 @@ await debugService.SetLineBreakpointsAsync( // The above just tests that the debug service returns the correct new value string. // Let's step the debugger and make sure the values got set to the new values. - await Task.Run(debugService.StepOver); - AssertDebuggerStopped(variableScriptFile.FilePath); + debugService.StepOver(); + await AssertDebuggerStopped(variableScriptFile.FilePath); // Test set of a local string variable (not strongly typed) variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -746,12 +751,12 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerSetsVariablesWithConversion() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 14) }); // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); VariableScope[] scopes = debugService.GetVariableScopes(0); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -779,8 +784,8 @@ await debugService.SetLineBreakpointsAsync( // The above just tests that the debug service returns the correct new value string. // Let's step the debugger and make sure the values got set to the new values. - await Task.Run(debugService.StepOver); - AssertDebuggerStopped(variableScriptFile.FilePath); + debugService.StepOver(); + await AssertDebuggerStopped(variableScriptFile.FilePath); // Test set of a local string variable (not strongly typed but force conversion) variables = await GetVariables(VariableContainerDetails.LocalScopeName); @@ -802,12 +807,12 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerVariableEnumDisplaysCorrectly() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 15) }); // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync(); VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None); @@ -822,12 +827,12 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerVariableHashtableDisplaysCorrectly() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 11) }); // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync(); VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None); @@ -855,12 +860,12 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerVariableNullStringDisplaysCorrectly() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 16) }); // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync(); VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None); @@ -875,12 +880,12 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerVariablePSObjectDisplaysCorrectly() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 17) }); // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync(); VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None); @@ -907,7 +912,7 @@ public async Task DebuggerEnumerableShowsRawView() // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(commandBreakpointDetails: breakpoint); + await AssertDebuggerStopped(commandBreakpointDetails: breakpoint); VariableDetailsBase simpleArrayVar = Array.Find( await GetVariables(VariableContainerDetails.ScriptScopeName), @@ -964,7 +969,7 @@ public async Task DebuggerDictionaryShowsRawView() // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(commandBreakpointDetails: breakpoint); + await AssertDebuggerStopped(commandBreakpointDetails: breakpoint); VariableDetailsBase simpleDictionaryVar = Array.Find( await GetVariables(VariableContainerDetails.ScriptScopeName), @@ -1027,7 +1032,7 @@ public async Task DebuggerDerivedDictionaryPropertyInRawView() // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(commandBreakpointDetails: breakpoint); + await AssertDebuggerStopped(commandBreakpointDetails: breakpoint); VariableDetailsBase sortedDictionaryVar = Array.Find( await GetVariables(VariableContainerDetails.ScriptScopeName), @@ -1071,12 +1076,12 @@ await GetVariables(VariableContainerDetails.ScriptScopeName), public async Task DebuggerVariablePSCustomObjectDisplaysCorrectly() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 18) }); // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync(); VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None); @@ -1100,12 +1105,12 @@ await debugService.SetLineBreakpointsAsync( public async Task DebuggerVariableProcessObjectDisplaysCorrectly() { await debugService.SetLineBreakpointsAsync( - variableScriptFile, + variableScriptFile.FilePath, new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 19) }); // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(variableScriptFile.FilePath); + await AssertDebuggerStopped(variableScriptFile.FilePath); StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync(); VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None); @@ -1134,7 +1139,7 @@ await debugService.SetCommandBreakpointsAsync( ScriptFile testScript = GetDebugScript("GetChildItemTest.ps1"); Task _ = ExecuteScriptFileAsync(testScript.FilePath); - AssertDebuggerStopped(testScript.FilePath, 2); + await AssertDebuggerStopped(testScript.FilePath, 2); VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName); VariableDetailsBase var = Array.Find(variables, v => v.Name == "$file"); @@ -1154,7 +1159,7 @@ public async Task DebuggerToStringShouldMarshallToPipeline() // Execute the script and wait for the breakpoint to be hit Task _ = ExecuteVariableScriptFileAsync(); - AssertDebuggerStopped(commandBreakpointDetails: breakpoint); + await AssertDebuggerStopped(commandBreakpointDetails: breakpoint); VariableDetailsBase[] vars = await GetVariables(VariableContainerDetails.ScriptScopeName); VariableDetailsBase customToStrings = Array.Find(vars, i => i.Name is "$CustomToStrings"); diff --git a/test/PowerShellEditorServices.Test/Extensions/EditorOperationsServiceTests.cs b/test/PowerShellEditorServices.Test/Extensions/EditorOperationsServiceTests.cs new file mode 100644 index 000000000..45ef4e538 --- /dev/null +++ b/test/PowerShellEditorServices.Test/Extensions/EditorOperationsServiceTests.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.PowerShell.EditorServices.Extensions; +using Microsoft.PowerShell.EditorServices.Services; +using Microsoft.PowerShell.EditorServices.Services.Extension; +using Microsoft.PowerShell.EditorServices.Services.TextDocument; +using OmniSharp.Extensions.LanguageServer.Protocol; +using Xunit; + +namespace PowerShellEditorServices.Test.Extensions +{ + [Trait("Category", "Extensions")] + public class EditorOperationsServiceTests + { + [Fact] + public void GetWorkspaceOpenDocumentsReturnsOnlyOpenDocumentsAndCurrentInMemoryState() + { + WorkspaceService workspaceService = new(NullLoggerFactory.Instance); + + ScriptFile openSaved = CreateFileBuffer(workspaceService, "open-saved.ps1"); + openSaved.IsOpen = true; + openSaved.IsInMemory = false; + + ScriptFile openUnsaved = CreateFileBuffer(workspaceService, "open-unsaved.ps1"); + openUnsaved.IsOpen = true; + openUnsaved.IsInMemory = true; + + ScriptFile closed = CreateFileBuffer(workspaceService, "closed.ps1"); + closed.IsOpen = false; + closed.IsInMemory = false; + + EditorOperationsService editorOperationsService = new( + psesHost: null, + workspaceService, + languageServer: null); + + WorkspaceOpenDocument[] documents = editorOperationsService.GetWorkspaceOpenDocuments(); + + Assert.Equal(2, documents.Length); + Assert.Contains(documents, static document => document.Path.EndsWith("open-saved.ps1") && document.Saved); + Assert.Contains(documents, static document => document.Path.EndsWith("open-unsaved.ps1") && !document.Saved); + Assert.DoesNotContain(documents, static document => document.Path.EndsWith("closed.ps1")); + } + + [Fact] + public void GetWorkspaceOpenDocumentsTracksEditedAndUntitledSaveStates() + { + WorkspaceService workspaceService = new(NullLoggerFactory.Instance); + + ScriptFile openSaved = CreateFileBuffer(workspaceService, "open-saved.ps1"); + openSaved.IsOpen = true; + + ScriptFile openUntitled = workspaceService.GetFileBuffer("untitled:Untitled-1", initialBuffer: string.Empty); + openUntitled.IsOpen = true; + + EditorOperationsService editorOperationsService = new( + psesHost: null, + workspaceService, + languageServer: null); + + WorkspaceOpenDocument[] initialDocuments = editorOperationsService.GetWorkspaceOpenDocuments(); + Assert.Contains(initialDocuments, static document => document.Path.EndsWith("open-saved.ps1") && document.Saved); + Assert.Contains(initialDocuments, static document => document.Path.StartsWith("untitled:", StringComparison.Ordinal) && !document.Saved); + + openSaved.ApplyChange(new FileChange + { + Line = 1, + Offset = 1, + EndLine = 1, + EndOffset = 1, + InsertString = "Set-StrictMode -Version Latest" + }); + Assert.Contains("Set-StrictMode -Version Latest", openSaved.Contents, StringComparison.Ordinal); + + WorkspaceOpenDocument[] editedDocuments = editorOperationsService.GetWorkspaceOpenDocuments(); + Assert.Contains(editedDocuments, static document => document.Path.EndsWith("open-saved.ps1") && !document.Saved); + + MarkAsSaved(openSaved); + MarkAsSaved(openUntitled); + + WorkspaceOpenDocument[] savedDocuments = editorOperationsService.GetWorkspaceOpenDocuments(); + Assert.Contains(savedDocuments, static document => document.Path.EndsWith("open-saved.ps1") && document.Saved); + Assert.Contains(savedDocuments, static document => document.Path.StartsWith("untitled:", StringComparison.Ordinal) && !document.Saved); + } + + private static ScriptFile CreateFileBuffer(WorkspaceService workspaceService, string fileName) + { + string filePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"), fileName); + return workspaceService.GetFileBuffer(DocumentUri.FromFileSystemPath(filePath), initialBuffer: string.Empty); + } + + private static void MarkAsSaved(ScriptFile scriptFile) => scriptFile.IsInMemory = scriptFile.IsUntitled; + } +} diff --git a/test/PowerShellEditorServices.Test/Extensions/EditorWorkspaceTests.cs b/test/PowerShellEditorServices.Test/Extensions/EditorWorkspaceTests.cs new file mode 100644 index 000000000..673d864d5 --- /dev/null +++ b/test/PowerShellEditorServices.Test/Extensions/EditorWorkspaceTests.cs @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Extensions; +using Microsoft.PowerShell.EditorServices.Services.TextDocument; +using Xunit; + +namespace PowerShellEditorServices.Test.Extensions +{ + [Trait("Category", "Extensions")] + public class EditorWorkspaceTests + { + private static readonly string WorkspacePath = Path.Combine("test"); + + [Fact] + public void DocumentsReturnsOpenWorkspaceDocuments() + { + string firstPath = Path.Combine(WorkspacePath, "one.ps1"); + string secondPath = Path.Combine(WorkspacePath, "two.ps1"); + + TestEditorOperations editorOperations = new() + { + OpenDocuments = + [ + new WorkspaceOpenDocument(firstPath, saved: true), + new WorkspaceOpenDocument(secondPath, saved: true) + ] + }; + + EditorWorkspace workspace = new(editorOperations); + + WorkspaceOpenDocument[] documents = workspace.Documents; + + Assert.Collection( + documents, + document => + { + Assert.Equal(firstPath, document.Path); + Assert.True(document.Saved); + }, + document => + { + Assert.Equal(secondPath, document.Path); + Assert.True(document.Saved); + }); + } + + [Fact] + public void DocumentToStringReturnsFileNameAndSavedStatus() + { + string savedFilePath = Path.Combine(WorkspacePath, "file.ps1"); + string unsavedFilePath = Path.Combine(WorkspacePath, "other.ps1"); + TestEditorOperations editorOperations = new() + { + OpenDocuments = [ + new WorkspaceOpenDocument(savedFilePath, saved: true), + new WorkspaceOpenDocument(unsavedFilePath, saved: false) + ] + }; + + EditorWorkspace workspace = new(editorOperations); + IEnumerable documents = workspace.Documents; + + Assert.Collection( + documents, + document => Assert.Equal("file.ps1", document.ToString()), + document => Assert.Equal("other.ps1 [Unsaved]", document.ToString())); + } + + [Fact] + public void DocumentSavedReturnsWorkspaceSavedState() + { + TestEditorOperations editorOperations = new() + { + OpenDocuments = [ + new WorkspaceOpenDocument(Path.Combine(WorkspacePath, "saved.ps1"), saved: true), + new WorkspaceOpenDocument(Path.Combine(WorkspacePath, "unsaved.ps1"), saved: false) + ] + }; + + EditorWorkspace workspace = new(editorOperations); + IEnumerable documents = workspace.Documents; + + Assert.Collection( + documents, + document => Assert.True(document.Saved), + document => Assert.False(document.Saved)); + } + + private sealed class TestEditorOperations : IEditorOperations + { + public WorkspaceOpenDocument[] OpenDocuments { get; set; } = Array.Empty(); + + public List Calls { get; } = new(); + + public Task GetEditorContextAsync() => Task.FromResult(default(EditorContext)); + + public string GetWorkspacePath() => WorkspacePath; + + public string[] GetWorkspacePaths() => [WorkspacePath]; + + public WorkspaceOpenDocument[] GetWorkspaceOpenDocuments() => OpenDocuments; + + public string GetWorkspaceRelativePath(ScriptFile scriptFile) => scriptFile.FilePath; + + public Task NewFileAsync() => Task.CompletedTask; + + public Task NewFileAsync(string content) => Task.CompletedTask; + + public Task OpenFileAsync(string filePath) + { + Calls.Add("OpenFile:" + filePath); + return Task.CompletedTask; + } + + public Task OpenFileAsync(string filePath, bool preview) => Task.CompletedTask; + + public Task CloseFileAsync(string filePath) + { + Calls.Add("CloseFile:" + filePath); + return Task.CompletedTask; + } + + public Task SaveFileAsync(string filePath) + { + Calls.Add("SaveFile:" + filePath); + return Task.CompletedTask; + } + + public Task SaveFileAsync(string oldFilePath, string newFilePath) => Task.CompletedTask; + + public Task InsertTextAsync(string filePath, string insertText, BufferRange insertRange) => Task.CompletedTask; + + public Task SetSelectionAsync(BufferRange selectionRange) => Task.CompletedTask; + + public Task ShowInformationMessageAsync(string message) => Task.CompletedTask; + + public Task ShowErrorMessageAsync(string message) => Task.CompletedTask; + + public Task ShowWarningMessageAsync(string message) => Task.CompletedTask; + + public Task SetStatusBarMessageAsync(string message, int? timeout) => Task.CompletedTask; + + public void ClearTerminal() + { + } + } + } +} diff --git a/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs b/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs index 190ee5aae..70e35d3dd 100644 --- a/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs +++ b/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs @@ -20,33 +20,25 @@ namespace PowerShellEditorServices.Test.Extensions { [Trait("Category", "Extensions")] - public class ExtensionCommandTests : IDisposable + public class ExtensionCommandTests : IAsyncLifetime { - private readonly PsesInternalHost psesHost; + private PsesInternalHost psesHost; - private readonly ExtensionCommandService extensionCommandService; + private ExtensionCommandService extensionCommandService; - public ExtensionCommandTests() + public async Task InitializeAsync() { - psesHost = PsesHostFactory.Create(NullLoggerFactory.Instance); + psesHost = await PsesHostFactory.Create(NullLoggerFactory.Instance); ExtensionService extensionService = new( languageServer: null, serviceProvider: null, editorOperations: null, executionService: psesHost); -#pragma warning disable VSTHRD002 - extensionService.InitializeAsync().Wait(); -#pragma warning restore VSTHRD002 + await extensionService.InitializeAsync(); extensionCommandService = new(extensionService); } - public void Dispose() - { -#pragma warning disable VSTHRD002 - psesHost.StopAsync().Wait(); -#pragma warning restore VSTHRD002 - GC.SuppressFinalize(this); - } + public async Task DisposeAsync() => await psesHost.StopAsync(); [Fact] public async Task CanRegisterAndInvokeCommandWithCmdletName() diff --git a/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs b/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs index fae0f8104..4ccdc05a2 100644 --- a/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs +++ b/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -21,26 +20,20 @@ namespace PowerShellEditorServices.Test.Language { [Trait("Category", "Completions")] - public class CompletionHandlerTests : IDisposable + public class CompletionHandlerTests : IAsyncLifetime { - private readonly PsesInternalHost psesHost; - private readonly WorkspaceService workspace; - private readonly PsesCompletionHandler completionHandler; + private PsesInternalHost psesHost; + private WorkspaceService workspace; + private PsesCompletionHandler completionHandler; - public CompletionHandlerTests() + public async Task InitializeAsync() { - psesHost = PsesHostFactory.Create(NullLoggerFactory.Instance); + psesHost = await PsesHostFactory.Create(NullLoggerFactory.Instance); workspace = new WorkspaceService(NullLoggerFactory.Instance); completionHandler = new PsesCompletionHandler(NullLoggerFactory.Instance, psesHost, psesHost, workspace); } - public void Dispose() - { -#pragma warning disable VSTHRD002 - psesHost.StopAsync().Wait(); -#pragma warning restore VSTHRD002 - GC.SuppressFinalize(this); - } + public async Task DisposeAsync() => await Task.Run(psesHost.StopAsync); private ScriptFile GetScriptFile(ScriptRegion scriptRegion) => workspace.GetFile(TestUtilities.GetSharedPath(scriptRegion.File)); @@ -109,10 +102,9 @@ public async Task CompletesVariableInFile() Assert.Equal(CompleteVariableInFile.ExpectedCompletion, actual); } - [SkippableFact] + [Fact] public async Task CompletesAttributeValue() { - Skip.If(VersionUtils.IsPS74, "PowerShell 7.4 isn't returning these!"); (_, IEnumerable results) = await GetCompletionResultsAsync(CompleteAttributeValue.SourceDetails); // NOTE: Since the completions come through un-ordered from PowerShell, their SortText // (which has an index prepended from the original order) will mis-match our assumed @@ -133,5 +125,28 @@ public async Task CompletesFilePath() Assert.Equal(actual.TextEdit.TextEdit with { NewText = "" }, CompleteFilePath.ExpectedEdit); Assert.All(results, r => Assert.True(r.Kind is CompletionItemKind.File or CompletionItemKind.Folder)); } + + // TODO: These should be an integration tests at a higher level if/when https://github.com/PowerShell/PowerShell/pull/25108 is merged. As of today, we can't actually test this in the PS engine currently. + [Fact] + public void CanExtractTypeAndDescriptionFromTooltip() + { + string expectedType = "[string]"; + string expectedDescription = "Test String"; + string paramName = "TestParam"; + string testHelp = $"{expectedType} {paramName} - {expectedDescription}"; + Assert.True(PsesCompletionHandler.TryExtractType(testHelp, paramName, out string type, out string description)); + Assert.Equal(expectedType, type); + Assert.Equal(expectedDescription, description); + } + + [Fact] + public void CanExtractTypeFromTooltip() + { + string expectedType = "[string]"; + string testHelp = $"{expectedType}"; + Assert.True(PsesCompletionHandler.TryExtractType(testHelp, string.Empty, out string type, out string description)); + Assert.Null(description); + Assert.Equal(expectedType, type); + } } } diff --git a/test/PowerShellEditorServices.Test/Language/SemanticTokenTest.cs b/test/PowerShellEditorServices.Test/Language/SemanticTokenTest.cs index c9f3c01d4..196fa9ca6 100644 --- a/test/PowerShellEditorServices.Test/Language/SemanticTokenTest.cs +++ b/test/PowerShellEditorServices.Test/Language/SemanticTokenTest.cs @@ -20,7 +20,8 @@ public void TokenizesFunctionElements() { const string text = @" function Get-Sum { - param( [int]$a, [int]$b ) + param( [parameter()] [int]$a, [int]$b ) + :loopLabel while (0) {break loopLabel} return $a + $b } "; @@ -38,10 +39,21 @@ function Get-Sum { case "function": case "param": case "return": + case "while": + case "break": Assert.Single(mappedTokens, sToken => SemanticTokenType.Keyword == sToken.Type); break; - case "Get-Sum": - Assert.Single(mappedTokens, sToken => SemanticTokenType.Function == sToken.Type); + case "parameter": + Assert.Single(mappedTokens, sToken => SemanticTokenType.Decorator == sToken.Type); + break; + case "0": + Assert.Single(mappedTokens, sToken => SemanticTokenType.Number == sToken.Type); + break; + case ":loopLabel": + Assert.Single(mappedTokens, sToken => SemanticTokenType.Label == sToken.Type); + break; + case "loopLabel": + Assert.Single(mappedTokens, sToken => SemanticTokenType.Property == sToken.Type); break; case "$a": case "$b": @@ -74,7 +86,6 @@ public void TokenizesStringExpansion() Token stringExpandableToken = scriptFile.ScriptTokens[1]; mappedTokens = new List(PsesSemanticTokensHandler.ConvertToSemanticTokens(stringExpandableToken)); Assert.Collection(mappedTokens, - sToken => Assert.Equal(SemanticTokenType.Function, sToken.Type), sToken => Assert.Equal(SemanticTokenType.Function, sToken.Type), sToken => Assert.Equal(SemanticTokenType.Function, sToken.Type) ); @@ -103,7 +114,11 @@ function Get-A*A { Assert.Single(mappedTokens, sToken => SemanticTokenType.Keyword == sToken.Type); break; case "Get-A*A": - Assert.Single(mappedTokens, sToken => SemanticTokenType.Function == sToken.Type); + if (t.TokenFlags.HasFlag(TokenFlags.CommandName)) + { + Assert.Single(mappedTokens, sToken => SemanticTokenType.Function == sToken.Type); + } + break; } } diff --git a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs index f078d8d42..593fddb04 100644 --- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; @@ -22,7 +21,6 @@ using Microsoft.PowerShell.EditorServices.Test.Shared.References; using Microsoft.PowerShell.EditorServices.Test.Shared.SymbolDetails; using Microsoft.PowerShell.EditorServices.Test.Shared.Symbols; -using Microsoft.PowerShell.EditorServices.Utility; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using Xunit; @@ -30,16 +28,16 @@ namespace PowerShellEditorServices.Test.Language { [Trait("Category", "Symbols")] - public class SymbolsServiceTests : IDisposable + public class SymbolsServiceTests : IAsyncLifetime { - private readonly PsesInternalHost psesHost; - private readonly WorkspaceService workspace; - private readonly SymbolsService symbolsService; - private static readonly bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + private PsesInternalHost psesHost; + private WorkspaceService workspace; + private SymbolsService symbolsService; + private static readonly bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - public SymbolsServiceTests() + public async Task InitializeAsync() { - psesHost = PsesHostFactory.Create(NullLoggerFactory.Instance); + psesHost = await PsesHostFactory.Create(NullLoggerFactory.Instance); workspace = new WorkspaceService(NullLoggerFactory.Instance); workspace.WorkspaceFolders.Add(new WorkspaceFolder { @@ -53,14 +51,11 @@ public SymbolsServiceTests() new ConfigurationService()); } - public void Dispose() + public async Task DisposeAsync() { -#pragma warning disable VSTHRD002 - psesHost.StopAsync().GetAwaiter().GetResult(); -#pragma warning restore VSTHRD002 + psesHost.StopAsync(); CommandHelpers.s_cmdletToAliasCache.Clear(); CommandHelpers.s_aliasToCmdletCache.Clear(); - GC.SuppressFinalize(this); } private static void AssertIsRegion( @@ -760,17 +755,16 @@ public async Task FindsReferencesOnEnumMember() Assert.Equal(symbols, GetOccurrences(FindsOccurrencesOnTypeSymbolsData.EnumMemberSourceDetails)); } - [SkippableFact] + [Fact] public async Task FindsDetailsForBuiltInCommand() { - Skip.IfNot(VersionUtils.IsMacOS, "macOS gets the right synopsis but others don't."); SymbolDetails symbolDetails = await symbolsService.FindSymbolDetailsAtLocationAsync( GetScriptFile(FindsDetailsForBuiltInCommandData.SourceDetails), FindsDetailsForBuiltInCommandData.SourceDetails.StartLineNumber, FindsDetailsForBuiltInCommandData.SourceDetails.StartColumnNumber, CancellationToken.None); - Assert.Equal("Gets the processes that are running on the local computer.", symbolDetails.Documentation); + Assert.Equal("Extracts files from a specified archive (zipped) file.", symbolDetails.Documentation); } [Fact] @@ -800,43 +794,43 @@ public void FindsSymbolsInFile() Assert.False(symbol.IsDeclaration); AssertIsRegion(symbol.NameRegion, 16, 29, 16, 39); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Workflow)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Workflow); Assert.Equal("fn AWorkflow", symbol.Id); Assert.Equal("workflow AWorkflow ()", symbol.Name); Assert.True(symbol.IsDeclaration); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Class)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Class); Assert.Equal("type AClass", symbol.Id); Assert.Equal("class AClass { }", symbol.Name); Assert.True(symbol.IsDeclaration); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Property)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Property); Assert.Equal("prop AProperty", symbol.Id); Assert.Equal("[string] $AProperty", symbol.Name); Assert.True(symbol.IsDeclaration); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Constructor)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Constructor); Assert.Equal("mtd AClass", symbol.Id); Assert.Equal("AClass([string]$AParameter)", symbol.Name); Assert.True(symbol.IsDeclaration); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Method)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Method); Assert.Equal("mtd AMethod", symbol.Id); Assert.Equal("void AMethod([string]$param1, [int]$param2, $param3)", symbol.Name); Assert.True(symbol.IsDeclaration); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Enum)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Enum); Assert.Equal("type AEnum", symbol.Id); Assert.Equal("enum AEnum { }", symbol.Name); Assert.True(symbol.IsDeclaration); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.EnumMember)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.EnumMember); Assert.Equal("prop AValue", symbol.Id); Assert.Equal("AValue", symbol.Name); Assert.True(symbol.IsDeclaration); // There should be no region symbols unless the provider has been registered. - Assert.Empty(symbols.Where(i => i.Type == SymbolType.Region)); + Assert.DoesNotContain(symbols, i => i.Type == SymbolType.Region); } [Fact] @@ -870,53 +864,54 @@ public void FindsSymbolsWithNewLineInFile() { IEnumerable symbols = FindSymbolsInFile(FindSymbolsInNewLineSymbolFile.SourceDetails); - SymbolReference symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Function)); + SymbolReference symbol = Assert.Single(symbols, i => i.Type == SymbolType.Function); Assert.Equal("fn returnTrue", symbol.Id); AssertIsRegion(symbol.NameRegion, 2, 1, 2, 11); AssertIsRegion(symbol.ScriptRegion, 1, 1, 4, 2); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Class)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Class); Assert.Equal("type NewLineClass", symbol.Id); AssertIsRegion(symbol.NameRegion, 7, 1, 7, 13); AssertIsRegion(symbol.ScriptRegion, 6, 1, 23, 2); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Constructor)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Constructor); Assert.Equal("mtd NewLineClass", symbol.Id); AssertIsRegion(symbol.NameRegion, 8, 5, 8, 17); AssertIsRegion(symbol.ScriptRegion, 8, 5, 10, 6); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Property)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Property); Assert.Equal("prop SomePropWithDefault", symbol.Id); AssertIsRegion(symbol.NameRegion, 15, 5, 15, 25); AssertIsRegion(symbol.ScriptRegion, 12, 5, 15, 40); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Method)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Method); Assert.Equal("mtd MyClassMethod", symbol.Id); Assert.Equal("string MyClassMethod([MyNewLineEnum]$param1)", symbol.Name); AssertIsRegion(symbol.NameRegion, 20, 5, 20, 18); AssertIsRegion(symbol.ScriptRegion, 17, 5, 22, 6); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.Enum)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.Enum); Assert.Equal("type MyNewLineEnum", symbol.Id); AssertIsRegion(symbol.NameRegion, 26, 1, 26, 14); AssertIsRegion(symbol.ScriptRegion, 25, 1, 28, 2); - symbol = Assert.Single(symbols.Where(i => i.Type == SymbolType.EnumMember)); + symbol = Assert.Single(symbols, i => i.Type == SymbolType.EnumMember); Assert.Equal("prop First", symbol.Id); AssertIsRegion(symbol.NameRegion, 27, 5, 27, 10); AssertIsRegion(symbol.ScriptRegion, 27, 5, 27, 10); } - [Fact(Skip = "DSC symbols don't work yet.")] + [SkippableFact] public void FindsSymbolsInDSCFile() { - Skip.If(!s_isWindows, "DSC only works properly on Windows."); + Skip.If(!isWindows, "DSC only works properly on Windows."); IEnumerable symbols = FindSymbolsInFile(FindSymbolsInDSCFile.SourceDetails); SymbolReference symbol = Assert.Single(symbols, i => i.Type == SymbolType.Configuration); - Assert.Equal("AConfiguration", symbol.Id); + // The prefix "dsc" is added for sorting reasons. + Assert.Equal("dsc AConfiguration", symbol.Id); Assert.Equal(2, symbol.ScriptRegion.StartLineNumber); - Assert.Equal(15, symbol.ScriptRegion.StartColumnNumber); + Assert.Equal(1, symbol.ScriptRegion.StartColumnNumber); } [Fact] diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj index 5c2534509..6161a113e 100644 --- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj +++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj @@ -2,7 +2,7 @@ - net8.0;net7.0;net6.0;net462 + net8.0;net462 Microsoft.PowerShell.EditorServices.Test x64 @@ -18,29 +18,19 @@ - - - - - - - - - - - + - + - - - - + + + + diff --git a/test/PowerShellEditorServices.Test/PsesHostFactory.cs b/test/PowerShellEditorServices.Test/PsesHostFactory.cs index 0e02b6389..9f63236d4 100644 --- a/test/PowerShellEditorServices.Test/PsesHostFactory.cs +++ b/test/PowerShellEditorServices.Test/PsesHostFactory.cs @@ -7,6 +7,7 @@ using System.Management.Automation.Host; using System.Management.Automation.Runspaces; using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.PowerShell.EditorServices.Hosting; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host; @@ -28,7 +29,7 @@ internal static class PsesHostFactory public static readonly string BundledModulePath = Path.GetFullPath(TestUtilities.NormalizePath("../../../../../module")); - public static PsesInternalHost Create(ILoggerFactory loggerFactory, bool loadProfiles = false) + public static async Task Create(ILoggerFactory loggerFactory, bool loadProfiles = false) { // We intentionally use `CreateDefault2()` as it loads `Microsoft.PowerShell.Core` only, // which is a more minimal and therefore safer state. @@ -62,9 +63,7 @@ public static PsesInternalHost Create(ILoggerFactory loggerFactory, bool loadPro PsesInternalHost psesHost = new(loggerFactory, null, testHostDetails); - #pragma warning disable VSTHRD002 // Because this is used by constructors it can't use await. - if (psesHost.TryStartAsync(new HostStartOptions { LoadProfiles = loadProfiles }, CancellationToken.None).GetAwaiter().GetResult()) - #pragma warning restore VSTHRD002 + if (await psesHost.TryStartAsync(new HostStartOptions { LoadProfiles = loadProfiles }, CancellationToken.None)) { return psesHost; } diff --git a/test/PowerShellEditorServices.Test/Refactoring/PrepareRenameHandlerTests.cs b/test/PowerShellEditorServices.Test/Refactoring/PrepareRenameHandlerTests.cs new file mode 100644 index 000000000..b95b735cf --- /dev/null +++ b/test/PowerShellEditorServices.Test/Refactoring/PrepareRenameHandlerTests.cs @@ -0,0 +1,237 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.PowerShell.EditorServices.Handlers; +using Microsoft.PowerShell.EditorServices.Services; +using Microsoft.PowerShell.EditorServices.Test.Shared; +using Newtonsoft.Json.Linq; +using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Progress; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using PowerShellEditorServices.Test.Shared.Refactoring; +using Xunit; +using Xunit.Abstractions; + +namespace PowerShellEditorServices.Test.Handlers; + +[Trait("Category", "PrepareRename")] +public class PrepareRenameHandlerTests +{ + private readonly PrepareRenameHandler testHandler; + + public PrepareRenameHandlerTests() + { + WorkspaceService workspace = new(NullLoggerFactory.Instance); + workspace.WorkspaceFolders.Add(new WorkspaceFolder + { + Uri = DocumentUri.FromFileSystemPath(TestUtilities.GetSharedPath("Refactoring")) + }); + + testHandler = new + ( + new RenameService + ( + workspace, + new FakeLspSendMessageRequestFacade("I Accept"), + new EmptyConfiguration() + ) + { + DisclaimerAcceptedForSession = true //Disables UI prompts + } + ); + } + + /// + /// Convert test cases into theory data. This keeps us from needing xunit in the test data project + /// This type has a special ToString to add a data-driven test name which is why we dont convert directly to the param type first + /// + public static TheoryData VariableTestCases() + => new(RefactorVariableTestCases.TestCases.Select(RenameTestTargetSerializable.FromRenameTestTarget)); + + public static TheoryData FunctionTestCases() + => new(RefactorFunctionTestCases.TestCases.Select(RenameTestTargetSerializable.FromRenameTestTarget)); + + [Theory] + [MemberData(nameof(FunctionTestCases))] + public async Task FindsFunction(RenameTestTarget s) + { + PrepareRenameParams testParams = s.ToPrepareRenameParams("Functions"); + + RangeOrPlaceholderRange? result; + try + { + result = await testHandler.Handle(testParams, CancellationToken.None); + } + catch (HandlerErrorException err) + { + Assert.True(s.ShouldThrow, $"Unexpected HandlerErrorException: {err.Message}"); + return; + } + if (s.ShouldFail) + { + Assert.Null(result); + return; + } + + Assert.NotNull(result); + Assert.True(result?.DefaultBehavior?.DefaultBehavior); + } + + [Theory] + [MemberData(nameof(VariableTestCases))] + public async Task FindsVariable(RenameTestTarget s) + { + PrepareRenameParams testParams = s.ToPrepareRenameParams("Variables"); + + RangeOrPlaceholderRange? result; + try + { + result = await testHandler.Handle(testParams, CancellationToken.None); + } + catch (HandlerErrorException err) + { + Assert.True(s.ShouldThrow, $"Unexpected HandlerErrorException: {err.Message}"); + return; + } + if (s.ShouldFail) + { + Assert.Null(result); + return; + } + + Assert.NotNull(result); + Assert.True(result?.DefaultBehavior?.DefaultBehavior); + } + + // TODO: Bad Path Tests (strings, parameters, etc.) +} + +public static partial class RenameTestTargetExtensions +{ + public static PrepareRenameParams ToPrepareRenameParams(this RenameTestTarget testCase, string baseFolder) + => new() + { + Position = new ScriptPositionAdapter(Line: testCase.Line, Column: testCase.Column), + TextDocument = new TextDocumentIdentifier + { + Uri = DocumentUri.FromFileSystemPath( + TestUtilities.GetSharedPath($"Refactoring/{baseFolder}/{testCase.FileName}") + ) + } + }; +} + +public class FakeLspSendMessageRequestFacade(string title) : ILanguageServerFacade +{ + public async Task SendRequest(IRequest request, CancellationToken cancellationToken) + { + if (request is ShowMessageRequestParams) + { + return (TResponse)(object)new MessageActionItem { Title = title }; + } + else + { + throw new NotSupportedException(); + } + } + + public ITextDocumentLanguageServer TextDocument => throw new NotImplementedException(); + public INotebookDocumentLanguageServer NotebookDocument => throw new NotImplementedException(); + public IClientLanguageServer Client => throw new NotImplementedException(); + public IGeneralLanguageServer General => throw new NotImplementedException(); + public IWindowLanguageServer Window => throw new NotImplementedException(); + public IWorkspaceLanguageServer Workspace => throw new NotImplementedException(); + public IProgressManager ProgressManager => throw new NotImplementedException(); + public InitializeParams ClientSettings => throw new NotImplementedException(); + public InitializeResult ServerSettings => throw new NotImplementedException(); + public object GetService(Type serviceType) => throw new NotImplementedException(); + public IDisposable Register(Action registryAction) => throw new NotImplementedException(); + public void SendNotification(string method) => throw new NotImplementedException(); + public void SendNotification(string method, T @params) => throw new NotImplementedException(); + public void SendNotification(IRequest request) => throw new NotImplementedException(); + public IResponseRouterReturns SendRequest(string method) => throw new NotImplementedException(); + public IResponseRouterReturns SendRequest(string method, T @params) => throw new NotImplementedException(); + public bool TryGetRequest(long id, out string method, out TaskCompletionSource pendingTask) => throw new NotImplementedException(); +} + +public class EmptyConfiguration : ConfigurationRoot, ILanguageServerConfiguration, IScopedConfiguration +{ + public EmptyConfiguration() : base([]) { } + + public bool IsSupported => throw new NotImplementedException(); + + public ILanguageServerConfiguration AddConfigurationItems(IEnumerable configurationItems) => throw new NotImplementedException(); + public Task GetConfiguration(params ConfigurationItem[] items) => throw new NotImplementedException(); + public Task GetScopedConfiguration(DocumentUri scopeUri, CancellationToken cancellationToken) => Task.FromResult((IScopedConfiguration)this); + public ILanguageServerConfiguration RemoveConfigurationItems(IEnumerable configurationItems) => throw new NotImplementedException(); + public bool TryGetScopedConfiguration(DocumentUri scopeUri, out IScopedConfiguration configuration) => throw new NotImplementedException(); +} + +public static partial class RenameTestTargetExtensions +{ + /// + /// Extension Method to convert a RenameTestTarget to a RenameParams. Needed because RenameTestTarget is in a separate project. + /// + public static RenameParams ToRenameParams(this RenameTestTarget testCase, string subPath) + => new() + { + Position = new ScriptPositionAdapter(Line: testCase.Line, Column: testCase.Column), + TextDocument = new TextDocumentIdentifier + { + Uri = DocumentUri.FromFileSystemPath( + TestUtilities.GetSharedPath($"Refactoring/{subPath}/{testCase.FileName}") + ) + }, + NewName = testCase.NewName + }; +} + +/// +/// This is necessary for the MS test explorer to display the test cases +/// Ref: +/// +public class RenameTestTargetSerializable : RenameTestTarget, IXunitSerializable +{ + public RenameTestTargetSerializable() : base() { } + + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(FileName), FileName); + info.AddValue(nameof(Line), Line); + info.AddValue(nameof(Column), Column); + info.AddValue(nameof(NewName), NewName); + info.AddValue(nameof(ShouldFail), ShouldFail); + info.AddValue(nameof(ShouldThrow), ShouldThrow); + } + + public void Deserialize(IXunitSerializationInfo info) + { + FileName = info.GetValue(nameof(FileName)); + Line = info.GetValue(nameof(Line)); + Column = info.GetValue(nameof(Column)); + NewName = info.GetValue(nameof(NewName)); + ShouldFail = info.GetValue(nameof(ShouldFail)); + ShouldThrow = info.GetValue(nameof(ShouldThrow)); + } + + public static RenameTestTargetSerializable FromRenameTestTarget(RenameTestTarget t) + => new RenameTestTargetSerializable() + { + FileName = t.FileName, + Column = t.Column, + Line = t.Line, + NewName = t.NewName, + ShouldFail = t.ShouldFail, + ShouldThrow = t.ShouldThrow + }; +} diff --git a/test/PowerShellEditorServices.Test/Refactoring/RefactorUtilities.cs b/test/PowerShellEditorServices.Test/Refactoring/RefactorUtilities.cs new file mode 100644 index 000000000..c8c7c3902 --- /dev/null +++ b/test/PowerShellEditorServices.Test/Refactoring/RefactorUtilities.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using System.Linq; +using System.Collections.Generic; +using TextEditRange = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; + +namespace PowerShellEditorServices.Test.Refactoring +{ + public static class RefactorUtilities + { + /// + /// A simplistic "Mock" implementation of vscode client performing rename activities. It is not comprehensive and an E2E test is recommended. + /// + /// + /// + /// + internal static string GetModifiedScript(string OriginalScript, TextEdit[] Modifications) + { + string normalizedScript = OriginalScript + .Replace("\r\n", "\n") + .Replace('\r', '\n'); + string[] Lines = normalizedScript.Split('\n'); + + // FIXME: Verify that we should be returning modifications in ascending order anyways as the LSP spec dictates it + IEnumerable sortedModifications = Modifications.OrderBy + ( + x => x, new TextEditComparer() + ); + + foreach (TextEdit change in sortedModifications) + { + TextEditRange editRange = change.Range; + string TargetLine = Lines[editRange.Start.Line]; + string begin = TargetLine.Substring(0, editRange.Start.Character); + string end = TargetLine.Substring(editRange.End.Character); + Lines[editRange.Start.Line] = begin + change.NewText + end; + } + + return string.Join(Environment.NewLine, Lines); + } + } + + internal class TextEditComparer : IComparer + { + public int Compare(TextEdit a, TextEdit b) + { + return a.Range.Start.Line == b.Range.Start.Line + ? b.Range.End.Character - a.Range.End.Character + : b.Range.Start.Line - a.Range.Start.Line; + } + } +} diff --git a/test/PowerShellEditorServices.Test/Refactoring/RenameHandlerTests.cs b/test/PowerShellEditorServices.Test/Refactoring/RenameHandlerTests.cs new file mode 100644 index 000000000..3f0be5be4 --- /dev/null +++ b/test/PowerShellEditorServices.Test/Refactoring/RenameHandlerTests.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.PowerShell.EditorServices.Handlers; +using Microsoft.PowerShell.EditorServices.Services; +using Microsoft.PowerShell.EditorServices.Services.TextDocument; +using Microsoft.PowerShell.EditorServices.Test.Shared; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using static PowerShellEditorServices.Test.Refactoring.RefactorUtilities; +using System.Linq; +using System.Threading; +using Xunit; +using PowerShellEditorServices.Test.Shared.Refactoring; +using System.Threading.Tasks; + +namespace PowerShellEditorServices.Test.Handlers; +#pragma warning disable VSTHRD100 // XUnit handles async void with a custom SyncContext + +[Trait("Category", "RenameHandlerFunction")] +public class RenameHandlerTests +{ + private readonly WorkspaceService workspace = new(NullLoggerFactory.Instance); + + private readonly RenameHandler testHandler; + public RenameHandlerTests() + { + workspace.WorkspaceFolders.Add(new WorkspaceFolder + { + Uri = DocumentUri.FromFileSystemPath(TestUtilities.GetSharedPath("Refactoring")) + }); + + testHandler = new + ( + new RenameService + ( + workspace, + new FakeLspSendMessageRequestFacade("I Accept"), + new EmptyConfiguration() + ) + { + DisclaimerAcceptedForSession = true //Disables UI prompts + } + ); + } + + // Decided to keep this DAMP instead of DRY due to memberdata boundaries, duplicates with PrepareRenameHandler + public static TheoryData VariableTestCases() + => new(RefactorVariableTestCases.TestCases.Select(RenameTestTargetSerializable.FromRenameTestTarget)); + + public static TheoryData FunctionTestCases() + => new(RefactorFunctionTestCases.TestCases.Select(RenameTestTargetSerializable.FromRenameTestTarget)); + + [Theory] + [MemberData(nameof(FunctionTestCases))] + public async Task RenamedFunction(RenameTestTarget s) + { + RenameParams request = s.ToRenameParams("Functions"); + WorkspaceEdit response; + try + { + response = await testHandler.Handle(request, CancellationToken.None); + } + catch (HandlerErrorException err) + { + Assert.True(s.ShouldThrow, $"Unexpected HandlerErrorException: {err.Message}"); + return; + } + if (s.ShouldFail) + { + Assert.Null(response); + return; + } + + DocumentUri testScriptUri = request.TextDocument.Uri; + + string expected = workspace.GetFile + ( + testScriptUri.ToString().Substring(0, testScriptUri.ToString().Length - 4) + "Renamed.ps1" + ).Contents; + + ScriptFile scriptFile = workspace.GetFile(testScriptUri); + + Assert.NotEmpty(response.Changes[testScriptUri]); + + string actual = GetModifiedScript(scriptFile.Contents, response.Changes[testScriptUri].ToArray()); + + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(VariableTestCases))] + public async Task RenamedVariable(RenameTestTarget s) + { + RenameParams request = s.ToRenameParams("Variables"); + WorkspaceEdit response; + try + { + response = await testHandler.Handle(request, CancellationToken.None); + } + catch (HandlerErrorException err) + { + Assert.True(s.ShouldThrow, $"Unexpected HandlerErrorException: {err.Message}"); + return; + } + if (s.ShouldFail) + { + Assert.Null(response); + return; + } + DocumentUri testScriptUri = request.TextDocument.Uri; + + string expected = workspace.GetFile + ( + testScriptUri.ToString().Substring(0, testScriptUri.ToString().Length - 4) + "Renamed.ps1" + ).Contents; + + ScriptFile scriptFile = workspace.GetFile(testScriptUri); + + Assert.NotNull(response); + Assert.NotEmpty(response.Changes[testScriptUri]); + + string actual = GetModifiedScript(scriptFile.Contents, response.Changes[testScriptUri].ToArray()); + + Assert.Equal(expected, actual); + } +} diff --git a/test/PowerShellEditorServices.Test/Services/Symbols/PSScriptAnalyzerTests.cs b/test/PowerShellEditorServices.Test/Services/Symbols/PSScriptAnalyzerTests.cs index 1155db102..273e5ecec 100644 --- a/test/PowerShellEditorServices.Test/Services/Symbols/PSScriptAnalyzerTests.cs +++ b/test/PowerShellEditorServices.Test/Services/Symbols/PSScriptAnalyzerTests.cs @@ -29,7 +29,7 @@ public class PSScriptAnalyzerTests profileId: "", version: null, psHost: null, - profilePaths: null, + profilePaths: default, featureFlags: null, additionalModules: null, initialSessionState: null, diff --git a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs index d489704f4..e9b5cf37c 100644 --- a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs @@ -20,19 +20,13 @@ namespace PowerShellEditorServices.Test.Session using System.Management.Automation.Runspaces; [Trait("Category", "PsesInternalHost")] - public class PsesInternalHostTests : IDisposable + public class PsesInternalHostTests : IAsyncLifetime { - private readonly PsesInternalHost psesHost; + private PsesInternalHost psesHost; - public PsesInternalHostTests() => psesHost = PsesHostFactory.Create(NullLoggerFactory.Instance); + public async Task InitializeAsync() => psesHost = await PsesHostFactory.Create(NullLoggerFactory.Instance); - public void Dispose() - { -#pragma warning disable VSTHRD002 - psesHost.StopAsync().Wait(); -#pragma warning restore VSTHRD002 - GC.SuppressFinalize(this); - } + public async Task DisposeAsync() => await psesHost.StopAsync(); [Fact] public async Task CanExecutePSCommand() @@ -88,7 +82,7 @@ public async Task CanQueueParallelPSCommands() public async Task CanCancelExecutionWithToken() { using CancellationTokenSource cancellationSource = new(millisecondsDelay: 1000); - _ = await Assert.ThrowsAsync(() => + await Assert.ThrowsAsync(() => { return psesHost.ExecutePSCommandAsync( new PSCommand().AddScript("Start-Sleep 10"), @@ -104,31 +98,52 @@ public async Task CanCancelExecutionWithMethod() new PSCommand().AddScript("Start-Sleep 10"), CancellationToken.None); - // Wait until our task has started. - Thread.Sleep(2000); - psesHost.CancelCurrentTask(); + // Cancel the task after 1 second in another thread. + Task.Run(() => { Thread.Sleep(1000); psesHost.CancelCurrentTask(); }); await Assert.ThrowsAsync(() => executeTask); Assert.True(executeTask.IsCanceled); } [Fact] - public async Task CanHandleNoProfiles() + public async Task CanHandleMissingProfilePaths() { - // Call LoadProfiles with profile paths that won't exist, and assert that it does not - // throw PSInvalidOperationException (which it previously did when it tried to invoke an - // empty command). + // Call LoadProfileScripts with profile paths that won't exist, and assert that it does + // not throw PSInvalidOperationException (which it previously did when it tried to + // invoke an empty command). ProfilePathInfo emptyProfilePaths = new("", "", "", ""); await psesHost.ExecuteDelegateAsync( - "LoadProfiles", + "SetProfileVariableAndLoadProfileScripts", executionOptions: null, (pwsh, _) => { - pwsh.LoadProfiles(emptyProfilePaths); + pwsh.SetProfileVariable(emptyProfilePaths); + pwsh.LoadProfileScripts(emptyProfilePaths); + + Assert.Equal(emptyProfilePaths.CurrentUserCurrentHost, pwsh.Runspace.SessionStateProxy.GetVariable("PROFILE")?.ToString()); Assert.Empty(pwsh.Commands.Commands); }, CancellationToken.None); } + [Fact] + public async Task SetsProfileVariableWhenProfilesAreNotLoaded() + { + // This host fixture starts with LoadProfiles = false. Ensure $PROFILE is still set. + IReadOnlyList profileVariable = await psesHost.ExecutePSCommandAsync( + new PSCommand().AddScript("$PROFILE"), + CancellationToken.None); + + Assert.Collection(profileVariable, + (p) => Assert.Equal(PsesHostFactory.TestProfilePaths.CurrentUserCurrentHost, p)); + + // Ensure profile scripts were not loaded as part of startup. + IReadOnlyList profileLoadedCommand = await psesHost.ExecutePSCommandAsync( + new PSCommand().AddScript("Get-Command Assert-ProfileLoaded -ErrorAction Ignore"), + CancellationToken.None); + + Assert.Empty(profileLoadedCommand); + } + // NOTE: Tests where we call functions that use PowerShell runspaces are slightly more // complicated than one would expect because we explicitly need the methods to run on the // pipeline thread, otherwise Windows complains about the the thread's apartment state not @@ -238,19 +253,13 @@ public async Task CanHandleBadInitialWorkingDirectory(string path) } [Trait("Category", "PsesInternalHost")] - public class PsesInternalHostWithProfileTests : IDisposable + public class PsesInternalHostWithProfileTests : IAsyncLifetime { - private readonly PsesInternalHost psesHost; + private PsesInternalHost psesHost; - public PsesInternalHostWithProfileTests() => psesHost = PsesHostFactory.Create(NullLoggerFactory.Instance, loadProfiles: true); + public async Task InitializeAsync() => psesHost = await PsesHostFactory.Create(NullLoggerFactory.Instance, loadProfiles: true); - public void Dispose() - { -#pragma warning disable VSTHRD002 - psesHost.StopAsync().Wait(); -#pragma warning restore VSTHRD002 - GC.SuppressFinalize(this); - } + public async Task DisposeAsync() => await psesHost.StopAsync(); [Fact] public async Task CanResolveAndLoadProfilesForHostId() diff --git a/test/PowerShellEditorServices.Test/Utility/AstExtensionsTests.cs b/test/PowerShellEditorServices.Test/Utility/AstExtensionsTests.cs new file mode 100644 index 000000000..c716f0e6e --- /dev/null +++ b/test/PowerShellEditorServices.Test/Utility/AstExtensionsTests.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using Microsoft.PowerShell.EditorServices.Language; +using Xunit; + +namespace PowerShellEditorServices.Test.Utility; + +[Trait("Category", "AstExtensions")] +public class AstExtensionsTests +{ + [Fact] + public void GetFunctionNameOffsetsHandlesFilter() + { + const string definition = "filter AFilter { $_ }"; + const string name = "AFilter"; + + (int start, int end) = AstExtensions.GetFunctionNameOffsets(definition, name); + + Assert.Equal(definition.IndexOf(name, StringComparison.Ordinal), start); + Assert.Equal(start + name.Length, end); + } + + [Fact] + public void GetFunctionNameOffsetsHandlesWorkflow() + { + const string definition = "workflow AWorkflow { \"ok\" }"; + const string name = "AWorkflow"; + + (int start, int end) = AstExtensions.GetFunctionNameOffsets(definition, name); + + Assert.Equal(definition.IndexOf(name, StringComparison.Ordinal), start); + Assert.Equal(start + name.Length, end); + } + + [Fact] + public void GetFunctionNameOffsetsThrowsForUnexpectedKeyword() + { + InvalidOperationException ex = Assert.Throws( + () => AstExtensions.GetFunctionNameOffsets("configuration MyConfig {}", "MyConfig")); + + Assert.Contains("Unexpected function definition keyword", ex.Message); + } +} diff --git a/test/PowerShellEditorServices.Test/packages.lock.json b/test/PowerShellEditorServices.Test/packages.lock.json deleted file mode 100644 index 10c7dcbe2..000000000 --- a/test/PowerShellEditorServices.Test/packages.lock.json +++ /dev/null @@ -1,3938 +0,0 @@ -{ - "version": 1, - "dependencies": { - ".NETFramework,Version=v4.6.2": { - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.9.0" - } - }, - "Microsoft.NETFramework.ReferenceAssemblies": { - "type": "Direct", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", - "dependencies": { - "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3" - } - }, - "Microsoft.PowerShell.5.ReferenceAssemblies": { - "type": "Direct", - "requested": "[1.1.0, )", - "resolved": "1.1.0", - "contentHash": "EE87t3aUXlO0Rjq83Ti8z1g4wwsWnB4W+pOn84i3QWzUOOuP5gZg8n4Y8XTZ7GkxGcoSR6w/d/kBJTJbc3VZPQ==" - }, - "xunit": { - "type": "Direct", - "requested": "[2.6.6, )", - "resolved": "2.6.6", - "contentHash": "MAbOOMtZIKyn2lrAmMlvhX0BhDOX/smyrTB+8WTXnSKkrmTGBS2fm8g1PZtHBPj91Dc5DJA7fY+/81TJ/yUFZw==", - "dependencies": { - "xunit.analyzers": "1.10.0", - "xunit.assert": "2.6.6", - "xunit.core": "[2.6.6]" - } - }, - "xunit.runner.visualstudio": { - "type": "Direct", - "requested": "[2.5.7, )", - "resolved": "2.5.7", - "contentHash": "31Rl7dBJriX0DNwZfDp8gqFOPsiM0c9kqpcH/HvNi9vDp+K7Ydf42H7mVIvYT918Ywzn1ymLg1c4DDC6iU754w==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.8.0" - } - }, - "Xunit.SkippableFact": { - "type": "Direct", - "requested": "[1.4.13, )", - "resolved": "1.4.13", - "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", - "dependencies": { - "Validation": "2.4.18", - "xunit.extensibility.execution": "2.4.0" - } - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0", - "System.ValueTuple": "4.5.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0", - "System.ValueTuple": "4.5.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0", - "System.ValueTuple": "4.5.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.NETFramework.ReferenceAssemblies.net462": { - "type": "Transitive", - "resolved": "1.0.3", - "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ==" - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.8.0", - "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", - "dependencies": { - "NuGet.Frameworks": "6.5.0", - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "6.5.0", - "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "PowerShellStandard.Library": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "Serilog": { - "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "7.0.2", - "System.ValueTuple": "4.5.0" - } - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Transitive", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==", - "dependencies": { - "System.Memory": "4.5.4" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.IO.Pipes.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==", - "dependencies": { - "System.Collections.Immutable": "1.5.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==" - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.ValueTuple": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" - }, - "Validation": { - "type": "Transitive", - "resolved": "2.4.18", - "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" - }, - "xunit.abstractions": { - "type": "Transitive", - "resolved": "2.0.3", - "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" - }, - "xunit.analyzers": { - "type": "Transitive", - "resolved": "1.10.0", - "contentHash": "Lw8CiDy5NaAWcO6keqD7iZHYUTIuCOcoFrUHw5Sv84ITZ9gFeDybdkVdH0Y2maSlP9fUjtENyiykT44zwFQIHA==" - }, - "xunit.assert": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "74Cm9lAZOk5TKCz2MvCBCByKsS23yryOKDIMxH3XRDHXmfGM02jKZWzRA7g4mGB41GnBnv/pcWP3vUYkrCtEcg==" - }, - "xunit.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "tqi7RfaNBqM7t8zx6QHryuBPzmotsZXKGaWnopQG2Ez5UV7JoWuyoNdT6gLpDIcKdGYey6YTXJdSr9IXDMKwjg==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]", - "xunit.extensibility.execution": "[2.6.6]" - } - }, - "xunit.extensibility.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "ty6VKByzbx4Toj4/VGJLEnlmOawqZiMv0in/tLju+ftA+lbWuAWDERM+E52Jfhj4ZYHrAYVa14KHK5T+dq0XxA==", - "dependencies": { - "xunit.abstractions": "2.0.3" - } - }, - "xunit.extensibility.execution": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "UDjIVGj2TepVKN3n32/qXIdb3U6STwTb9L6YEwoQO2A8OxiJS5QAVv2l1aT6tDwwv/9WBmm8Khh/LyHALipcng==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]" - } - }, - "Microsoft.PowerShell.EditorServices": { - "type": "Project", - "dependencies": { - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )", - "Microsoft.Extensions.Logging": "[8.0.0, )", - "OmniSharp.Extensions.DebugAdapter.Server": "[0.19.9, )", - "OmniSharp.Extensions.LanguageServer": "[0.19.9, )", - "PowerShellStandard.Library": "[5.1.1, )", - "Serilog": "[3.1.1, )", - "Serilog.Extensions.Logging": "[8.0.0, )", - "Serilog.Sinks.Async": "[1.5.0, )", - "Serilog.Sinks.File": "[5.0.0, )", - "System.IO.Pipes.AccessControl": "[5.0.0, )", - "System.Security.Principal": "[4.3.0, )", - "System.Security.Principal.Windows": "[5.0.0, )" - } - }, - "Microsoft.PowerShell.EditorServices.Test.Shared": { - "type": "Project", - "dependencies": { - "Microsoft.PowerShell.EditorServices": "[3.17.0, )" - } - } - }, - "net6.0": { - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.9.0", - "Microsoft.TestPlatform.TestHost": "17.9.0" - } - }, - "Microsoft.PowerShell.SDK": { - "type": "Direct", - "requested": "[7.2.17, )", - "resolved": "7.2.17", - "contentHash": "ZzW3f5zCWA+aPmpovTVz1bMdmrq+yjkHdH3xd3KZlE0Rfb9lSuIBvXt9ktu2/sAaGPoLL4c0Rn2OPhGrRIm81A==", - "dependencies": { - "Microsoft.Extensions.ObjectPool": "5.0.17", - "Microsoft.Management.Infrastructure.CimCmdlets": "7.2.17", - "Microsoft.NETCore.Windows.ApiSets": "1.0.1", - "Microsoft.PowerShell.Commands.Diagnostics": "7.2.17", - "Microsoft.PowerShell.Commands.Management": "7.2.17", - "Microsoft.PowerShell.Commands.Utility": "7.2.17", - "Microsoft.PowerShell.ConsoleHost": "7.2.17", - "Microsoft.PowerShell.Security": "7.2.17", - "Microsoft.WSMan.Management": "7.2.17", - "Microsoft.Windows.Compatibility": "6.0.7", - "System.Data.SqlClient": "4.8.5", - "System.IO.Packaging": "6.0.0", - "System.Management.Automation": "7.2.17", - "System.Net.Http.WinHttpHandler": "6.0.1", - "System.Private.ServiceModel": "4.9.0", - "System.ServiceModel.Duplex": "4.9.0", - "System.ServiceModel.Http": "4.9.0", - "System.ServiceModel.NetTcp": "4.9.0", - "System.ServiceModel.Primitives": "4.9.0", - "System.ServiceModel.Security": "4.9.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "xunit": { - "type": "Direct", - "requested": "[2.6.6, )", - "resolved": "2.6.6", - "contentHash": "MAbOOMtZIKyn2lrAmMlvhX0BhDOX/smyrTB+8WTXnSKkrmTGBS2fm8g1PZtHBPj91Dc5DJA7fY+/81TJ/yUFZw==", - "dependencies": { - "xunit.analyzers": "1.10.0", - "xunit.assert": "2.6.6", - "xunit.core": "[2.6.6]" - } - }, - "xunit.runner.visualstudio": { - "type": "Direct", - "requested": "[2.5.7, )", - "resolved": "2.5.7", - "contentHash": "31Rl7dBJriX0DNwZfDp8gqFOPsiM0c9kqpcH/HvNi9vDp+K7Ydf42H7mVIvYT918Ywzn1ymLg1c4DDC6iU754w==" - }, - "Xunit.SkippableFact": { - "type": "Direct", - "requested": "[1.4.13, )", - "resolved": "1.4.13", - "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", - "dependencies": { - "Validation": "2.4.18", - "xunit.extensibility.execution": "2.4.0" - } - }, - "Markdig.Signed": { - "type": "Transitive", - "resolved": "0.31.0", - "contentHash": "u05eQvNRunYLR+J0SZAgt8ia+qCF3cMfyYh7LR4jSjG5Tg+0HuRrv7u/Gox9kOItWlSacMITcHBio7jas/zaEQ==" - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.ApplicationInsights": { - "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "btZEDWAFNo9CoYliMCriSMTX3ruRGZTtYw4mo2XyyfLlowFicYVM2Xszi5evDG95QRYV7MbbH3D2RqVwfZlJHw==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "5.0.0" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==" - }, - "Microsoft.CodeAnalysis.Analyzers": { - "type": "Transitive", - "resolved": "3.3.2", - "contentHash": "7xt6zTlIEizUgEsYAIgm37EbdkiMmr6fP6J9pDoKEpiGM4pi32BCPGr/IczmSJI9Zzp0a6HOzpr9OvpMP+2veA==" - }, - "Microsoft.CodeAnalysis.Common": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "SMREwaVD5SzatlWhh9aahQAtSWdb63NcE//f+bQzgHSECU6xtDtaxk0kwV+asdFfr6HtW38UeO6jvqdfzudg3w==", - "dependencies": { - "Microsoft.CodeAnalysis.Analyzers": "3.3.2", - "System.Collections.Immutable": "5.0.0", - "System.Memory": "4.5.4", - "System.Reflection.Metadata": "5.0.0", - "System.Runtime.CompilerServices.Unsafe": "5.0.0", - "System.Text.Encoding.CodePages": "4.5.1", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.CodeAnalysis.CSharp": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "Q9RxxydPpUElj/x1/qykDTUGsRoKbJG8H5XUSeMGmMu54fBiuX1xyanom9caa1oQfh5JIW1BgLxobSaWs4WyHQ==", - "dependencies": { - "Microsoft.CodeAnalysis.Common": "[4.0.1]" - } - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.ObjectPool": { - "type": "Transitive", - "resolved": "5.0.17", - "contentHash": "EkIghF7cRBcogXKrfhopcCRjMs6b19THqSvACV5Oppp0nDA8oNyTLpAsfBQJ1hLgOjHfc5eNKFaFocKdg9nmnA==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.Management.Infrastructure": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "IaKZRNBBv3sdrmBWd+aqwHq8cVHk/3WgWFAN/dt40MRY9rbtHiDfTTmaEN0tGTmQqGCGDo/ncntA8MvFMvcsRw==", - "dependencies": { - "Microsoft.Management.Infrastructure.Runtime.Unix": "2.0.0", - "Microsoft.Management.Infrastructure.Runtime.Win": "2.0.0" - } - }, - "Microsoft.Management.Infrastructure.CimCmdlets": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "Kbx9i64RgLvL8DUEADveHNpuY3MdX7VtPxMoAsxKjPvLVUIbUAEino3NCHKz7OGkduzDwyDH/rM4OSEoiyK5+A==", - "dependencies": { - "System.Management.Automation": "7.2.17" - } - }, - "Microsoft.Management.Infrastructure.Runtime.Unix": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "p0lslMX5bdWLxO2P7ao+rjAMOB0LEwPYpzvdCQ2OEYgX2NxFpQ8ILvqPGnYlTAb53rT8gu5DyIol1HboHFYfxQ==" - }, - "Microsoft.Management.Infrastructure.Runtime.Win": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "vjBWQeDOjgernkrOdbEgn7M70SF7hof7ORdKPSlL06Uc15+oYdth5dZju9KsgUoti/cwnkZTiwtDx/lRtay0sA==" - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.NETCore.Windows.ApiSets": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "SaToCvvsGMxTgtLv/BrFQ5IFMPRE1zpWbnqbpwykJa8W5XiX82CXI6K2o7yf5xS7EP6t/JzFLV0SIDuWpvBZVw==" - }, - "Microsoft.PowerShell.Commands.Diagnostics": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "bY18hRl+fyAm8GZE/q5HG5X3aQqizuTfqfudcBK/+X3i+cO6iK7CSQbuxPjfFXWD6MSe/ljxVRAKdbIXDE/BuQ==", - "dependencies": { - "System.Management.Automation": "7.2.17" - } - }, - "Microsoft.PowerShell.Commands.Management": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "5p9eoKORNcVyjWgxnTVU/3niZgdzuXz13U8qLCljHIceOjLuzI2mYKvJaB9isTxbSEuXDKuncqBNy8XjTTmR7g==", - "dependencies": { - "Microsoft.PowerShell.Security": "7.2.17", - "System.ServiceProcess.ServiceController": "6.0.1" - } - }, - "Microsoft.PowerShell.Commands.Utility": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "i1kmPVMvQuZx8XNNYeHNz140v/J0EcAet/PswFzWpnK5SlxzWL9j9ozC5XFNAhGsWJwN1lpute1baf0ZKOoaqA==", - "dependencies": { - "Markdig.Signed": "0.31.0", - "Microsoft.CodeAnalysis.CSharp": "4.0.1", - "Microsoft.PowerShell.MarkdownRender": "7.2.1", - "Microsoft.Win32.SystemEvents": "6.0.1", - "NJsonSchema": "10.5.2", - "Namotion.Reflection": "2.0.10", - "System.Drawing.Common": "6.0.0", - "System.Management.Automation": "7.2.17", - "System.Threading.AccessControl": "6.0.0" - } - }, - "Microsoft.PowerShell.ConsoleHost": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "0ccO91QqNYCtXfRGVx6ajfKGyH0AAEDqWsyOUQHcrEtd3O8HyFsri0uspXcMfn5pn5zViNGVf852jECeAIQ/fA==", - "dependencies": { - "System.Management.Automation": "7.2.17" - } - }, - "Microsoft.PowerShell.CoreCLR.Eventing": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "MBUnwBfUzLDDtgPSb9vd2Irj83fC8nXBEBgbE04tgtIN8ofezWLuuRhopIPAi8tJpFfsTefYu8Vi7oEo7Wgj5w==", - "dependencies": { - "System.Diagnostics.EventLog": "6.0.0" - } - }, - "Microsoft.PowerShell.MarkdownRender": { - "type": "Transitive", - "resolved": "7.2.1", - "contentHash": "o5oUwL23R/KnjQPD2Oi49WAG5j4O4VLo1fPRSyM/aq0HuTrY2RnF4B3MCGk13BfcmK51p9kPlHZ1+8a/ZjO4Jg==", - "dependencies": { - "Markdig.Signed": "0.31.0" - } - }, - "Microsoft.PowerShell.Native": { - "type": "Transitive", - "resolved": "7.2.1", - "contentHash": "Ce7sccSKHemYA/p/ADD3twqp2RgvtPV6ch+hY6n50tWkGmytfSccYgnhtG30/1SaU0ktCLvg0/NSE6XB10XFqA==" - }, - "Microsoft.PowerShell.Security": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "HaMmLZtFPb/HuHm89j3g7d8MR0AaEMMFuvJnF4yxCukANkFiIFe3Tuyt1C+p4PJmooGDm3zkDX5UiCfDB4i4kg==", - "dependencies": { - "System.Management.Automation": "7.2.17" - } - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "Spmg7Wx49Ya3SxBjyeAR+nQpjMTKZwTwpZ7KyeOTIqI/WHNPnBU4HUvl5kuHPQAwGWqMy4FGZja1HvEwvoaDiA==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.9.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Microsoft.Win32.Registry.AccessControl": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UoE+eeuBKL+GFHxHV3FjHlY5K8Wr/IR7Ee/a2oDNqFodF1iMqyt5hIs0U9Z217AbWrHrNle4750kD03hv1IMZw==", - "dependencies": { - "System.Security.AccessControl": "6.0.0" - } - }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "AlsaDWyQHLFB7O2nfbny0x0oziB34WWzGnf/4Q5R8KjXhu8MnCsxE2MIePr192lIIaxarfTLI9bQg+qtmM+9ag==" - }, - "Microsoft.Windows.Compatibility": { - "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "ubX/cYXBas9hMYhzb4ZkdsMuS+Z1z0X43oZ0rOQq9HnVsQgdbsBCEUmvgFwBxPd41KiPwSZiNZeuBZC+BSyFPw==", - "dependencies": { - "Microsoft.Win32.Registry.AccessControl": "6.0.0", - "Microsoft.Win32.SystemEvents": "6.0.1", - "System.CodeDom": "6.0.0", - "System.ComponentModel.Composition": "6.0.0", - "System.ComponentModel.Composition.Registration": "6.0.0", - "System.Configuration.ConfigurationManager": "6.0.1", - "System.Data.Odbc": "6.0.1", - "System.Data.OleDb": "6.0.0", - "System.Data.SqlClient": "4.8.5", - "System.Diagnostics.EventLog": "6.0.0", - "System.Diagnostics.PerformanceCounter": "6.0.1", - "System.DirectoryServices": "6.0.1", - "System.DirectoryServices.AccountManagement": "6.0.0", - "System.DirectoryServices.Protocols": "6.0.2", - "System.Drawing.Common": "6.0.0", - "System.IO.Packaging": "6.0.0", - "System.IO.Ports": "6.0.0", - "System.Management": "6.0.2", - "System.Reflection.Context": "6.0.0", - "System.Runtime.Caching": "6.0.0", - "System.Security.AccessControl": "6.0.0", - "System.Security.Cryptography.Pkcs": "6.0.4", - "System.Security.Cryptography.ProtectedData": "6.0.0", - "System.Security.Cryptography.Xml": "6.0.1", - "System.Security.Permissions": "6.0.0", - "System.ServiceModel.Duplex": "4.9.0", - "System.ServiceModel.Http": "4.9.0", - "System.ServiceModel.NetTcp": "4.9.0", - "System.ServiceModel.Primitives": "4.9.0", - "System.ServiceModel.Security": "4.9.0", - "System.ServiceModel.Syndication": "6.0.0", - "System.ServiceProcess.ServiceController": "6.0.1", - "System.Speech": "6.0.0", - "System.Text.Encoding.CodePages": "6.0.0", - "System.Threading.AccessControl": "6.0.0", - "System.Web.Services.Description": "4.9.0" - } - }, - "Microsoft.WSMan.Management": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "4WvKb8xQgs+EpDkWBCx2MMg8jdnf7yncuOOf/S2qMA6ISOCGIwla46/Faixp+d67pZhAMXkvNVx3C/ym5MkyXw==", - "dependencies": { - "Microsoft.WSMan.Runtime": "7.2.17", - "System.Management.Automation": "7.2.17", - "System.ServiceProcess.ServiceController": "6.0.1" - } - }, - "Microsoft.WSMan.Runtime": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "Q/e/EwioaaufbinB2nFrIT+AkCiSJ3pRAV1zHCwWKjx4X26YYsNdamd+9EFWqAccQHZuBs9pOAWsLvMXwNzR0w==" - }, - "Namotion.Reflection": { - "type": "Transitive", - "resolved": "2.0.10", - "contentHash": "KHndyscosup/AnzMQLzW0g6+z0h2NCmTyW9hnEL/T/ZkiUIQWBA1RadYgUT+dXuMORmQI/BXm+DXYySWwq8h0Q==", - "dependencies": { - "Microsoft.CSharp": "4.3.0" - } - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "NJsonSchema": { - "type": "Transitive", - "resolved": "10.5.2", - "contentHash": "Vr2CbySuXh74TQFU0rGJYZOS492xOE64cPXdB7a0cfXJb/N45Bf4v7sd4LOla0jNhgc5V/B61Ko3qecriL195w==", - "dependencies": { - "Namotion.Reflection": "2.0.3", - "Newtonsoft.Json": "9.0.1" - } - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "PowerShellStandard.Library": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "runtime.linux-arm.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "75q52H7CSpgIoIDwXb9o833EvBZIXJ0mdPhz1E6jSisEXUBlSCPalC29cj3EXsjpuDwr0dj1LRXZepIQH/oL4Q==" - }, - "runtime.linux-arm64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xn2bMThmXr3CsvOYmS8ex2Yz1xo+kcnhVg2iVhS9PlmqjZPAkrEo/I40wjrBZH/tU4kvH0s1AE8opAvQ3KIS8g==" - }, - "runtime.linux-x64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "16nbNXwv0sC+gLGIuecri0skjuh6R1maIJggsaNP7MQBcbVcEfWFUOkEnsnvoLEjy0XerfibuRptfQ8AmdIcWA==" - }, - "runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==", - "dependencies": { - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0" - } - }, - "runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "KaaXlpOcuZjMdmyF5wzzx3b+PRKIzt6A5Ax9dKenPDQbVJAFpev+casD0BIig1pBcbs3zx7CqWemzUJKAeHdSQ==", - "dependencies": { - "runtime.linux-arm.runtime.native.System.IO.Ports": "6.0.0", - "runtime.linux-arm64.runtime.native.System.IO.Ports": "6.0.0", - "runtime.linux-x64.runtime.native.System.IO.Ports": "6.0.0", - "runtime.osx-arm64.runtime.native.System.IO.Ports": "6.0.0", - "runtime.osx-x64.runtime.native.System.IO.Ports": "6.0.0" - } - }, - "runtime.osx-arm64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "fXG12NodG1QrCdoaeSQ1gVnk/koi4WYY4jZtarMkZeQMyReBm1nZlSRoPnUjLr2ZR36TiMjpcGnQfxymieUe7w==" - }, - "runtime.osx-x64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/As+zPY49+dSUXkh+fTUbyPhqrdGN//evLxo4Vue88pfh1BHZgF7q4kMblTkxYvwR6Vi03zSYxysSFktO8/SDQ==" - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==" - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==" - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" - }, - "Serilog": { - "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==" - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Transitive", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==" - }, - "System.ComponentModel.Composition": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "60Qv+F7oxomOjJeTDA5Z4iCyFbQ0B/2Mi5HT+13pxxq0lVnu2ipbWMzFB+RWKr3wWKA8BSncXr9PH/fECwMX5Q==" - }, - "System.ComponentModel.Composition.Registration": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+i3RLlOgTsf15VeADBPpzPyRiXq71aLSuzdHeNtmq9f6BwpF3OWhB76p0WDUNCa3Z+SLD4dJbBM9yAep7kQCGA==", - "dependencies": { - "System.ComponentModel.Composition": "6.0.0", - "System.Reflection.Context": "6.0.0" - } - }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==", - "dependencies": { - "System.Security.Cryptography.ProtectedData": "6.0.0", - "System.Security.Permissions": "6.0.0" - } - }, - "System.Data.Odbc": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "4vl7z0b8gcwc2NotcpEkqaLVQAw/wo46zV1uVSoIx2UfJdqlxWKD3ViUicCNJGo41th4kaGcY9kyVe2q9EuB4w==", - "dependencies": { - "System.Text.Encoding.CodePages": "6.0.0" - } - }, - "System.Data.OleDb": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "LQ8PjTIF1LtrrlGiyiTVjAkQtTWKm9GSNnygIlWjhN9y88s7xhy6DUNDDkmQQ9f6ex7mA4k0Tl97lz/CklaiLg==", - "dependencies": { - "System.Configuration.ConfigurationManager": "6.0.0", - "System.Diagnostics.PerformanceCounter": "6.0.0" - } - }, - "System.Data.SqlClient": { - "type": "Transitive", - "resolved": "4.8.5", - "contentHash": "fRqxut4lrndPHrXD+ht1XRmCL3obuKldm4XjCRYS9p5f7FSR7shBxAwTkDrpFMsHC9BhNgjjmUtiIjvehn5zkg==", - "dependencies": { - "Microsoft.Win32.Registry": "4.7.0", - "System.Security.Principal.Windows": "4.7.0", - "runtime.native.System.Data.SqlClient.sni": "4.7.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "5.0.1", - "contentHash": "uXQEYqav2V3zP6OwkOKtLv+qIi6z3m1hsGyKwXX7ZA7htT4shoVccGxnJ9kVRFPNAsi1ArZTq2oh7WOto6GbkQ==" - }, - "System.Diagnostics.EventLog": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" - }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "dDl7Gx3bmSrM2k2ZIm+ucEJnLloZRyvfQF1DvfvATcGF3jtaUBiPvChma+6ZcZzxWMirN3kCywkW7PILphXyMQ==", - "dependencies": { - "System.Configuration.ConfigurationManager": "6.0.0" - } - }, - "System.DirectoryServices": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "935IbO7h5FDGYxeO3cbx/CuvBBuk/VI8sENlfmXlh1pupNOB3LAGzdYdPY8CawGJFP7KNrHK5eUlsFoz3F6cuA==", - "dependencies": { - "System.Security.AccessControl": "6.0.0", - "System.Security.Permissions": "6.0.0" - } - }, - "System.DirectoryServices.AccountManagement": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "2iKkY6VC4WX6H13N8WhH2SRUfWCwg2KZR5w9JIS9cw9N8cZhT7VXxHX0L6OX6Po419aSu2LWrJE9tu6b+cUnPA==", - "dependencies": { - "System.Configuration.ConfigurationManager": "6.0.0", - "System.DirectoryServices": "6.0.0", - "System.DirectoryServices.Protocols": "6.0.0", - "System.Security.AccessControl": "6.0.0" - } - }, - "System.DirectoryServices.Protocols": { - "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "vDDPWwHn3/DNZ+kPkdXHoada+tKPEC9bVqDOr4hK6HBSP7hGCUTA0Zw6WU5qpGaqa5M1/V+axHMIv+DNEbIf6g==" - }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "6.0.0" - } - }, - "System.Formats.Asn1": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA==" - }, - "System.IO.Packaging": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "C7OkTRIjqIjAKu6ef/fuj8ynCZTPcTYZnvHaq48bniACgXXJogmEoIc56YCDNTc14xhsbLmgpS3KP+evbsUa2g==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==" - }, - "System.IO.Pipes.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.IO.Ports": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dRyGI7fUESar5ZLIpiBOaaNLW7YyOBGftjj5Of+xcduC/Rjl7RjhEnWDvvNBmHuF3d0tdXoqdVI/yrVA8f00XA==", - "dependencies": { - "runtime.native.System.IO.Ports": "6.0.0" - } - }, - "System.Management": { - "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "s6c9x2Kghd+ncEDnT6ApYVOacDXr/Y57oSUSx6wjegMOfKxhtrXn3PdASPNU59y3kB9OJ1yb3l5k6uKr3bhqew==", - "dependencies": { - "System.CodeDom": "6.0.0" - } - }, - "System.Management.Automation": { - "type": "Transitive", - "resolved": "7.2.17", - "contentHash": "PY6LHEH3mjHzzHgtSdPp8uTiUcL4+lLwj3T6ZASNgZVw+BXdHQeMBSteIVV72DAQzk0DblS2xkVasoSem0ggIw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.21.0", - "Microsoft.CSharp": "4.7.0", - "Microsoft.Management.Infrastructure": "2.0.0", - "Microsoft.PowerShell.CoreCLR.Eventing": "7.2.17", - "Microsoft.PowerShell.Native": "7.2.1", - "Microsoft.Win32.Registry.AccessControl": "6.0.0", - "Newtonsoft.Json": "13.0.3", - "System.Configuration.ConfigurationManager": "6.0.1", - "System.Diagnostics.DiagnosticSource": "5.0.1", - "System.DirectoryServices": "6.0.1", - "System.Management": "6.0.2", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Security.AccessControl": "6.0.0", - "System.Security.Cryptography.Pkcs": "6.0.4", - "System.Security.Permissions": "6.0.0", - "System.Text.Encoding.CodePages": "6.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" - }, - "System.Net.Http.WinHttpHandler": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "zdSpn2+EPBEXmsopvUjkpvbXNN53dqL7BifCKBJ5M+A/P3JEiCL/guuPwuiazu+RzCuEX/NJLnoXhllaf3vBxg==" - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Private.ServiceModel": { - "type": "Transitive", - "resolved": "4.9.0", - "contentHash": "d3RjkrtpjUQ63PzFmm/SZ4aOXeJNP+8YW5QeP0lCJy8iX4xlHdlNLWTF9sRn9SmrFTK757kQXT9Op/R4l858uw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "Microsoft.Extensions.ObjectPool": "5.0.10", - "System.Numerics.Vectors": "4.5.0", - "System.Reflection.DispatchProxy": "4.7.1", - "System.Security.Cryptography.Xml": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" - }, - "System.Reflection.Context": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vi+Gb41oyOYie7uLSsjRmfRg3bryUg5DssJvj3gDUl0D8z6ipSm6/yi/XNx2rcS5iVMvHcwRUHjcx7ixv0K3/w==" - }, - "System.Reflection.DispatchProxy": { - "type": "Transitive", - "resolved": "4.7.1", - "contentHash": "C1sMLwIG6ILQ2bmOT4gh62V6oJlyF4BlHcVMrOoor49p0Ji2tA8QAoqyMcIhAdH6OHKJ8m7BU+r4LK2CUEOKqw==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==" - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.Caching": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "E0e03kUp5X2k+UAoVl6efmI7uU7JRBWi5EIdlQ7cr0NpBGjHG4fWII35PgsBY9T4fJQ8E4QPsL0rKksU9gcL5A==", - "dependencies": { - "System.Configuration.ConfigurationManager": "6.0.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==" - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "6.0.4", - "contentHash": "LGbXi1oUJ9QgCNGXRO9ndzBL/GZgANcsURpMhNR8uO+rca47SZmciS3RSQUvlQRwK3QHZSHNOXzoMUASKA+Anw==", - "dependencies": { - "System.Formats.Asn1": "6.0.0" - } - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==" - }, - "System.Security.Cryptography.Xml": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "5e5bI28T0x73AwTsbuFP4qSRzthmU2C0Gqgg3AZ3KTxmSyA+Uhk31puA3srdaeWaacVnHhLdJywCzqOiEpbO/w==", - "dependencies": { - "System.Security.AccessControl": "6.0.0", - "System.Security.Cryptography.Pkcs": "6.0.1" - } - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==", - "dependencies": { - "System.Security.AccessControl": "6.0.0", - "System.Windows.Extensions": "6.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.ServiceModel.Duplex": { - "type": "Transitive", - "resolved": "4.9.0", - "contentHash": "Yb8MFiJxBBtm2JnfS/5SxYzm2HqkEmHu5xeaVIHXy83sNpty9wc30JifH2xgda821D6nr1UctbwbdZqN4LBUKQ==", - "dependencies": { - "System.Private.ServiceModel": "4.9.0", - "System.ServiceModel.Primitives": "4.9.0" - } - }, - "System.ServiceModel.Http": { - "type": "Transitive", - "resolved": "4.9.0", - "contentHash": "Z+s3RkLNzJ31fDXAjqXdXp67FqsNG4V3Md3r7FOrzMkHmg61gY8faEfTFPBLxU9tax1HPWt6IHVAquXBKySJaw==", - "dependencies": { - "System.Private.ServiceModel": "4.9.0", - "System.ServiceModel.Primitives": "4.9.0" - } - }, - "System.ServiceModel.NetTcp": { - "type": "Transitive", - "resolved": "4.9.0", - "contentHash": "nXgnnkrZERUF/KwmoLwZPkc7fqgiq94DXkmUZBvDNh/LdZquDvjy2NbhJLElpApOa5x8zEoQoBZyJ2PqNC39qg==", - "dependencies": { - "System.Private.ServiceModel": "4.9.0", - "System.ServiceModel.Primitives": "4.9.0" - } - }, - "System.ServiceModel.Primitives": { - "type": "Transitive", - "resolved": "4.9.0", - "contentHash": "LTFPVdS8Nf76xg/wRZkDa+2Q+GnjTOmwkTlwuoetwX37mAfYnGkf7p8ydhpDwVmomNljpUOhUUGxfjQyd5YcOg==", - "dependencies": { - "System.Private.ServiceModel": "4.9.0" - } - }, - "System.ServiceModel.Security": { - "type": "Transitive", - "resolved": "4.9.0", - "contentHash": "iurpbSmPgotHps94VQ6acvL6hU2gjiuBmQI7PwLLN76jsbSpUcahT0PglccKIAwoMujATk/LWtAapBHpwCFn2g==", - "dependencies": { - "System.Private.ServiceModel": "4.9.0", - "System.ServiceModel.Primitives": "4.9.0" - } - }, - "System.ServiceModel.Syndication": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "cp1mMNG87iJtE0oHXFtfWT6cfski2JNo5iU0siTPi/uN2k1CIJI6FE4jr4v3got2dzt7wBq17fSy44btun9GiA==" - }, - "System.ServiceProcess.ServiceController": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "LJGWSUfoEZ6NBVPGnDsCMDrT8sDI7QJ8SUzuJQUnIDOtkZiC1LFUmsGu+Dq6OdwSnaW9nENIbL7uSd4PF9YpIA==", - "dependencies": { - "System.Diagnostics.EventLog": "6.0.0" - } - }, - "System.Speech": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GQovERMrNP0Vbtgk8LzH4PlFS6lqHgsL9WkUmv8Kkxa0m0vNakitytpHZlfJ9WR7n9WKLXh68nn2kyL9mflnLg==" - }, - "System.Text.Encoding.CodePages": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Threading.AccessControl": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "2258mqWesMch/xCpcnjJBgJP33yhpZLGLbEOm01qwq0efG4b+NG8c9sxYOWNxmDQ82swXrnQRl1Yp2wC1NrfZA==", - "dependencies": { - "System.Security.AccessControl": "6.0.0" - } - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.Web.Services.Description": { - "type": "Transitive", - "resolved": "4.9.0", - "contentHash": "d20B3upsWddwSG5xF3eQLs0cAV3tXDsBNqP4kh02ylfgZwqfpf4f/9KiZVIGIoxULt2cKqxWs+U4AdNAJ7L8cQ==" - }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==", - "dependencies": { - "System.Drawing.Common": "6.0.0" - } - }, - "Validation": { - "type": "Transitive", - "resolved": "2.4.18", - "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" - }, - "xunit.abstractions": { - "type": "Transitive", - "resolved": "2.0.3", - "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" - }, - "xunit.analyzers": { - "type": "Transitive", - "resolved": "1.10.0", - "contentHash": "Lw8CiDy5NaAWcO6keqD7iZHYUTIuCOcoFrUHw5Sv84ITZ9gFeDybdkVdH0Y2maSlP9fUjtENyiykT44zwFQIHA==" - }, - "xunit.assert": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "74Cm9lAZOk5TKCz2MvCBCByKsS23yryOKDIMxH3XRDHXmfGM02jKZWzRA7g4mGB41GnBnv/pcWP3vUYkrCtEcg==" - }, - "xunit.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "tqi7RfaNBqM7t8zx6QHryuBPzmotsZXKGaWnopQG2Ez5UV7JoWuyoNdT6gLpDIcKdGYey6YTXJdSr9IXDMKwjg==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]", - "xunit.extensibility.execution": "[2.6.6]" - } - }, - "xunit.extensibility.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "ty6VKByzbx4Toj4/VGJLEnlmOawqZiMv0in/tLju+ftA+lbWuAWDERM+E52Jfhj4ZYHrAYVa14KHK5T+dq0XxA==", - "dependencies": { - "xunit.abstractions": "2.0.3" - } - }, - "xunit.extensibility.execution": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "UDjIVGj2TepVKN3n32/qXIdb3U6STwTb9L6YEwoQO2A8OxiJS5QAVv2l1aT6tDwwv/9WBmm8Khh/LyHALipcng==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]" - } - }, - "Microsoft.PowerShell.EditorServices": { - "type": "Project", - "dependencies": { - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )", - "Microsoft.Extensions.Logging": "[8.0.0, )", - "OmniSharp.Extensions.DebugAdapter.Server": "[0.19.9, )", - "OmniSharp.Extensions.LanguageServer": "[0.19.9, )", - "PowerShellStandard.Library": "[5.1.1, )", - "Serilog": "[3.1.1, )", - "Serilog.Extensions.Logging": "[8.0.0, )", - "Serilog.Sinks.Async": "[1.5.0, )", - "Serilog.Sinks.File": "[5.0.0, )", - "System.IO.Pipes.AccessControl": "[5.0.0, )", - "System.Security.Principal": "[4.3.0, )", - "System.Security.Principal.Windows": "[5.0.0, )" - } - }, - "Microsoft.PowerShell.EditorServices.Test.Shared": { - "type": "Project", - "dependencies": { - "Microsoft.PowerShell.EditorServices": "[3.17.0, )" - } - } - }, - "net7.0": { - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.9.0", - "Microsoft.TestPlatform.TestHost": "17.9.0" - } - }, - "Microsoft.PowerShell.SDK": { - "type": "Direct", - "requested": "[7.3.10, )", - "resolved": "7.3.10", - "contentHash": "3sxhHjdfcbMvg2kpjmXkZp2J7Tox61YOIXlpn0PUypSy6uI52Rvg2q3PHz9RqKVmZ0VvaETFQsyajltEdQjRXg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.Extensions.ObjectPool": "7.0.14", - "Microsoft.Management.Infrastructure.CimCmdlets": "7.3.10", - "Microsoft.PowerShell.Commands.Diagnostics": "7.3.10", - "Microsoft.PowerShell.Commands.Management": "7.3.10", - "Microsoft.PowerShell.Commands.Utility": "7.3.10", - "Microsoft.PowerShell.ConsoleHost": "7.3.10", - "Microsoft.PowerShell.Security": "7.3.10", - "Microsoft.WSMan.Management": "7.3.10", - "Microsoft.Win32.Registry": "5.0.0", - "Microsoft.Windows.Compatibility": "7.0.5", - "System.Data.SqlClient": "4.8.5", - "System.IO.Packaging": "7.0.0", - "System.Management.Automation": "7.3.10", - "System.Net.Http.WinHttpHandler": "7.0.0", - "System.Private.ServiceModel": "4.10.3", - "System.Security.Cryptography.ProtectedData": "7.0.1", - "System.Security.Cryptography.Xml": "7.0.1", - "System.ServiceModel.Duplex": "4.10.3", - "System.ServiceModel.Http": "4.10.3", - "System.ServiceModel.NetTcp": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3", - "System.ServiceModel.Security": "4.10.3", - "System.Text.Encodings.Web": "7.0.0", - "System.Web.Services.Description": "4.10.3" - } - }, - "xunit": { - "type": "Direct", - "requested": "[2.6.6, )", - "resolved": "2.6.6", - "contentHash": "MAbOOMtZIKyn2lrAmMlvhX0BhDOX/smyrTB+8WTXnSKkrmTGBS2fm8g1PZtHBPj91Dc5DJA7fY+/81TJ/yUFZw==", - "dependencies": { - "xunit.analyzers": "1.10.0", - "xunit.assert": "2.6.6", - "xunit.core": "[2.6.6]" - } - }, - "xunit.runner.visualstudio": { - "type": "Direct", - "requested": "[2.5.7, )", - "resolved": "2.5.7", - "contentHash": "31Rl7dBJriX0DNwZfDp8gqFOPsiM0c9kqpcH/HvNi9vDp+K7Ydf42H7mVIvYT918Ywzn1ymLg1c4DDC6iU754w==" - }, - "Xunit.SkippableFact": { - "type": "Direct", - "requested": "[1.4.13, )", - "resolved": "1.4.13", - "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", - "dependencies": { - "Validation": "2.4.18", - "xunit.extensibility.execution": "2.4.0" - } - }, - "Markdig.Signed": { - "type": "Transitive", - "resolved": "0.31.0", - "contentHash": "u05eQvNRunYLR+J0SZAgt8ia+qCF3cMfyYh7LR4jSjG5Tg+0HuRrv7u/Gox9kOItWlSacMITcHBio7jas/zaEQ==" - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.ApplicationInsights": { - "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "btZEDWAFNo9CoYliMCriSMTX3ruRGZTtYw4mo2XyyfLlowFicYVM2Xszi5evDG95QRYV7MbbH3D2RqVwfZlJHw==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "5.0.0" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==" - }, - "Microsoft.CodeAnalysis.Analyzers": { - "type": "Transitive", - "resolved": "3.3.3", - "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" - }, - "Microsoft.CodeAnalysis.Common": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "JfHupS/B7Jb5MZoYkFFABn3mux0wQgxi2D8F/rJYZeRBK2ZOyk7TjQ2Kq9rh6W/DCh0KNbbSbn5qoFar+ueHqw==", - "dependencies": { - "Microsoft.CodeAnalysis.Analyzers": "3.3.3", - "System.Collections.Immutable": "6.0.0", - "System.Memory": "4.5.5", - "System.Reflection.Metadata": "5.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encoding.CodePages": "6.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.CodeAnalysis.CSharp": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "eD2w0xHRoaqK07hjlOKGR9eLNy3nimiGNeCClNax1NDgS/+DBtBqCjXelOa+TNy99kIB3nHhUqDmr46nDXy/RQ==", - "dependencies": { - "Microsoft.CodeAnalysis.Common": "[4.4.0]" - } - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.ObjectPool": { - "type": "Transitive", - "resolved": "7.0.14", - "contentHash": "C2N75sDAj4xY1hex2Tzv6l8s5Wcuvh6v6kjkLwyahJb7l2V7mrWtlKbSP19Q0AxqGyvTkd7Is/M2DBTwCbKBSQ==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" - }, - "Microsoft.Management.Infrastructure": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "IaKZRNBBv3sdrmBWd+aqwHq8cVHk/3WgWFAN/dt40MRY9rbtHiDfTTmaEN0tGTmQqGCGDo/ncntA8MvFMvcsRw==", - "dependencies": { - "Microsoft.Management.Infrastructure.Runtime.Unix": "2.0.0", - "Microsoft.Management.Infrastructure.Runtime.Win": "2.0.0" - } - }, - "Microsoft.Management.Infrastructure.CimCmdlets": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "RM1yPzl67HjotHMuaw2dBdlKLUBR3pmYQ+JGcw9z6Osls6WmKoQZcJKiItPAMZX38AaTB21VEutyBTI5LhpJ7Q==", - "dependencies": { - "System.Management.Automation": "7.3.10" - } - }, - "Microsoft.Management.Infrastructure.Runtime.Unix": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "p0lslMX5bdWLxO2P7ao+rjAMOB0LEwPYpzvdCQ2OEYgX2NxFpQ8ILvqPGnYlTAb53rT8gu5DyIol1HboHFYfxQ==" - }, - "Microsoft.Management.Infrastructure.Runtime.Win": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "vjBWQeDOjgernkrOdbEgn7M70SF7hof7ORdKPSlL06Uc15+oYdth5dZju9KsgUoti/cwnkZTiwtDx/lRtay0sA==" - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.PowerShell.Commands.Diagnostics": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "aopcVFcevPACu9il0zeIuGv0NU/cR7H9zHNAN5Ug4h7Q07eJM14mWGr4BcYwk7xRBK2Lk7AZmNWxC5CHvfKTAA==", - "dependencies": { - "System.Management.Automation": "7.3.10" - } - }, - "Microsoft.PowerShell.Commands.Management": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "YN1NT9h/x/EiL4XnNtC4/1CsDAvmJwW/2RqaHMqMzCXRwq38+hAH1zeVy+N4BbLhAjMSvCAVHI+vh+KRgBylfw==", - "dependencies": { - "Microsoft.PowerShell.Security": "7.3.10", - "System.ServiceProcess.ServiceController": "7.0.1" - } - }, - "Microsoft.PowerShell.Commands.Utility": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "ayzddFHB7+o7UI33EziTiqitOUGCMhpenxrbjhD3neeqPHJj2SDQEh1SmCb4dHY2Gso/JIOR10qbjhQfWpKItQ==", - "dependencies": { - "Markdig.Signed": "0.31.0", - "Microsoft.CodeAnalysis.CSharp": "4.4.0", - "Microsoft.PowerShell.MarkdownRender": "7.2.1", - "NJsonSchema": "10.8.0", - "Namotion.Reflection": "2.1.2", - "System.Drawing.Common": "7.0.0", - "System.Management.Automation": "7.3.10", - "System.Threading.AccessControl": "7.0.1" - } - }, - "Microsoft.PowerShell.ConsoleHost": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "HxP8ZtGRIZYxLgWMIxCfY4FJimpVilcG7Gca0PPX91vua624OBt+IxpPtVz678uH/14YeJIbpSGzOAwh6xvQhg==", - "dependencies": { - "System.Management.Automation": "7.3.10" - } - }, - "Microsoft.PowerShell.CoreCLR.Eventing": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "/rU9EUbko0hftXg8Gor5xhpQipM0Y4CrxwanYR5/6ZLrZBkTroanOhhWTCjW0phI8d8a9sAQA2FECFzWO8kWKg==", - "dependencies": { - "System.Diagnostics.EventLog": "7.0.0" - } - }, - "Microsoft.PowerShell.MarkdownRender": { - "type": "Transitive", - "resolved": "7.2.1", - "contentHash": "o5oUwL23R/KnjQPD2Oi49WAG5j4O4VLo1fPRSyM/aq0HuTrY2RnF4B3MCGk13BfcmK51p9kPlHZ1+8a/ZjO4Jg==", - "dependencies": { - "Markdig.Signed": "0.31.0" - } - }, - "Microsoft.PowerShell.Native": { - "type": "Transitive", - "resolved": "7.3.2", - "contentHash": "MlLhJgzrUlxijTKJ19Eht++iGTUdg/F1jSbqwzjnc2Q8XStkUYNh8/81aUcNxWcg+0z1Yj/iUjW7czgWUYdV6Q==" - }, - "Microsoft.PowerShell.Security": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "I8zMNzrEN8cCUQUTpwRzYtGi+vXoO5DvSfHT8+x2I6yrgAhyjzZyYQkvGm+n/hZBIA8Ayc6o67L7Ev0cLpW1cw==", - "dependencies": { - "System.Management.Automation": "7.3.10" - } - }, - "Microsoft.Security.Extensions": { - "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "GjHZBE5PHKrxPRyGujWQKwbKNjPQYds6HcAWKeV49X3KPgBfF2B1vV5uJey5UluyGQlvAO/DezL7WzEx9HlPQA==" - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "Spmg7Wx49Ya3SxBjyeAR+nQpjMTKZwTwpZ7KyeOTIqI/WHNPnBU4HUvl5kuHPQAwGWqMy4FGZja1HvEwvoaDiA==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.9.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Microsoft.Win32.Registry.AccessControl": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "JwM65WXVca58WzqY/Rpz7FGyHbN/SMdyr/3EI2CwPIYkB55EIRJUdPQJwO64x3ntOwPQoqCATKuDYA9K7Np5Ww==" - }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ==" - }, - "Microsoft.Windows.Compatibility": { - "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "N4aTGZVV1PYPLVLtNn6jsh2b20oS87jegwkB1yDbV4Fy8bs2FZvsjEjjQg1wc7E29JKuwdNXOUYd9ww9LKuLtA==", - "dependencies": { - "Microsoft.Win32.Registry.AccessControl": "7.0.0", - "Microsoft.Win32.SystemEvents": "7.0.0", - "System.CodeDom": "7.0.0", - "System.ComponentModel.Composition": "7.0.0", - "System.ComponentModel.Composition.Registration": "7.0.0", - "System.Configuration.ConfigurationManager": "7.0.0", - "System.Data.Odbc": "7.0.0", - "System.Data.OleDb": "7.0.0", - "System.Data.SqlClient": "4.8.5", - "System.Diagnostics.EventLog": "7.0.0", - "System.Diagnostics.PerformanceCounter": "7.0.0", - "System.DirectoryServices": "7.0.1", - "System.DirectoryServices.AccountManagement": "7.0.1", - "System.DirectoryServices.Protocols": "7.0.1", - "System.Drawing.Common": "7.0.0", - "System.IO.Packaging": "7.0.0", - "System.IO.Ports": "7.0.0", - "System.Management": "7.0.2", - "System.Reflection.Context": "7.0.0", - "System.Runtime.Caching": "7.0.0", - "System.Security.Cryptography.Pkcs": "7.0.2", - "System.Security.Cryptography.ProtectedData": "7.0.1", - "System.Security.Cryptography.Xml": "7.0.1", - "System.Security.Permissions": "7.0.0", - "System.ServiceModel.Duplex": "4.9.0", - "System.ServiceModel.Http": "4.9.0", - "System.ServiceModel.NetTcp": "4.9.0", - "System.ServiceModel.Primitives": "4.9.0", - "System.ServiceModel.Security": "4.9.0", - "System.ServiceModel.Syndication": "7.0.0", - "System.ServiceProcess.ServiceController": "7.0.1", - "System.Speech": "7.0.0", - "System.Text.Encoding.CodePages": "7.0.0", - "System.Threading.AccessControl": "7.0.1", - "System.Web.Services.Description": "4.9.0" - } - }, - "Microsoft.WSMan.Management": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "uEfRWYS1FA2vipN16A/ZnT5hOA010AukAFJENxyTj7Xmb8m3IN0k915BkxO+n/abLMJ4PVRRi01vA0voodFVig==", - "dependencies": { - "Microsoft.WSMan.Runtime": "7.3.10", - "System.Management.Automation": "7.3.10", - "System.ServiceProcess.ServiceController": "7.0.1" - } - }, - "Microsoft.WSMan.Runtime": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "e2DHtDuYcDGj2s8q+UsqJM5u/dhxjfqxpO3ojSITz0zuMlrsaJytoiG9dn6R4eSKV9C1XuvrN1xAQCUWfVMl7Q==" - }, - "Namotion.Reflection": { - "type": "Transitive", - "resolved": "2.1.2", - "contentHash": "7tSHAzX8GWKy0qrW6OgQWD7kAZiqzhq+m1503qczuwuK6ZYhOGCQUxw+F3F4KkRM70aB6RMslsRVSCFeouIehw==", - "dependencies": { - "Microsoft.CSharp": "4.3.0" - } - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "NJsonSchema": { - "type": "Transitive", - "resolved": "10.8.0", - "contentHash": "lChjsLWaxyvElh4WJjVhdIiCtx7rimYGFTxtSi2pAkZf0ZnKaXYIX484HCVyzbDDHejDZPgOrcfAJ3kqNSTONw==", - "dependencies": { - "Namotion.Reflection": "2.1.0", - "Newtonsoft.Json": "9.0.1" - } - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "PowerShellStandard.Library": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "runtime.linux-arm.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "CBvgRaF+M0xGLDv2Geb/0v0LEADheH8aK72GRAUJdnqnJVsQO60ki1XO8M3keEhnjm+T5NvLm41pNXAVYAPiSg==" - }, - "runtime.linux-arm64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "5VCyRCtCIYU8FR/W8oo7ouFuJ8tmAg9ddsuXhfCKZfZrbaVZSKxkmNBa6fxkfYPueD0jQfOvwFBmE5c6zalCSw==" - }, - "runtime.linux-x64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "DV9dWDUs23OoZqMWl5IhLr3D+b9koDiSHQxFKdYgWnQbnthv8c/yDjrlrI8nMrDc71RAKCO8jlUojzuPMX04gg==" - }, - "runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==", - "dependencies": { - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0" - } - }, - "runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "L4Ivegqc3B0Fee7VifFy2JST9nndm+uvJ0viLIZUaImDfnr+JmRin9Tbqd56KuMtm0eVxHpNOWZBPtKrA/1h5Q==", - "dependencies": { - "runtime.linux-arm.runtime.native.System.IO.Ports": "7.0.0", - "runtime.linux-arm64.runtime.native.System.IO.Ports": "7.0.0", - "runtime.linux-x64.runtime.native.System.IO.Ports": "7.0.0", - "runtime.osx-arm64.runtime.native.System.IO.Ports": "7.0.0", - "runtime.osx-x64.runtime.native.System.IO.Ports": "7.0.0" - } - }, - "runtime.osx-arm64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jFwh4sKSXZ7al5XrItEO4GdGWa6XNxvNx+LhEHjrSzOwawO1znwJ+Dy+VjnrkySX9Qi4bnHNLoiqOXbqMuka4g==" - }, - "runtime.osx-x64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "X4LrHEfke/z9+z+iuVr35NlkhdZldY8JGNMYUN+sfPK/U/6TcE+vP44I0Yv0ir1v0bqIzq3v6Qdv1c1vmp8s4g==" - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==" - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==" - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" - }, - "Serilog": { - "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==" - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Transitive", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.ComponentModel.Composition": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "orv0h38ZVPCPo/FW0LGv8/TigXwX8cIwXeQcaNYhikkqELDm8sUFLMcof/Sjcq5EvYCm5NA7MV3hG4u75H44UQ==" - }, - "System.ComponentModel.Composition.Registration": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "yy/xYOznnc7Hfg2/LeVqAMlJGv1v7b1ILxFShzx5PWUv53PwU0MaKPG8Dh9DC3gxayzw44UVuQJImhw7LtMKlw==", - "dependencies": { - "System.ComponentModel.Composition": "7.0.0", - "System.Reflection.Context": "7.0.0" - } - }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "WvRUdlL1lB0dTRZSs5XcQOd5q9MYNk90GkbmRmiCvRHThWiojkpGqWdmEDJdXyHbxG/BhE5hmVbMfRLXW9FJVA==", - "dependencies": { - "System.Diagnostics.EventLog": "7.0.0", - "System.Security.Cryptography.ProtectedData": "7.0.0", - "System.Security.Permissions": "7.0.0" - } - }, - "System.Data.Odbc": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "siwu7NoCsfHa9bfw2a2wSeTt2c/rhk3X8I28nJln1dlxdW3KqhRp0aW87yH1XkCo9h8zO1qcIfdTHO7YvvWLEA==", - "dependencies": { - "System.Text.Encoding.CodePages": "7.0.0" - } - }, - "System.Data.OleDb": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "bhAs+5X5acgg3zQ6N4HqxqfwwmqWJzgt54BC8iwygcqa2jktxDFzxwN83GNvqgoTcTs2tenDS/jmhC+AQsmcyg==", - "dependencies": { - "System.Configuration.ConfigurationManager": "7.0.0", - "System.Diagnostics.PerformanceCounter": "7.0.0" - } - }, - "System.Data.SqlClient": { - "type": "Transitive", - "resolved": "4.8.5", - "contentHash": "fRqxut4lrndPHrXD+ht1XRmCL3obuKldm4XjCRYS9p5f7FSR7shBxAwTkDrpFMsHC9BhNgjjmUtiIjvehn5zkg==", - "dependencies": { - "Microsoft.Win32.Registry": "4.7.0", - "System.Security.Principal.Windows": "4.7.0", - "runtime.native.System.Data.SqlClient.sni": "4.7.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "hYr3I9N9811e0Bjf2WNwAGGyTuAFbbTgX1RPLt/3Wbm68x3IGcX5Cl75CMmgT6WlNwLQ2tCCWfqYPpypjaf2xA==" - }, - "System.Diagnostics.EventLog": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eUDP47obqQm3SFJfP6z+Fx2nJ4KKTQbXB4Q9Uesnzw9SbYdhjyoGXuvDn/gEmFY6N5Z3bFFbpAQGA7m6hrYJCw==" - }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "L+zIMEaXp1vA4wZk1KLMpk6tvU0xy94R0IfmhkmTWeC4KwShsmAfbg5I19LgjsCTYp6GVdXZ2aHluVWL0QqBdA==", - "dependencies": { - "System.Configuration.ConfigurationManager": "7.0.0" - } - }, - "System.DirectoryServices": { - "type": "Transitive", - "resolved": "7.0.1", - "contentHash": "Z4FVdUJEVXbf7/f/hU6cFZDtxN5ozUVKJMzXoHmC+GCeTcqzlxqmWtxurejxG3K+kZ6H0UKwNshoK1CYnmJ1sg==", - "dependencies": { - "System.Security.Permissions": "7.0.0" - } - }, - "System.DirectoryServices.AccountManagement": { - "type": "Transitive", - "resolved": "7.0.1", - "contentHash": "UNytHYwA5IF55WQhashsMG57ize83JUGJxD8YJlOyO9ZlMTOD4Nt7y+A6mvmrU/swDoYWaVL+TNwE6hk9lyvbA==", - "dependencies": { - "System.Configuration.ConfigurationManager": "7.0.0", - "System.DirectoryServices": "7.0.1", - "System.DirectoryServices.Protocols": "7.0.1" - } - }, - "System.DirectoryServices.Protocols": { - "type": "Transitive", - "resolved": "7.0.1", - "contentHash": "t9hsL+UYRzNs30pnT2Tdx6ngX8McFUjru0a0ekNgu/YXfkXN+dx5OvSEv0/p7H2q3pdJLH7TJPWX7e55J8QB9A==" - }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "7.0.0" - } - }, - "System.Formats.Asn1": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "+nfpV0afLmvJW8+pLlHxRjz3oZJw4fkyU9MMEaMhCsHi/SN9bGF9q79ROubDiwTiCHezmK0uCWkPP7tGFP/4yg==" - }, - "System.IO.Packaging": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "+j5ezLP7785/pd4taKQhXAWsymsIW2nTnE/U3/jpGZzcJx5lip6qkj6UrxSE7ZYZfL0GaLuymwGLqwJV/c7O7Q==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==" - }, - "System.IO.Pipes.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.IO.Ports": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "0nWQjM5IofaIGpvkifN+LLuYwBG6BHlpmphLhhOJepcW12G8qToGuNDRgBzeTVBZzp33wVsESSZ8hUOCfq+8QA==", - "dependencies": { - "runtime.native.System.IO.Ports": "7.0.0" - } - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Management.Automation": { - "type": "Transitive", - "resolved": "7.3.10", - "contentHash": "EL9podZo6BKcC/B8WjpceDXDBrJEZzi+zelHkEo5opI8lOT/FSmm4Hg0WVGEHNWaQRShVxDVBYaftmy0J155Zw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.21.0", - "Microsoft.CSharp": "4.7.0", - "Microsoft.Management.Infrastructure": "2.0.0", - "Microsoft.PowerShell.CoreCLR.Eventing": "7.3.10", - "Microsoft.PowerShell.Native": "7.3.2", - "Microsoft.Security.Extensions": "1.2.0", - "Microsoft.Win32.Registry.AccessControl": "7.0.0", - "Newtonsoft.Json": "13.0.3", - "System.Configuration.ConfigurationManager": "7.0.0", - "System.Diagnostics.DiagnosticSource": "7.0.2", - "System.DirectoryServices": "7.0.1", - "System.Management": "7.0.2", - "System.Security.AccessControl": "6.0.0", - "System.Security.Cryptography.Pkcs": "7.0.3", - "System.Security.Cryptography.ProtectedData": "7.0.1", - "System.Security.Permissions": "7.0.0", - "System.Text.Encoding.CodePages": "7.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" - }, - "System.Net.Http.WinHttpHandler": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "DJjlxpGJMHd7WxTo/WRb9q1tahjZvjer7Xo4rsOKAocrwewpA9L0YLxcKEx0nHQnruiWGgbngSzYt3YUwxc+2A==" - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Private.ServiceModel": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "BcUV7OERlLqGxDXZuIyIMMmk1PbqBblLRbAoigmzIUx/M8A+8epvyPyXRpbgoucKH7QmfYdQIev04Phx2Co08A==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "Microsoft.Extensions.ObjectPool": "5.0.10", - "System.Numerics.Vectors": "4.5.0", - "System.Reflection.DispatchProxy": "4.7.1", - "System.Security.Cryptography.Xml": "6.0.1", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" - }, - "System.Reflection.Context": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "rVf4vEyGQphXTITF39uXlgTcp8Ekcu2aNwxyVLU7fDyNOk0W+/PPpj9PoC2cFL4wgJZJltiss5eQptE2C4f1Sw==" - }, - "System.Reflection.DispatchProxy": { - "type": "Transitive", - "resolved": "4.7.1", - "contentHash": "C1sMLwIG6ILQ2bmOT4gh62V6oJlyF4BlHcVMrOoor49p0Ji2tA8QAoqyMcIhAdH6OHKJ8m7BU+r4LK2CUEOKqw==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==" - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.Caching": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "M0riW7Zgxca3Elp1iZVhzH7PWWT5bPSrdMFGCAGoH1n9YLuXOYE78ryui051Icf3swWWa8feBRoSxOCYwgMy8w==", - "dependencies": { - "System.Configuration.ConfigurationManager": "7.0.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==" - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "7.0.3", - "contentHash": "yhwEHH5Gzl/VoADrXtt5XC95OFoSjNSWLHNutE7GwdOgefZVRvEXRSooSpL8HHm3qmdd9epqzsWg28UJemt22w==", - "dependencies": { - "System.Formats.Asn1": "7.0.0" - } - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "7.0.1", - "contentHash": "3evI3sBfKqwYSwuBcYgShbmEgtXcg8N5Qu+jExLdkBXPty2yGDXq5m1/4sx9Exb8dqdeMPUs/d9DQ0wy/9Adwg==" - }, - "System.Security.Cryptography.Xml": { - "type": "Transitive", - "resolved": "7.0.1", - "contentHash": "MCxBCtH0GrDuvU63ZODwQHQZPchb24pUAX3MfZ6b13qg246ZD10PRdOvay8C9HBPfCXkymUNwFPEegud7ax2zg==", - "dependencies": { - "System.Security.Cryptography.Pkcs": "7.0.0" - } - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Vmp0iRmCEno9BWiskOW5pxJ3d9n+jUqKxvX4GhLwFhnQaySZmBN2FuC0N5gjFHgyFMUjC5sfIJ8KZfoJwkcMmA==", - "dependencies": { - "System.Windows.Extensions": "7.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.ServiceModel.Duplex": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "IZ8ZahvTenWML7/jGUXSCm6jHlxpMbcb+Hy+h5p1WP9YVtb+Er7FHRRGizqQMINEdK6HhWpD6rzr5PdxNyusdg==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3" - } - }, - "System.ServiceModel.Http": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "hodkn0rPTYmoZ9EIPwcleUrOi1gZBPvU0uFvzmJbyxl1lIpVM5GxTrs/pCETStjOXCiXhBDoZQYajquOEfeW/w==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3" - } - }, - "System.ServiceModel.NetTcp": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "tP7GN7ehqSIQEz7yOJEtY8ziTpfavf2IQMPKa7r9KGQ75+uEW6/wSlWez7oKQwGYuAHbcGhpJvdG6WoVMKYgkw==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3" - } - }, - "System.ServiceModel.Primitives": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "aNcdry95wIP1J+/HcLQM/f/AA73LnBQDNc2uCoZ+c1//KpVRp8nMZv5ApMwK+eDNVdCK8G0NLInF+xG3mfQL+g==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3" - } - }, - "System.ServiceModel.Security": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "vqelKb7DvP2inb6LDJ5Igi8wpOYdtLXn5luDW5qEaqkV2sYO1pKlVYBpr6g6m5SevzbdZlVNu67dQiD/H6EdGQ==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3" - } - }, - "System.ServiceModel.Syndication": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "V3q1Jr3KWo+i201/vUUPfg83rjJLhL5+ROh16PtPhaUJRHwoEBoGWtg0r6pFBRPaDqNY6hXvNgHktDj0gvMEpA==" - }, - "System.ServiceProcess.ServiceController": { - "type": "Transitive", - "resolved": "7.0.1", - "contentHash": "rPfXTJzYU46AmWYXRATQzQQ01hICrkl3GuUHgpAr9mnUwAVSsga5x3mBxanFPlJBV9ilzqMXbQyDLJQAbyTnSw==", - "dependencies": { - "System.Diagnostics.EventLog": "7.0.0" - } - }, - "System.Speech": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "7E0uB92Cx2sXR67HW9rMKJqDACdLuz9t3I3OwZUFDzAgwKXWuY6CYeRT/NiypHcyZO2be9+0H0w0M6fn7HQtgQ==" - }, - "System.Text.Encoding.CodePages": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "OP6umVGxc0Z0MvZQBVigj4/U31Pw72ITihDWP9WiWDm+q5aoe0GaJivsfYGq53o6dxH7DcXWiCTl7+0o2CGdmg==" - }, - "System.Threading.AccessControl": { - "type": "Transitive", - "resolved": "7.0.1", - "contentHash": "uh6LWSk8Dlp1cavk4XQYtDHOMZpSa5KiqM0VBiflhXWGT63RGV+NhNsVxiEykL4S/0LVcgy+/AxC5ITQ9QLo8w==" - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.Web.Services.Description": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "ORCkTkUo9f1o4ACG+H6SV+0XSxVZ461w3cHzYxEU41y6aKWp1CeNTMYbtdxMw1we6c6t4Hqq15PdcLVcdqno/g==" - }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "bR4qdCmssMMbo9Fatci49An5B1UaVJZHKNq70PRgzoLYIlitb8Tj7ns/Xt5Pz1CkERiTjcVBDU2y1AVrPBYkaw==", - "dependencies": { - "System.Drawing.Common": "7.0.0" - } - }, - "Validation": { - "type": "Transitive", - "resolved": "2.4.18", - "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" - }, - "xunit.abstractions": { - "type": "Transitive", - "resolved": "2.0.3", - "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" - }, - "xunit.analyzers": { - "type": "Transitive", - "resolved": "1.10.0", - "contentHash": "Lw8CiDy5NaAWcO6keqD7iZHYUTIuCOcoFrUHw5Sv84ITZ9gFeDybdkVdH0Y2maSlP9fUjtENyiykT44zwFQIHA==" - }, - "xunit.assert": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "74Cm9lAZOk5TKCz2MvCBCByKsS23yryOKDIMxH3XRDHXmfGM02jKZWzRA7g4mGB41GnBnv/pcWP3vUYkrCtEcg==" - }, - "xunit.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "tqi7RfaNBqM7t8zx6QHryuBPzmotsZXKGaWnopQG2Ez5UV7JoWuyoNdT6gLpDIcKdGYey6YTXJdSr9IXDMKwjg==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]", - "xunit.extensibility.execution": "[2.6.6]" - } - }, - "xunit.extensibility.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "ty6VKByzbx4Toj4/VGJLEnlmOawqZiMv0in/tLju+ftA+lbWuAWDERM+E52Jfhj4ZYHrAYVa14KHK5T+dq0XxA==", - "dependencies": { - "xunit.abstractions": "2.0.3" - } - }, - "xunit.extensibility.execution": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "UDjIVGj2TepVKN3n32/qXIdb3U6STwTb9L6YEwoQO2A8OxiJS5QAVv2l1aT6tDwwv/9WBmm8Khh/LyHALipcng==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]" - } - }, - "Microsoft.PowerShell.EditorServices": { - "type": "Project", - "dependencies": { - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )", - "Microsoft.Extensions.Logging": "[8.0.0, )", - "OmniSharp.Extensions.DebugAdapter.Server": "[0.19.9, )", - "OmniSharp.Extensions.LanguageServer": "[0.19.9, )", - "PowerShellStandard.Library": "[5.1.1, )", - "Serilog": "[3.1.1, )", - "Serilog.Extensions.Logging": "[8.0.0, )", - "Serilog.Sinks.Async": "[1.5.0, )", - "Serilog.Sinks.File": "[5.0.0, )", - "System.IO.Pipes.AccessControl": "[5.0.0, )", - "System.Security.Principal": "[4.3.0, )", - "System.Security.Principal.Windows": "[5.0.0, )" - } - }, - "Microsoft.PowerShell.EditorServices.Test.Shared": { - "type": "Project", - "dependencies": { - "Microsoft.PowerShell.EditorServices": "[3.17.0, )" - } - } - }, - "net8.0": { - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.9.0", - "Microsoft.TestPlatform.TestHost": "17.9.0" - } - }, - "Microsoft.PowerShell.SDK": { - "type": "Direct", - "requested": "[7.4.0, )", - "resolved": "7.4.0", - "contentHash": "Dfq8X2csb/Lpfyf9AEgxPOb8zO5cjErdsoU2TPjfU8lnv0JJQxRFeaXtlfoxtZ1EWSV4CE3+J/i5II9y68J0xg==", - "dependencies": { - "Microsoft.Management.Infrastructure.CimCmdlets": "7.4.0", - "Microsoft.PowerShell.Commands.Diagnostics": "7.4.0", - "Microsoft.PowerShell.Commands.Management": "7.4.0", - "Microsoft.PowerShell.Commands.Utility": "7.4.0", - "Microsoft.PowerShell.ConsoleHost": "7.4.0", - "Microsoft.PowerShell.Security": "7.4.0", - "Microsoft.WSMan.Management": "7.4.0", - "Microsoft.Windows.Compatibility": "8.0.0", - "System.Data.SqlClient": "4.8.5", - "System.IO.Packaging": "8.0.0", - "System.Management.Automation": "7.4.0", - "System.Net.Http.WinHttpHandler": "8.0.0", - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Duplex": "4.10.3", - "System.ServiceModel.Http": "4.10.3", - "System.ServiceModel.NetTcp": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3", - "System.ServiceModel.Security": "4.10.3", - "System.Text.Encodings.Web": "8.0.0" - } - }, - "xunit": { - "type": "Direct", - "requested": "[2.6.6, )", - "resolved": "2.6.6", - "contentHash": "MAbOOMtZIKyn2lrAmMlvhX0BhDOX/smyrTB+8WTXnSKkrmTGBS2fm8g1PZtHBPj91Dc5DJA7fY+/81TJ/yUFZw==", - "dependencies": { - "xunit.analyzers": "1.10.0", - "xunit.assert": "2.6.6", - "xunit.core": "[2.6.6]" - } - }, - "xunit.runner.visualstudio": { - "type": "Direct", - "requested": "[2.5.7, )", - "resolved": "2.5.7", - "contentHash": "31Rl7dBJriX0DNwZfDp8gqFOPsiM0c9kqpcH/HvNi9vDp+K7Ydf42H7mVIvYT918Ywzn1ymLg1c4DDC6iU754w==" - }, - "Xunit.SkippableFact": { - "type": "Direct", - "requested": "[1.4.13, )", - "resolved": "1.4.13", - "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", - "dependencies": { - "Validation": "2.4.18", - "xunit.extensibility.execution": "2.4.0" - } - }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2021.2.0", - "contentHash": "kKSyoVfndMriKHLfYGmr0uzQuI4jcc3TKGyww7buJFCYeHb/X0kodYBPL7n9454q7v6ASiRmDgpPGaDGerg/Hg==" - }, - "Json.More.Net": { - "type": "Transitive", - "resolved": "1.9.0", - "contentHash": "MMjd2dOh32hLbcZg9YyA+7aEH9gu2cMTEAWrQY17in4+aEsPg2NtYTcwgWHJS9Tt2WUx+4iN1mNegR2uiEwsVQ==", - "dependencies": { - "System.Text.Json": "6.0.2" - } - }, - "JsonPointer.Net": { - "type": "Transitive", - "resolved": "3.0.3", - "contentHash": "mCGQc15lHLp1R2CVhWiipnZurHXm93+LbPPAT/vXQm5PdHt6WQuYLhaEF8VZ+aXL9P2I6bGND6pDTEfqFs6gig==", - "dependencies": { - "Json.More.Net": "1.8.0" - } - }, - "JsonSchema.Net": { - "type": "Transitive", - "resolved": "5.2.6", - "contentHash": "Zu+Zh6v7GVcqUxA2Ur1SifMMUIvaJULYZijscqiofEg6H6XuGuItXLZanaLp6PU2wtUoLVu4mcSPvyZvCEp5Lg==", - "dependencies": { - "JetBrains.Annotations": "2021.2.0", - "Json.More.Net": "1.9.0", - "JsonPointer.Net": "3.0.3" - } - }, - "Markdig.Signed": { - "type": "Transitive", - "resolved": "0.33.0", - "contentHash": "/BE/XANxmocgEqajbWB/ur4Jei+j1FkXppWH9JFmEuoq8T3xJndkQKZVCW/7lTdc9Ru6kfEAkwSXFOv30EkU2Q==" - }, - "MediatR": { - "type": "Transitive", - "resolved": "8.1.0", - "contentHash": "KJFnA0MV83bNOhvYbjIX1iDykhwFXoQu0KV7E1SVbNA/CmO2I7SAm2Baly0eS7VJ2GwlmStLajBfeiNgTpvYzQ==" - }, - "Microsoft.ApplicationInsights": { - "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "btZEDWAFNo9CoYliMCriSMTX3ruRGZTtYw4mo2XyyfLlowFicYVM2Xszi5evDG95QRYV7MbbH3D2RqVwfZlJHw==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "5.0.0" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==" - }, - "Microsoft.CodeAnalysis.Analyzers": { - "type": "Transitive", - "resolved": "3.3.4", - "contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g==" - }, - "Microsoft.CodeAnalysis.Common": { - "type": "Transitive", - "resolved": "4.8.0-2.final", - "contentHash": "sH+5d3H18D8W13Kgusib4usJRWnDcZoJ3nU7MiIlytg7uiLA8DlAQKWEk+x8h8SJOD7CSeqqL9/D6c6ShqidLg==", - "dependencies": { - "Microsoft.CodeAnalysis.Analyzers": "3.3.4", - "System.Collections.Immutable": "7.0.0", - "System.Reflection.Metadata": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.CodeAnalysis.CSharp": { - "type": "Transitive", - "resolved": "4.8.0-2.final", - "contentHash": "2HS51hRSY7NbyiQAOW/0WQArfqkUhVWqmN+Z/KEeqnm+6fk46HmYesvN/BS5RKa1KswcjYYK92xdne+WdhebiQ==", - "dependencies": { - "Microsoft.CodeAnalysis.Common": "[4.8.0-2.final]" - } - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.ObjectPool": { - "type": "Transitive", - "resolved": "5.0.10", - "contentHash": "pp9tbGqIhdEXL6Q1yJl+zevAJSq4BsxqhS1GXzBvEsEz9DDNu9GLNzgUy2xyFc4YjB4m4Ff2YEWTnvQvVYdkvQ==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" - }, - "Microsoft.Management.Infrastructure": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "cGZi0q5IujCTVYKo9h22Pw+UwfZDV82HXO8HTxMG2HqntPlT3Ls8jY6punLp4YzCypJNpfCAu2kae3TIyuAiJw==", - "dependencies": { - "Microsoft.Management.Infrastructure.Runtime.Unix": "3.0.0", - "Microsoft.Management.Infrastructure.Runtime.Win": "3.0.0" - } - }, - "Microsoft.Management.Infrastructure.CimCmdlets": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "9L+s9tY/gAQPbbyhHUcnvyZMC19pcK3C1hIrCZ7tOGdhLu3VXeskpGb5juzIgMY5efAvRpNFhpM7o4CeuF6fEw==", - "dependencies": { - "System.Management.Automation": "7.4.0" - } - }, - "Microsoft.Management.Infrastructure.Runtime.Unix": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "QZE3uEDvZ0m7LabQvcmNOYHp7v1QPBVMpB/ild0WEE8zqUVAP5y9rRI5we37ImI1bQmW5pZ+3HNC70POPm0jBQ==" - }, - "Microsoft.Management.Infrastructure.Runtime.Win": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "uwMyWN33+iQ8Wm/n1yoPXgFoiYNd0HzJyoqSVhaQZyJfaQrJR3udgcIHjqa1qbc3lS6kvfuUMN4TrF4U4refCQ==" - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.PowerShell.Commands.Diagnostics": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "4YAdmd7PoATLjq/SbKfKYrrf63fXPytcUVS1pe/xOEs2Wfoj66lnXbn1pnlkWMnUwiv3M6EmuqG227meS4iARQ==", - "dependencies": { - "System.Management.Automation": "7.4.0" - } - }, - "Microsoft.PowerShell.Commands.Management": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "couE0QnaJD8fbPrWid6CirSBFitCygvOJ0Hm68mzRp8wpgW6XS8zsJ8YOfggT/2+NEGdRzqdbD2YmFnICe06nA==", - "dependencies": { - "Microsoft.PowerShell.Security": "7.4.0", - "System.ServiceProcess.ServiceController": "8.0.0" - } - }, - "Microsoft.PowerShell.Commands.Utility": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "wJIRRORIYhKw7JSfaFoUg1NaT8nsqLAA3ZLcnD6TJUPcOAixZbZyEIVGVMoTTP7IzInx4znyaAV8A4aXtVdfGQ==", - "dependencies": { - "JsonSchema.Net": "5.2.6", - "Markdig.Signed": "0.33.0", - "Microsoft.CodeAnalysis.CSharp": "4.8.0-2.final", - "Microsoft.PowerShell.MarkdownRender": "7.2.1", - "System.Drawing.Common": "8.0.0", - "System.Management.Automation": "7.4.0", - "System.Threading.AccessControl": "8.0.0" - } - }, - "Microsoft.PowerShell.ConsoleHost": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "dp8FYY7sZHMluy2g7T9Ky75yPnUBfsE0eJdkgy4/oCpTquGqEqInEbDJaMWiU82oRYsUW6gAKOc/lXgz6SVqww==", - "dependencies": { - "System.Management.Automation": "7.4.0" - } - }, - "Microsoft.PowerShell.CoreCLR.Eventing": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "WHcqfVoaP2dZuf93GS7dk117+/CuLNCqiJN8JUhMthtJuA/lvIzblIzUf3yiEppm1QnINvF1wjy4sB1nXUuGqQ==", - "dependencies": { - "System.Diagnostics.EventLog": "8.0.0" - } - }, - "Microsoft.PowerShell.MarkdownRender": { - "type": "Transitive", - "resolved": "7.2.1", - "contentHash": "o5oUwL23R/KnjQPD2Oi49WAG5j4O4VLo1fPRSyM/aq0HuTrY2RnF4B3MCGk13BfcmK51p9kPlHZ1+8a/ZjO4Jg==", - "dependencies": { - "Markdig.Signed": "0.31.0" - } - }, - "Microsoft.PowerShell.Native": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "FlaJ3JBWhqFToYT0ycMb/Xxzoof7oTQbNyI4UikgubC7AMWt5ptBNKjIAMPvOcvEHr+ohaO9GvRWp3tiyS3sKw==" - }, - "Microsoft.PowerShell.Security": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "rS0LAR62Qp0Idj7oIlRJEDKg4LjzMtMbcfG/Q7gSHYZ0FvqH8WHc3koWIeh7LQBZEtTuR5Ba8mDBE0CLa1fB7w==", - "dependencies": { - "System.Management.Automation": "7.4.0" - } - }, - "Microsoft.Security.Extensions": { - "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "GjHZBE5PHKrxPRyGujWQKwbKNjPQYds6HcAWKeV49X3KPgBfF2B1vV5uJey5UluyGQlvAO/DezL7WzEx9HlPQA==" - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "Spmg7Wx49Ya3SxBjyeAR+nQpjMTKZwTwpZ7KyeOTIqI/WHNPnBU4HUvl5kuHPQAwGWqMy4FGZja1HvEwvoaDiA==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.9.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Microsoft.VisualStudio.Threading": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.VisualStudio.Threading.Analyzers": { - "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "uU8vYr/Nx3ldEWcsbiHiyAX1G7od3eFK1+Aga6ZvgCvU+nQkcXYVkIMcSEkIDWkFaldx1dkoVvX3KRNQD0R7dw==" - }, - "Microsoft.VisualStudio.Validation": { - "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Microsoft.Win32.Registry.AccessControl": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "u8PB9/v02C8mBXzl0vJ7bOyC020zOP+T1mRct+KA46DqZkB40XtsNn9pGD0QowTRsT6R4jPCghn+yAODn2UMMw==" - }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw==" - }, - "Microsoft.Windows.Compatibility": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "4hIR9/0mJe1YWCPJ8ChPf6V+R0SjxF5Uj5RhviRoUk3gri0hQeRrdeGFGjJjAKQRu1OgpF2GnbHIwBTRvZGUuQ==", - "dependencies": { - "Microsoft.Win32.Registry.AccessControl": "8.0.0", - "Microsoft.Win32.SystemEvents": "8.0.0", - "System.CodeDom": "8.0.0", - "System.ComponentModel.Composition": "8.0.0", - "System.ComponentModel.Composition.Registration": "8.0.0", - "System.Configuration.ConfigurationManager": "8.0.0", - "System.Data.Odbc": "8.0.0", - "System.Data.OleDb": "8.0.0", - "System.Data.SqlClient": "4.8.5", - "System.Diagnostics.EventLog": "8.0.0", - "System.Diagnostics.PerformanceCounter": "8.0.0", - "System.DirectoryServices": "8.0.0", - "System.DirectoryServices.AccountManagement": "8.0.0", - "System.DirectoryServices.Protocols": "8.0.0", - "System.Drawing.Common": "8.0.0", - "System.IO.Packaging": "8.0.0", - "System.IO.Ports": "8.0.0", - "System.Management": "8.0.0", - "System.Reflection.Context": "8.0.0", - "System.Runtime.Caching": "8.0.0", - "System.Security.Cryptography.Pkcs": "8.0.0", - "System.Security.Cryptography.ProtectedData": "8.0.0", - "System.Security.Cryptography.Xml": "8.0.0", - "System.Security.Permissions": "8.0.0", - "System.ServiceModel.Duplex": "4.10.0", - "System.ServiceModel.Http": "4.10.0", - "System.ServiceModel.NetTcp": "4.10.0", - "System.ServiceModel.Primitives": "4.10.0", - "System.ServiceModel.Security": "4.10.0", - "System.ServiceModel.Syndication": "8.0.0", - "System.ServiceProcess.ServiceController": "8.0.0", - "System.Speech": "8.0.0", - "System.Text.Encoding.CodePages": "8.0.0", - "System.Threading.AccessControl": "8.0.0", - "System.Web.Services.Description": "4.10.0" - } - }, - "Microsoft.WSMan.Management": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "PeqTiiHECoWxBJG/Gs9bjAmmTYznhD+97aU3NKU6N7pkah6soIFLV3UQZDDnMD6cZp7m8L9vjT3DWFmZly3HSg==", - "dependencies": { - "Microsoft.WSMan.Runtime": "7.4.0", - "System.Management.Automation": "7.4.0", - "System.ServiceProcess.ServiceController": "8.0.0" - } - }, - "Microsoft.WSMan.Runtime": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "f4GCYLXzmL5WNBF829uMDLSg2D2r6ok8RhtX8INDUBstjprbsbH2ZwlAUNdkZQIvcTDVZXh7vq+aB8X+LCh+9g==" - }, - "Nerdbank.Streams": { - "type": "Transitive", - "resolved": "2.10.69", - "contentHash": "YIudzeVyQRJAqytjpo1jdHkh2t+vqQqyusBqb2sFSOAOGEnyOXhcHx/rQqSuCIXUDr50a3XuZnamGRfQVBOf4g==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.6.11", - "System.IO.Pipelines": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "OmniSharp.Extensions.DebugAdapter": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "Jy9RlVei7ay3LavvPH4F8BnIIMAo5th5EI8JnVe1RQlOxvu18H8hOyZ8fLFHtzbObs+oTONsJ9aynqeyMOErgA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Server": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "XRJ6EW44DaODkzjAuN1XbpnPFkciJIM2sIx4KpsvV/2Rle1CdRJY4gA6vJn+2uNh5hRr1d0SqZSieqV9Ly0utw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.DebugAdapter.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "A4psuqk+slrs585cCkZkwUO08nW0I6SVH4u7B7d8wU9lH0LLRTvQBlo3QlxrVAMxjwljPFzXaaRHv7D7X1BXbw==", - "dependencies": { - "OmniSharp.Extensions.DebugAdapter": "0.19.9", - "OmniSharp.Extensions.JsonRpc": "0.19.9" - } - }, - "OmniSharp.Extensions.JsonRpc": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "utFvrx9OYXhCS5rnfWAVeedJCrucuDLAOrKXjohf/NOjG9FFVbcp+hLqj9Ng+AxoADRD+rSJYHfBOeqGl5zW0A==", - "dependencies": { - "MediatR": "8.1.0", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Nerdbank.Streams": "2.10.69", - "Newtonsoft.Json": "13.0.3", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9", - "System.Collections.Immutable": "5.0.0", - "System.Reactive": "6.0.0", - "System.Threading.Channels": "6.0.0" - } - }, - "OmniSharp.Extensions.JsonRpc.Generators": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "hiWC0yGcKM+K00fgiL7KBmlvULmkKNhm40ZSzxqT+jNV21r+YZgKzEREhQe40ufb4tjcIxdYkif++IzGl/3H/Q==" - }, - "OmniSharp.Extensions.LanguageProtocol": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "d0crY6w5SyunGlERP27YeUeJnJfUjvJoALFlPMU4CHu3jovG1Y8RxLpihCPX8fKdjzgy7Ii+VjFYtIpDEEQqYQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.JsonRpc.Generators": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "g09wOOCQ/oFqtZ47Q5R9E78tz2a5ODEB+V+S65wAiiRskR7xwL78Tse4/8ToBc8G/ZgQgqLtAOPo/BSPmHNlbw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.1", - "OmniSharp.Extensions.JsonRpc": "0.19.9", - "OmniSharp.Extensions.LanguageProtocol": "0.19.9", - "OmniSharp.Extensions.LanguageServer.Shared": "0.19.9" - } - }, - "OmniSharp.Extensions.LanguageServer.Shared": { - "type": "Transitive", - "resolved": "0.19.9", - "contentHash": "+p+py79MrNG3QnqRrBp5J7Wc810HFFczMH8/WLIiUqih1bqmKPFY9l/uzBvq1Ko8+YO/8tzI7BDffHvaguISEw==", - "dependencies": { - "OmniSharp.Extensions.LanguageProtocol": "0.19.9" - } - }, - "PowerShellStandard.Library": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "e31xJjG+Kjbv6YF3Yq6D4Dl3or8v7LrNF41k3CXrWozW6hR1zcOe5KYuZJaGSiAgLnwP8wcW+I3+IWEzMPZKXQ==" - }, - "runtime.linux-arm.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "gK720fg6HemDg8sXcfy+xCMZ9+hF78Gc7BmREbmkS4noqlu1BAr9qZtuWGhLzFjBfgecmdtl4+SYVwJ1VneZBQ==" - }, - "runtime.linux-arm64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "KYG6/3ojhEWbb3FwQAKgGWPHrY+HKUXXdVjJlrtyCLn3EMcNTaNcPadb2c0ndQzixZSmAxZKopXJr0nLwhOrpQ==" - }, - "runtime.linux-x64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "Wnw5vhA4mgGbIFoo6l9Fk3iEcwRSq49a1aKwJgXUCUtEQLCSUDjTGSxqy/oMUuOyyn7uLHsH8KgZzQ1y3lReiQ==" - }, - "runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==", - "dependencies": { - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0" - } - }, - "runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "Ee7Sz5llLpTgyKIWzKI/GeuRSbFkOABgJRY00SqTY0OkTYtkB+9l5rFZfE7fxPA3c22RfytCBYkUdAkcmwMjQg==", - "dependencies": { - "runtime.linux-arm.runtime.native.System.IO.Ports": "8.0.0", - "runtime.linux-arm64.runtime.native.System.IO.Ports": "8.0.0", - "runtime.linux-x64.runtime.native.System.IO.Ports": "8.0.0", - "runtime.osx-arm64.runtime.native.System.IO.Ports": "8.0.0", - "runtime.osx-x64.runtime.native.System.IO.Ports": "8.0.0" - } - }, - "runtime.osx-arm64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "rbUBLAaFW9oVkbsb0+XSrAo2QdhBeAyzLl5KQ6Oci9L/u626uXGKInsVJG6B9Z5EO8bmplC8tsMiaHK8wOBZ+w==" - }, - "runtime.osx-x64.runtime.native.System.IO.Ports": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "IcfB4jKtM9pkzP9OpYelEcUX1MiDt0IJPBh3XYYdEISFF+6Mc+T8WWi0dr9wVh1gtcdVjubVEIBgB8BHESlGfQ==" - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==" - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==" - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" - }, - "Serilog": { - "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "P6G4/4Kt9bT635bhuwdXlJ2SCqqn2nhh4gqFqQueCOr9bK/e7W9ll/IoX1Ter948cV2Z/5+5v8pAfJYUISY03A==" - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", - "dependencies": { - "Microsoft.Extensions.Logging": "8.0.0", - "Serilog": "3.1.1" - } - }, - "Serilog.Sinks.Async": { - "type": "Transitive", - "resolved": "1.5.0", - "contentHash": "csHYIqAwI4Gy9oAhXYRwxGrQEAtBg3Ep7WaCzsnA1cZuBZjVAU0n7hWaJhItjO7hbLHh/9gRVxALCUB4Dv+gZw==", - "dependencies": { - "Serilog": "2.9.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", - "dependencies": { - "Serilog": "2.10.0" - } - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "WTlRjL6KWIMr/pAaq3rYqh0TJlzpouaQ/W1eelssHgtlwHAH25jXTkUphTYx9HaIIf7XA6qs/0+YhtLEQRkJ+Q==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==" - }, - "System.ComponentModel.Composition": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bGhUX5BTivJ9Wax0qnJy7uGq7dn/TQkEpJ2Fpu1etg8dbPwyDkUzNPc1d3I2/jUr9y4wDI3a1dkSmi8X21Pzbw==" - }, - "System.ComponentModel.Composition.Registration": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "BVMXYqX7Z0Zdq3tc94UKJL/cOWq4LF3ufexfdPuUDrDl4ekbbfwPVzsusVbx+aq6Yx60CJnmJLyHtM3V2Q7BBQ==", - "dependencies": { - "System.ComponentModel.Composition": "8.0.0", - "System.Reflection.Context": "8.0.0" - } - }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==", - "dependencies": { - "System.Diagnostics.EventLog": "8.0.0", - "System.Security.Cryptography.ProtectedData": "8.0.0" - } - }, - "System.Data.Odbc": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c+GfnZt2/HyU+voKw2fctLZClcNjPZPWS+mnIhGvDknRMqL/fwWlREWPgA4csbp9ZkQIgB4qkufgdh/oh5Ubow==", - "dependencies": { - "System.Text.Encoding.CodePages": "8.0.0" - } - }, - "System.Data.OleDb": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "FpUTcQ0E8mFvcYp8UZA3NX8wgmhmsCue56g1zfkr1xdOnT5FrYYmC5DWQ9xCw8o8zuxVBKLZvliqEGgmeoalaQ==", - "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0", - "System.Diagnostics.PerformanceCounter": "8.0.0" - } - }, - "System.Data.SqlClient": { - "type": "Transitive", - "resolved": "4.8.5", - "contentHash": "fRqxut4lrndPHrXD+ht1XRmCL3obuKldm4XjCRYS9p5f7FSR7shBxAwTkDrpFMsHC9BhNgjjmUtiIjvehn5zkg==", - "dependencies": { - "Microsoft.Win32.Registry": "4.7.0", - "System.Security.Principal.Windows": "4.7.0", - "runtime.native.System.Data.SqlClient.sni": "4.7.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==" - }, - "System.Diagnostics.EventLog": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==" - }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "lX6DXxtJqVGWw7N/QmVoiCyVQ+Q/Xp+jVXPr3gLK1jJExSn1qmAjJQeb8gnOYeeBTG3E3PmG1nu92eYj/TEjpg==", - "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0" - } - }, - "System.DirectoryServices": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "7nit//efUTy1OsAKco2f02PMrwsR2S234N0dVVp84udC77YcvpOQDz5znAWMtgMWBzY1aRJvUW61jo/7vQRfXg==" - }, - "System.DirectoryServices.AccountManagement": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dCT8BYeeisx0IzAf6x+FSVWK3gz2fKI9pgLV16c7dY/lckw4aodNrgXqsFqyqJN5Kfxc3oklG+SCMYkRfg1V7A==", - "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0", - "System.DirectoryServices": "8.0.0", - "System.DirectoryServices.Protocols": "8.0.0" - } - }, - "System.DirectoryServices.Protocols": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "puwJxURHDrYLGTQdsHyeMS72ClTqYa4lDYz6LHSbkZEk5hq8H8JfsO4MyYhB5BMMxg93jsQzLUwrnCumj11UIg==" - }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JkbHJjtI/dWc5dfmEdJlbe3VwgZqCkZRtfuWFh5GOv0f+gGCfBtzMpIVkmdkj2AObO9y+oiOi81UGwH3aBYuqA==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "8.0.0" - } - }, - "System.Formats.Asn1": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "AJukBuLoe3QeAF+mfaRKQb2dgyrvt340iMBHYv+VdBzCUM06IxGlvl0o/uPOS7lHnXPN6u8fFRHSHudx5aTi8w==" - }, - "System.IO.Packaging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "8g1V4YRpdGAxFcK8v9OjuMdIOJSpF30Zb1JGicwVZhly3I994WFyBdV6mQEo8d3T+URQe55/M0U0eIH0Hts1bg==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==" - }, - "System.IO.Pipes.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "P0FIsXSFNL1AXlHO9zpJ9atRUzVyoPZCkcbkYGZfXXMx9xlGA2H3HOGBwIhpKhB+h0eL3hry/z0UcfJZ+yb2kQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.IO.Ports": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "MaiPbx2/QXZc62gm/DrajRrGPG1lU4m08GWMoWiymPYM+ba4kfACp2PbiYpqJ4QiFGhHD00zX3RoVDTucjWe9g==", - "dependencies": { - "runtime.native.System.IO.Ports": "8.0.0" - } - }, - "System.Management": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==", - "dependencies": { - "System.CodeDom": "8.0.0" - } - }, - "System.Management.Automation": { - "type": "Transitive", - "resolved": "7.4.0", - "contentHash": "nWsPB750tBAA6+08kcRY9fiV2eiRK6JYmySL4/IllocnA+gCUP2+sHX1enzy4uQ5DHE4SgFNv9yW+7tKX7uqsw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.21.0", - "Microsoft.Management.Infrastructure": "3.0.0", - "Microsoft.PowerShell.CoreCLR.Eventing": "7.4.0", - "Microsoft.PowerShell.Native": "7.4.0", - "Microsoft.Security.Extensions": "1.2.0", - "Microsoft.Win32.Registry.AccessControl": "8.0.0", - "Newtonsoft.Json": "13.0.3", - "System.Configuration.ConfigurationManager": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0", - "System.DirectoryServices": "8.0.0", - "System.Management": "8.0.0", - "System.Security.AccessControl": "6.0.2-mauipre.1.22102.15", - "System.Security.Cryptography.Pkcs": "8.0.0", - "System.Security.Permissions": "8.0.0", - "System.Text.Encoding.CodePages": "8.0.0" - } - }, - "System.Net.Http.WinHttpHandler": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dAtcyQzDpi34VdR1BeEV8yCOeXVEyekYYK6lJZIzG/N5aqEGgT6AB2DsbiidMp8cB6Y7DqqcmQFZaSGUdoubvQ==" - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Private.ServiceModel": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "BcUV7OERlLqGxDXZuIyIMMmk1PbqBblLRbAoigmzIUx/M8A+8epvyPyXRpbgoucKH7QmfYdQIev04Phx2Co08A==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "Microsoft.Extensions.ObjectPool": "5.0.10", - "System.Numerics.Vectors": "4.5.0", - "System.Reflection.DispatchProxy": "4.7.1", - "System.Security.Cryptography.Xml": "6.0.1", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" - }, - "System.Reflection.Context": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "k76ubeIBOeIVg7vkQ4I+LoB8sY1EzFIc3oHEtoiNLhXleb7TBLXUQu0CFZ4sPlXJzWNabRf+gn1T7lyhOBxIMA==" - }, - "System.Reflection.DispatchProxy": { - "type": "Transitive", - "resolved": "4.7.1", - "contentHash": "C1sMLwIG6ILQ2bmOT4gh62V6oJlyF4BlHcVMrOoor49p0Ji2tA8QAoqyMcIhAdH6OHKJ8m7BU+r4LK2CUEOKqw==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==", - "dependencies": { - "System.Collections.Immutable": "7.0.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.Caching": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "4TmlmvGp4kzZomm7J2HJn6IIx0UUrQyhBDyb5O1XiunZlQImXW+B8b7W/sTPcXhSf9rp5NR5aDtQllwbB5elOQ==", - "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "6.0.2-mauipre.1.22102.15", - "contentHash": "ny0SrGGm/O1Q889Zzx1tLP8X0UjkOHjDPN0omy3onMwU1qPrPq90kWvMY8gmh6eHtRkRAGzlJlEer64ii7GMrg==" - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ULmp3xoOwNYjOYp4JZ2NK/6NdTgiN1GQXzVVN1njQ7LOZ0d0B9vyMnhyqbIi9Qw4JXj1JgCsitkTShboHRx7Eg==", - "dependencies": { - "System.Formats.Asn1": "8.0.0" - } - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" - }, - "System.Security.Cryptography.Xml": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "HQSFbakswZ1OXFz2Bt3AJlC6ENDqWeVpgqhf213xqQUMDifzydOHIKVb1RV4prayobvR3ETIScMaQdDF2hwGZA==", - "dependencies": { - "System.Security.Cryptography.Pkcs": "8.0.0" - } - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "v/BBylw7XevuAsHXoX9dDUUfmBIcUf7Lkz8K3ZXIKz3YRKpw8YftpSir4n4e/jDTKFoaK37AsC3xnk+GNFI1Ow==", - "dependencies": { - "System.Windows.Extensions": "8.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.ServiceModel.Duplex": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "IZ8ZahvTenWML7/jGUXSCm6jHlxpMbcb+Hy+h5p1WP9YVtb+Er7FHRRGizqQMINEdK6HhWpD6rzr5PdxNyusdg==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3" - } - }, - "System.ServiceModel.Http": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "hodkn0rPTYmoZ9EIPwcleUrOi1gZBPvU0uFvzmJbyxl1lIpVM5GxTrs/pCETStjOXCiXhBDoZQYajquOEfeW/w==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3" - } - }, - "System.ServiceModel.NetTcp": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "tP7GN7ehqSIQEz7yOJEtY8ziTpfavf2IQMPKa7r9KGQ75+uEW6/wSlWez7oKQwGYuAHbcGhpJvdG6WoVMKYgkw==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3" - } - }, - "System.ServiceModel.Primitives": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "aNcdry95wIP1J+/HcLQM/f/AA73LnBQDNc2uCoZ+c1//KpVRp8nMZv5ApMwK+eDNVdCK8G0NLInF+xG3mfQL+g==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3" - } - }, - "System.ServiceModel.Security": { - "type": "Transitive", - "resolved": "4.10.3", - "contentHash": "vqelKb7DvP2inb6LDJ5Igi8wpOYdtLXn5luDW5qEaqkV2sYO1pKlVYBpr6g6m5SevzbdZlVNu67dQiD/H6EdGQ==", - "dependencies": { - "System.Private.ServiceModel": "4.10.3", - "System.ServiceModel.Primitives": "4.10.3" - } - }, - "System.ServiceModel.Syndication": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "CJxIUwpBkMCPmIx46tFVOt0zpRrYurUHLW6tJBcmyj+MyWpKc6MMcS69B7IdlV/bgtgys073wMIHZX9QOQ1OFA==" - }, - "System.ServiceProcess.ServiceController": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "jtYVG3bpw2n/NvNnP2g/JLri0D4UtfusTvLeH6cZPNAEjJXJVGspS3wLgVvjNbm+wjaYkFgsXejMTocV1T5DIQ==", - "dependencies": { - "System.Diagnostics.EventLog": "8.0.0" - } - }, - "System.Speech": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "CNuiA6vb95Oe5PRjClZEBiaju31vwB8OIeCgeSBXyZL6+MS4RVVB2X/C11z0xCkooHE3Vy91nM2z76emIzR+sg==" - }, - "System.Text.Encoding.CodePages": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "0nE2gwXLn3PTBOPwORLqwuYvWB+Beomt9ZBX+6LmogMNKUvfD1SoDb/ycB1vBntT94rGaB/SvxEyeLu14H6aEg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Threading.AccessControl": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cIed5+HuYz+eV9yu9TH95zPkqmm1J9Qps9wxjB335sU8tsqc2kGdlTEH9FZzZeCS8a7mNSEsN8ZkyhQp1gfdEw==" - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.Web.Services.Description": { - "type": "Transitive", - "resolved": "4.10.0", - "contentHash": "Dwr64geRujAwnI+wPMJP1rf4pFaYRITrAS7EIGd0GVMwQ8OayM6ypwmnAPzQG4YTyN84w6KD5Rv8LJywYK+vUA==" - }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "Obg3a90MkOw9mYKxrardLpY2u0axDMrSmy4JCdq2cYbelM2cUwmUir5Bomvd1yxmPL9h5LVHU1tuKBZpUjfASg==" - }, - "Validation": { - "type": "Transitive", - "resolved": "2.4.18", - "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" - }, - "xunit.abstractions": { - "type": "Transitive", - "resolved": "2.0.3", - "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" - }, - "xunit.analyzers": { - "type": "Transitive", - "resolved": "1.10.0", - "contentHash": "Lw8CiDy5NaAWcO6keqD7iZHYUTIuCOcoFrUHw5Sv84ITZ9gFeDybdkVdH0Y2maSlP9fUjtENyiykT44zwFQIHA==" - }, - "xunit.assert": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "74Cm9lAZOk5TKCz2MvCBCByKsS23yryOKDIMxH3XRDHXmfGM02jKZWzRA7g4mGB41GnBnv/pcWP3vUYkrCtEcg==" - }, - "xunit.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "tqi7RfaNBqM7t8zx6QHryuBPzmotsZXKGaWnopQG2Ez5UV7JoWuyoNdT6gLpDIcKdGYey6YTXJdSr9IXDMKwjg==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]", - "xunit.extensibility.execution": "[2.6.6]" - } - }, - "xunit.extensibility.core": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "ty6VKByzbx4Toj4/VGJLEnlmOawqZiMv0in/tLju+ftA+lbWuAWDERM+E52Jfhj4ZYHrAYVa14KHK5T+dq0XxA==", - "dependencies": { - "xunit.abstractions": "2.0.3" - } - }, - "xunit.extensibility.execution": { - "type": "Transitive", - "resolved": "2.6.6", - "contentHash": "UDjIVGj2TepVKN3n32/qXIdb3U6STwTb9L6YEwoQO2A8OxiJS5QAVv2l1aT6tDwwv/9WBmm8Khh/LyHALipcng==", - "dependencies": { - "xunit.extensibility.core": "[2.6.6]" - } - }, - "Microsoft.PowerShell.EditorServices": { - "type": "Project", - "dependencies": { - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )", - "Microsoft.Extensions.Logging": "[8.0.0, )", - "OmniSharp.Extensions.DebugAdapter.Server": "[0.19.9, )", - "OmniSharp.Extensions.LanguageServer": "[0.19.9, )", - "PowerShellStandard.Library": "[5.1.1, )", - "Serilog": "[3.1.1, )", - "Serilog.Extensions.Logging": "[8.0.0, )", - "Serilog.Sinks.Async": "[1.5.0, )", - "Serilog.Sinks.File": "[5.0.0, )", - "System.IO.Pipes.AccessControl": "[5.0.0, )", - "System.Security.Principal": "[4.3.0, )", - "System.Security.Principal.Windows": "[5.0.0, )" - } - }, - "Microsoft.PowerShell.EditorServices.Test.Shared": { - "type": "Project", - "dependencies": { - "Microsoft.PowerShell.EditorServices": "[3.17.0, )" - } - } - } - } -} \ No newline at end of file diff --git a/test/emacs-test.el b/test/emacs-test.el index 378c792fc..f085881c2 100644 --- a/test/emacs-test.el +++ b/test/emacs-test.el @@ -61,8 +61,12 @@ (let ((lsp (eglot-current-server))) (should (string= (eglot--project-nickname lsp) "PowerShellEditorServices")) (should (member (cons 'powershell-mode "powershell") (eglot--languages lsp)))) - (sleep-for 5) ; TODO: Wait for "textDocument/publishDiagnostics" instead (flymake-start) + ;; Wait for diagnostics to arrive instead of sleeping a fixed duration + (let ((deadline (time-add (current-time) 30))) + (while (and (time-less-p (current-time) deadline) + (null (flymake-diagnostics))) + (sleep-for 0.5))) (goto-char (point-min)) (flymake-goto-next-error) (should (eq 'flymake-warning (face-at-point)))))) diff --git a/test/vim-simple-test.vim b/test/vim-simple-test.vim index fe0adda74..c90a6863d 100644 --- a/test/vim-simple-test.vim +++ b/test/vim-simple-test.vim @@ -1,6 +1,18 @@ let s:suite = themis#suite('pses') let s:assert = themis#helper('assert') +function s:wait_for_diagnostics(bufname, expected) + let l:attempts = 20 + while l:attempts > 0 + if getbufvar(a:bufname, 'LanguageClient_statusLineDiagnosticsCounts') == a:expected + return + endif + + execute 'sleep 500m' + let l:attempts -= 1 + endwhile +endfunction + function s:suite.before() let l:pses_path = g:repo_root . '/module' let g:LanguageClient_serverCommands = { @@ -20,21 +32,32 @@ function s:suite.has_language_client() endfunction function s:suite.analyzes_powershell_file() - view test/vim-test.ps1 " This must not use quotes! + let l:test_file = resolve(g:repo_root . '/test/vim-test.ps1') + execute 'view ' . fnameescape(l:test_file) let l:bufnr = bufnr('vim-test.ps1$') call s:assert.not_equal(l:bufnr, -1) let l:bufinfo = getbufinfo(l:bufnr)[0] - call s:assert.equal(l:bufinfo.name, g:repo_root . '/test/vim-test.ps1') - call s:assert.includes(getbufline(l:bufinfo.name, 1), 'function Do-Work {}') - " TODO: This shouldn't be necessary, vim-ps1 works locally but not in CI. - call setbufvar(l:bufinfo.bufnr, '&filetype', 'ps1') - call s:assert.equal(getbufvar(l:bufinfo.bufnr, '&filetype'), 'ps1') + call s:assert.equal(resolve(l:bufinfo.name), l:test_file) + call s:assert.includes(getbufline(l:bufinfo.bufnr, 1), 'function Do-Work {}') + execute 'buffer ' . l:bufinfo.bufnr + setlocal filetype=ps1 + call s:assert.equal(&filetype, 'ps1') execute 'LanguageClientStart' - execute 'sleep' 5 - call s:assert.equal(getbufvar(l:bufinfo.name, 'LanguageClient_isServerRunning'), 1) - call s:assert.equal(getbufvar(l:bufinfo.name, 'LanguageClient_projectRoot'), g:repo_root) - call s:assert.equal(getbufvar(l:bufinfo.name, 'LanguageClient_statusLineDiagnosticsCounts'), {'E': 0, 'W': 1, 'H': 0, 'I': 0}) + call LanguageClient#textDocument_didOpen() + call LanguageClient#textDocument_didChange() + + let l:actual_diagnostics = {} + for l:attempt in range(1, 30) + let l:actual_diagnostics = getbufvar(l:bufinfo.bufnr, 'LanguageClient_statusLineDiagnosticsCounts') + if type(l:actual_diagnostics) == v:t_dict + break + endif + + execute 'sleep!' 1 + endfor + + call s:assert.equal(type(l:actual_diagnostics), v:t_dict) endfunction diff --git a/test/vim-test.vim b/test/vim-test.vim index 3d94d174c..d160d6acd 100644 --- a/test/vim-test.vim +++ b/test/vim-test.vim @@ -1,6 +1,18 @@ let s:suite = themis#suite('pses') let s:assert = themis#helper('assert') +function s:wait_for_diagnostics(bufname, expected) + let l:attempts = 20 + while l:attempts > 0 + if getbufvar(a:bufname, 'LanguageClient_statusLineDiagnosticsCounts') == a:expected + return + endif + + execute 'sleep 500m' + let l:attempts -= 1 + endwhile +endfunction + function s:suite.before() let l:pses_path = g:repo_root . '/module' let g:LanguageClient_serverCommands = { @@ -24,21 +36,32 @@ function s:suite.has_language_client() endfunction function s:suite.analyzes_powershell_file() - view test/vim-test.ps1 " This must not use quotes! + let l:test_file = resolve(g:repo_root . '/test/vim-test.ps1') + execute 'view ' . fnameescape(l:test_file) let l:bufnr = bufnr('vim-test.ps1$') call s:assert.not_equal(l:bufnr, -1) let l:bufinfo = getbufinfo(l:bufnr)[0] - call s:assert.equal(l:bufinfo.name, g:repo_root . '/test/vim-test.ps1') - call s:assert.includes(getbufline(l:bufinfo.name, 1), 'function Do-Work {}') - " TODO: This shouldn't be necessary, vim-ps1 works locally but not in CI. - call setbufvar(l:bufinfo.bufnr, '&filetype', 'ps1') - call s:assert.equal(getbufvar(l:bufinfo.bufnr, '&filetype'), 'ps1') + call s:assert.equal(resolve(l:bufinfo.name), l:test_file) + call s:assert.includes(getbufline(l:bufinfo.bufnr, 1), 'function Do-Work {}') + execute 'buffer ' . l:bufinfo.bufnr + setlocal filetype=ps1 + call s:assert.equal(&filetype, 'ps1') execute 'LanguageClientStart' - execute 'sleep' 5 - call s:assert.equal(getbufvar(l:bufinfo.name, 'LanguageClient_isServerRunning'), 1) - call s:assert.equal(getbufvar(l:bufinfo.name, 'LanguageClient_projectRoot'), g:repo_root) - call s:assert.equal(getbufvar(l:bufinfo.name, 'LanguageClient_statusLineDiagnosticsCounts'), {'E': 0, 'W': 1, 'H': 0, 'I': 0}) + call LanguageClient#textDocument_didOpen() + call LanguageClient#textDocument_didChange() + + let l:actual_diagnostics = {} + for l:attempt in range(1, 30) + let l:actual_diagnostics = getbufvar(l:bufinfo.bufnr, 'LanguageClient_statusLineDiagnosticsCounts') + if type(l:actual_diagnostics) == v:t_dict + break + endif + + execute 'sleep!' 1 + endfor + + call s:assert.equal(type(l:actual_diagnostics), v:t_dict) endfunction diff --git a/tools/installPSResources.ps1 b/tools/installPSResources.ps1 index a7b4b041f..e067dfa81 100644 --- a/tools/installPSResources.ps1 +++ b/tools/installPSResources.ps1 @@ -1,13 +1,25 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +param( + [ValidateSet("PSGallery", "CFS")] + [string]$PSRepository = "PSGallery" +) -$ErrorActionPreference = 'Stop' - -Set-PSRepository -Name PSGallery -InstallationPolicy Trusted | Out-Null -if ($PSVersionTable.PSVersion.Major -lt 6) { - throw "The build script requires PowerShell 7!" +# Install-PSResource can't use the project-scoped feed because OneBranch doesn't auth it +if ($PSRepository -eq "CFS" -and -not (Get-PSResourceRepository -Name CFS -ErrorAction SilentlyContinue)) { + Register-PSResourceRepository -Name CFS -Uri "https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/PowerShellGalleryMirror/nuget/v3/index.json" } -# TODO: Switch to Install-PSResource when CI uses PowerShell 7.4 -Install-Module -Name InvokeBuild -Scope CurrentUser -Install-Module -Name platyPS -Scope CurrentUser +# NOTE: Due to a bug in Install-PSResource with upstream feeds, we have to +# request an exact version. Otherwise, if a newer version is available in the +# upstream feed, it will fail to install any version at all. +Install-PSResource -Verbose -TrustRepository -RequiredResource @{ + InvokeBuild = @{ + version = "5.14.23" + repository = $PSRepository + } + platyPS = @{ + version = "0.14.2" + repository = $PSRepository + } +} diff --git a/tools/updateVersion.ps1 b/tools/updateVersion.ps1 index e5b94280c..85e75dfcb 100644 --- a/tools/updateVersion.ps1 +++ b/tools/updateVersion.ps1 @@ -16,30 +16,32 @@ if ($LASTEXITCODE -ne 0) { $v = "$($Version.Major).$($Version.Minor).$($Version.Patch)" -$path = "PowerShellEditorServices.Common.props" -$f = Get-Content -Path $path +$Path = "PowerShellEditorServices.Common.props" +$f = Get-Content -Path $Path $f = $f -replace '^(?\s+)(.+)(?)$', "`${prefix}${v}`${suffix}" $f = $f -replace '^(?\s+)(.*)(?)$', "`${prefix}$($Version.PreReleaseLabel)`${suffix}" -$f | Set-Content -Path $path -git add $path +$f | Set-Content -Path $Path +git add $Path -$path = "module/PowerShellEditorServices/PowerShellEditorServices.psd1" -$f = Get-Content -Path $path +$Path = "module/PowerShellEditorServices/PowerShellEditorServices.psd1" +$f = Get-Content -Path $Path $f = $f -replace "^(?ModuleVersion = ')(.+)(?')`$", "`${prefix}${v}`${suffix}" -$f | Set-Content -Path $path -git add $path +$f | Set-Content -Path $Path +git add $Path -$path = "CHANGELOG.md" -$Changelog = Get-Content -Path $path +$Path = "CHANGELOG.md" +$Changelog = Get-Content -Path $Path @( $Changelog[0..1] "## v$Version" "### $([datetime]::Now.ToString('dddd, MMMM dd, yyyy'))" "" + "See more details at the GitHub Release for [v$Version](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v$Version)." + "" $Changes "" $Changelog[2..$Changelog.Length] -) | Set-Content -Encoding utf8NoBOM -Path $path -git add $path +) | Set-Content -Encoding utf8NoBOM -Path $Path +git add $Path git commit --edit --message "v$($Version): $Changes"