mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-17 19:51:06 +00:00
Compare commits
12 Commits
main
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff5a1f5ae3 | ||
|
|
47a8bfee96 | ||
|
|
7abe28069d | ||
|
|
5c3cb5ad69 | ||
|
|
c736fbac2e | ||
|
|
9771a64ad5 | ||
|
|
69064f3011 | ||
|
|
565fe8f9ff | ||
|
|
0e22d64221 | ||
|
|
7d965ee028 | ||
|
|
3ccf958fa5 | ||
|
|
d05367991e |
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"codebaseName": "VSTS_Microsoft_OSGS_OpenConsole",
|
||||
"instanceUrl": "https://microsoft.visualstudio.com",
|
||||
"projectName": "OS",
|
||||
"areaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SHINE\\Terminal",
|
||||
|
||||
63
build/pipelines/1espt-nightly.yml
Normal file
63
build/pipelines/1espt-nightly.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
trigger: none
|
||||
pr: none
|
||||
schedules:
|
||||
- cron: "30 3 * * 2-6" # Run at 03:30 UTC Tuesday through Saturday (After the work day in Pacific, Mon-Fri)
|
||||
displayName: "Nightly Terminal Build"
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
always: false # only run if there's code changes!
|
||||
|
||||
parameters:
|
||||
- name: publishToAzure
|
||||
displayName: "Deploy to **PUBLIC** Azure Storage"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: official
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
|
||||
|
||||
variables:
|
||||
- template: templates-v2/variables-nuget-package-version.yml
|
||||
parameters:
|
||||
branding: Canary
|
||||
|
||||
extends:
|
||||
template: templates-v2/pipeline-1espt-full-release-build.yml
|
||||
parameters:
|
||||
official: ${{parameters.official}}
|
||||
branding: Canary
|
||||
buildTerminal: true
|
||||
pgoBuildMode: Optimize
|
||||
codeSign: true
|
||||
signingIdentity:
|
||||
serviceName: $(SigningServiceName)
|
||||
appId: $(SigningAppId)
|
||||
tenantId: $(SigningTenantId)
|
||||
akvName: $(SigningAKVName)
|
||||
authCertName: $(SigningAuthCertName)
|
||||
signCertName: $(SigningSignCertName)
|
||||
useManagedIdentity: $(SigningUseManagedIdentity)
|
||||
clientId: $(SigningOriginalClientId)
|
||||
publishSymbolsToPublic: true
|
||||
symbolExpiryTime: 1
|
||||
symbolPublishingSubscription: $(SymbolPublishingServiceConnection)
|
||||
symbolPublishingProject: $(SymbolPublishingProject)
|
||||
${{ if eq(true, parameters.publishToAzure) }}:
|
||||
extraPublishJobs:
|
||||
- template: build/pipelines/templates-v2/job-deploy-to-azure-storage.yml@self
|
||||
parameters:
|
||||
pool:
|
||||
name: SHINE-INT-S
|
||||
os: windows
|
||||
dependsOn: [PublishSymbols]
|
||||
storagePublicRootURL: $(AppInstallerRootURL)
|
||||
subscription: $(AzureSubscriptionName)
|
||||
storageAccount: $(AzureStorageAccount)
|
||||
storageContainer: $(AzureStorageContainer)
|
||||
buildConfiguration: Release
|
||||
buildPlatforms: [x64, x86, arm64]
|
||||
environment: production-canary
|
||||
|
||||
@@ -30,9 +30,13 @@ parameters:
|
||||
- name: signingIdentity
|
||||
type: object
|
||||
default: {}
|
||||
- name: outerTemplateContext
|
||||
type: object
|
||||
default: {}
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.jobName }}
|
||||
templateContext: ${{ parameters.outerTemplateContext }}
|
||||
${{ if ne(length(parameters.pool), 0) }}:
|
||||
pool: ${{ parameters.pool }}
|
||||
${{ if eq(parameters.codeSign, true) }}:
|
||||
|
||||
@@ -74,9 +74,13 @@ parameters:
|
||||
- name: afterBuildSteps
|
||||
type: stepList
|
||||
default: []
|
||||
- name: outerTemplateContext
|
||||
type: object
|
||||
default: {}
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.jobName }}
|
||||
templateContext: ${{ parameters.outerTemplateContext }}
|
||||
${{ if ne(length(parameters.pool), 0) }}:
|
||||
pool: ${{ parameters.pool }}
|
||||
strategy:
|
||||
|
||||
@@ -35,9 +35,13 @@ parameters:
|
||||
- name: signingIdentity
|
||||
type: object
|
||||
default: {}
|
||||
- name: outerTemplateContext
|
||||
type: object
|
||||
default: {}
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.jobName }}
|
||||
templateContext: ${{ parameters.outerTemplateContext }}
|
||||
${{ if ne(length(parameters.pool), 0) }}:
|
||||
pool: ${{ parameters.pool }}
|
||||
${{ if eq(parameters.codeSign, true) }}:
|
||||
|
||||
@@ -30,9 +30,13 @@ parameters:
|
||||
- name: signingIdentity
|
||||
type: object
|
||||
default: {}
|
||||
- name: outerTemplateContext
|
||||
type: object
|
||||
default: {}
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.jobName }}
|
||||
templateContext: ${{ parameters.outerTemplateContext }}
|
||||
${{ if ne(length(parameters.pool), 0) }}:
|
||||
pool: ${{ parameters.pool }}
|
||||
${{ if eq(parameters.codeSign, true) }}:
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
parameters:
|
||||
- name: official
|
||||
type: boolean
|
||||
default: false
|
||||
- name: branding
|
||||
type: string
|
||||
default: Release
|
||||
values:
|
||||
- Release
|
||||
- Preview
|
||||
- Canary
|
||||
- Dev
|
||||
- name: buildTerminal
|
||||
type: boolean
|
||||
default: true
|
||||
- name: buildConPTY
|
||||
type: boolean
|
||||
default: false
|
||||
- name: buildWPF
|
||||
type: boolean
|
||||
default: false
|
||||
- name: pgoBuildMode
|
||||
type: string
|
||||
default: Optimize
|
||||
values:
|
||||
- Optimize
|
||||
- Instrument
|
||||
- None
|
||||
- name: buildConfigurations
|
||||
type: object
|
||||
default:
|
||||
- Release
|
||||
- name: buildPlatforms
|
||||
type: object
|
||||
default:
|
||||
- x64
|
||||
- x86
|
||||
- arm64
|
||||
- name: codeSign
|
||||
type: boolean
|
||||
default: true
|
||||
- name: terminalInternalPackageVersion
|
||||
type: string
|
||||
default: '0.0.8'
|
||||
|
||||
- name: publishSymbolsToPublic
|
||||
type: boolean
|
||||
default: true
|
||||
- name: symbolExpiryTime
|
||||
type: string
|
||||
default: 36530 # This is the default from PublishSymbols@2
|
||||
- name: symbolPublishingSubscription
|
||||
type: string
|
||||
- name: symbolPublishingProject
|
||||
type: string
|
||||
|
||||
- name: extraPublishJobs
|
||||
type: object
|
||||
default: []
|
||||
- name: signingIdentity
|
||||
type: object
|
||||
default: {}
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: 1esPipelines
|
||||
type: git
|
||||
name: 1ESPipelineTemplates/1ESPipelineTemplates
|
||||
ref: refs/tags/release
|
||||
|
||||
extends:
|
||||
${{ if eq(parameters.official, true) }}:
|
||||
template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines
|
||||
${{ else }}:
|
||||
template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines
|
||||
parameters:
|
||||
customBuildTags:
|
||||
- 1ES.PT.ViaStartRight
|
||||
pool:
|
||||
name: SHINE-INT-L
|
||||
os: windows
|
||||
sdl:
|
||||
tsa:
|
||||
enabled: true
|
||||
configFile: '$(Build.SourcesDirectory)\build\config\tsa.json'
|
||||
binskim:
|
||||
enabled: true
|
||||
policheck:
|
||||
enabled: false
|
||||
severity: Note
|
||||
baseline:
|
||||
baselineFile: '$(Build.SourcesDirectory)\build\config\release.gdnbaselines'
|
||||
suppressionSet: default
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
displayName: Build
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: ./build/pipelines/templates-v2/job-build-project.yml@self
|
||||
parameters:
|
||||
outerTemplateContext:
|
||||
outputs:
|
||||
- output: pipelineArtifact
|
||||
targetPath: $(JobOutputDirectory)
|
||||
artifactName: $(JobOutputArtifactName)
|
||||
publishArtifacts: false # Handled by 1ESPT
|
||||
branding: ${{ parameters.branding }}
|
||||
buildTerminal: ${{ parameters.buildTerminal }}
|
||||
buildConPTY: ${{ parameters.buildConPTY }}
|
||||
buildWPF: ${{ parameters.buildWPF }}
|
||||
pgoBuildMode: ${{ parameters.pgoBuildMode }}
|
||||
buildConfigurations: ${{ parameters.buildConfigurations }}
|
||||
buildPlatforms: ${{ parameters.buildPlatforms }}
|
||||
generateSbom: false # this is handled by 1ESPT
|
||||
removeAllNonSignedFiles: true # appease the overlords
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity: ${{ parameters.signingIdentity }}
|
||||
beforeBuildSteps:
|
||||
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
|
||||
|
||||
- template: ./build/pipelines/templates-v2/steps-install-terrapin.yml@self
|
||||
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
inputs:
|
||||
feedListDownload: 2b3f8893-a6e8-411f-b197-a9e05576da48
|
||||
packageListDownload: e82d490c-af86-4733-9dc4-07b772033204
|
||||
versionListDownload: ${{ parameters.terminalInternalPackageVersion }}
|
||||
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
# Add an Any CPU build flavor for the WPF control bits
|
||||
- template: ./build/pipelines/templates-v2/job-build-project.yml@self
|
||||
parameters:
|
||||
outerTemplateContext:
|
||||
outputs:
|
||||
- output: pipelineArtifact
|
||||
targetPath: $(JobOutputDirectory)
|
||||
artifactName: $(JobOutputArtifactName)
|
||||
publishArtifacts: false # Handled by 1ESPT
|
||||
jobName: BuildWPF
|
||||
branding: ${{ parameters.branding }}
|
||||
buildTerminal: false
|
||||
buildWPFDotNetComponents: true
|
||||
buildConfigurations: ${{ parameters.buildConfigurations }}
|
||||
buildPlatforms:
|
||||
- Any CPU
|
||||
generateSbom: false # this is handled by 1ESPT
|
||||
removeAllNonSignedFiles: true # appease the overlords
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity: ${{ parameters.signingIdentity }}
|
||||
beforeBuildSteps:
|
||||
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
|
||||
# WPF doesn't need the localizations or the universal package, but if it does... put them here.
|
||||
|
||||
- stage: Package
|
||||
displayName: Package
|
||||
dependsOn: [Build]
|
||||
jobs:
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- template: ./build/pipelines/templates-v2/job-merge-msix-into-bundle.yml@self
|
||||
parameters:
|
||||
pool:
|
||||
name: SHINE-INT-S
|
||||
os: windows
|
||||
outerTemplateContext:
|
||||
outputs:
|
||||
- output: pipelineArtifact
|
||||
targetPath: $(JobOutputDirectory)
|
||||
artifactName: $(JobOutputArtifactName)
|
||||
publishArtifacts: false # Handled by 1ESPT
|
||||
jobName: Bundle
|
||||
branding: ${{ parameters.branding }}
|
||||
buildConfigurations: ${{ parameters.buildConfigurations }}
|
||||
buildPlatforms: ${{ parameters.buildPlatforms }}
|
||||
generateSbom: false # Handled by 1ESPT
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity: ${{ parameters.signingIdentity }}
|
||||
|
||||
- ${{ if eq(parameters.buildConPTY, true) }}:
|
||||
- template: ./build/pipelines/templates-v2/job-package-conpty.yml@self
|
||||
parameters:
|
||||
pool:
|
||||
name: SHINE-INT-S
|
||||
os: windows
|
||||
outerTemplateContext:
|
||||
outputs:
|
||||
- output: pipelineArtifact
|
||||
targetPath: $(JobOutputDirectory)
|
||||
artifactName: $(JobOutputArtifactName)
|
||||
publishArtifacts: false # Handled by 1ESPT
|
||||
buildConfigurations: ${{ parameters.buildConfigurations }}
|
||||
buildPlatforms: ${{ parameters.buildPlatforms }}
|
||||
generateSbom: false # this is handled by 1ESPT
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity: ${{ parameters.signingIdentity }}
|
||||
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- template: ./build/pipelines/templates-v2/job-build-package-wpf.yml@self
|
||||
parameters:
|
||||
pool:
|
||||
name: SHINE-INT-S
|
||||
os: windows
|
||||
outerTemplateContext:
|
||||
outputs:
|
||||
- output: pipelineArtifact
|
||||
targetPath: $(JobOutputDirectory)
|
||||
artifactName: $(JobOutputArtifactName)
|
||||
publishArtifacts: false # Handled by 1ESPT
|
||||
buildConfigurations: ${{ parameters.buildConfigurations }}
|
||||
buildPlatforms: ${{ parameters.buildPlatforms }}
|
||||
generateSbom: false # this is handled by 1ESPT
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity: ${{ parameters.signingIdentity }}
|
||||
|
||||
- stage: Publish
|
||||
displayName: Publish
|
||||
dependsOn:
|
||||
- Build
|
||||
- ${{ if or(parameters.buildTerminal, parameters.buildConPTY, parameters.buildWPF) }}:
|
||||
- Package
|
||||
jobs:
|
||||
- template: ./build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml@self
|
||||
parameters:
|
||||
pool:
|
||||
name: SHINE-INT-S
|
||||
os: windows
|
||||
includePublicSymbolServer: ${{ parameters.publishSymbolsToPublic }}
|
||||
symbolExpiryTime: ${{ parameters.symbolExpiryTime }}
|
||||
subscription: ${{ parameters.symbolPublishingSubscription }}
|
||||
symbolProject: ${{ parameters.symbolPublishingProject }}
|
||||
|
||||
- ${{ parameters.extraPublishJobs }}
|
||||
@@ -153,7 +153,10 @@ stages:
|
||||
- stage: Publish
|
||||
displayName: Publish
|
||||
pool: ${{ parameters.pool }}
|
||||
dependsOn: [Build, Package]
|
||||
dependsOn:
|
||||
- Build
|
||||
- ${{ if or(parameters.buildTerminal, parameters.buildConPTY, parameters.buildWPF) }}:
|
||||
- Package
|
||||
jobs:
|
||||
# We only support the vpack for Release builds that include Terminal
|
||||
- ${{ if and(containsValue(parameters.buildConfigurations, 'Release'), parameters.buildTerminal, parameters.publishVpackToWindows) }}:
|
||||
|
||||
@@ -279,7 +279,10 @@ extends:
|
||||
|
||||
- stage: Publish
|
||||
displayName: Publish
|
||||
dependsOn: [Build]
|
||||
dependsOn:
|
||||
- Build
|
||||
- ${{ if or(parameters.buildTerminal, parameters.buildConPTY, parameters.buildWPF) }}:
|
||||
- Package
|
||||
jobs:
|
||||
- template: ./build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml@self
|
||||
parameters:
|
||||
|
||||
6
build/pipelines/templates-v2/steps-install-terrapin.yml
Normal file
6
build/pipelines/templates-v2/steps-install-terrapin.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
steps:
|
||||
- pwsh: |-
|
||||
nuget install -source "https://pkgs.dev.azure.com/microsoft/_packaging/WindowsTerminal/nuget/v3/index.json" TerrapinRetrievalTool -Prerelease -OutputDirectory _trt
|
||||
$TerrapinRetrievalToolPath = (Get-Item _trt\TerrapinRetrievalTool.*\win-x64\TerrapinRetrievalTool.exe).FullName
|
||||
Write-Host "##vso[task.setvariable variable=X_VCPKG_ASSET_SOURCES]x-script,${TerrapinRetrievalToolPath} -b https://vcpkg.storage.devpackages.microsoft.io/artifacts/ -a true -u None -p {url} -s {sha512} -d {dst};x-block-origin"
|
||||
displayName: Set up the Terrapin Retrieval Tool (vcpkg cache)
|
||||
@@ -1143,13 +1143,6 @@ til::CoordType ROW::GetTrailingColumnAtCharOffset(const ptrdiff_t offset) const
|
||||
return _createCharToColumnMapper(offset).GetTrailingColumnAt(offset);
|
||||
}
|
||||
|
||||
uint16_t ROW::GetCharOffset(til::CoordType col) const noexcept
|
||||
{
|
||||
const auto columns = GetReadableColumnCount();
|
||||
const auto colBeg = clamp(col, 0, columns);
|
||||
return _uncheckedCharOffset(gsl::narrow_cast<size_t>(colBeg));
|
||||
}
|
||||
|
||||
DelimiterClass ROW::DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept
|
||||
{
|
||||
const auto col = _clampedColumn(column);
|
||||
|
||||
@@ -172,7 +172,6 @@ public:
|
||||
std::wstring_view GetText(til::CoordType columnBegin, til::CoordType columnEnd) const noexcept;
|
||||
til::CoordType GetLeadingColumnAtCharOffset(ptrdiff_t offset) const noexcept;
|
||||
til::CoordType GetTrailingColumnAtCharOffset(ptrdiff_t offset) const noexcept;
|
||||
uint16_t GetCharOffset(til::CoordType col) const noexcept;
|
||||
DelimiterClass DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept;
|
||||
|
||||
auto AttrBegin() const noexcept { return _attr.begin(); }
|
||||
|
||||
@@ -308,11 +308,6 @@ namespace TerminalAppLocalTests
|
||||
// TerminalPage and not only create them successfully, but also create a
|
||||
// tab using those settings successfully.
|
||||
|
||||
// - - - IMPORTANT - - -
|
||||
// GH#14623: "closeOnExit": "never" is important for all test profiles. Without
|
||||
// it, the spawned process exits immediately in the UAP test environment,
|
||||
// and the default "automatic" close-on-exit behavior removes the
|
||||
// tab/pane asynchronously, racing against test assertions.
|
||||
static constexpr std::wstring_view settingsJson0{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
@@ -320,14 +315,12 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -354,6 +347,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TryDuplicateBadTab()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _DuplicateFocusedTab on tab 1
|
||||
@@ -368,14 +365,12 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -387,8 +382,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -444,6 +438,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TryDuplicateBadPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _SplitPane(Duplicate) on tab 1
|
||||
@@ -458,14 +456,12 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -477,8 +473,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -577,29 +572,25 @@ namespace TerminalAppLocalTests
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 0",
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 1",
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 2
|
||||
},
|
||||
{
|
||||
"name" : "profile2",
|
||||
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 2",
|
||||
"historySize": 3,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 3
|
||||
},
|
||||
{
|
||||
"name" : "profile3",
|
||||
"guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 3",
|
||||
"historySize": 4,
|
||||
"closeOnExit": "never"
|
||||
"historySize": 4
|
||||
}
|
||||
],
|
||||
"schemes":
|
||||
@@ -702,6 +693,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -741,6 +733,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::MoveFocusFromZoomedPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create a second pane");
|
||||
@@ -786,6 +782,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::CloseZoomedPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create a second pane");
|
||||
@@ -841,6 +841,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::SwapPanes()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Setup 4 panes.");
|
||||
@@ -1047,31 +1051,31 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::NextMRUTab()
|
||||
{
|
||||
// This is a test for GH#8025 - we want to make sure that MRU tab
|
||||
// ordering works correctly and that in-order/disabled switching works.
|
||||
//
|
||||
// Note: We test MRU ordering directly rather than going through the
|
||||
// command palette tab switcher, because the palette's anchor key
|
||||
// handling auto-dismisses when no modifier keys are held (which we
|
||||
// can't simulate in the test environment).
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// This is a test for GH#8025 - we want to make sure that we can do both
|
||||
// in-order and MRU tab traversal, using the tab switcher and with the
|
||||
// tab switcher disabled.
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create Tab[1]");
|
||||
Log::Comment(L"Create a second tab");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 1 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
|
||||
Log::Comment(L"Create Tab[2]");
|
||||
Log::Comment(L"Create a third tab");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 2 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(3u, page->_tabs.Size());
|
||||
|
||||
Log::Comment(L"Create Tab[3]");
|
||||
Log::Comment(L"Create a fourth tab");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 3 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
@@ -1080,89 +1084,99 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
});
|
||||
|
||||
Log::Comment(L"Select Tab[1]");
|
||||
Log::Comment(L"Select the second tab");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectTab(1);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify Tab[1] is focused");
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
|
||||
});
|
||||
|
||||
// MRU order should now be: Tab[1], Tab[3], Tab[2], Tab[0]
|
||||
// Verify the MRU list directly.
|
||||
Log::Comment(L"Verify MRU order: MRU[0]=Tab[1], MRU[1]=Tab[3]");
|
||||
Log::Comment(L"Change the tab switch order to MRU switching");
|
||||
TestOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
uint32_t mruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(0), mruIdx);
|
||||
VERIFY_ARE_EQUAL(1u, mruIdx, L"MRU[0] should be Tab[1] (most recent)");
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), mruIdx);
|
||||
VERIFY_ARE_EQUAL(3u, mruIdx, L"MRU[1] should be Tab[3] (last tab added)");
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
||||
});
|
||||
|
||||
Log::Comment(L"Select MRU[1]=Tab[3] directly");
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the fourth tab");
|
||||
TestOnUIThread([&page]() {
|
||||
// The next MRU tab after Tab[1] is Tab[3]
|
||||
uint32_t nextMruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), nextMruIdx);
|
||||
page->_SelectTab(nextMruIdx);
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
|
||||
Log::Comment(L"Sleep to let events propagate");
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Hide the command palette, to confirm the selection");
|
||||
// If you don't do this, the palette will just stay open, and the
|
||||
// next time we call _HandleNextTab, we'll continue traversing the
|
||||
// MRU list, instead of just hoping one entry.
|
||||
page->LoadCommandPalette().Visibility(Visibility::Collapsed);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
});
|
||||
|
||||
Log::Comment(L"Select MRU[1]=Tab[1] directly");
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the second tab");
|
||||
TestOnUIThread([&page]() {
|
||||
uint32_t nextMruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), nextMruIdx);
|
||||
page->_SelectTab(nextMruIdx);
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
|
||||
Log::Comment(L"Sleep to let events propagate");
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Hide the command palette, to confirm the selection");
|
||||
// If you don't do this, the palette will just stay open, and the
|
||||
// next time we call _HandleNextTab, we'll continue traversing the
|
||||
// MRU list, instead of just hoping one entry.
|
||||
page->LoadCommandPalette().Visibility(Visibility::Collapsed);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify Tab[1] is focused");
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
|
||||
});
|
||||
|
||||
Log::Comment(L"Change the tab switch order to in-order switching");
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::InOrder);
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab, which is the third tab");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(2u, focusedIndex, L"Verify the third tab is the focused one");
|
||||
});
|
||||
|
||||
// The Disabled tab switcher mode uses direct index-based switching
|
||||
// without the command palette, so it works in the test environment.
|
||||
Log::Comment(L"Change the tab switch order to not use the tab switcher (which is in-order always)");
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::Disabled);
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab: Tab[2]");
|
||||
Log::Comment(L"Switch to the next in-order tab, which is the fourth tab");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(2u, focusedIndex, L"Verify Tab[2] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab: Tab[3]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::VerifyCommandPaletteTabSwitcherOrder()
|
||||
{
|
||||
// This is a test for GH#8188 - we want to make sure that the MRU
|
||||
// ordering is correctly maintained as tabs are selected.
|
||||
//
|
||||
// Note: We verify MRU ordering directly rather than going through
|
||||
// the command palette tab switcher, because the palette's anchor key
|
||||
// handling auto-dismisses when no modifier keys are held (which we
|
||||
// can't simulate in the test environment).
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// This is a test for GH#8188 - we want to make sure that the order of tabs
|
||||
// is preserved in the CommandPalette's TabSwitcher
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
@@ -1175,7 +1189,7 @@ namespace TerminalAppLocalTests
|
||||
});
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
|
||||
Log::Comment(L"give alphabetical names to all tabs");
|
||||
Log::Comment(L"give alphabetical names to all switch tab actions");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_GetTabImpl(page->_tabs.GetAt(0))->Title(L"a");
|
||||
});
|
||||
@@ -1197,14 +1211,18 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"c", page->_tabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_tabs.GetAt(3).Title());
|
||||
|
||||
// MRU order after creating Tab[0]-Tab[3]: MRU[0]=Tab[3], MRU[3]=Tab[0]
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
Log::Comment(L"Select Tab[0] through Tab[3] to establish MRU order");
|
||||
Log::Comment(L"Change the tab switch order to MRU switching");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
||||
});
|
||||
|
||||
Log::Comment(L"Select the tabs from 0 to 3");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(0));
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(1));
|
||||
@@ -1212,31 +1230,47 @@ namespace TerminalAppLocalTests
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(3));
|
||||
});
|
||||
|
||||
Log::Comment(L"Verify MRU order: MRU[0]='d', MRU[1]='c', MRU[2]='b', MRU[3]='a'");
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
|
||||
Log::Comment(L"Select Tab[2]='c' (MRU[1] after 'd')");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectTab(2);
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the third tab");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
// In the course of a single tick, the Command Palette will:
|
||||
// * open
|
||||
// * select the proper tab from the mru's list
|
||||
// * raise an event for _filteredActionsView().SelectionChanged to
|
||||
// immediately preview the new tab
|
||||
// * raise a _SwitchToTabRequestedHandlers event
|
||||
// * then dismiss itself, because we can't fake holing down an
|
||||
// anchor key in the tests
|
||||
});
|
||||
|
||||
Log::Comment(L"Verify MRU order updated: MRU[0]='c', MRU[1]='d', MRU[2]='b', MRU[3]='a'");
|
||||
TestOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
const auto palette = winrt::get_self<winrt::TerminalApp::implementation::CommandPalette>(page->LoadCommandPalette());
|
||||
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode");
|
||||
// At this point, the contents of the command palette's _mruTabs list is
|
||||
// still the _old_ ordering (d, c, b, a). The ordering is only updated
|
||||
// in TerminalPage::_SelectNextTab, but as we saw before, the palette
|
||||
// will also dismiss itself immediately when that's called. So we can't
|
||||
// really inspect the contents of the list in this test, unfortunately.
|
||||
}
|
||||
|
||||
void TabTests::TestWindowRenameSuccessful()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1269,6 +1303,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1301,6 +1336,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewCommitScheme()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1363,6 +1402,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewDismissScheme()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then dismissed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1411,6 +1454,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewSchemeWhilePreviewing()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme, then preview another scheme. ");
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly");
|
||||
@@ -1478,6 +1525,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestClampSwitchToTab()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Test that switching to a tab index higher than the number of tabs just clamps to the last tab.");
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
@@ -2906,6 +2906,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do we ever get here (= uninitialized terminal)? If so: How?
|
||||
assert(false);
|
||||
return { 10, 10 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1061,7 +1061,38 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||
ROW* rowBackup = nullptr;
|
||||
if (row == compositionRow)
|
||||
{
|
||||
rowBackup = _PaintBufferOutputComposition(buffer, r, activeComposition);
|
||||
auto& scratch = buffer.GetScratchpadRow();
|
||||
scratch.CopyFrom(r);
|
||||
rowBackup = &scratch;
|
||||
|
||||
std::wstring_view text{ activeComposition.text };
|
||||
RowWriteState state{
|
||||
.columnLimit = r.GetReadableColumnCount(),
|
||||
.columnEnd = _compositionCache->absoluteOrigin.x,
|
||||
};
|
||||
|
||||
size_t off = 0;
|
||||
for (const auto& range : activeComposition.attributes)
|
||||
{
|
||||
const auto len = range.len;
|
||||
auto attr = range.attr;
|
||||
|
||||
// Use the color at the cursor if TSF didn't specify any explicit color.
|
||||
if (attr.GetBackground().IsDefault())
|
||||
{
|
||||
attr.SetBackground(_compositionCache->baseAttribute.GetBackground());
|
||||
}
|
||||
if (attr.GetForeground().IsDefault())
|
||||
{
|
||||
attr.SetForeground(_compositionCache->baseAttribute.GetForeground());
|
||||
}
|
||||
|
||||
state.text = text.substr(off, len);
|
||||
state.columnBegin = state.columnEnd;
|
||||
const_cast<ROW&>(r).ReplaceText(state);
|
||||
const_cast<ROW&>(r).ReplaceAttributes(state.columnBegin, state.columnEnd, attr);
|
||||
off += len;
|
||||
}
|
||||
}
|
||||
const auto restore = wil::scope_exit([&] {
|
||||
if (rowBackup)
|
||||
@@ -1101,107 +1132,6 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||
}
|
||||
}
|
||||
|
||||
ROW* Renderer::_PaintBufferOutputComposition(TextBuffer& buffer, const ROW& r, const Composition& activeComposition)
|
||||
{
|
||||
auto& scratch = buffer.GetScratchpadRow();
|
||||
scratch.CopyFrom(r);
|
||||
|
||||
// *Overwrite* the original text with the active composition...
|
||||
til::CoordType compositionEnd = 0;
|
||||
{
|
||||
std::wstring_view text{ activeComposition.text };
|
||||
RowWriteState state{
|
||||
.columnLimit = r.GetReadableColumnCount(),
|
||||
.columnEnd = _compositionCache->absoluteOrigin.x,
|
||||
};
|
||||
|
||||
size_t off = 0;
|
||||
for (const auto& range : activeComposition.attributes)
|
||||
{
|
||||
const auto len = range.len;
|
||||
auto attr = range.attr;
|
||||
|
||||
// Use the color at the cursor if TSF didn't specify any explicit color.
|
||||
if (attr.GetBackground().IsDefault())
|
||||
{
|
||||
attr.SetBackground(_compositionCache->baseAttribute.GetBackground());
|
||||
}
|
||||
if (attr.GetForeground().IsDefault())
|
||||
{
|
||||
attr.SetForeground(_compositionCache->baseAttribute.GetForeground());
|
||||
}
|
||||
|
||||
state.text = text.substr(off, len);
|
||||
state.columnBegin = state.columnEnd;
|
||||
const_cast<ROW&>(r).ReplaceText(state);
|
||||
const_cast<ROW&>(r).ReplaceAttributes(state.columnBegin, state.columnEnd, attr);
|
||||
off += len;
|
||||
}
|
||||
|
||||
compositionEnd = state.columnEnd;
|
||||
}
|
||||
|
||||
// The text we've overwritten may have been crucial to the user,
|
||||
// so copy it back by absorbing available whitespace to the right
|
||||
// and re-inserting the non-whitespace characters instead.
|
||||
const auto compositionWidth = compositionEnd - _compositionCache->absoluteOrigin.x;
|
||||
const auto colLimit = r.GetReadableColumnCount();
|
||||
if (compositionWidth > 0 && compositionEnd < colLimit)
|
||||
{
|
||||
const auto text = scratch.GetText();
|
||||
auto srcCol = _compositionCache->absoluteOrigin.x;
|
||||
auto dstCol = compositionEnd;
|
||||
auto remaining = compositionWidth;
|
||||
size_t i = scratch.GetCharOffset(srcCol);
|
||||
|
||||
while (i < text.size() && dstCol < colLimit)
|
||||
{
|
||||
// Treat whitespace we encounter as a credit towards our composition width.
|
||||
// This loop essentially absorbs the whitespace.
|
||||
while (i < text.size() && til::at(text, i) == L' ' && remaining > 0)
|
||||
{
|
||||
remaining--;
|
||||
srcCol++;
|
||||
i++;
|
||||
}
|
||||
if (remaining <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the end of the non-whitespace span: Our span of text to insert.
|
||||
auto spanEnd = i;
|
||||
while (spanEnd < text.size() && til::at(text, spanEnd) != L' ')
|
||||
{
|
||||
spanEnd++;
|
||||
}
|
||||
|
||||
// Copy the non-whitespace segment from the original text (scratch) back in.
|
||||
RowCopyTextFromState state{
|
||||
.source = scratch,
|
||||
.columnBegin = dstCol,
|
||||
.columnLimit = colLimit,
|
||||
.sourceColumnBegin = srcCol,
|
||||
.sourceColumnLimit = scratch.GetLeadingColumnAtCharOffset(spanEnd),
|
||||
};
|
||||
const_cast<ROW&>(r).CopyTextFrom(state);
|
||||
|
||||
const auto srcBeg = gsl::narrow_cast<uint16_t>(srcCol);
|
||||
const auto srcEnd = gsl::narrow_cast<uint16_t>(state.sourceColumnEnd);
|
||||
const auto attr = scratch.Attributes().slice(srcBeg, srcEnd);
|
||||
const auto dstBeg = gsl::narrow_cast<uint16_t>(dstCol);
|
||||
const auto dstEnd = gsl::narrow_cast<uint16_t>(dstCol + attr.size());
|
||||
const_cast<ROW&>(r).Attributes().replace(dstBeg, dstEnd, attr);
|
||||
|
||||
dstCol = state.columnEnd;
|
||||
srcCol = state.sourceColumnEnd;
|
||||
i = spanEnd;
|
||||
}
|
||||
}
|
||||
|
||||
return &scratch;
|
||||
}
|
||||
|
||||
static bool _IsAllSpaces(const std::wstring_view v)
|
||||
{
|
||||
// first non-space char is not found (is npos)
|
||||
|
||||
@@ -121,7 +121,6 @@ namespace Microsoft::Console::Render
|
||||
void _scheduleRenditionBlink();
|
||||
[[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine);
|
||||
void _PaintBufferOutput(_In_ IRenderEngine* const pEngine);
|
||||
ROW* _PaintBufferOutputComposition(TextBuffer& buffer, const ROW& r, const Composition& activeComposition);
|
||||
void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, TextBufferCellIterator it, const til::point target);
|
||||
void _PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine, const TextAttribute textAttribute, const size_t cchLine, const til::point coordTarget);
|
||||
bool _isHoveredHyperlink(const TextAttribute& textAttribute) const noexcept;
|
||||
|
||||
Reference in New Issue
Block a user