Compare commits
77 Commits
release-1.
...
dev/miniks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b2659bf65 | ||
|
|
466b80496d | ||
|
|
af2712b09e | ||
|
|
d2372342e2 | ||
|
|
e59f2b8477 | ||
|
|
60ef914956 | ||
|
|
cdc5a6a747 | ||
|
|
da24f7d939 | ||
|
|
da3f02a6e2 | ||
|
|
d972ea2c28 | ||
|
|
a5ff7459b7 | ||
|
|
544bd44851 | ||
|
|
9bd097f4ad | ||
|
|
9069fcab79 | ||
|
|
f4d487efef | ||
|
|
fb734d166c | ||
|
|
2ed367fb49 | ||
|
|
acdcdcaccb | ||
|
|
d749df70ed | ||
|
|
a979e52f7c | ||
|
|
1519236f2b | ||
|
|
43c469fc95 | ||
|
|
8346968881 | ||
|
|
c5124956a9 | ||
|
|
44f1ba6d4d | ||
|
|
99b09c08d5 | ||
|
|
fbdfc4d446 | ||
|
|
28da6c4ecb | ||
|
|
b76087f561 | ||
|
|
efc92de19f | ||
|
|
af896cd4bc | ||
|
|
1a1b4ff21e | ||
|
|
691e02ef1c | ||
|
|
3029bb8a68 | ||
|
|
1e4c6978b7 | ||
|
|
6cd4e03a58 | ||
|
|
a47ed99272 | ||
|
|
95b031e27c | ||
|
|
8f9ccc55d1 | ||
|
|
3909cc103a | ||
|
|
8358f8d93f | ||
|
|
48d59e8304 | ||
|
|
83f2a3bb3d | ||
|
|
87fa526fb7 | ||
|
|
c6a31710d9 | ||
|
|
19bd0c94e7 | ||
|
|
629c06d0ad | ||
|
|
1202f89399 | ||
|
|
4a95341caf | ||
|
|
3cf7677d17 | ||
|
|
ac3fecb134 | ||
|
|
5aaf5b4d59 | ||
|
|
2961a104af | ||
|
|
7b1a660e59 | ||
|
|
cb03b97e67 | ||
|
|
f26c246c7c | ||
|
|
649c546960 | ||
|
|
a930aa390e | ||
|
|
d1bf0fcd1e | ||
|
|
66033dcb01 | ||
|
|
95f63a9100 | ||
|
|
6654f0d155 | ||
|
|
ce99c2a349 | ||
|
|
ce0505073c | ||
|
|
9b8c9d4f51 | ||
|
|
69538c4ab3 | ||
|
|
324c0942c8 | ||
|
|
c65161d60e | ||
|
|
b591355103 | ||
|
|
a1c1d5cc09 | ||
|
|
67d591b11d | ||
|
|
a692bb2f0f | ||
|
|
f00ffb0291 | ||
|
|
33e9a9b22a | ||
|
|
8114fa0dd0 | ||
|
|
00ca3f204c | ||
|
|
1ed8ef0847 |
54
.github/ISSUE_TEMPLATE/Bug_Report.md
vendored
@@ -1,54 +0,0 @@
|
||||
---
|
||||
name: "Bug report 🐛"
|
||||
about: Report errors or unexpected behavior
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
||||
|
||||
I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
|
||||
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
|
||||
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
|
||||
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
|
||||
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
|
||||
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.
|
||||
|
||||
All good? Then proceed!
|
||||
-->
|
||||
|
||||
<!--
|
||||
This bug tracker is monitored by Windows Terminal development team and other technical folks.
|
||||
|
||||
**Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**.
|
||||
Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue.
|
||||
|
||||
If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link.
|
||||
|
||||
Please use this form and describe your issue, concisely but precisely, with as much detail as possible.
|
||||
|
||||
-->
|
||||
|
||||
# Environment
|
||||
|
||||
```none
|
||||
Windows build number: [run `[Environment]::OSVersion` for powershell, or `ver` for cmd]
|
||||
Windows Terminal version (if applicable):
|
||||
|
||||
Any other software?
|
||||
```
|
||||
|
||||
# Steps to reproduce
|
||||
|
||||
<!-- A description of how to trigger this bug. -->
|
||||
|
||||
# Expected behavior
|
||||
|
||||
<!-- A description of what you're expecting, possibly containing screenshots or reference material. -->
|
||||
|
||||
# Actual behavior
|
||||
|
||||
<!-- What's actually happening? -->
|
||||
57
.github/ISSUE_TEMPLATE/Bug_Report.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: "Bug report 🐛"
|
||||
description: Report errors or unexpected behavior
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
issue_body: true
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please make sure to [search for existing issues](https://github.com/microsoft/terminal/issues) before filing a new one!
|
||||
|
||||
If this is an application crash, please also provide a [Feedback Hub](https://aka.ms/terminal-feedback-hub) submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal" and choose "Share My Feedback" after submission to get the link.
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Windows Terminal version (or Windows build number)
|
||||
placeholder: "10.0.19042.0, 1.7.3651.0"
|
||||
description: |
|
||||
If you are reporting an issue in Windows Terminal, you can find the version in the about dialog.
|
||||
|
||||
If you are reporting an issue with the Windows Console, please run `ver` or `[Environment]::OSVersion`.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Other Software
|
||||
description: If you're reporting a bug about our interaction with other software, what software? What versions?
|
||||
placeholder: |
|
||||
vim 8.2 (inside WSL)
|
||||
OpenSSH_for_Windows_8.1p1
|
||||
My Cool Application v0.3 (include a code snippet if it would help!)
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
placeholder: Tell us the steps required to trigger your bug.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: If you want to include screenshots, paste them into the markdown editor below the form or follow up with a separate comment.
|
||||
placeholder: What were you expecting?
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual Behavior
|
||||
placeholder: What happened instead?
|
||||
validations:
|
||||
required: true
|
||||
13
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"ms-vscode.cpptools"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
|
||||
]
|
||||
}
|
||||
24
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug OpenConsole by Launching (x64, debug)",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}\\bin\\x64\\debug\\openconsole.exe",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
},
|
||||
{
|
||||
"name": "Debug Terminal by Attaching (You go build/register/launch it first.)",
|
||||
"type": "cppvsdbg",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
||||
32
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"C_Cpp.default.browse.databaseFilename": "${workspaceFolder}\\.vscode\\.BROWSE.VC.DB",
|
||||
"C_Cpp.default.browse.path": [
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"C_Cpp.loggingLevel": "None",
|
||||
"files.associations": {
|
||||
"xstring": "cpp",
|
||||
"*.idl": "cpp",
|
||||
"array": "cpp",
|
||||
"future": "cpp",
|
||||
"istream": "cpp",
|
||||
"memory": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"variant": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"multi_span": "cpp",
|
||||
"pointers": "cpp",
|
||||
"vector": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/bin/**": true,
|
||||
"**/obj/**": true,
|
||||
"**/packages/**": true,
|
||||
"**/generated files/**": true
|
||||
}
|
||||
}
|
||||
107
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Build Terminal/Console",
|
||||
"command": "powershell.exe",
|
||||
"args": [
|
||||
"-Command",
|
||||
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
|
||||
"Set-MsBuildDevEnvironment;",
|
||||
"$project = switch(\"${input:buildProjectChoice}\"){OpenConsole{\"Conhost\\Host_EXE\"} Terminal{\"Terminal\\CascadiaPackage\"}};",
|
||||
"$target = switch(\"${input:buildModeChoice}\"){Build{\"\"} Rebuild{\":Rebuild\"} Clean{\":Clean\"}};",
|
||||
"$target = $project + $target;",
|
||||
"msbuild",
|
||||
"${workspaceFolder}\\OpenConsole.sln",
|
||||
"/p:Configuration=${input:configChoice}",
|
||||
"/p:Platform=${input:platformChoice}",
|
||||
"/t:$target",
|
||||
"/verbosity:minimal"
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"runOptions": {
|
||||
"reevaluateOnRerun": false,
|
||||
"instanceLimit": 1,
|
||||
"runOn": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Register Windows Terminal x64 Debug",
|
||||
"command": "powershell.exe",
|
||||
"args": [
|
||||
"-Command",
|
||||
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
|
||||
"Set-MsBuildDevEnvironment;",
|
||||
"Set-Location -Path ${workspaceFolder}\\src\\cascadia\\CascadiaPackage\\AppPackages\\CascadiaPackage_0.0.1.0_x64_Debug_Test;",
|
||||
"if ((Get-AppxPackage -Name 'WindowsTerminalDev*') -ne $null) { Remove-AppxPackage 'WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe'};",
|
||||
"New-Item ..\\loose -Type Directory -Force;",
|
||||
"makeappx unpack /v /o /p .\\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\\Loose\\;",
|
||||
"Add-AppxPackage -Path ..\\loose\\AppxManifest.xml -Register -ForceUpdateFromAnyVersion -ForceApplicationShutdown"
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Run Windows Terminal Dev",
|
||||
"command": "wtd.exe",
|
||||
"args": [
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
}
|
||||
],
|
||||
"inputs":[
|
||||
{
|
||||
"id": "platformChoice",
|
||||
"type": "pickString",
|
||||
"description": "Processor architecture choice",
|
||||
"options":[
|
||||
"x64",
|
||||
"x86",
|
||||
"arm64"
|
||||
],
|
||||
"default": "x64"
|
||||
},
|
||||
{
|
||||
"id": "configChoice",
|
||||
"type": "pickString",
|
||||
"description": "Debug or release?",
|
||||
"options":[
|
||||
"Debug",
|
||||
"Release"
|
||||
],
|
||||
"default": "Debug"
|
||||
},
|
||||
{
|
||||
"id": "buildModeChoice",
|
||||
"type": "pickString",
|
||||
"description": "Build, rebuild, or clean?",
|
||||
"options":[
|
||||
"Build",
|
||||
"Rebuild",
|
||||
"Clean"
|
||||
],
|
||||
"default": "Build"
|
||||
},
|
||||
{
|
||||
"id": "buildProjectChoice",
|
||||
"type": "pickString",
|
||||
"description": "OpenConsole or Terminal?",
|
||||
"options":[
|
||||
"OpenConsole",
|
||||
"Terminal"
|
||||
],
|
||||
"default": "Terminal"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
@@ -161,7 +161,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalConnection", "src\c
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalCore", "src\cascadia\TerminalCore\lib\TerminalCore-lib.vcxproj", "{CA5CAD1A-ABCD-429C-B551-8562EC954746}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalControl", "src\cascadia\TerminalControl\TerminalControl.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalControlLib", "src\cascadia\TerminalControl\TerminalControlLib.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
|
||||
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
|
||||
@@ -169,10 +169,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalControl", "src\casc
|
||||
{48D21369-3D7B-4431-9967-24E81292CF63} = {48D21369-3D7B-4431-9967-24E81292CF63}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalControl", "src\cascadia\TerminalControl\dll\TerminalControl.vcxproj", "{CA5CAD1A-F542-4635-A069-7CAEFB930070}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminal", "src\cascadia\WindowsTerminal\WindowsTerminal.vcxproj", "{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}
|
||||
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
|
||||
{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}
|
||||
@@ -185,7 +190,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalApp", "src\cascadia
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
|
||||
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminalShellExt", "src\cascadia\ShellExtension\WindowsTerminalShellExt.vcxproj", "{F2ED628A-DB22-446F-A081-4CC845B51A2B}"
|
||||
@@ -236,7 +241,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\casca
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
|
||||
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}"
|
||||
@@ -326,7 +331,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model", "src\cascadia\TerminalSettingsModel\dll\Microsoft.Terminal.Settings.Model.vcxproj", "{CA5CAD1A-082C-4476-9F33-94B339494076}"
|
||||
@@ -338,7 +343,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel",
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
@@ -364,7 +369,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Remoting", "src\c
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wpf", "wpf", "{4DAF0299-495E-4CD1-A982-9BAC16A45932}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Proxy", "src\host\proxy\Host.Proxy.vcxproj", "{E437B604-3E98-4F40-A927-E173E818EA4B}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenConsoleProxy", "src\host\proxy\Host.Proxy.vcxproj", "{E437B604-3E98-4F40-A927-E173E818EA4B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Window", "Window", "{2D17E75D-2DDC-42C4-AD70-704D95A937AE}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Settings", "Settings", "{77875138-BB08-49F9-8BB1-409C2150E0E1}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Control", "Control", "{9921CA0A-320C-4460-8623-3A3196E7F4CB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -1518,6 +1529,33 @@ Global
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x64.Build.0 = Release|x64
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x86.Build.0 = Release|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|Any CPU.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|ARM64.ActiveCfg = Release|ARM64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|x86.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|ARM.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|x64.Build.0 = Debug|x64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|ARM.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|x64.ActiveCfg = Release|x64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|x64.Build.0 = Release|x64
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|x86.Build.0 = Release|Win32
|
||||
{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|Any CPU.ActiveCfg = Debug|x64
|
||||
{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|ARM64.ActiveCfg = Release|ARM64
|
||||
@@ -2617,11 +2655,12 @@ Global
|
||||
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {1E4A062E-293B-4817-B20D-BF16B979E350}
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62} = {05500DEF-2294-41E3-AF9A-24E580B82836}
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {9921CA0A-320C-4460-8623-3A3196E7F4CB}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {9921CA0A-320C-4460-8623-3A3196E7F4CB}
|
||||
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {9921CA0A-320C-4460-8623-3A3196E7F4CB}
|
||||
{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{F2ED628A-DB22-446F-A081-4CC845B51A2B} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{F2ED628A-DB22-446F-A081-4CC845B51A2B} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE}
|
||||
{2C2BEEF4-9333-4D05-B12A-1905CBF112F9} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
|
||||
{16376381-CE22-42BE-B667-C6B35007008D} = {81C352DB-1818-45B7-A284-18E259F1CC87}
|
||||
@@ -2652,17 +2691,20 @@ Global
|
||||
{6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87}
|
||||
{1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {4DAF0299-495E-4CD1-A982-9BAC16A45932}
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {77875138-BB08-49F9-8BB1-409C2150E0E1}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {77875138-BB08-49F9-8BB1-409C2150E0E1}
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {77875138-BB08-49F9-8BB1-409C2150E0E1}
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{21B7EA5E-1EF8-49B6-AC07-11714AF0E37D} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||||
{43CE4CE5-0010-4B99-9569-672670D26E26} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{43CE4CE5-0010-4B99-9569-672670D26E26} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE}
|
||||
{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE}
|
||||
{68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{4DAF0299-495E-4CD1-A982-9BAC16A45932} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{E437B604-3E98-4F40-A927-E173E818EA4B} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
|
||||
{2D17E75D-2DDC-42C4-AD70-704D95A937AE} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{77875138-BB08-49F9-8BB1-409C2150E0E1} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{9921CA0A-320C-4460-8623-3A3196E7F4CB} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
||||
17
README.md
@@ -33,10 +33,23 @@ This is our preferred method.
|
||||
|
||||
#### Via GitHub
|
||||
|
||||
For users who are unable to install Terminal from the Microsoft Store, Terminal
|
||||
builds can be manually downloaded from this repository's [Releases
|
||||
For users who are unable to install Windows Terminal from the Microsoft Store,
|
||||
released builds can be manually downloaded from this repository's [Releases
|
||||
page](https://github.com/microsoft/terminal/releases).
|
||||
|
||||
Download the `Microsoft.WindowsTerminal_<versionNumber>.msixbundle` file from
|
||||
the **Assets** section. To install the app, you can simply double-click on the
|
||||
`.msixbundle` file, and the app installer should automatically run. If that
|
||||
fails for any reason, you can try the following command at a PowerShell prompt:
|
||||
|
||||
```powershell
|
||||
# NOTE: If you are using PowerShell 7+, please run
|
||||
# Import-Module Appx -UseWindowsPowerShell
|
||||
# before using Add-AppxPackage.
|
||||
|
||||
Add-AppxPackage Microsoft.WindowsTerminal_<versionNumber>.msixbundle
|
||||
```
|
||||
|
||||
> 🔴 Note: If you install Terminal manually:
|
||||
>
|
||||
> * Terminal will not auto-update when new builds are released so you will need
|
||||
|
||||
@@ -58,7 +58,7 @@ Try {
|
||||
|
||||
### Check the activatable class entries for a few DLLs we need.
|
||||
$inProcServers = $Manifest.Package.Extensions.Extension.InProcessServer.Path
|
||||
$RequiredInProcServers = ("TerminalApp.dll", "TerminalControl.dll", "TerminalConnection.dll")
|
||||
$RequiredInProcServers = ("TerminalApp.dll", "Microsoft.Terminal.Control.dll", "Microsoft.Terminal.Remoting.dll", "Microsoft.Terminal.Settings.Editor.dll", "Microsoft.Terminal.Settings.Model.dll", "TerminalConnection.dll")
|
||||
|
||||
Write-Verbose "InProc Servers: $inProcServers"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>7</VersionMinor>
|
||||
<VersionMinor>8</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -222,10 +222,28 @@
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": null,
|
||||
"description": "If provided, will set the tab's color to the given value"
|
||||
},
|
||||
"suppressApplicationTitle": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "When set to true, tabTitle overrides the default title of the tab and any title change messages from the application will be suppressed. When set to false, tabTitle behaves as normal"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"SwitchToAdjacentTabArgs" : {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"enum": [
|
||||
"mru",
|
||||
"inOrder",
|
||||
"disabled"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ShortcutAction": {
|
||||
"properties": {
|
||||
"action": {
|
||||
@@ -597,6 +615,38 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"PrevTabAction": {
|
||||
"description": "Arguments corresponding to a Previous Tab Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type":"string", "pattern": "prevTab" },
|
||||
"tabSwitcherMode": {
|
||||
"$ref": "#/definitions/SwitchToAdjacentTabArgs",
|
||||
"default": null,
|
||||
"description": "Move to the previous tab using \"tabSwitcherMode\". If no mode is provided, use the one globally defined one."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"NextTabAction": {
|
||||
"description": "Arguments corresponding to a Next Tab Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type":"string", "pattern": "nextTab" },
|
||||
"tabSwitcherMode": {
|
||||
"$ref": "#/definitions/SwitchToAdjacentTabArgs",
|
||||
"default": null,
|
||||
"description": "Move to the next tab using \"tabSwitcherMode\". If no mode is provided, use the one globally defined one."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -623,6 +673,8 @@
|
||||
{ "$ref": "#/definitions/MoveTabAction" },
|
||||
{ "$ref": "#/definitions/FindMatchAction" },
|
||||
{ "$ref": "#/definitions/NewWindowAction" },
|
||||
{ "$ref": "#/definitions/NextTabAction" },
|
||||
{ "$ref": "#/definitions/PrevTabAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
@@ -993,9 +1045,9 @@
|
||||
"description": "Sets the color of the cursor. Overrides the cursor color from the color scheme. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"cursorHeight": {
|
||||
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 25-100.",
|
||||
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 1-100.",
|
||||
"maximum": 100,
|
||||
"minimum": 25,
|
||||
"minimum": 1,
|
||||
"type": ["integer","null"],
|
||||
"default": 25
|
||||
},
|
||||
@@ -1137,11 +1189,6 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"tabColor": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color of the profile's tab. Using the tab color picker will override this color.",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"tabTitle": {
|
||||
"description": "If set, will replace the name as the title to pass to the shell on startup. Some shells (like bash) may choose to ignore this initial value, while others (cmd, powershell) may use this value over the lifetime of the application.",
|
||||
"type": ["string", "null"]
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the roadmap towards delivering Windows Terminal 2.0 by Spring 2021.
|
||||
|
||||
This document outlines the roadmap towards delivering Windows Terminal 2.0 by Winter 2021.
|
||||
|
||||
## Milestones
|
||||
|
||||
The Windows Terminal project is engineered and delivered as a set of 4-week milestones. New features will go into [Windows Terminal Preview](https://aka.ms/terminal-preview) first, then a month after they've been in Preview, those features will move into [Windows Terminal](https://aka.ms/terminal).
|
||||
Windows Terminal is engineered and delivered as a set of 6-week milestones. New features will go into [Windows Terminal Preview](https://aka.ms/terminal-preview) first, then a month after they've been in Preview, those features will move into [Windows Terminal](https://aka.ms/terminal).
|
||||
|
||||
| Duration | Activity | Releases |
|
||||
| --- | --- | --- |
|
||||
| 2 weeks | Dev Work<br/> <ul><li>Fixes / Features for future Windows Releases</li><li>Fixes / Features for Windows Terminal</li></ul> | Release to Internal Selfhosters at end of week 2 |
|
||||
| 1 week | Quality & Stability<br/> <ul><li>Bug Fixes</li><li>Perf & Stability</li><li>UI Polish</li><li>Tests</li><li>etc.</li></ul>| Push to Microsoft Store at end of week 3 |
|
||||
| 4 weeks | Dev Work<br/> <ul><li>Fixes / Features for future Windows Releases</li><li>Fixes / Features for Windows Terminal</li></ul> | Release to Internal Selfhosters at end of week 4 |
|
||||
| 1 week | Quality & Stability<br/> <ul><li>Bug Fixes</li><li>Perf & Stability</li><li>UI Polish</li><li>Tests</li><li>etc.</li></ul>| Push to Microsoft Store at end of week 5 |
|
||||
| 1 week | Release <br/> <ul><li>Available from [Microsoft Store](https://aka.ms/terminal) & [GitHub Releases](https://github.com/microsoft/terminal/releases)</li><li>Release Notes & Announcement Blog published</li><li>Engineering System Maintenance</li><li>Community Engagement</li><li>Docs</li><li>Future Milestone Planning</li></ul> | Release available from Microsoft Store & GitHub Releases |
|
||||
|
||||
## Terminal Roadmap / Timeline
|
||||
@@ -26,12 +25,15 @@ Below is the schedule for when milestones will be included in release builds of
|
||||
| 2020-08-31 | [1.3] in Windows Terminal Preview<br>[1.2] in Windows Terminal | [Windows Terminal Preview 1.3 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-3-release/) |
|
||||
| 2020-09-30 | [1.4] in Windows Terminal Preview<br>[1.3] in Windows Terminal | [Windows Terminal Preview 1.4 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-4-release/) |
|
||||
| 2020-11-30 | [1.5] in Windows Terminal Preview<br>[1.4] in Windows Terminal | [Windows Terminal Preview 1.5 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-5-release/) |
|
||||
| 2020-12-31 | [1.6] in Windows Terminal Preview<br>[1.5] in Windows Terminal | |
|
||||
| 2021-01-31 | 1.7 in Windows Terminal Preview<br>[1.6] in Windows Terminal | |
|
||||
| 2021-02-28 | 1.8 in Windows Terminal Preview<br>1.8 in Windows Terminal | |
|
||||
| 2021-03-31 | 1.9 in Windows Terminal Preview<br>1.9 in Windows Terminal | |
|
||||
| 2021-04-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| 2021-05-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
| 2021-01-31 | [1.6] in Windows Terminal Preview<br>[1.5] in Windows Terminal | [Windows Terminal Preview 1.6 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-6-release/) |
|
||||
| 2021-03-01 | [1.7] in Windows Terminal Preview<br>[1.6] in Windows Terminal | [Windows Terminal Preview 1.7 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-7-release/) |
|
||||
| 2021-04-30 | [1.8] in Windows Terminal Preview<br>[1.7] in Windows Terminal | |
|
||||
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | |
|
||||
| 2021-07-31 | 1.10 in Windows Terminal Preview<br>[1.9] in Windows Terminal | |
|
||||
| 2021-08-30 | 1.11 in Windows Terminal Preview<br>1.10 in Windows Terminal | |
|
||||
| 2021-10-31 | 1.12 in Windows Terminal Preview<br>1.11 in Windows Terminal | |
|
||||
| 2021-11-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| 2021-12-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
|
||||
## Issue Triage & Prioritization
|
||||
|
||||
@@ -84,6 +86,9 @@ Feature Notes:
|
||||
[1.4]: https://github.com/microsoft/terminal/milestone/28
|
||||
[1.5]: https://github.com/microsoft/terminal/milestone/30
|
||||
[1.6]: https://github.com/microsoft/terminal/milestone/31
|
||||
[1.7]: https://github.com/microsoft/terminal/milestone/32
|
||||
[1.8]: https://github.com/microsoft/terminal/milestone/33
|
||||
[1.9]: https://github.com/microsoft/terminal/milestone/34
|
||||
[2.0]: https://github.com/microsoft/terminal/milestone/22
|
||||
[#1564]: https://github.com/microsoft/terminal/issues/1564
|
||||
[#6720]: https://github.com/microsoft/terminal/pull/6720
|
||||
|
||||
BIN
res/terminal/images-Dev/SplashScreen.scale-100.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/terminal/images-Dev/SplashScreen.scale-125.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
BIN
res/terminal/images-Dev/SplashScreen.scale-150.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/terminal/images-Dev/SplashScreen.scale-200.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
BIN
res/terminal/images-Dev/SplashScreen.scale-400.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
BIN
res/terminal/images-Pre/SplashScreen.scale-100.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/terminal/images-Pre/SplashScreen.scale-125.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
BIN
res/terminal/images-Pre/SplashScreen.scale-150.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
BIN
res/terminal/images-Pre/SplashScreen.scale-200.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
BIN
res/terminal/images-Pre/SplashScreen.scale-400.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
BIN
res/terminal/images/SplashScreen.scale-100.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
res/terminal/images/SplashScreen.scale-100_contrast-black.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
res/terminal/images/SplashScreen.scale-100_contrast-white.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
res/terminal/images/SplashScreen.scale-125.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
res/terminal/images/SplashScreen.scale-125_contrast-black.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
res/terminal/images/SplashScreen.scale-125_contrast-white.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
res/terminal/images/SplashScreen.scale-150.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
res/terminal/images/SplashScreen.scale-150_contrast-black.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
res/terminal/images/SplashScreen.scale-150_contrast-white.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
res/terminal/images/SplashScreen.scale-200.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
res/terminal/images/SplashScreen.scale-200_contrast-black.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
res/terminal/images/SplashScreen.scale-200_contrast-white.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
res/terminal/images/SplashScreen.scale-400.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
res/terminal/images/SplashScreen.scale-400_contrast-black.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
res/terminal/images/SplashScreen.scale-400_contrast-white.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
@@ -43,7 +43,6 @@
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Server" Name="1A541C01-589A-496E-85A7-A9E02170166D"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser" Name="c9ba2a84-d3ca-5e19-2bd6-776a0910cb9d"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Render.VtEngine" Name="c9ba2a95-d3ca-5e19-2bd6-776a0910cb9d"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.UIA" Name="e7ebce59-2161-572d-b263-2f16a6afb9e5"/>
|
||||
<!-- Now define some profiles. We'll call them by ID when collecting. Also, the Base is where it is inheriting from and is a .wprpi file built... -->
|
||||
<!-- ... into WPR automatically. Go look in the WPR install directory or in the documentation to find it. -->
|
||||
<Profile Id="ConsolePerfProfile.Verbose.File" Base="GeneralProfile.Light.File" LoggingMode="File" Name="ConsolePerfProfile" DetailLevel="Verbose" Description="Console Performance default profile">
|
||||
@@ -67,7 +66,6 @@
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Server"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Render.VtEngine"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.UIA"/>
|
||||
</EventProviders>
|
||||
</EventCollectorId>
|
||||
</Collectors>
|
||||
|
||||
@@ -4,36 +4,6 @@
|
||||
#include "precomp.h"
|
||||
#include "AttrRow.hpp"
|
||||
|
||||
// Routine Description:
|
||||
// - constructor
|
||||
// Arguments:
|
||||
// - cchRowWidth - the length of the default text attribute
|
||||
// - attr - the default text attribute
|
||||
// Return Value:
|
||||
// - constructed object
|
||||
ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
_list.emplace_back(TextAttributeRun(cchRowWidth, attr));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL_FAST_CAUGHT_EXCEPTION();
|
||||
}
|
||||
_cchRowWidth = cchRowWidth;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets all properties of the ATTR_ROW to default values
|
||||
// Arguments:
|
||||
// - attr - The default text attributes to use on text in this row.
|
||||
void ATTR_ROW::Reset(const TextAttribute attr)
|
||||
{
|
||||
_list.clear();
|
||||
_list.emplace_back(TextAttributeRun(_cchRowWidth, attr));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Takes an existing row of attributes, and changes the length so that it fills the NewWidth.
|
||||
// If the new size is bigger, then the last attr is extended to fill the NewWidth.
|
||||
@@ -45,46 +15,12 @@ void ATTR_ROW::Reset(const TextAttribute attr)
|
||||
// - <none>, throws exceptions on failures.
|
||||
void ATTR_ROW::Resize(const size_t newWidth)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, 0 == newWidth);
|
||||
mybase::resize(gsl::narrow<UINT>(newWidth));
|
||||
}
|
||||
|
||||
// Easy case. If the new row is longer, increase the length of the last run by how much new space there is.
|
||||
if (newWidth > _cchRowWidth)
|
||||
{
|
||||
// Get the attribute that covers the final column of old width.
|
||||
const auto runPos = FindAttrIndex(_cchRowWidth - 1, nullptr);
|
||||
auto& run = _list.at(runPos);
|
||||
|
||||
// Extend its length by the additional columns we're adding.
|
||||
run.SetLength(run.GetLength() + newWidth - _cchRowWidth);
|
||||
|
||||
// Store that the new total width we represent is the new width.
|
||||
_cchRowWidth = newWidth;
|
||||
}
|
||||
// harder case: new row is shorter.
|
||||
else
|
||||
{
|
||||
// Get the attribute that covers the final column of the new width
|
||||
size_t CountOfAttr = 0;
|
||||
const auto runPos = FindAttrIndex(newWidth - 1, &CountOfAttr);
|
||||
auto& run = _list.at(runPos);
|
||||
|
||||
// CountOfAttr was given to us as "how many columns left from this point forward are covered by the returned run"
|
||||
// So if the original run was B5 covering a 5 size OldWidth and we have a NewWidth of 3
|
||||
// then when we called FindAttrIndex, it returned the B5 as the pIndexedRun and a 2 for how many more segments it covers
|
||||
// after and including the 3rd column.
|
||||
// B5-2 = B3, which is what we desire to cover the new 3 size buffer.
|
||||
run.SetLength(run.GetLength() - CountOfAttr + 1);
|
||||
|
||||
// Store that the new total width we represent is the new width.
|
||||
_cchRowWidth = newWidth;
|
||||
|
||||
// Erase segments after the one we just updated.
|
||||
_list.erase(_list.cbegin() + runPos + 1, _list.cend());
|
||||
|
||||
// NOTE: Under some circumstances here, we have leftover run segments in memory or blank run segments
|
||||
// in memory. We're not going to waste time redimensioning the array in the heap. We're just noting that the useful
|
||||
// portions of it have changed.
|
||||
}
|
||||
void ATTR_ROW::Reset(const TextAttribute attr)
|
||||
{
|
||||
mybase::fill(attr);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -97,7 +33,7 @@ void ATTR_ROW::Resize(const size_t newWidth)
|
||||
// - will throw on error
|
||||
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
|
||||
{
|
||||
return GetAttrByColumn(column, nullptr);
|
||||
return mybase::at(gsl::narrow<UINT>(column));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -112,73 +48,10 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
|
||||
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
|
||||
size_t* const pApplies) const
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth);
|
||||
const auto runPos = FindAttrIndex(column, pApplies);
|
||||
return _list.at(runPos).GetAttributes();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - reports how many runs we have stored (to be used for some optimizations
|
||||
// Return Value:
|
||||
// - Count of runs. 1 means we have 1 color to represent the entire row.
|
||||
size_t ATTR_ROW::GetNumberOfRuns() const noexcept
|
||||
{
|
||||
return _list.size();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - This routine finds the nth attribute in this ATTR_ROW.
|
||||
// Arguments:
|
||||
// - index - which attribute to find
|
||||
// - applies - on output, contains corrected length of indexed attr.
|
||||
// for example, if the attribute string was { 5, BLUE } and the requested
|
||||
// index was 3, CountOfAttr would be 2.
|
||||
// Return Value:
|
||||
// - const reference to attribute run object
|
||||
size_t ATTR_ROW::FindAttrIndex(const size_t index, size_t* const pApplies) const
|
||||
{
|
||||
FAIL_FAST_IF(!(index < _cchRowWidth)); // The requested index cannot be longer than the total length described by this set of Attrs.
|
||||
|
||||
size_t cTotalLength = 0;
|
||||
|
||||
FAIL_FAST_IF(!(_list.size() > 0)); // There should be a non-zero and positive number of items in the array.
|
||||
|
||||
// Scan through the internal array from position 0 adding up the lengths that each attribute applies to
|
||||
auto runPos = _list.cbegin();
|
||||
do
|
||||
{
|
||||
cTotalLength += runPos->GetLength();
|
||||
|
||||
if (cTotalLength > index)
|
||||
{
|
||||
// If we've just passed up the requested index with the length we added, break early
|
||||
break;
|
||||
}
|
||||
|
||||
runPos++;
|
||||
} while (runPos < _list.cend());
|
||||
|
||||
// we should have broken before falling out the while case.
|
||||
// if we didn't break, then this ATTR_ROW wasn't filled with enough attributes for the entire row of characters
|
||||
FAIL_FAST_IF(runPos >= _list.cend());
|
||||
|
||||
// The remaining iterator position is the position of the attribute that is applicable at the position requested (index)
|
||||
// Calculate its remaining applicability if requested
|
||||
|
||||
// The length on which the found attribute applies is the total length seen so far minus the index we were searching for.
|
||||
FAIL_FAST_IF(!(cTotalLength > index)); // The length of all attributes we counted up so far should be longer than the index requested or we'll underflow.
|
||||
|
||||
if (nullptr != pApplies)
|
||||
{
|
||||
const auto attrApplies = cTotalLength - index;
|
||||
FAIL_FAST_IF(!(attrApplies > 0)); // An attribute applies for >0 characters
|
||||
// MSFT: 17130145 - will restore this and add a better assert to catch the real issue.
|
||||
//FAIL_FAST_IF(!(attrApplies <= _cchRowWidth)); // An attribute applies for a maximum of the total length available to us
|
||||
|
||||
*pApplies = attrApplies;
|
||||
}
|
||||
|
||||
return runPos - _list.cbegin();
|
||||
UINT applies = 0;
|
||||
const auto attr = mybase::at(gsl::narrow<UINT>(column), applies);
|
||||
*pApplies = applies;
|
||||
return attr;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -188,11 +61,11 @@ size_t ATTR_ROW::FindAttrIndex(const size_t index, size_t* const pApplies) const
|
||||
std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
|
||||
{
|
||||
std::vector<uint16_t> ids;
|
||||
for (const auto& run : _list)
|
||||
for (const auto& run : *this)
|
||||
{
|
||||
if (run.GetAttributes().IsHyperlink())
|
||||
if (run.IsHyperlink())
|
||||
{
|
||||
ids.emplace_back(run.GetAttributes().GetHyperlinkId());
|
||||
ids.emplace_back(run.GetHyperlinkId());
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
@@ -207,10 +80,8 @@ std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
|
||||
// - <none>
|
||||
bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
|
||||
{
|
||||
size_t const length = _cchRowWidth - iStart;
|
||||
|
||||
const TextAttributeRun run(length, attr);
|
||||
return SUCCEEDED(InsertAttrRuns({ &run, 1 }, iStart, _cchRowWidth - 1, _cchRowWidth));
|
||||
mybase::fill(attr, iStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -223,13 +94,7 @@ bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
|
||||
// - <none>
|
||||
void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept
|
||||
{
|
||||
for (auto& run : _list)
|
||||
{
|
||||
if (run.GetAttributes() == toBeReplacedAttr)
|
||||
{
|
||||
run.SetAttributes(replaceWith);
|
||||
}
|
||||
}
|
||||
mybase::replace(toBeReplacedAttr, replaceWith);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -248,392 +113,12 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
// otherwise STATUS_SUCCESS if we were successful.
|
||||
[[nodiscard]] HRESULT ATTR_ROW::InsertAttrRuns(const gsl::span<const TextAttributeRun> newAttrs,
|
||||
const size_t iStart,
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth)
|
||||
const size_t /*iEnd*/,
|
||||
const size_t /*cBufferWidth*/)
|
||||
try
|
||||
{
|
||||
// Definitions:
|
||||
// Existing Run = The run length encoded color array we're already storing in memory before this was called.
|
||||
// Insert Run = The run length encoded color array that someone is asking us to inject into our stored memory run.
|
||||
// New Run = The run length encoded color array that we have to allocate and rebuild to store internally
|
||||
// which will replace Existing Run at the end of this function.
|
||||
// Example:
|
||||
// cBufferWidth = 10.
|
||||
// Existing Run: R3 -> G5 -> B2
|
||||
// Insert Run: Y1 -> N1 at iStart = 5 and iEnd = 6
|
||||
// (rgInsertAttrs is a 2 length array with Y1->N1 in it and cInsertAttrs = 2)
|
||||
// Final Run: R3 -> G2 -> Y1 -> N1 -> G1 -> B2
|
||||
|
||||
// We'll need to know what the last valid column is for some calculations versus iEnd
|
||||
// because iEnd is specified to us as an inclusive index value.
|
||||
// Do the -1 math here now so we don't have to have -1s scattered all over this function.
|
||||
const size_t iLastBufferCol = cBufferWidth - 1;
|
||||
|
||||
// If the insertion size is 1, do some pre-processing to
|
||||
// see if we can get this done quickly.
|
||||
if (newAttrs.size() == 1)
|
||||
{
|
||||
// Get the new color attribute we're trying to apply
|
||||
const TextAttribute NewAttr = til::at(newAttrs, 0).GetAttributes();
|
||||
|
||||
// If the existing run was only 1 element...
|
||||
// ...and the new color is the same as the old, we don't have to do anything and can exit quick.
|
||||
if (_list.size() == 1 && _list.at(0).GetAttributes() == NewAttr)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
// .. otherwise if we internally have a list of 2 or more and we're about to insert a single color
|
||||
// it's possible that we just walk left-to-right through the row and find a quick exit.
|
||||
else if (iStart >= 0 && iStart == iEnd)
|
||||
{
|
||||
// First we try to find the run where the insertion happens, using lowerBound and upperBound to track
|
||||
// where we are currently at.
|
||||
const auto begin = _list.begin();
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = 0;
|
||||
for (size_t i = 0; i < _list.size(); i++)
|
||||
{
|
||||
const auto curr = begin + i;
|
||||
upperBound += curr->GetLength();
|
||||
|
||||
if (iStart >= lowerBound && iStart < upperBound)
|
||||
{
|
||||
// The run that we try to insert into has the same color as the new one.
|
||||
// e.g.
|
||||
// AAAAABBBBBBBCCC
|
||||
// ^
|
||||
// AAAAABBBBBBBCCC
|
||||
//
|
||||
// 'B' is the new color and '^' represents where iStart is. We don't have to
|
||||
// do anything.
|
||||
if (curr->GetAttributes() == NewAttr)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// If the current run has length of exactly one, we can simply change the attribute
|
||||
// of the current run.
|
||||
// e.g.
|
||||
// AAAAABCCCCCCCCC
|
||||
// ^
|
||||
// AAAAADCCCCCCCCC
|
||||
//
|
||||
// Here 'D' is the new color.
|
||||
if (curr->GetLength() == 1)
|
||||
{
|
||||
curr->SetAttributes(NewAttr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// If the insertion happens at current run's lower boundary...
|
||||
if (iStart == lowerBound && i > 0)
|
||||
{
|
||||
const auto prev = std::prev(curr, 1);
|
||||
// ... and the previous run has the same color as the new one, we can
|
||||
// just adjust the counts in the existing two elements in our internal list.
|
||||
// e.g.
|
||||
// AAAAABBBBBBBCCC
|
||||
// ^
|
||||
// AAAAAABBBBBBCCC
|
||||
//
|
||||
// Here 'A' is the new color.
|
||||
if (NewAttr == prev->GetAttributes())
|
||||
{
|
||||
prev->IncrementLength();
|
||||
curr->DecrementLength();
|
||||
|
||||
// If we just reduced the right half to zero, just erase it out of the list.
|
||||
if (curr->GetLength() == 0)
|
||||
{
|
||||
_list.erase(curr);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If the insertion happens at current run's upper boundary...
|
||||
if (iStart == upperBound - 1 && i + 1 < _list.size())
|
||||
{
|
||||
// ...then let's try our luck with the next run if possible. This is basically the opposite
|
||||
// of what we did with the previous run.
|
||||
// e.g.
|
||||
// AAAAAABBBBBBCCC
|
||||
// ^
|
||||
// AAAAABBBBBBBCCC
|
||||
//
|
||||
// Here 'B' is the new color.
|
||||
const auto next = std::next(curr, 1);
|
||||
if (NewAttr == next->GetAttributes())
|
||||
{
|
||||
curr->DecrementLength();
|
||||
next->IncrementLength();
|
||||
|
||||
if (curr->GetLength() == 0)
|
||||
{
|
||||
_list.erase(curr);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Advance one run in the _list.
|
||||
lowerBound = upperBound;
|
||||
|
||||
// The lowerBound is larger than iStart, which means we fail to find an early exit at the run
|
||||
// where the insertion happens. We can just break out.
|
||||
if (lowerBound > iStart)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we're about to cover the entire existing run with a new one, we can also make an optimization.
|
||||
if (iStart == 0 && iEnd == iLastBufferCol)
|
||||
{
|
||||
// Just dump what we're given over what we have and call it a day.
|
||||
_list.assign(newAttrs.begin(), newAttrs.end());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// In the worst case scenario, we will need a new run that is the length of
|
||||
// The existing run in memory + The new run in memory + 1.
|
||||
// This worst case occurs when we inject a new item in the middle of an existing run like so
|
||||
// Existing R3->B5->G2, Insertion Y2 starting at 5 (in the middle of the B5)
|
||||
// becomes R3->B2->Y2->B1->G2.
|
||||
// The original run was 3 long. The insertion run was 1 long. We need 1 more for the
|
||||
// fact that an existing piece of the run was split in half (to hold the latter half).
|
||||
const size_t cNewRun = _list.size() + newAttrs.size() + 1;
|
||||
decltype(_list) newRun;
|
||||
newRun.reserve(cNewRun);
|
||||
|
||||
// We will start analyzing from the beginning of our existing run.
|
||||
// Use some pointers to keep track of where we are in walking through our runs.
|
||||
|
||||
// Get the existing run that we'll be updating/manipulating.
|
||||
const auto existingRun = _list.begin();
|
||||
auto pExistingRunPos = existingRun;
|
||||
const auto pExistingRunEnd = _list.end();
|
||||
auto pInsertRunPos = newAttrs.begin();
|
||||
size_t cInsertRunRemaining = newAttrs.size();
|
||||
size_t iExistingRunCoverage = 0;
|
||||
|
||||
// Copy the existing run into the new buffer up to the "start index" where the new run will be injected.
|
||||
// If the new run starts at 0, we have nothing to copy from the beginning.
|
||||
if (iStart != 0)
|
||||
{
|
||||
// While we're less than the desired insertion position...
|
||||
while (iExistingRunCoverage < iStart)
|
||||
{
|
||||
// Add up how much length we can cover by copying an item from the existing run.
|
||||
iExistingRunCoverage += pExistingRunPos->GetLength();
|
||||
|
||||
// Copy it to the new run buffer and advance both pointers.
|
||||
newRun.push_back(*pExistingRunPos++);
|
||||
}
|
||||
|
||||
// When we get to this point, we've copied full segments from the original existing run
|
||||
// into our new run buffer. We will have 1 or more full segments of color attributes and
|
||||
// we MIGHT have to cut the last copied segment's length back depending on where the inserted
|
||||
// attributes will fall in the final/new run.
|
||||
// Some examples:
|
||||
// - Starting with the original string R3 -> G5 -> B2
|
||||
// - 1. If the insertion is Y5 at start index 3
|
||||
// We are trying to get a result/final/new run of R3 -> Y5 -> B2.
|
||||
// We just copied R3 to the new destination buffer and we cang skip down and start inserting the new attrs.
|
||||
// - 2. If the insertion is Y3 at start index 5
|
||||
// We are trying to get a result/final/new run of R3 -> G2 -> Y3 -> B2.
|
||||
// We just copied R3 -> G5 to the new destination buffer with the code above.
|
||||
// But the insertion is going to cut out some of the length of the G5.
|
||||
// We need to fix this up below so it says G2 instead to leave room for the Y3 to fit in
|
||||
// the new/final run.
|
||||
|
||||
// Fetch out the length so we can fix it up based on the below conditions.
|
||||
size_t length = newRun.back().GetLength();
|
||||
|
||||
// If we've covered more cells already than the start of the attributes to be inserted...
|
||||
if (iExistingRunCoverage > iStart)
|
||||
{
|
||||
// ..then subtract some of the length of the final cell we copied.
|
||||
// We want to take remove the difference in distance between the cells we've covered in the new
|
||||
// run and the insertion point.
|
||||
// (This turns G5 into G2 from Example 2 just above)
|
||||
length -= (iExistingRunCoverage - iStart);
|
||||
}
|
||||
|
||||
// Now we're still on that "last cell copied" into the new run.
|
||||
// If the color of that existing copied cell matches the color of the first segment
|
||||
// of the run we're about to insert, we can just increment the length to extend the coverage.
|
||||
if (newRun.back().GetAttributes() == pInsertRunPos->GetAttributes())
|
||||
{
|
||||
length += pInsertRunPos->GetLength();
|
||||
|
||||
// Since the color matched, we have already "used up" part of the insert run
|
||||
// and can skip it in our big "memcopy" step below that will copy the bulk of the insert run.
|
||||
cInsertRunRemaining--;
|
||||
pInsertRunPos++;
|
||||
}
|
||||
|
||||
// We're done manipulating the length. Store it back.
|
||||
newRun.back().SetLength(length);
|
||||
}
|
||||
|
||||
// Bulk copy the majority (or all, depending on circumstance) of the insert run into the final run buffer.
|
||||
std::copy_n(pInsertRunPos, cInsertRunRemaining, std::back_inserter(newRun));
|
||||
|
||||
// We're technically done with the insert run now and have 0 remaining, but won't bother updating its pointers
|
||||
// and counts any further because we won't use them.
|
||||
|
||||
// Now we need to move our pointer for the original existing run forward and update our counts
|
||||
// on how many cells we could have copied from the source before finishing off the new run.
|
||||
while (iExistingRunCoverage <= iEnd)
|
||||
{
|
||||
FAIL_FAST_IF(!(pExistingRunPos != pExistingRunEnd));
|
||||
iExistingRunCoverage += pExistingRunPos->GetLength();
|
||||
pExistingRunPos++;
|
||||
}
|
||||
|
||||
// If we still have original existing run cells remaining, copy them into the final new run.
|
||||
if (pExistingRunPos != pExistingRunEnd || iExistingRunCoverage != (iEnd + 1))
|
||||
{
|
||||
// We advanced the existing run pointer and its count to on or past the end of what the insertion run filled in.
|
||||
// If this ended up being past the end of what the insertion run covers, we have to account for the cells after
|
||||
// the insertion run but before the next piece of the original existing run.
|
||||
// The example in this case is if we had...
|
||||
// Existing Run = R3 -> G5 -> B2 -> X5
|
||||
// Insert Run = Y2 @ iStart = 7 and iEnd = 8
|
||||
// ... then at this point in time, our states would look like...
|
||||
// New Run so far = R3 -> G4 -> Y2
|
||||
// Existing Run Pointer is at X5
|
||||
// Existing run coverage count at 3 + 5 + 2 = 10.
|
||||
// However, in order to get the final desired New Run
|
||||
// (which is R3 -> G4 -> Y2 -> B1 -> X5)
|
||||
// we would need to grab a piece of that B2 we already skipped past.
|
||||
// iExistingRunCoverage = 10. iEnd = 8. iEnd+1 = 9. 10 > 9. So we skipped something.
|
||||
if (iExistingRunCoverage > (iEnd + 1))
|
||||
{
|
||||
// Back up the existing run pointer so we can grab the piece we skipped.
|
||||
pExistingRunPos--;
|
||||
|
||||
// If the color matches what's already in our run, just increment the count value.
|
||||
// This case is slightly off from the example above. This case is for if the B2 above was actually Y2.
|
||||
// That Y2 from the existing run is the same color as the Y2 we just filled a few columns left in the final run
|
||||
// so we can just adjust the final run's column count instead of adding another segment here.
|
||||
if (newRun.back().GetAttributes() == pExistingRunPos->GetAttributes())
|
||||
{
|
||||
size_t length = newRun.back().GetLength();
|
||||
length += (iExistingRunCoverage - (iEnd + 1));
|
||||
newRun.back().SetLength(length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the color didn't match, then we just need to copy the piece we skipped and adjust
|
||||
// its length for the discrepancy in columns not yet covered by the final/new run.
|
||||
|
||||
// Move forward to a blank spot in the new run
|
||||
newRun.emplace_back();
|
||||
|
||||
// Copy the existing run's color information to the new run
|
||||
newRun.back().SetAttributes(pExistingRunPos->GetAttributes());
|
||||
|
||||
// Adjust the length of that copied color to cover only the reduced number of columns needed
|
||||
// now that some have been replaced by the insert run.
|
||||
newRun.back().SetLength(iExistingRunCoverage - (iEnd + 1));
|
||||
}
|
||||
|
||||
// Now that we're done recovering a piece of the existing run we skipped, move the pointer forward again.
|
||||
pExistingRunPos++;
|
||||
}
|
||||
|
||||
// OK. In this case, we didn't skip anything. The end of the insert run fell right at a boundary
|
||||
// in columns that was in the original existing run.
|
||||
// However, the next piece of the original existing run might happen to have the same color attribute
|
||||
// as the final piece of what we just copied.
|
||||
// As an example...
|
||||
// Existing Run = R3 -> G5 -> B2.
|
||||
// Insert Run = B5 @ iStart = 3 and iEnd = 7
|
||||
// New Run so far = R3 -> B5
|
||||
// New Run desired when done = R3 -> B7
|
||||
// Existing run pointer is on B2.
|
||||
// We want to merge the 2 from the B2 into the B5 so we get B7.
|
||||
else if (newRun.back().GetAttributes() == pExistingRunPos->GetAttributes())
|
||||
{
|
||||
// Add the value from the existing run into the current new run position.
|
||||
size_t length = newRun.back().GetLength();
|
||||
length += pExistingRunPos->GetLength();
|
||||
newRun.back().SetLength(length);
|
||||
|
||||
// Advance the existing run position since we consumed its value and merged it in.
|
||||
pExistingRunPos++;
|
||||
}
|
||||
|
||||
// Now bulk copy any segments left in the original existing run
|
||||
if (pExistingRunPos < pExistingRunEnd)
|
||||
{
|
||||
std::copy_n(pExistingRunPos, (pExistingRunEnd - pExistingRunPos), std::back_inserter(newRun));
|
||||
}
|
||||
}
|
||||
|
||||
// OK, phew. We're done. Now we just need to free the existing run and store the new run in its place.
|
||||
_list.swap(newRun);
|
||||
mybase::replace(iStart, mybase::npos, newAttrs.begin(), newAttrs.end());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - packs a vector of TextAttribute into a vector of TextAttributeRun
|
||||
// Arguments:
|
||||
// - attrs - text attributes to pack
|
||||
// Return Value:
|
||||
// - packed text attribute run
|
||||
std::vector<TextAttributeRun> ATTR_ROW::PackAttrs(const std::vector<TextAttribute>& attrs)
|
||||
{
|
||||
std::vector<TextAttributeRun> runs;
|
||||
if (attrs.empty())
|
||||
{
|
||||
return runs;
|
||||
}
|
||||
for (auto attr : attrs)
|
||||
{
|
||||
if (runs.empty() || runs.back().GetAttributes() != attr)
|
||||
{
|
||||
runs.emplace_back(TextAttributeRun(1, attr));
|
||||
}
|
||||
else
|
||||
{
|
||||
runs.back().SetLength(runs.back().GetLength() + 1);
|
||||
}
|
||||
}
|
||||
return runs;
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::begin() const noexcept
|
||||
{
|
||||
return AttrRowIterator(this);
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::end() const noexcept
|
||||
{
|
||||
return AttrRowIterator::CreateEndIterator(this);
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::cbegin() const noexcept
|
||||
{
|
||||
return AttrRowIterator(this);
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::cend() const noexcept
|
||||
{
|
||||
return AttrRowIterator::CreateEndIterator(this);
|
||||
}
|
||||
|
||||
bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept
|
||||
{
|
||||
return (a._list.size() == b._list.size() &&
|
||||
a._list.data() == b._list.data() &&
|
||||
a._cchRowWidth == b._cchRowWidth);
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
@@ -20,37 +20,27 @@ Revision History:
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TextAttributeRun.hpp"
|
||||
#include "AttrRowIterator.hpp"
|
||||
#include "til/rle.h"
|
||||
|
||||
class ATTR_ROW final
|
||||
#include "TextAttributeRun.hpp"
|
||||
|
||||
class ATTR_ROW final : public til::rle<TextAttribute, unsigned int>
|
||||
{
|
||||
public:
|
||||
using const_iterator = typename AttrRowIterator;
|
||||
using mybase = til::rle<TextAttribute, unsigned int>;
|
||||
|
||||
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
||||
noexcept;
|
||||
using const_iterator = mybase::const_iterator;
|
||||
using const_reverse_iterator = mybase::const_reverse_iterator;
|
||||
|
||||
~ATTR_ROW() = default;
|
||||
|
||||
ATTR_ROW(const ATTR_ROW&) = default;
|
||||
ATTR_ROW& operator=(const ATTR_ROW&) = default;
|
||||
ATTR_ROW(ATTR_ROW&&)
|
||||
noexcept = default;
|
||||
ATTR_ROW& operator=(ATTR_ROW&&) noexcept = default;
|
||||
using mybase::mybase; // use base constructor
|
||||
|
||||
TextAttribute GetAttrByColumn(const size_t column) const;
|
||||
TextAttribute GetAttrByColumn(const size_t column,
|
||||
size_t* const pApplies) const;
|
||||
|
||||
size_t GetNumberOfRuns() const noexcept;
|
||||
|
||||
size_t FindAttrIndex(const size_t index,
|
||||
size_t* const pApplies) const;
|
||||
|
||||
std::vector<uint16_t> GetHyperlinks();
|
||||
|
||||
bool SetAttrToEnd(const UINT iStart, const TextAttribute attr);
|
||||
bool SetAttrToEnd(const unsigned int iStart, const TextAttribute attr);
|
||||
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept;
|
||||
|
||||
void Resize(const size_t newWidth);
|
||||
@@ -60,26 +50,19 @@ public:
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth);
|
||||
|
||||
static std::vector<TextAttributeRun> PackAttrs(const std::vector<TextAttribute>& attrs);
|
||||
using mybase::begin;
|
||||
using mybase::cbegin;
|
||||
using mybase::cend;
|
||||
using mybase::end;
|
||||
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
using mybase::operator==;
|
||||
|
||||
const_iterator cbegin() const noexcept;
|
||||
const_iterator cend() const noexcept;
|
||||
|
||||
friend bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept;
|
||||
friend class AttrRowIterator;
|
||||
friend class ROW;
|
||||
|
||||
private:
|
||||
void Reset(const TextAttribute attr);
|
||||
|
||||
boost::container::small_vector<TextAttributeRun, 1> _list;
|
||||
size_t _cchRowWidth;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class AttrRowTests;
|
||||
friend class CommonState;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -22,29 +22,23 @@ Revision History:
|
||||
|
||||
#include "TextAttribute.hpp"
|
||||
|
||||
class TextAttributeRun final
|
||||
class TextAttributeRun final : public std::pair<TextAttribute, unsigned int>
|
||||
{
|
||||
public:
|
||||
TextAttributeRun() = default;
|
||||
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
|
||||
_cchLength(gsl::narrow<unsigned int>(cchLength))
|
||||
using mybase = std::pair<TextAttribute, unsigned int>;
|
||||
|
||||
using mybase::mybase;
|
||||
|
||||
TextAttributeRun(const size_t cchLength, const TextAttribute attr) :
|
||||
mybase(attr, gsl::narrow<unsigned int>(cchLength))
|
||||
{
|
||||
SetAttributes(attr);
|
||||
}
|
||||
|
||||
size_t GetLength() const noexcept { return _cchLength; }
|
||||
void SetLength(const size_t cchLength) noexcept { _cchLength = gsl::narrow<unsigned int>(cchLength); }
|
||||
void IncrementLength() noexcept { _cchLength++; }
|
||||
void DecrementLength() noexcept { _cchLength--; }
|
||||
size_t GetLength() const noexcept { return mybase::second; }
|
||||
void SetLength(const size_t cchLength) noexcept { mybase::second = gsl::narrow<unsigned int>(cchLength); }
|
||||
void IncrementLength() noexcept { mybase::second++; }
|
||||
void DecrementLength() noexcept { mybase::second--; }
|
||||
|
||||
const TextAttribute& GetAttributes() const noexcept { return _attributes; }
|
||||
void SetAttributes(const TextAttribute textAttribute) noexcept { _attributes = textAttribute; }
|
||||
|
||||
private:
|
||||
unsigned int _cchLength{ 0 };
|
||||
TextAttribute _attributes{ 0 };
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class AttrRowTests;
|
||||
#endif
|
||||
const TextAttribute& GetAttributes() const noexcept { return mybase::first; }
|
||||
void SetAttributes(const TextAttribute textAttribute) noexcept { mybase::first = textAttribute; }
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\AttrRow.cpp" />
|
||||
<ClCompile Include="..\AttrRowIterator.cpp" />
|
||||
<ClCompile Include="..\cursor.cpp" />
|
||||
<ClCompile Include="..\OutputCell.cpp" />
|
||||
<ClCompile Include="..\OutputCellIterator.cpp" />
|
||||
@@ -34,7 +33,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\AttrRow.hpp" />
|
||||
<ClInclude Include="..\AttrRowIterator.hpp" />
|
||||
<ClInclude Include="..\cursor.h" />
|
||||
<ClInclude Include="..\DbcsAttribute.hpp" />
|
||||
<ClInclude Include="..\ICharRow.hpp" />
|
||||
|
||||
@@ -30,7 +30,6 @@ PRECOMPILED_INCLUDE = ..\precomp.h
|
||||
|
||||
SOURCES= \
|
||||
..\AttrRow.cpp \
|
||||
..\AttrRowIterator.cpp \
|
||||
..\cursor.cpp \
|
||||
..\OutputCell.cpp \
|
||||
..\OutputCellIterator.cpp \
|
||||
|
||||
@@ -281,9 +281,9 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
|
||||
// - false otherwise (out of memory)
|
||||
bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute)
|
||||
{
|
||||
// This function corrects most errors. If this is false, we had an uncorrectable one which
|
||||
// older versions of conhost simply let pass by unflinching.
|
||||
LOG_HR_IF(E_NOT_VALID_STATE, !(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
|
||||
// Assert the buffer state is ready for this character
|
||||
// This function corrects most errors. If this is false, we had an uncorrectable one.
|
||||
FAIL_FAST_IF(!(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
|
||||
|
||||
bool fSuccess = true;
|
||||
// Now compensate if we don't have enough space for the upcoming double byte sequence
|
||||
|
||||
@@ -15,8 +15,8 @@ Author(s):
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AttrRowIterator.hpp"
|
||||
#include "CharRow.hpp"
|
||||
#include "AttrRow.hpp"
|
||||
#include "OutputCellView.hpp"
|
||||
#include "../../types/inc/viewport.hpp"
|
||||
|
||||
@@ -55,7 +55,7 @@ protected:
|
||||
OutputCellView _view;
|
||||
|
||||
const ROW* _pRow;
|
||||
AttrRowIterator _attrIter;
|
||||
ATTR_ROW::const_iterator _attrIter;
|
||||
const TextBuffer& _buffer;
|
||||
const Microsoft::Console::Types::Viewport _bounds;
|
||||
bool _exceeded;
|
||||
|
||||
@@ -1,731 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "WexTestClass.h"
|
||||
#include "../../../inc/consoletaeftemplates.hpp"
|
||||
|
||||
#include "../textBuffer.hpp"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
namespace WEX
|
||||
{
|
||||
namespace TestExecution
|
||||
{
|
||||
template<>
|
||||
class VerifyOutputTraits<TextAttributeRun>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const TextAttributeRun& tar)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(
|
||||
L"Length:%d, attr:%s",
|
||||
tar.GetLength(),
|
||||
VerifyOutputTraits<TextAttribute>::ToString(tar.GetAttributes()).GetBuffer());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class VerifyCompareTraits<TextAttributeRun, TextAttributeRun>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const TextAttributeRun& expected, const TextAttributeRun& actual)
|
||||
{
|
||||
return expected.GetAttributes() == actual.GetAttributes() &&
|
||||
expected.GetLength() == actual.GetLength();
|
||||
}
|
||||
|
||||
static bool AreSame(const TextAttributeRun& expected, const TextAttributeRun& actual)
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
|
||||
|
||||
static bool IsGreaterThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
|
||||
|
||||
static bool IsNull(const TextAttributeRun& object)
|
||||
{
|
||||
return object.GetAttributes().IsLegacy() && object.GetAttributes().GetLegacyAttributes() == 0 &&
|
||||
object.GetLength() == 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class AttrRowTests
|
||||
{
|
||||
ATTR_ROW* pSingle;
|
||||
ATTR_ROW* pChain;
|
||||
|
||||
short _sDefaultLength = 80;
|
||||
short _sDefaultChainLength = 6;
|
||||
|
||||
short sChainSegLength;
|
||||
short sChainLeftover;
|
||||
short sChainSegmentsNeeded;
|
||||
|
||||
WORD __wDefaultAttr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
|
||||
WORD __wDefaultChainAttr = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
|
||||
TextAttribute _DefaultAttr = TextAttribute(__wDefaultAttr);
|
||||
TextAttribute _DefaultChainAttr = TextAttribute(__wDefaultChainAttr);
|
||||
|
||||
TEST_CLASS(AttrRowTests);
|
||||
|
||||
TEST_METHOD_SETUP(MethodSetup)
|
||||
{
|
||||
pSingle = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
|
||||
|
||||
// Segment length is the expected length divided by the row length
|
||||
// E.g. row of 80, 4 segments, 20 segment length each
|
||||
sChainSegLength = _sDefaultLength / _sDefaultChainLength;
|
||||
|
||||
// Leftover is spaces that don't fit evenly
|
||||
// E.g. row of 81, 4 segments, 1 leftover length segment
|
||||
sChainLeftover = _sDefaultLength % _sDefaultChainLength;
|
||||
|
||||
// Start with the number of segments we expect
|
||||
sChainSegmentsNeeded = _sDefaultChainLength;
|
||||
|
||||
// If we had a remainder, add one more segment
|
||||
if (sChainLeftover)
|
||||
{
|
||||
sChainSegmentsNeeded++;
|
||||
}
|
||||
|
||||
// Create the chain
|
||||
pChain = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
|
||||
pChain->_list.resize(sChainSegmentsNeeded);
|
||||
|
||||
// Attach all chain segments that are even multiples of the row length
|
||||
for (short iChain = 0; iChain < _sDefaultChainLength; iChain++)
|
||||
{
|
||||
TextAttributeRun* pRun = &pChain->_list[iChain];
|
||||
|
||||
pRun->SetAttributes(TextAttribute{ gsl::narrow_cast<WORD>(iChain) }); // Just use the chain position as the value
|
||||
pRun->SetLength(sChainSegLength);
|
||||
}
|
||||
|
||||
if (sChainLeftover > 0)
|
||||
{
|
||||
// If we had a leftover, then this chain is one longer than we expected (the default length)
|
||||
// So use it as the index (because indices start at 0)
|
||||
TextAttributeRun* pRun = &pChain->_list[_sDefaultChainLength];
|
||||
|
||||
pRun->SetAttributes(_DefaultChainAttr);
|
||||
pRun->SetLength(sChainLeftover);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_METHOD_CLEANUP(MethodCleanup)
|
||||
{
|
||||
delete pSingle;
|
||||
|
||||
delete pChain;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_METHOD(TestInitialize)
|
||||
{
|
||||
// Properties needed for test
|
||||
const WORD wAttr = FOREGROUND_RED | BACKGROUND_BLUE;
|
||||
TextAttribute attr = TextAttribute(wAttr);
|
||||
// Cases to test
|
||||
ATTR_ROW* pTestItems[]{ pSingle, pChain };
|
||||
|
||||
// Loop cases
|
||||
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
|
||||
{
|
||||
ATTR_ROW* pUnderTest = pTestItems[iIndex];
|
||||
|
||||
pUnderTest->Reset(attr);
|
||||
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), attr);
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Packs an array of words representing attributes into the more compact storage form used by the row.
|
||||
// Arguments:
|
||||
// - rgAttrs - Array of words representing the attribute associated with each character position in the row.
|
||||
// - cRowLength - Length of preceding array.
|
||||
// - outAttrRun - reference to unique_ptr that will contain packed attr run on success.
|
||||
// Return Value:
|
||||
// - Success if success. Buffer too small if row length is incorrect.
|
||||
HRESULT PackAttrs(_In_reads_(cRowLength) const TextAttribute* const rgAttrs,
|
||||
const size_t cRowLength,
|
||||
_Inout_ std::unique_ptr<TextAttributeRun[]>& outAttrRun,
|
||||
_Out_ size_t* const cOutAttrRun)
|
||||
{
|
||||
RETURN_HR_IF(E_NOT_SUFFICIENT_BUFFER, cRowLength == 0);
|
||||
|
||||
// first count up the deltas in the array
|
||||
size_t cDeltas = 1;
|
||||
|
||||
const TextAttribute* pPrevAttr = &rgAttrs[0];
|
||||
|
||||
for (size_t i = 1; i < cRowLength; i++)
|
||||
{
|
||||
const TextAttribute* pCurAttr = &rgAttrs[i];
|
||||
|
||||
if (*pCurAttr != *pPrevAttr)
|
||||
{
|
||||
cDeltas++;
|
||||
}
|
||||
|
||||
pPrevAttr = pCurAttr;
|
||||
}
|
||||
|
||||
// This whole situation was too complicated with a one off holder for one row run
|
||||
// new method:
|
||||
// delete the old buffer
|
||||
// make a new buffer, one run + one run for each change
|
||||
// set the values for each run one run index at a time
|
||||
|
||||
std::unique_ptr<TextAttributeRun[]> attrRun = std::make_unique<TextAttributeRun[]>(cDeltas);
|
||||
RETURN_HR_IF_NULL(E_OUTOFMEMORY, attrRun);
|
||||
|
||||
TextAttributeRun* pCurrentRun = attrRun.get();
|
||||
pCurrentRun->SetAttributes(rgAttrs[0]);
|
||||
pCurrentRun->SetLength(1);
|
||||
for (size_t i = 1; i < cRowLength; i++)
|
||||
{
|
||||
if (pCurrentRun->GetAttributes() == rgAttrs[i])
|
||||
{
|
||||
pCurrentRun->SetLength(pCurrentRun->GetLength() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCurrentRun++;
|
||||
pCurrentRun->SetAttributes(rgAttrs[i]);
|
||||
pCurrentRun->SetLength(1);
|
||||
}
|
||||
}
|
||||
attrRun.swap(outAttrRun);
|
||||
*cOutAttrRun = cDeltas;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
NoThrowString LogRunElement(_In_ TextAttributeRun& run)
|
||||
{
|
||||
return NoThrowString().Format(L"%wc%d", run.GetAttributes().GetLegacyAttributes(), run.GetLength());
|
||||
}
|
||||
|
||||
void LogChain(_In_ PCWSTR pwszPrefix,
|
||||
boost::container::small_vector_base<TextAttributeRun>& chain)
|
||||
{
|
||||
NoThrowString str(pwszPrefix);
|
||||
|
||||
if (chain.size() > 0)
|
||||
{
|
||||
str.Append(LogRunElement(chain[0]));
|
||||
|
||||
for (size_t i = 1; i < chain.size(); i++)
|
||||
{
|
||||
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(str);
|
||||
}
|
||||
|
||||
void LogChain(_In_ PCWSTR pwszPrefix,
|
||||
std::vector<TextAttributeRun>& chain)
|
||||
{
|
||||
NoThrowString str(pwszPrefix);
|
||||
|
||||
if (chain.size() > 0)
|
||||
{
|
||||
str.Append(LogRunElement(chain[0]));
|
||||
|
||||
for (size_t i = 1; i < chain.size(); i++)
|
||||
{
|
||||
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(str);
|
||||
}
|
||||
|
||||
void DoTestInsertAttrRuns(UINT& uiStartPos, WORD& ch1, UINT& uiChar1Length, WORD& ch2, UINT& uiChar2Length)
|
||||
{
|
||||
Log::Comment(String().Format(L"StartPos: %d, Char1: %wc, Char1Length: %d, Char2: %wc, Char2Length: %d",
|
||||
uiStartPos,
|
||||
ch1,
|
||||
uiChar1Length,
|
||||
ch2,
|
||||
uiChar2Length));
|
||||
|
||||
bool const fUseStr2 = (ch2 != L'0');
|
||||
|
||||
// Set up our "original row" that we are going to try to insert into.
|
||||
// This will represent a 10 column run of R3->B5->G2 that we will use for all tests.
|
||||
ATTR_ROW originalRow{ static_cast<UINT>(_sDefaultLength), _DefaultAttr };
|
||||
originalRow._list.resize(3);
|
||||
originalRow._cchRowWidth = 10;
|
||||
originalRow._list[0].SetAttributes(TextAttribute{ 'R' });
|
||||
originalRow._list[0].SetLength(3);
|
||||
originalRow._list[1].SetAttributes(TextAttribute{ 'B' });
|
||||
originalRow._list[1].SetLength(5);
|
||||
originalRow._list[2].SetAttributes(TextAttribute{ 'G' });
|
||||
originalRow._list[2].SetLength(2);
|
||||
LogChain(L"Original: ", originalRow._list);
|
||||
|
||||
// Set up our "insertion run"
|
||||
size_t cInsertRow = 1;
|
||||
if (fUseStr2)
|
||||
{
|
||||
cInsertRow++;
|
||||
}
|
||||
|
||||
std::vector<TextAttributeRun> insertRow;
|
||||
insertRow.resize(cInsertRow);
|
||||
insertRow[0].SetAttributes(TextAttribute{ ch1 });
|
||||
insertRow[0].SetLength(uiChar1Length);
|
||||
if (fUseStr2)
|
||||
{
|
||||
insertRow[1].SetAttributes(TextAttribute{ ch2 });
|
||||
insertRow[1].SetLength(uiChar2Length);
|
||||
}
|
||||
|
||||
LogChain(L"Insert: ", insertRow);
|
||||
Log::Comment(NoThrowString().Format(L"At Index: %d", uiStartPos));
|
||||
|
||||
UINT uiTotalLength = uiChar1Length;
|
||||
if (fUseStr2)
|
||||
{
|
||||
uiTotalLength += uiChar2Length;
|
||||
}
|
||||
|
||||
VERIFY_IS_TRUE((uiStartPos + uiTotalLength) >= 1); // assert we won't underflow.
|
||||
UINT const uiEndPos = uiStartPos + uiTotalLength - 1;
|
||||
|
||||
// Calculate our expected final/result run by unpacking original, laying our insertion on it at the index
|
||||
// then using our pack function to repack it.
|
||||
// This method is easy to understand and very reliable, but its performance is bad.
|
||||
// The InsertAttrRuns method we test against below is hard to understand but very high performance in production.
|
||||
|
||||
// - 1. Unpack
|
||||
std::vector<TextAttribute> unpackedOriginal = { originalRow.begin(), originalRow.end() };
|
||||
|
||||
// - 2. Overlay insertion
|
||||
UINT uiInsertedCount = 0;
|
||||
UINT uiInsertIndex = 0;
|
||||
|
||||
// --- Walk through the unpacked run from start to end....
|
||||
for (UINT uiUnpackedIndex = uiStartPos; uiUnpackedIndex <= uiEndPos; uiUnpackedIndex++)
|
||||
{
|
||||
// Pull the item from the insert run to analyze.
|
||||
TextAttributeRun run = insertRow[uiInsertIndex];
|
||||
|
||||
// Copy the attribute from the run into the unpacked array
|
||||
unpackedOriginal[uiUnpackedIndex] = run.GetAttributes();
|
||||
|
||||
// Increment how many times we've copied this particular portion of the run
|
||||
uiInsertedCount++;
|
||||
|
||||
// If we've now inserted enough of them to match the length, advance the insert index and reset the counter.
|
||||
if (uiInsertedCount >= run.GetLength())
|
||||
{
|
||||
uiInsertIndex++;
|
||||
uiInsertedCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// - 3. Pack.
|
||||
std::unique_ptr<TextAttributeRun[]> packedRun;
|
||||
size_t cPackedRun = 0;
|
||||
VERIFY_SUCCEEDED(PackAttrs(unpackedOriginal.data(), originalRow._cchRowWidth, packedRun, &cPackedRun));
|
||||
|
||||
// Now send parameters into InsertAttrRuns and get its opinion on the subject.
|
||||
VERIFY_SUCCEEDED(originalRow.InsertAttrRuns({ insertRow.data(), insertRow.size() }, uiStartPos, uiEndPos, (UINT)originalRow._cchRowWidth));
|
||||
|
||||
// Compare and ensure that the expected and actual match.
|
||||
VERIFY_ARE_EQUAL(cPackedRun, originalRow._list.size(), L"Ensure that number of array elements required for RLE are the same.");
|
||||
|
||||
std::vector<TextAttributeRun> packedRunExpected;
|
||||
std::copy_n(packedRun.get(), cPackedRun, std::back_inserter(packedRunExpected));
|
||||
|
||||
LogChain(L"Expected: ", packedRunExpected);
|
||||
LogChain(L"Actual: ", originalRow._list);
|
||||
|
||||
for (size_t testIndex = 0; testIndex < cPackedRun; testIndex++)
|
||||
{
|
||||
VERIFY_ARE_EQUAL(packedRun[testIndex], originalRow._list[testIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestInsertAttrRunsSingle)
|
||||
{
|
||||
UINT const uiTestRunLength = 10;
|
||||
|
||||
UINT uiStartPos = 0;
|
||||
WORD ch1 = L'0';
|
||||
UINT uiChar1Length = 0;
|
||||
WORD ch2 = L'0';
|
||||
UINT uiChar2Length = 0;
|
||||
|
||||
Log::Comment(L"Test inserting a single item of a variable length into the run.");
|
||||
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
|
||||
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
|
||||
{
|
||||
ch1 = rgch1Options[iCh1Option];
|
||||
for (UINT iCh1Length = 1; iCh1Length <= uiTestRunLength; iCh1Length++)
|
||||
{
|
||||
uiChar1Length = iCh1Length;
|
||||
|
||||
// We can't try to insert a run that's longer than would fit.
|
||||
// If the run is of length 10 and we're trying to insert a length of 10,
|
||||
// we can only insert at position 0.
|
||||
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
|
||||
// And so on...
|
||||
UINT const uiMaxPos = uiTestRunLength - uiChar1Length;
|
||||
|
||||
for (UINT iStartPos = 0; iStartPos < uiMaxPos; iStartPos++)
|
||||
{
|
||||
uiStartPos = iStartPos;
|
||||
|
||||
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestInsertAttrRunsMultiple)
|
||||
{
|
||||
UINT const uiTestRunLength = 10;
|
||||
|
||||
UINT uiStartPos = 0;
|
||||
WORD ch1 = L'0';
|
||||
UINT uiChar1Length = 0;
|
||||
WORD ch2 = L'0';
|
||||
UINT uiChar2Length = 0;
|
||||
|
||||
Log::Comment(L"Test inserting a multiple item run with each piece having variable length into the existing run.");
|
||||
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
|
||||
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
|
||||
{
|
||||
ch1 = rgch1Options[iCh1Option];
|
||||
|
||||
UINT const uiMaxCh1Length = uiTestRunLength - 1; // leave at least 1 space for the second piece of the insert run.
|
||||
for (UINT iCh1Length = 1; iCh1Length <= uiMaxCh1Length; iCh1Length++)
|
||||
{
|
||||
uiChar1Length = iCh1Length;
|
||||
|
||||
WORD rgch2Options[] = { L'Y' };
|
||||
for (size_t iCh2Option = 0; iCh2Option < ARRAYSIZE(rgch2Options); iCh2Option++)
|
||||
{
|
||||
ch2 = rgch2Options[iCh2Option];
|
||||
|
||||
// When choosing the length of the second item, it can't be bigger than the remaining space in the run
|
||||
// when accounting for the length of the first piece chosen.
|
||||
// For example if the total run length is 10 and the first piece chosen was 8 long,
|
||||
// the second piece can only be 1 or 2 long.
|
||||
UINT const uiMaxCh2Length = uiTestRunLength - uiMaxCh1Length;
|
||||
for (UINT iCh2Length = 1; iCh2Length <= uiMaxCh2Length; iCh2Length++)
|
||||
{
|
||||
uiChar2Length = iCh2Length;
|
||||
|
||||
// We can't try to insert a run that's longer than would fit.
|
||||
// If the run is of length 10 and we're trying to insert a total length of 10,
|
||||
// we can only insert at position 0.
|
||||
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
|
||||
// And so on...
|
||||
UINT const uiMaxPos = uiTestRunLength - (uiChar1Length + uiChar2Length);
|
||||
|
||||
for (UINT iStartPos = 0; iStartPos <= uiMaxPos; iStartPos++)
|
||||
{
|
||||
uiStartPos = iStartPos;
|
||||
|
||||
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestUnpackAttrs)
|
||||
{
|
||||
Log::Comment(L"Checking unpack of a single color for the entire length");
|
||||
{
|
||||
const std::vector<TextAttribute> attrs{ pSingle->begin(), pSingle->end() };
|
||||
|
||||
for (auto& attr : attrs)
|
||||
{
|
||||
VERIFY_ARE_EQUAL(attr, _DefaultAttr);
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(L"Checking unpack of the multiple color chain");
|
||||
|
||||
const std::vector<TextAttribute> attrs{ pChain->begin(), pChain->end() };
|
||||
|
||||
short cChainRun = 0; // how long we've been looking at the current piece of the chain
|
||||
short iChainSegIndex = 0; // which piece of the chain we should be on right now
|
||||
|
||||
for (auto& attr : attrs)
|
||||
{
|
||||
// by default the chain was assembled above to have the chain segment index be the attribute
|
||||
TextAttribute MatchingAttr = TextAttribute(iChainSegIndex);
|
||||
|
||||
// However, if the index is greater than the expected chain length, a remainder piece was made with a default attribute
|
||||
if (iChainSegIndex >= _sDefaultChainLength)
|
||||
{
|
||||
MatchingAttr = _DefaultChainAttr;
|
||||
}
|
||||
|
||||
VERIFY_ARE_EQUAL(attr, MatchingAttr);
|
||||
|
||||
// Add to the chain run
|
||||
cChainRun++;
|
||||
|
||||
// If the chain run is greater than the length the segments were specified to be
|
||||
if (cChainRun >= sChainSegLength)
|
||||
{
|
||||
// reset to 0
|
||||
cChainRun = 0;
|
||||
|
||||
// move to the next chain segment down the line
|
||||
iChainSegIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestReverseIteratorWalkFromMiddle)
|
||||
{
|
||||
// GH #3409, walking backwards through color range runs out of bounds
|
||||
// We're going to create an attribute row with assorted colors and varying lengths
|
||||
// just like the row of text on the Ubuntu prompt line that triggered this bug being found.
|
||||
// Then we're going to walk backwards through the iterator like a selection-expand-to-left
|
||||
// operation and ensure we don't run off the bounds.
|
||||
|
||||
// walk the chain, from index, stepSize at a time
|
||||
// ensure we don't crash
|
||||
auto testWalk = [](ATTR_ROW* chain, size_t index, int stepSize) {
|
||||
// move to starting index
|
||||
auto iter = chain->cbegin();
|
||||
iter += index;
|
||||
|
||||
// Now walk backwards in a loop until 0.
|
||||
while (iter)
|
||||
{
|
||||
iter -= stepSize;
|
||||
}
|
||||
|
||||
Log::Comment(L"We made it through without crashing!");
|
||||
};
|
||||
|
||||
// take one step of size stepSize on the chain
|
||||
// index is where we start from
|
||||
// expectedAttribute is what we expect to read here
|
||||
auto verifyStep = [](ATTR_ROW* chain, size_t index, int stepSize, TextAttribute expectedAttribute) {
|
||||
// move to starting index
|
||||
auto iter = chain->cbegin();
|
||||
iter += index;
|
||||
|
||||
// Now step backwards
|
||||
iter -= stepSize;
|
||||
|
||||
VERIFY_ARE_EQUAL(expectedAttribute, *iter);
|
||||
};
|
||||
|
||||
Log::Comment(L"Reverse iterate through ubuntu prompt");
|
||||
{
|
||||
// Create attr row representing a buffer that's 121 wide.
|
||||
auto chain = std::make_unique<ATTR_ROW>(121, _DefaultAttr);
|
||||
|
||||
// The repro case had 4 chain segments.
|
||||
chain->_list.resize(4);
|
||||
|
||||
// The color 10 went for the first 18.
|
||||
chain->_list[0].SetAttributes(TextAttribute(0xA));
|
||||
chain->_list[0].SetLength(18);
|
||||
|
||||
// Default color for the next 1
|
||||
chain->_list[1].SetAttributes(TextAttribute());
|
||||
chain->_list[1].SetLength(1);
|
||||
|
||||
// Color 12 for the next 29
|
||||
chain->_list[2].SetAttributes(TextAttribute(0xC));
|
||||
chain->_list[2].SetLength(29);
|
||||
|
||||
// Then default color to end the run
|
||||
chain->_list[3].SetAttributes(TextAttribute());
|
||||
chain->_list[3].SetLength(73);
|
||||
|
||||
// The sum of the lengths should be 121.
|
||||
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength + chain->_list[3]._cchLength);
|
||||
|
||||
auto index = chain->_list[0].GetLength();
|
||||
auto stepSize = 1;
|
||||
testWalk(chain.get(), index, stepSize);
|
||||
}
|
||||
|
||||
Log::Comment(L"Reverse iterate across a text run in the chain");
|
||||
{
|
||||
// Create attr row representing a buffer that's 3 wide.
|
||||
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
|
||||
|
||||
// The repro case had 3 chain segments.
|
||||
chain->_list.resize(3);
|
||||
|
||||
// The color 10 went for the first 1.
|
||||
chain->_list[0].SetAttributes(TextAttribute(0xA));
|
||||
chain->_list[0].SetLength(1);
|
||||
|
||||
// The color 11 for the next 1
|
||||
chain->_list[1].SetAttributes(TextAttribute(0xB));
|
||||
chain->_list[1].SetLength(1);
|
||||
|
||||
// Color 12 for the next 1
|
||||
chain->_list[2].SetAttributes(TextAttribute(0xC));
|
||||
chain->_list[2].SetLength(1);
|
||||
|
||||
// The sum of the lengths should be 3.
|
||||
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
|
||||
|
||||
// on 'ABC', step from B to A
|
||||
auto index = 1;
|
||||
auto stepSize = 1;
|
||||
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
|
||||
}
|
||||
|
||||
Log::Comment(L"Reverse iterate across two text runs in the chain");
|
||||
{
|
||||
// Create attr row representing a buffer that's 3 wide.
|
||||
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
|
||||
|
||||
// The repro case had 3 chain segments.
|
||||
chain->_list.resize(3);
|
||||
|
||||
// The color 10 went for the first 1.
|
||||
chain->_list[0].SetAttributes(TextAttribute(0xA));
|
||||
chain->_list[0].SetLength(1);
|
||||
|
||||
// The color 11 for the next 1
|
||||
chain->_list[1].SetAttributes(TextAttribute(0xB));
|
||||
chain->_list[1].SetLength(1);
|
||||
|
||||
// Color 12 for the next 1
|
||||
chain->_list[2].SetAttributes(TextAttribute(0xC));
|
||||
chain->_list[2].SetLength(1);
|
||||
|
||||
// The sum of the lengths should be 3.
|
||||
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
|
||||
|
||||
// on 'ABC', step from C to A
|
||||
auto index = 2;
|
||||
auto stepSize = 2;
|
||||
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestSetAttrToEnd)
|
||||
{
|
||||
const WORD wTestAttr = FOREGROUND_BLUE | BACKGROUND_GREEN;
|
||||
TextAttribute TestAttr = TextAttribute(wTestAttr);
|
||||
|
||||
Log::Comment(L"FIRST: Set index to > 0 to test making/modifying chains");
|
||||
const short iTestIndex = 50;
|
||||
VERIFY_IS_TRUE(iTestIndex >= 0 && iTestIndex < _sDefaultLength);
|
||||
|
||||
Log::Comment(L"SetAttrToEnd for single color applied to whole string.");
|
||||
pSingle->SetAttrToEnd(iTestIndex, TestAttr);
|
||||
|
||||
// Was 1 (single), should now have 2 segments
|
||||
VERIFY_ARE_EQUAL(pSingle->_list.size(), 2u);
|
||||
|
||||
VERIFY_ARE_EQUAL(pSingle->_list[0].GetAttributes(), _DefaultAttr);
|
||||
VERIFY_ARE_EQUAL(pSingle->_list[0].GetLength(), (unsigned int)(_sDefaultLength - (_sDefaultLength - iTestIndex)));
|
||||
|
||||
VERIFY_ARE_EQUAL(pSingle->_list[1].GetAttributes(), TestAttr);
|
||||
VERIFY_ARE_EQUAL(pSingle->_list[1].GetLength(), (unsigned int)(_sDefaultLength - iTestIndex));
|
||||
|
||||
Log::Comment(L"SetAttrToEnd for existing chain of multiple colors.");
|
||||
pChain->SetAttrToEnd(iTestIndex, TestAttr);
|
||||
|
||||
// From 7 segments down to 5.
|
||||
VERIFY_ARE_EQUAL(pChain->_list.size(), 5u);
|
||||
|
||||
// Verify chain colors and lengths
|
||||
VERIFY_ARE_EQUAL(TextAttribute(0), pChain->_list[0].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[0].GetLength(), (unsigned int)13);
|
||||
|
||||
VERIFY_ARE_EQUAL(TextAttribute(1), pChain->_list[1].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[1].GetLength(), (unsigned int)13);
|
||||
|
||||
VERIFY_ARE_EQUAL(TextAttribute(2), pChain->_list[2].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[2].GetLength(), (unsigned int)13);
|
||||
|
||||
VERIFY_ARE_EQUAL(TextAttribute(3), pChain->_list[3].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[3].GetLength(), (unsigned int)11);
|
||||
|
||||
VERIFY_ARE_EQUAL(TestAttr, pChain->_list[4].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[4].GetLength(), (unsigned int)30);
|
||||
|
||||
Log::Comment(L"SECOND: Set index to 0 to test replacing anything with a single");
|
||||
|
||||
ATTR_ROW* pTestItems[]{ pSingle, pChain };
|
||||
|
||||
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
|
||||
{
|
||||
ATTR_ROW* pUnderTest = pTestItems[iIndex];
|
||||
|
||||
pUnderTest->SetAttrToEnd(0, TestAttr);
|
||||
|
||||
// should be down to 1 attribute set from beginning to end of string
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
|
||||
|
||||
// singular pair should contain the color
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), TestAttr);
|
||||
|
||||
// and its length should be the length of the whole string
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestTotalLength)
|
||||
{
|
||||
ATTR_ROW* pTestItems[]{ pSingle, pChain };
|
||||
|
||||
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
|
||||
{
|
||||
ATTR_ROW* pUnderTest = pTestItems[iIndex];
|
||||
|
||||
const size_t Result = pUnderTest->_cchRowWidth;
|
||||
|
||||
VERIFY_ARE_EQUAL((short)Result, _sDefaultLength);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestResize)
|
||||
{
|
||||
pSingle->Resize(240);
|
||||
pChain->Resize(240);
|
||||
|
||||
pSingle->Resize(255);
|
||||
pChain->Resize(255);
|
||||
|
||||
pSingle->Resize(255);
|
||||
pChain->Resize(255);
|
||||
|
||||
pSingle->Resize(60);
|
||||
pChain->Resize(60);
|
||||
|
||||
pSingle->Resize(60);
|
||||
pChain->Resize(60);
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(pSingle->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
|
||||
VERIFY_THROWS_SPECIFIC(pChain->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
|
||||
}
|
||||
};
|
||||
@@ -6,11 +6,10 @@
|
||||
<RootNamespace>TextBufferUnitTests</RootNamespace>
|
||||
<ProjectName>TextBuffer.Unit.Tests</ProjectName>
|
||||
<TargetName>TextBuffer.Unit.Tests</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AttrRowTests.cpp" />
|
||||
<ClCompile Include="ReflowTests.cpp" />
|
||||
<ClCompile Include="TextColorTests.cpp" />
|
||||
<ClCompile Include="TextAttributeTests.cpp" />
|
||||
|
||||
@@ -14,7 +14,6 @@ DLLDEF =
|
||||
|
||||
SOURCES = \
|
||||
$(SOURCES) \
|
||||
AttrRowTests.cpp \
|
||||
ReflowTests.cpp \
|
||||
TextColorTests.cpp \
|
||||
TextAttributeTests.cpp \
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
<uap:ShowOn Tile="square310x310Logo"/>
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png"/>
|
||||
</uap:VisualElements>
|
||||
|
||||
<Extensions>
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
<uap:ShowOn Tile="square310x310Logo"/>
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png"/>
|
||||
</uap:VisualElements>
|
||||
|
||||
<Extensions>
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
<uap:ShowOn Tile="square310x310Logo"/>
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png"/>
|
||||
</uap:VisualElements>
|
||||
|
||||
<Extensions>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
@@ -15,7 +15,7 @@ using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
|
||||
namespace SettingsModelLocalTests
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
@@ -15,7 +15,7 @@ using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
|
||||
namespace SettingsModelLocalTests
|
||||
{
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<ClCompile Include="CommandTests.cpp" />
|
||||
<ClCompile Include="DeserializationTests.cpp" />
|
||||
<ClCompile Include="SerializationTests.cpp" />
|
||||
<ClCompile Include="TerminalSettingsTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
@@ -57,7 +58,7 @@
|
||||
|
||||
<!-- If you don't reference these projects here, the
|
||||
_ConsoleGenerateAdditionalWinmdManifests step won't gather the winmd's -->
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\dll\TerminalControl.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\cascadia\TerminalSettingsModel\dll\Microsoft.Terminal.Settings.Model.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
543
src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
Normal file
@@ -0,0 +1,543 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../TerminalSettingsModel/TerminalSettings.h"
|
||||
#include "TestUtils.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
|
||||
namespace SettingsModelLocalTests
|
||||
{
|
||||
// TODO:microsoft/terminal#3838:
|
||||
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
||||
// an updated TAEF that will let us install framework packages when the test
|
||||
// package is deployed. Until then, these tests won't deploy in CI.
|
||||
|
||||
class TerminalSettingsTests
|
||||
{
|
||||
// Use a custom AppxManifest to ensure that we can activate winrt types
|
||||
// from our test. This property will tell taef to manually use this as
|
||||
// the AppxManifest for this test class.
|
||||
// This does not yet work for anything XAML-y. See TabTests.cpp for more
|
||||
// details on that.
|
||||
BEGIN_TEST_CLASS(TerminalSettingsTests)
|
||||
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
||||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(TryCreateWinRTType);
|
||||
|
||||
TEST_METHOD(TestTerminalArgsForBinding);
|
||||
|
||||
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
|
||||
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
||||
|
||||
TEST_METHOD(TestLayerProfileOnColorScheme);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void TerminalSettingsTests::TryCreateWinRTType()
|
||||
{
|
||||
TerminalSettings settings;
|
||||
VERIFY_IS_NOT_NULL(settings);
|
||||
auto oldFontSize = settings.FontSize();
|
||||
settings.FontSize(oldFontSize + 5);
|
||||
auto newFontSize = settings.FontSize();
|
||||
VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize);
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::TestTerminalArgsForBinding()
|
||||
{
|
||||
const std::string settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"commandline": "cmd.exe"
|
||||
},
|
||||
{
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2,
|
||||
"commandline": "pwsh.exe"
|
||||
},
|
||||
{
|
||||
"name": "profile2",
|
||||
"historySize": 3,
|
||||
"commandline": "wsl.exe"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
{ "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+b"], "command": { "action": "splitPane", "split": "vertical", "profile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" } },
|
||||
{ "keys": ["ctrl+c"], "command": { "action": "splitPane", "split": "vertical", "profile": "profile1" } },
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "splitPane", "split": "vertical", "profile": "profile2" } },
|
||||
{ "keys": ["ctrl+e"], "command": { "action": "splitPane", "split": "horizontal", "commandline": "foo.exe" } },
|
||||
{ "keys": ["ctrl+f"], "command": { "action": "splitPane", "split": "horizontal", "profile": "profile1", "commandline": "foo.exe" } },
|
||||
{ "keys": ["ctrl+g"], "command": { "action": "newTab" } },
|
||||
{ "keys": ["ctrl+h"], "command": { "action": "newTab", "startingDirectory": "c:\\foo" } },
|
||||
{ "keys": ["ctrl+i"], "command": { "action": "newTab", "profile": "profile2", "startingDirectory": "c:\\foo" } },
|
||||
{ "keys": ["ctrl+j"], "command": { "action": "newTab", "tabTitle": "bar" } },
|
||||
{ "keys": ["ctrl+k"], "command": { "action": "newTab", "profile": "profile2", "tabTitle": "bar" } },
|
||||
{ "keys": ["ctrl+l"], "command": { "action": "newTab", "profile": "profile1", "tabTitle": "bar", "startingDirectory": "c:\\foo", "commandline":"foo.exe" } }
|
||||
]
|
||||
})" };
|
||||
|
||||
const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
|
||||
const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}") };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
|
||||
auto keymap = settings.GlobalSettings().KeyMap();
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
const auto profile2Guid = settings.ActiveProfiles().GetAt(2).Guid();
|
||||
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
|
||||
|
||||
VERIFY_ARE_EQUAL(12u, keymap.Size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('B') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('I') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('J') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('K') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('L') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::MakeSettingsForProfileThatDoesntExist()
|
||||
{
|
||||
// Test that making settings throws when the GUID doesn't exist
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
CascadiaSettings settings{ til::u8u16(settingsString) };
|
||||
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid1, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid2, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
|
||||
}
|
||||
|
||||
VERIFY_THROWS(auto terminalSettings = TerminalSettings::CreateWithProfileByID(settings, guid3, nullptr), wil::ResultException, L"This call to constructor should fail");
|
||||
|
||||
try
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithNewTerminalArgs should succeed");
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::MakeSettingsForDefaultProfileThatDoesntExist()
|
||||
{
|
||||
// Test that MakeSettings _doesnt_ throw when we load settings with a
|
||||
// defaultProfile that's not in the list, we validate the settings, and
|
||||
// then call MakeSettings(nullopt). The validation should ensure that
|
||||
// the default profile is something reasonable
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
CascadiaSettings settings{ til::u8u16(settingsString) };
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings.ActiveProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(settings.GlobalSettings().DefaultProfile(), settings.ActiveProfiles().GetAt(0).Guid());
|
||||
try
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithNewTerminalArgs should succeed");
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::TestLayerProfileOnColorScheme()
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Ensure that setting (or not) a property in the profile that should override a property of the color scheme works correctly."));
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"defaultProfile": "profile5",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"colorScheme": "schemeWithCursorColor"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"colorScheme": "schemeWithoutCursorColor"
|
||||
},
|
||||
{
|
||||
"name" : "profile2",
|
||||
"colorScheme": "schemeWithCursorColor",
|
||||
"cursorColor": "#234567"
|
||||
},
|
||||
{
|
||||
"name" : "profile3",
|
||||
"colorScheme": "schemeWithoutCursorColor",
|
||||
"cursorColor": "#345678"
|
||||
},
|
||||
{
|
||||
"name" : "profile4",
|
||||
"cursorColor": "#456789"
|
||||
},
|
||||
{
|
||||
"name" : "profile5"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
{
|
||||
"name": "schemeWithCursorColor",
|
||||
"cursorColor": "#123456"
|
||||
},
|
||||
{
|
||||
"name": "schemeWithoutCursorColor"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settings0String) };
|
||||
|
||||
VERIFY_ARE_EQUAL(6u, settings.ActiveProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings.GlobalSettings().ColorSchemes().Size());
|
||||
|
||||
auto createTerminalSettings = [&](const auto& profile, const auto& schemes) {
|
||||
auto terminalSettings{ winrt::make_self<implementation::TerminalSettings>() };
|
||||
terminalSettings->_ApplyProfileSettings(profile, schemes);
|
||||
return terminalSettings;
|
||||
};
|
||||
|
||||
auto terminalSettings0 = createTerminalSettings(settings.ActiveProfiles().GetAt(0), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings1 = createTerminalSettings(settings.ActiveProfiles().GetAt(1), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings2 = createTerminalSettings(settings.ActiveProfiles().GetAt(2), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings3 = createTerminalSettings(settings.ActiveProfiles().GetAt(3), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings4 = createTerminalSettings(settings.ActiveProfiles().GetAt(4), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings5 = createTerminalSettings(settings.ActiveProfiles().GetAt(5), settings.GlobalSettings().ColorSchemes());
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings1->CursorColor()); // default
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x23, 0x45, 0x67), terminalSettings2->CursorColor()); // from profile (trumps color scheme)
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x34, 0x56, 0x78), terminalSettings3->CursorColor()); // from profile (not set in color scheme)
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme)
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings5->CursorColor()); // default
|
||||
}
|
||||
}
|
||||
@@ -24,18 +24,18 @@ public:
|
||||
// Return Value:
|
||||
// - The ActionAndArgs bound to the given key, or nullptr if nothing is bound to it.
|
||||
static const winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs GetActionAndArgs(const winrt::Microsoft::Terminal::Settings::Model::KeyMapping& keymap,
|
||||
const winrt::Microsoft::Terminal::TerminalControl::KeyChord& kc)
|
||||
const winrt::Microsoft::Terminal::Control::KeyChord& kc)
|
||||
{
|
||||
std::wstring buffer{ L"" };
|
||||
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::TerminalControl::KeyModifiers::Ctrl))
|
||||
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::Control::KeyModifiers::Ctrl))
|
||||
{
|
||||
buffer += L"Ctrl+";
|
||||
}
|
||||
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::TerminalControl::KeyModifiers::Shift))
|
||||
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::Control::KeyModifiers::Shift))
|
||||
{
|
||||
buffer += L"Shift+";
|
||||
}
|
||||
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::TerminalControl::KeyModifiers::Alt))
|
||||
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::Control::KeyModifiers::Alt))
|
||||
{
|
||||
buffer += L"Alt+";
|
||||
}
|
||||
|
||||
@@ -1594,59 +1594,155 @@ namespace TerminalAppLocalTests
|
||||
void CommandlineTest::TestFindTargetWindow()
|
||||
{
|
||||
{
|
||||
Log::Comment(L"wt.exe with no args should always use the value from"
|
||||
L" the settings (passed as the second argument).");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"-w -1 should always result in a new window");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"-1" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"\"new\" should always result in a new window");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"new" };
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"-w with a negative number should always result in a "
|
||||
L"new window");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"-12345" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"-w with a positive number should result in us trying"
|
||||
L" to either make a new one or find an existing one "
|
||||
L"with that ID, depending on the provided argument");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"12345" };
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(12345, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(12345, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(12345, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"-w 0 should always use the \"current\" window");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"0" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"-w last should always use the most recent window on "
|
||||
L"this desktop");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"last" };
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Make sure we follow the provided argument when a "
|
||||
L"--window-id wasn't explicitly provided");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"new-tab" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Even if someone uses a subcommand as a window name, "
|
||||
L"that should work");
|
||||
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"new-tab" };
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"new-tab", result.WindowName());
|
||||
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"new-tab", result.WindowName());
|
||||
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"new-tab", result.WindowName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1657,19 +1753,23 @@ namespace TerminalAppLocalTests
|
||||
// This is a little helper to make sure that these args _always_ return
|
||||
// UseNew, regardless of the windowing behavior.
|
||||
auto testHelper = [](auto&& args) {
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
};
|
||||
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"--help" });
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"new-tab", L"--help" });
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"-w", L"0", L"new-tab", L"--help" });
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"-w", L"foo", L"new-tab", L"--help" });
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"new-tab", L";", L"--help" });
|
||||
}
|
||||
|
||||
@@ -1680,14 +1780,17 @@ namespace TerminalAppLocalTests
|
||||
// This is a little helper to make sure that these args _always_ return
|
||||
// UseNew, regardless of the windowing behavior.
|
||||
auto testHelper = [](auto&& args) {
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
|
||||
VERIFY_ARE_EQUAL(L"", result.WindowName());
|
||||
};
|
||||
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"--version" });
|
||||
|
||||
@@ -11,7 +11,7 @@ using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalApp/TerminalPage.h"
|
||||
#include "../TerminalApp/TerminalSettings.h"
|
||||
#include "../LocalTests_SettingsModel/TestUtils.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
@@ -13,7 +12,7 @@ using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
@@ -34,15 +33,6 @@ namespace TerminalAppLocalTests
|
||||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(TryCreateWinRTType);
|
||||
|
||||
TEST_METHOD(TestTerminalArgsForBinding);
|
||||
|
||||
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
|
||||
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
||||
|
||||
TEST_METHOD(TestLayerProfileOnColorScheme);
|
||||
|
||||
TEST_METHOD(TestIterateCommands);
|
||||
TEST_METHOD(TestIterateOnGeneratedNamedCommands);
|
||||
TEST_METHOD(TestIterateOnBadJson);
|
||||
@@ -83,487 +73,6 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
};
|
||||
|
||||
void SettingsTests::TryCreateWinRTType()
|
||||
{
|
||||
TerminalSettings settings;
|
||||
VERIFY_IS_NOT_NULL(settings);
|
||||
auto oldFontSize = settings.FontSize();
|
||||
settings.FontSize(oldFontSize + 5);
|
||||
auto newFontSize = settings.FontSize();
|
||||
VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize);
|
||||
}
|
||||
|
||||
void SettingsTests::TestTerminalArgsForBinding()
|
||||
{
|
||||
const std::string settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"commandline": "cmd.exe"
|
||||
},
|
||||
{
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2,
|
||||
"commandline": "pwsh.exe"
|
||||
},
|
||||
{
|
||||
"name": "profile2",
|
||||
"historySize": 3,
|
||||
"commandline": "wsl.exe"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
{ "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+b"], "command": { "action": "splitPane", "split": "vertical", "profile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" } },
|
||||
{ "keys": ["ctrl+c"], "command": { "action": "splitPane", "split": "vertical", "profile": "profile1" } },
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "splitPane", "split": "vertical", "profile": "profile2" } },
|
||||
{ "keys": ["ctrl+e"], "command": { "action": "splitPane", "split": "horizontal", "commandline": "foo.exe" } },
|
||||
{ "keys": ["ctrl+f"], "command": { "action": "splitPane", "split": "horizontal", "profile": "profile1", "commandline": "foo.exe" } },
|
||||
{ "keys": ["ctrl+g"], "command": { "action": "newTab" } },
|
||||
{ "keys": ["ctrl+h"], "command": { "action": "newTab", "startingDirectory": "c:\\foo" } },
|
||||
{ "keys": ["ctrl+i"], "command": { "action": "newTab", "profile": "profile2", "startingDirectory": "c:\\foo" } },
|
||||
{ "keys": ["ctrl+j"], "command": { "action": "newTab", "tabTitle": "bar" } },
|
||||
{ "keys": ["ctrl+k"], "command": { "action": "newTab", "profile": "profile2", "tabTitle": "bar" } },
|
||||
{ "keys": ["ctrl+l"], "command": { "action": "newTab", "profile": "profile1", "tabTitle": "bar", "startingDirectory": "c:\\foo", "commandline":"foo.exe" } }
|
||||
]
|
||||
})" };
|
||||
|
||||
const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
|
||||
const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}") };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
|
||||
auto keymap = settings.GlobalSettings().KeyMap();
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
const auto profile2Guid = settings.ActiveProfiles().GetAt(2).Guid();
|
||||
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
|
||||
|
||||
VERIFY_ARE_EQUAL(12u, keymap.Size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('B') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('I') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('J') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('K') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('L') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, realArgs.TerminalArgs(), nullptr);
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsTests::MakeSettingsForProfileThatDoesntExist()
|
||||
{
|
||||
// Test that MakeSettings throws when the GUID doesn't exist
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
CascadiaSettings settings{ til::u8u16(settingsString) };
|
||||
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings = winrt::make<winrt::TerminalApp::implementation::TerminalSettings>(settings, guid1, nullptr);
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings = winrt::make<winrt::TerminalApp::implementation::TerminalSettings>(settings, guid2, nullptr);
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
|
||||
}
|
||||
|
||||
VERIFY_THROWS(auto terminalSettings = winrt::make<winrt::TerminalApp::implementation::TerminalSettings>(settings, guid3, nullptr), wil::ResultException, L"This call to BuildSettings should fail");
|
||||
|
||||
try
|
||||
{
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, nullptr, nullptr);
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsTests::MakeSettingsForDefaultProfileThatDoesntExist()
|
||||
{
|
||||
// Test that MakeSettings _doesnt_ throw when we load settings with a
|
||||
// defaultProfile that's not in the list, we validate the settings, and
|
||||
// then call MakeSettings(nullopt). The validation should ensure that
|
||||
// the default profile is something reasonable
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
CascadiaSettings settings{ til::u8u16(settingsString) };
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings.ActiveProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(settings.GlobalSettings().DefaultProfile(), settings.ActiveProfiles().GetAt(0).Guid());
|
||||
try
|
||||
{
|
||||
const auto [guid, termSettings] = winrt::TerminalApp::implementation::TerminalSettings::BuildSettings(settings, nullptr, nullptr);
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsTests::TestLayerProfileOnColorScheme()
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Ensure that setting (or not) a property in the profile that should override a property of the color scheme works correctly."));
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"defaultProfile": "profile5",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"colorScheme": "schemeWithCursorColor"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"colorScheme": "schemeWithoutCursorColor"
|
||||
},
|
||||
{
|
||||
"name" : "profile2",
|
||||
"colorScheme": "schemeWithCursorColor",
|
||||
"cursorColor": "#234567"
|
||||
},
|
||||
{
|
||||
"name" : "profile3",
|
||||
"colorScheme": "schemeWithoutCursorColor",
|
||||
"cursorColor": "#345678"
|
||||
},
|
||||
{
|
||||
"name" : "profile4",
|
||||
"cursorColor": "#456789"
|
||||
},
|
||||
{
|
||||
"name" : "profile5"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
{
|
||||
"name": "schemeWithCursorColor",
|
||||
"cursorColor": "#123456"
|
||||
},
|
||||
{
|
||||
"name": "schemeWithoutCursorColor"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settings0String) };
|
||||
|
||||
VERIFY_ARE_EQUAL(6u, settings.ActiveProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings.GlobalSettings().ColorSchemes().Size());
|
||||
|
||||
auto createTerminalSettings = [&](const auto& profile, const auto& schemes) {
|
||||
auto terminalSettings{ winrt::make_self<winrt::TerminalApp::implementation::TerminalSettings>() };
|
||||
terminalSettings->_ApplyProfileSettings(profile, schemes);
|
||||
return terminalSettings;
|
||||
};
|
||||
|
||||
auto terminalSettings0 = createTerminalSettings(settings.ActiveProfiles().GetAt(0), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings1 = createTerminalSettings(settings.ActiveProfiles().GetAt(1), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings2 = createTerminalSettings(settings.ActiveProfiles().GetAt(2), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings3 = createTerminalSettings(settings.ActiveProfiles().GetAt(3), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings4 = createTerminalSettings(settings.ActiveProfiles().GetAt(4), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings5 = createTerminalSettings(settings.ActiveProfiles().GetAt(5), settings.GlobalSettings().ColorSchemes());
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings1->CursorColor()); // default
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x23, 0x45, 0x67), terminalSettings2->CursorColor()); // from profile (trumps color scheme)
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x34, 0x56, 0x78), terminalSettings3->CursorColor()); // from profile (not set in color scheme)
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme)
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings5->CursorColor()); // default
|
||||
}
|
||||
|
||||
void SettingsTests::TestIterateCommands()
|
||||
{
|
||||
// For this test, put an iterable command with a given `name`,
|
||||
|
||||
@@ -330,7 +330,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
// * 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 _DuplicateTabViewItem on tab 1
|
||||
// * Try calling _DuplicateFocusedTab on tab 1
|
||||
// * No new tab should be created (and more importantly, the app should not crash)
|
||||
//
|
||||
// Created to test GH#2455
|
||||
@@ -392,7 +392,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Duplicate the first tab");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateTabViewItem();
|
||||
page->_DuplicateFocusedTab();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -407,7 +407,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Duplicate the tab, and don't crash");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateTabViewItem();
|
||||
page->_DuplicateFocusedTab();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -782,8 +782,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the fourth tab");
|
||||
TestOnUIThread([&page]() {
|
||||
ActionEventArgs eventArgs{};
|
||||
page->_HandleNextTab(nullptr, eventArgs);
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
|
||||
Log::Comment(L"Sleep to let events propagate");
|
||||
@@ -804,8 +803,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the second tab");
|
||||
TestOnUIThread([&page]() {
|
||||
ActionEventArgs eventArgs{};
|
||||
page->_HandleNextTab(nullptr, eventArgs);
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
|
||||
Log::Comment(L"Sleep to let events propagate");
|
||||
@@ -829,8 +827,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab, which is the third tab");
|
||||
TestOnUIThread([&page]() {
|
||||
ActionEventArgs eventArgs{};
|
||||
page->_HandleNextTab(nullptr, eventArgs);
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
@@ -842,8 +839,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab, which is the fourth tab");
|
||||
TestOnUIThread([&page]() {
|
||||
ActionEventArgs eventArgs{};
|
||||
page->_HandleNextTab(nullptr, eventArgs);
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
@@ -916,7 +912,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the third tab");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true);
|
||||
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
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<!-- If you don't reference these projects here, the
|
||||
_ConsoleGenerateAdditionalWinmdManifests step won't gather the winmd's -->
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettingsEditor\Microsoft.Terminal.Settings.Editor.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\dll\TerminalControl.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\cascadia\TerminalApp\dll\TerminalApp.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\cascadia\TerminalSettingsModel\dll\Microsoft.Terminal.Settings.Model.vcxproj" />
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
</ProjectReference>
|
||||
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettingsEditor\Microsoft.Terminal.Settings.Editor.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\dll\TerminalControl.vcxproj" />
|
||||
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalApp\dll\TerminalApp.vcxproj">
|
||||
<Project>{ca5cad1a-44bd-4ac7-ac72-f16e576fdd12}</Project>
|
||||
|
||||
@@ -25,8 +25,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct FindTargetWindowArgs : public FindTargetWindowArgsT<FindTargetWindowArgs>
|
||||
{
|
||||
GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr);
|
||||
GETSET_PROPERTY(int, ResultTargetWindow, -1);
|
||||
WINRT_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr);
|
||||
WINRT_PROPERTY(int, ResultTargetWindow, -1);
|
||||
WINRT_PROPERTY(winrt::hstring, ResultTargetWindowName);
|
||||
|
||||
public:
|
||||
FindTargetWindowArgs(winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) :
|
||||
|
||||
@@ -147,6 +147,59 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Find the ID of the peasant with the given name. If no such peasant
|
||||
// exists, then we'll return 0. If we encounter any peasants who have died
|
||||
// during this process, then we'll remove them from the set of _peasants
|
||||
// Arguments:
|
||||
// - name: The window name to look for
|
||||
// Return Value:
|
||||
// - 0 if we didn't find the given peasant, otherwise a positive number for
|
||||
// the window's ID.
|
||||
uint64_t Monarch::_lookupPeasantIdForName(std::wstring_view name)
|
||||
{
|
||||
if (name.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> peasantsToErase{};
|
||||
uint64_t result = 0;
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto otherName = p.WindowName();
|
||||
if (otherName == name)
|
||||
{
|
||||
result = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
// Normally, we'd just erase the peasant here. However, we can't
|
||||
// erase from the map while we're iterating over it like this.
|
||||
// Instead, pull a good ole Java and collect this id for removal
|
||||
// later.
|
||||
peasantsToErase.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the dead peasants we came across while iterating.
|
||||
for (const auto& id : peasantsToErase)
|
||||
{
|
||||
// Remove the peasant from the list of peasants
|
||||
_peasants.erase(id);
|
||||
// Remove the peasant from the list of MRU windows. They're dead.
|
||||
// They can't be the MRU anymore.
|
||||
_clearOldMruEntries(id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handler for the `Peasant::WindowActivated` event. We'll make a in-proc
|
||||
// copy of the WindowActivatedArgs from the peasant. That way, we won't
|
||||
@@ -400,6 +453,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// After the event was handled, ResultTargetWindow() will be filled with
|
||||
// the parsed result.
|
||||
const auto targetWindow = findWindowArgs->ResultTargetWindow();
|
||||
const auto targetWindowName = findWindowArgs->ResultTargetWindowName();
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_ProposeCommandline",
|
||||
@@ -410,6 +464,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// that goes with it. Alternatively, if we were given a magic windowing
|
||||
// constant, we can use that to look up an appropriate peasant.
|
||||
if (targetWindow >= 0 ||
|
||||
targetWindow == WindowingBehaviorUseName ||
|
||||
targetWindow == WindowingBehaviorUseExisting ||
|
||||
targetWindow == WindowingBehaviorUseAnyExisting)
|
||||
{
|
||||
@@ -429,6 +484,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
case WindowingBehaviorUseAnyExisting:
|
||||
windowID = _getMostRecentPeasantID(false);
|
||||
break;
|
||||
case WindowingBehaviorUseName:
|
||||
windowID = _lookupPeasantIdForName(targetWindowName);
|
||||
break;
|
||||
default:
|
||||
windowID = ::base::saturated_cast<uint64_t>(targetWindow);
|
||||
break;
|
||||
@@ -449,7 +507,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
if (auto targetPeasant{ _getPeasant(windowID) })
|
||||
{
|
||||
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(false) };
|
||||
|
||||
try
|
||||
{
|
||||
// This will raise the peasant's ExecuteCommandlineRequested
|
||||
@@ -463,6 +520,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// If we fail to propose the commandline to the peasant (it
|
||||
// died?) then just tell this process to become a new window
|
||||
// instead.
|
||||
result->WindowName(targetWindowName);
|
||||
result->ShouldCreateWindow(true);
|
||||
|
||||
// If this fails, it'll be logged in the following
|
||||
@@ -497,6 +555,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true) };
|
||||
result->Id(windowID);
|
||||
result->WindowName(targetWindowName);
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
@@ -508,6 +567,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
// In this case, no usable ID was provided. Return { true, nullopt }
|
||||
return winrt::make<Remoting::implementation::ProposeCommandlineResult>(true);
|
||||
auto result = winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true);
|
||||
result->WindowName(targetWindowName);
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID);
|
||||
uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop);
|
||||
uint64_t _lookupPeasantIdForName(std::wstring_view name);
|
||||
|
||||
void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||
|
||||
@@ -9,11 +9,12 @@ namespace Microsoft.Terminal.Remoting
|
||||
[default_interface] runtimeclass FindTargetWindowArgs {
|
||||
CommandlineArgs Args { get; };
|
||||
Int32 ResultTargetWindow;
|
||||
String ResultTargetWindowName;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ProposeCommandlineResult {
|
||||
Windows.Foundation.IReference<UInt64> Id { get; };
|
||||
// TODO:projects/5 - also return the name here, if the name was set on the commandline
|
||||
String WindowName { get; };
|
||||
Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
|
||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||
|
||||
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs);
|
||||
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs);
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Microsoft.Terminal.Remoting
|
||||
Boolean ExecuteCommandline(CommandlineArgs args);
|
||||
void ActivateWindow(WindowActivatedArgs args);
|
||||
WindowActivatedArgs GetLastActivatedArgs();
|
||||
String WindowName { get; };
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||
|
||||
@@ -26,8 +26,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
struct ProposeCommandlineResult : public ProposeCommandlineResultT<ProposeCommandlineResult>
|
||||
{
|
||||
public:
|
||||
GETSET_PROPERTY(Windows::Foundation::IReference<uint64_t>, Id);
|
||||
GETSET_PROPERTY(bool, ShouldCreateWindow, true);
|
||||
WINRT_PROPERTY(Windows::Foundation::IReference<uint64_t>, Id);
|
||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||
WINRT_PROPERTY(bool, ShouldCreateWindow, true);
|
||||
|
||||
public:
|
||||
ProposeCommandlineResult(bool shouldCreateWindow) :
|
||||
|
||||
@@ -27,10 +27,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
};
|
||||
struct WindowActivatedArgs : public WindowActivatedArgsT<WindowActivatedArgs>
|
||||
{
|
||||
GETSET_PROPERTY(uint64_t, PeasantID, 0);
|
||||
GETSET_PROPERTY(winrt::guid, DesktopID);
|
||||
GETSET_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {});
|
||||
GETSET_PROPERTY(uint64_t, Hwnd, 0);
|
||||
WINRT_PROPERTY(uint64_t, PeasantID, 0);
|
||||
WINRT_PROPERTY(winrt::guid, DesktopID);
|
||||
WINRT_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {});
|
||||
WINRT_PROPERTY(uint64_t, Hwnd, 0);
|
||||
|
||||
public:
|
||||
WindowActivatedArgs(uint64_t peasantID,
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "WindowManager.h"
|
||||
#include "MonarchFactory.h"
|
||||
#include "CommandlineArgs.h"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
#include "FindTargetWindowArgs.h"
|
||||
|
||||
#include "WindowManager.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
@@ -69,6 +71,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// Otherwise, the King will tell us if we should make a new window
|
||||
_shouldCreateWindow = _isKing;
|
||||
std::optional<uint64_t> givenID;
|
||||
winrt::hstring givenName{};
|
||||
if (!_isKing)
|
||||
{
|
||||
// The monarch may respond back "you should be a new
|
||||
@@ -86,7 +89,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
givenID = result.Id().Value();
|
||||
}
|
||||
|
||||
givenName = result.WindowName();
|
||||
// TraceLogging doesn't have a good solution for logging an
|
||||
// optional. So we have to repeat the calls here:
|
||||
if (givenID)
|
||||
@@ -95,6 +98,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
"WindowManager_ProposeCommandline",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"),
|
||||
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
else
|
||||
@@ -103,6 +107,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
"WindowManager_ProposeCommandline",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingPointer(nullptr, "Id", "No ID provided"),
|
||||
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
}
|
||||
@@ -110,17 +115,60 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
// We're the monarch, we don't need to propose anything. We're just
|
||||
// going to do it.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline_AsMonarch",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
//
|
||||
// However, we _do_ need to ask what our name should be. It's
|
||||
// possible someone started the _first_ wt with something like `wt
|
||||
// -w king` as the commandline - we want to make sure we set our
|
||||
// name to "king".
|
||||
//
|
||||
// The FindTargetWindow event is the WindowManager's way of saying
|
||||
// "I do not know how to figure out how to turn this list of args
|
||||
// into a window ID/name. Whoever's listening to this event does, so
|
||||
// I'll ask them". It's a convoluted way of hooking the
|
||||
// WindowManager up to AppLogic without actually telling it anything
|
||||
// about TerminalApp (or even WindowsTerminal)
|
||||
auto findWindowArgs{ winrt::make_self<Remoting::implementation::FindTargetWindowArgs>(args) };
|
||||
_raiseFindTargetWindowRequested(nullptr, *findWindowArgs);
|
||||
|
||||
const auto responseId = findWindowArgs->ResultTargetWindow();
|
||||
if (responseId > 0)
|
||||
{
|
||||
givenID = ::base::saturated_cast<uint64_t>(responseId);
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline_AsMonarch",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"),
|
||||
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
else if (responseId == WindowingBehaviorUseName)
|
||||
{
|
||||
givenName = findWindowArgs->ResultTargetWindowName();
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline_AsMonarch",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingUInt64(0, "Id", "The ID we should assign our peasant"),
|
||||
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline_AsMonarch",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingUInt64(0, "Id", "The ID we should assign our peasant"),
|
||||
TraceLoggingWideString(L"", "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
}
|
||||
|
||||
if (_shouldCreateWindow)
|
||||
{
|
||||
// If we should create a new window, then instantiate our Peasant
|
||||
// instance, and tell that peasant to handle that commandline.
|
||||
_createOurPeasant({ givenID });
|
||||
_createOurPeasant({ givenID }, givenName);
|
||||
|
||||
// Spawn a thread to wait on the monarch, and handle the election
|
||||
if (!_isKing)
|
||||
@@ -208,13 +256,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
return (ourPID == kingPID);
|
||||
}
|
||||
|
||||
Remoting::IPeasant WindowManager::_createOurPeasant(std::optional<uint64_t> givenID)
|
||||
Remoting::IPeasant WindowManager::_createOurPeasant(std::optional<uint64_t> givenID,
|
||||
const winrt::hstring& givenName)
|
||||
{
|
||||
auto p = winrt::make_self<Remoting::implementation::Peasant>();
|
||||
if (givenID)
|
||||
{
|
||||
p->AssignID(givenID.value());
|
||||
}
|
||||
|
||||
// If the name wasn't specified, this will be an empty string.
|
||||
p->WindowName(givenName);
|
||||
_peasant = *p;
|
||||
|
||||
// Try to add us to the monarch. If that fails, try to find a monarch
|
||||
|
||||
@@ -54,7 +54,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void _createMonarch();
|
||||
void _createMonarchAndCallbacks();
|
||||
bool _areWeTheKing();
|
||||
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional<uint64_t> givenID);
|
||||
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional<uint64_t> givenID,
|
||||
const winrt::hstring& givenName);
|
||||
|
||||
bool _performElection();
|
||||
void _createPeasantThread();
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<!-- ========================= Misc Files ======================== -->
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Microsoft.Terminal.Remoting.def" />
|
||||
<None Include="$(ProjectName).def" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Project References ======================== -->
|
||||
<ItemGroup>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="WindowsTerminalShellExt.def" />
|
||||
<None Include="$(ProjectName).def" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Project References ======================== -->
|
||||
<ItemGroup>
|
||||
@@ -49,6 +49,7 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)\build\rules\Branding.targets" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<!-- Override GetPackagingOutputs to roll up our DLL.
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace winrt::TerminalApp::implementation
|
||||
ActionPaletteItem() = default;
|
||||
ActionPaletteItem(Microsoft::Terminal::Settings::Model::Command const& command);
|
||||
|
||||
GETSET_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr);
|
||||
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _commandChangedRevoker;
|
||||
|
||||
@@ -35,8 +35,8 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
is unfortunately static. -->
|
||||
<SolidColorBrush x:Name="ErrorTextBrush" Color="{ThemeResource SystemErrorTextColor}" />
|
||||
|
||||
<!-- Suppress top padding -->
|
||||
<Thickness x:Key="TabViewHeaderPadding">8,0,8,0</Thickness>
|
||||
<!-- Suppress all padding except left around the tabs. The TabView looks far better like this. -->
|
||||
<Thickness x:Key="TabViewHeaderPadding">8,0,0,0</Thickness>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleDuplicateTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_DuplicateTabViewItem();
|
||||
_DuplicateFocusedTab();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
@@ -89,15 +89,23 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleNextTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_SelectNextTab(true);
|
||||
args.Handled(true);
|
||||
const auto& realArgs = args.ActionArgs().try_as<NextTabArgs>();
|
||||
if (realArgs)
|
||||
{
|
||||
_SelectNextTab(true, realArgs.SwitcherMode());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandlePrevTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_SelectNextTab(false);
|
||||
args.Handled(true);
|
||||
const auto& realArgs = args.ActionArgs().try_as<PrevTabArgs>();
|
||||
if (realArgs)
|
||||
{
|
||||
_SelectNextTab(false, realArgs.SwitcherMode());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSendInput(const IInspectable& /*sender*/,
|
||||
@@ -244,8 +252,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SwitchToTabArgs>())
|
||||
{
|
||||
_SelectTab({ realArgs.TabIndex() });
|
||||
args.Handled(true);
|
||||
const auto handled = _SelectTab({ realArgs.TabIndex() });
|
||||
args.Handled(handled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +384,7 @@ namespace winrt::TerminalApp::implementation
|
||||
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
|
||||
{
|
||||
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
controlSettings->ApplyColorScheme(scheme);
|
||||
controlSettings.ApplyColorScheme(scheme);
|
||||
activeControl.UpdateSettings();
|
||||
args.Handled(true);
|
||||
}
|
||||
@@ -598,8 +606,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Important: Don't take the param by reference, since we'll be doing work
|
||||
// on another thread.
|
||||
fire_and_forget _OpenNewWindow(const bool elevate,
|
||||
const NewTerminalArgs newTerminalArgs)
|
||||
fire_and_forget TerminalPage::_OpenNewWindow(const bool elevate,
|
||||
const NewTerminalArgs newTerminalArgs)
|
||||
{
|
||||
// Hop to the BG thread
|
||||
co_await winrt::resume_background();
|
||||
@@ -658,9 +666,8 @@ namespace winrt::TerminalApp::implementation
|
||||
newTerminalArgs = NewTerminalArgs();
|
||||
}
|
||||
|
||||
auto [profileGuid, settings] = TerminalSettings::BuildSettings(_settings,
|
||||
newTerminalArgs,
|
||||
*_bindings);
|
||||
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
|
||||
// Manually fill in the evaluated profile.
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profileGuid));
|
||||
|
||||
@@ -185,9 +185,9 @@ void AppCommandlineArgs::_buildParser()
|
||||
maximized->excludes(fullscreen);
|
||||
focus->excludes(fullscreen);
|
||||
|
||||
_app.add_option<std::optional<int>, int>("-w,--window",
|
||||
_windowTarget,
|
||||
RS_A(L"CmdWindowTargetArgDesc"));
|
||||
_app.add_option("-w,--window",
|
||||
_windowTarget,
|
||||
RS_A(L"CmdWindowTargetArgDesc"));
|
||||
|
||||
// Subcommands
|
||||
_buildNewTabParser();
|
||||
@@ -417,6 +417,11 @@ void AppCommandlineArgs::_addNewTerminalArgs(AppCommandlineArgs::NewTerminalSubc
|
||||
_startingTabColor,
|
||||
RS_A(L"CmdTabColorArgDesc"));
|
||||
|
||||
subcommand.suppressApplicationTitleOption = subcommand.subcommand->add_flag(
|
||||
"--suppressApplicationTitle,!--useApplicationTitle",
|
||||
_suppressApplicationTitle,
|
||||
RS_A(L"CmdSuppressApplicationTitleDesc"));
|
||||
|
||||
// Using positionals_at_end allows us to support "wt new-tab -d wsl -d Ubuntu"
|
||||
// without CLI11 thinking that we've specified -d twice.
|
||||
// There's an alternate construction where we make all subcommands "prefix commands",
|
||||
@@ -484,6 +489,11 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT
|
||||
args.TabColor(static_cast<winrt::Windows::UI::Color>(tabColor));
|
||||
}
|
||||
|
||||
if (*subcommand.suppressApplicationTitleOption)
|
||||
{
|
||||
args.SuppressApplicationTitle(_suppressApplicationTitle);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -522,6 +532,7 @@ void AppCommandlineArgs::_resetStateToDefault()
|
||||
_startingTitle.clear();
|
||||
_startingTabColor.clear();
|
||||
_commandline.clear();
|
||||
_suppressApplicationTitle = false;
|
||||
|
||||
_splitVertical = false;
|
||||
_splitHorizontal = false;
|
||||
@@ -854,16 +865,10 @@ void AppCommandlineArgs::FullResetState()
|
||||
_exitMessage = "";
|
||||
_shouldExitEarly = false;
|
||||
|
||||
_windowTarget = std::nullopt;
|
||||
_windowTarget = {};
|
||||
}
|
||||
|
||||
std::optional<int> AppCommandlineArgs::GetTargetWindow() const noexcept
|
||||
std::string_view AppCommandlineArgs::GetTargetWindow() const noexcept
|
||||
{
|
||||
// If the user provides _any_ negative number, then treat it as -1, for "use a new window".
|
||||
if (_windowTarget.has_value() && *_windowTarget < 0)
|
||||
{
|
||||
return { -1 };
|
||||
}
|
||||
|
||||
return _windowTarget;
|
||||
}
|
||||
|
||||