Compare commits
113 Commits
dev/miniks
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a223d3f5fa | ||
|
|
efd305e9eb | ||
|
|
428f2a752b | ||
|
|
37850bfe7f | ||
|
|
3042ac5d2c | ||
|
|
1934e72d6e | ||
|
|
caeb5b5db7 | ||
|
|
cedbee42e0 | ||
|
|
7f63434055 | ||
|
|
bbc4a904ee | ||
|
|
dbf9ee93a3 | ||
|
|
ebabf32f71 | ||
|
|
6617eb33ac | ||
|
|
4a3f173e17 | ||
|
|
646904c3c2 | ||
|
|
cb4c4f7b73 | ||
|
|
3c64fcd12b | ||
|
|
7264d5cc43 | ||
|
|
5756236362 | ||
|
|
6e907fd5f2 | ||
|
|
afd6a87de5 | ||
|
|
a3de0d0ece | ||
|
|
a2ab200e88 | ||
|
|
90e78ec074 | ||
|
|
3c30877294 | ||
|
|
e593c46679 | ||
|
|
a736712da9 | ||
|
|
0499604eee | ||
|
|
c8ab75b265 | ||
|
|
b345d39c1d | ||
|
|
c4e02e7274 | ||
|
|
cb7f481c95 | ||
|
|
69397b3797 | ||
|
|
e7ec200f34 | ||
|
|
ba07f65eb9 | ||
|
|
0e810d2c35 | ||
|
|
ed3feef213 | ||
|
|
ca71ac4100 | ||
|
|
fba9493dbb | ||
|
|
ac864d2c65 | ||
|
|
89032b8b1c | ||
|
|
c2cb365c62 | ||
|
|
76a609b3ae | ||
|
|
99e0f95318 | ||
|
|
2c0889223a | ||
|
|
aad3855287 | ||
|
|
4c09a235cc | ||
|
|
8078db5fd5 | ||
|
|
f2afb223f5 | ||
|
|
9882e9f835 | ||
|
|
6072be5436 | ||
|
|
bc368b7c29 | ||
|
|
2219014385 | ||
|
|
b8e36bae9f | ||
|
|
7df4b3c823 | ||
|
|
8f79f7c4c8 | ||
|
|
83bd241cd2 | ||
|
|
9a276c6371 | ||
|
|
ca9e5e0fb0 | ||
|
|
912bd4dadb | ||
|
|
a9a58f7156 | ||
|
|
d367c6b6b0 | ||
|
|
959c423e7a | ||
|
|
b310b1cffc | ||
|
|
9e83655b08 | ||
|
|
7f5a19b627 | ||
|
|
8e7a866b06 | ||
|
|
ed1cd32f1f | ||
|
|
361877cf1b | ||
|
|
cdf2630204 | ||
|
|
c3f968b6c6 | ||
|
|
e80e9b9e96 | ||
|
|
6ca35b4445 | ||
|
|
b0c07ef1eb | ||
|
|
faf372f165 | ||
|
|
ebd07d7125 | ||
|
|
24b9a7a247 | ||
|
|
6f754a61eb | ||
|
|
fb597ed304 | ||
|
|
4b7d955012 | ||
|
|
fd99b012f5 | ||
|
|
c09472347c | ||
|
|
07c5735471 | ||
|
|
940254dd57 | ||
|
|
9d729a50b0 | ||
|
|
8f16fdd817 | ||
|
|
5ab78fcafb | ||
|
|
69df0de6bf | ||
|
|
295fa38295 | ||
|
|
03ea0f49ad | ||
|
|
c585a93fc9 | ||
|
|
19fb9b21da | ||
|
|
c7d2a818b0 | ||
|
|
cf5dc285a9 | ||
|
|
3323dc5724 | ||
|
|
c19aa89123 | ||
|
|
d6954244ad | ||
|
|
477c4d9986 | ||
|
|
b67f40f4c4 | ||
|
|
25527789c0 | ||
|
|
eac3eea484 | ||
|
|
ba543c0696 | ||
|
|
8470857016 | ||
|
|
704836e45d | ||
|
|
5a78566628 | ||
|
|
806d992a06 | ||
|
|
12275c8599 | ||
|
|
ea3e56db81 | ||
|
|
906edf7002 | ||
|
|
690fbbbc27 | ||
|
|
005b8cc5e0 | ||
|
|
e02d9a48e3 | ||
|
|
da1e1a693e |
12
.config/dotnet-tools.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"XamlStyler.Console": {
|
||||
"version": "3.2008.4",
|
||||
"commands": [
|
||||
"xstyler"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="vswhere" version="2.6.7" />
|
||||
</packages>
|
||||
</packages>
|
||||
|
||||
17
.vscode/tasks.json
vendored
@@ -16,7 +16,9 @@
|
||||
"${workspaceFolder}\\OpenConsole.sln",
|
||||
"/p:Configuration=${input:configChoice}",
|
||||
"/p:Platform=${input:platformChoice}",
|
||||
"/p:AppxSymbolPackageEnabled=false", // This takes a long time, so false if we don't really need it.
|
||||
"/t:$target",
|
||||
"/m", // Parallel builds
|
||||
"/verbosity:minimal"
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
@@ -46,8 +48,7 @@
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
"kind": "build"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -57,6 +58,18 @@
|
||||
"args": [
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
},
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Run Code Format",
|
||||
"command": "powershell.exe",
|
||||
"args": [
|
||||
"-Command",
|
||||
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
|
||||
"Set-MsBuildDevEnvironment;",
|
||||
"Invoke-CodeFormat",
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
}
|
||||
],
|
||||
"inputs":[
|
||||
|
||||
722
OpenConsole.sln
41
XamlStyler.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"AttributesTolerance": 1,
|
||||
"KeepFirstAttributeOnSameLine": true,
|
||||
"MaxAttributeCharactersPerLine": 0,
|
||||
"MaxAttributesPerLine": 1,
|
||||
"NewlineExemptionElements": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransform, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter",
|
||||
"SeparateByGroups": false,
|
||||
"AttributeIndentation": 0,
|
||||
"AttributeIndentationStyle": 1,
|
||||
"RemoveDesignTimeReferences": false,
|
||||
"EnableAttributeReordering": true,
|
||||
"AttributeOrderingRuleGroups": [
|
||||
"x:Class",
|
||||
"xmlns, xmlns:x",
|
||||
"xmlns:*",
|
||||
"x:Key, Key, x:Name, Name, x:Uid, Uid, Title",
|
||||
"Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom",
|
||||
"Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight",
|
||||
"Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex",
|
||||
"*:*, *",
|
||||
"PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint",
|
||||
"mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText",
|
||||
"Storyboard.*, From, To, Duration"
|
||||
],
|
||||
"FirstLineAttributes": "",
|
||||
"OrderAttributesByName": true,
|
||||
"PutEndingBracketOnNewLine": false,
|
||||
"RemoveEndingTagOfEmptyElement": true,
|
||||
"SpaceBeforeClosingSlash": true,
|
||||
"RootElementLineBreakRule": 0,
|
||||
"ReorderVSM": 2,
|
||||
"ReorderGridChildren": false,
|
||||
"ReorderCanvasChildren": false,
|
||||
"ReorderSetters": 0,
|
||||
"FormatMarkupExtension": true,
|
||||
"NoNewLineMarkupExtensions": "x:Bind, Binding",
|
||||
"ThicknessSeparator": 2,
|
||||
"ThicknessAttributes": "Margin, Padding, BorderThickness, ThumbnailClipMargin",
|
||||
"FormatOnSave": true,
|
||||
"CommentPadding": 2,
|
||||
}
|
||||
@@ -19,8 +19,8 @@ New-Item -ItemType Directory -Force -Path $payloadDir
|
||||
|
||||
# Copy files from nuget packages
|
||||
Copy-Item "$nugetPackagesDir\microsoft.windows.apps.test.1.0.181203002\lib\netcoreapp2.1\*.dll" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$Platform\*" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$Platform\CoreClr\*" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\*" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\CoreClr\*" $payloadDir
|
||||
New-Item -ItemType Directory -Force -Path "$payloadDir\.NETCoreApp2.1\"
|
||||
Copy-Item "$nugetPackagesDir\runtime.win-$Platform.microsoft.netcore.app.2.1.0\runtimes\win-$Platform\lib\netcoreapp2.1\*" "$payloadDir\.NETCoreApp2.1\"
|
||||
Copy-Item "$nugetPackagesDir\runtime.win-$Platform.microsoft.netcore.app.2.1.0\runtimes\win-$Platform\native\*" "$payloadDir\.NETCoreApp2.1\"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
|
||||
<package id="TAEF.Redist.Wlk" version="10.57.200731005-develop" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.58.210305002" targetFramework="native" />
|
||||
<package id="microsoft.windows.apps.test" version="1.0.181203002" targetFramework="native" />
|
||||
<package id="runtime.win-x86.microsoft.netcore.app" version="2.1.0" targetFramework="native" />
|
||||
<package id="runtime.win-x64.microsoft.netcore.app" version="2.1.0" targetFramework="native" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
|
||||
<package id="TAEF.Redist.Wlk" version="10.57.200731005-develop" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.58.210305002" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -12,4 +12,4 @@ steps:
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\Helix\GenerateTestProjFile.ps1
|
||||
arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\taef.redist.wlk.10.57.200731005-develop\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'
|
||||
arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
matrix: ${{ parameters.matrix }}
|
||||
variables:
|
||||
artifactsDir: $(Build.SourcesDirectory)\Artifacts
|
||||
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$(buildPlatform)
|
||||
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\$(buildPlatform)
|
||||
helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}'
|
||||
|
||||
|
||||
|
||||
97
build/rules/GenerateFeatureFlags.proj
Normal file
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- THIS PROJECT CANNOT BE LOADED INTO THE SOLUTION. -->
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Release|Any CPU">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Fuzzing|Any CPU">
|
||||
<Configuration>Fuzzing</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="AuditMode|Any CPU">
|
||||
<Configuration>AuditMode</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Any CPU">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>d97c3c61-53cd-4e72-919b-9a0940e038f9</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<IntermediateOutputPath>$(SolutionDir)obj\$(Configuration)\GenerateFeatureFlags\</IntermediateOutputPath>
|
||||
<OpenConsoleCommonOutDir>$(SolutionDir)bin\$(Configuration)\</OpenConsoleCommonOutDir>
|
||||
|
||||
<_WTBrandingName Condition="'$(WindowsTerminalBranding)'=='Preview'">Preview</_WTBrandingName>
|
||||
<_WTBrandingName Condition="'$(WindowsTerminalBranding)'=='Release'">Release</_WTBrandingName>
|
||||
<_WTBrandingName Condition="'$(_WTBrandingName)'==''">Dev</_WTBrandingName>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="_GenerateBranchAndBrandingCache">
|
||||
<Exec Command="git.exe rev-parse --abbrev-ref HEAD"
|
||||
CustomWarningRegularExpression="^fatal:.*"
|
||||
ConsoleToMsBuild="true"
|
||||
IgnoreExitCode="true">
|
||||
<Output TaskParameter="ConsoleOutput" ItemName="_GitBranchLines" />
|
||||
</Exec>
|
||||
|
||||
<ItemGroup>
|
||||
<_BrandingLines Include="$(_WTBrandingName)" />
|
||||
</ItemGroup>
|
||||
|
||||
<WriteLinesToFile File="$(IntermediateOutputPath)branch_branding_cache.txt"
|
||||
Lines="@(_GitBranchLines);@(_BrandingLines)"
|
||||
Overwrite="true"
|
||||
WriteOnlyWhenDifferent="true" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(IntermediateOutputPath)branch_branding_cache.txt" />
|
||||
<_BranchBrandingCacheFiles Include="$(IntermediateOutputPath)branch_branding_cache.txt" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_RunFeatureFlagScript"
|
||||
Inputs="@(FeatureFlagFile);@(_BranchBrandingCacheFiles)"
|
||||
Outputs="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h"
|
||||
DependsOnTargets="_GenerateBranchAndBrandingCache">
|
||||
<MakeDir Directories="$(OpenConsoleCommonOutDir)\inc" />
|
||||
<Exec
|
||||
Command="powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy ByPass -Command "$(SolutionDir)\tools\Generate-FeatureStagingHeader.ps1" -Path "%(FeatureFlagFile.FullPath)" -Branding $(_WTBrandingName)"
|
||||
ConsoleToMsBuild="true"
|
||||
StandardOutputImportance="low">
|
||||
<Output TaskParameter="ConsoleOutput" ItemName="_FeatureFlagFileLines" />
|
||||
</Exec>
|
||||
|
||||
<!--
|
||||
We gather the feature flag output in MSBuild and emit the file so that we can take advantage of
|
||||
WriteOnlyWhenDifferent. Doing this ensures that we don't rebuild the world when the branch changes
|
||||
(if it results in a new TilFeatureStaging.h that would have had the same content/features as the previous one)
|
||||
-->
|
||||
|
||||
<WriteLinesToFile File="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h"
|
||||
Lines="@(_FeatureFlagFileLines)"
|
||||
Overwrite="true"
|
||||
WriteOnlyWhenDifferent="true" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="Build" DependsOnTargets="_RunFeatureFlagScript" />
|
||||
<Target Name="Clean">
|
||||
<Delete Files="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<FeatureFlagFile Include="$(SolutionDir)\src\features.xml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -3,12 +3,24 @@
|
||||
# Checks for code formatting errors. Will throw exception if any are found.
|
||||
function Invoke-CheckBadCodeFormatting() {
|
||||
Import-Module ./tools/OpenConsole.psm1
|
||||
Invoke-CodeFormat
|
||||
|
||||
# Don't run the XAML formatter in this step - even if it changes nothing,
|
||||
# it'll still touch all the .xaml files.
|
||||
Invoke-CodeFormat -IgnoreXaml
|
||||
|
||||
# returns a non-zero exit code if there are any diffs in the tracked files in the repo
|
||||
git diff-index --quiet HEAD --
|
||||
if ($lastExitCode -eq 1) {
|
||||
|
||||
# Write the list of files that need updating to the log
|
||||
git diff-index --name-only HEAD
|
||||
|
||||
throw "code formatting bad, run Invoke-CodeFormat on branch"
|
||||
}
|
||||
|
||||
# Manually check the formatting of our .xaml files, without touching them.
|
||||
Verify-XamlFormat
|
||||
|
||||
}
|
||||
|
||||
Invoke-CheckBadCodeFormatting
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"/.github/",
|
||||
"/samples/",
|
||||
"/res/terminal/",
|
||||
"/res/fonts/",
|
||||
"/doc/specs/",
|
||||
"/doc/cascadia/",
|
||||
"/doc/user-docs/",
|
||||
@@ -38,6 +39,7 @@
|
||||
".wrn",
|
||||
".rec",
|
||||
".err",
|
||||
"XamlStyler.json",
|
||||
".xlsx"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ Use the [TAEF Verify Macros for C++](https://docs.microsoft.com/en-us/windows-ha
|
||||
|
||||
### Running Tests
|
||||
|
||||
If you have Visual Studio and related C++ components installed, and you have successfully restored NuGets, you should have the TAEF test runner `te.exe` available locally as part of the `Taef.Redist.Wlk` package.
|
||||
If you have Visual Studio and related C++ components installed, and you have successfully restored NuGets, you should have the TAEF test runner `te.exe` available locally as part of the `Microsoft.Taef` package.
|
||||
|
||||
> Note that you cannot easily run TAEF tests directly through Visual Studio. The `Taef.Redist.Wlk` NuGet package comes with an adapter that will let you browse and execute TAEF tests inside of Visual Studio, but its performance and reliability prevent us from recommending it here.
|
||||
> Note that you cannot easily run TAEF tests directly through Visual Studio. The `Microsoft.Taef` NuGet package comes with an adapter that will let you browse and execute TAEF tests inside of Visual Studio, but its performance and reliability prevent us from recommending it here.
|
||||
|
||||
In a "normal" CMD environment, `te.exe` may not be directly available. Try the following command to set up the development enviroment first:
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ should be working just the same as before.
|
||||
Now that you have a static library project, you can start building your unittest
|
||||
dll. Start by creating a new directory for your unittest code, and creating a
|
||||
`.vcxproj` for a TAEF unittest dll. For the Terminal solution, we use the TAEF
|
||||
nuget package `Taef.Redist.Wlk`.
|
||||
nuget package `Microsoft.Taef`.
|
||||
|
||||
### Referencing your C++/WinRT static lib
|
||||
|
||||
|
||||
@@ -52,6 +52,117 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"AppearanceConfig": {
|
||||
"properties": {
|
||||
"colorScheme": {
|
||||
"description": "The name of a color scheme to use when unfocused.",
|
||||
"type": "string"
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#cccccc",
|
||||
"description": "Sets the text color when unfocused. Overrides \"foreground\" from the color scheme. Uses hex color format: \"#rrggbb\".",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"background": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#0c0c0c",
|
||||
"description": "Sets the background color of the text when unfocused. Overrides \"background\" from the color scheme. Uses hex color format: \"#rrggbb\".",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"selectionBackground": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/definitions/Color"},
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "Sets the background color of selected text when unfocused. Overrides selectionBackground set in the color scheme. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"cursorColor": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/Color" },
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "Sets the color of the cursor when unfocused. Overrides the cursor color from the color scheme. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"cursorShape": {
|
||||
"default": "bar",
|
||||
"description": "Sets the shape of the cursor when unfocused. Possible values:\n -\"bar\" ( ┃, default )\n -\"doubleUnderscore\" ( ‗ )\n -\"emptyBox\" ( ▯ )\n -\"filledBox\" ( █ )\n -\"underscore\" ( ▁ )\n -\"vintage\" ( ▃ )",
|
||||
"enum": [
|
||||
"bar",
|
||||
"doubleUnderscore",
|
||||
"emptyBox",
|
||||
"filledBox",
|
||||
"underscore",
|
||||
"vintage"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"cursorHeight": {
|
||||
"description": "Sets the percentage height of the cursor (when unfocused) starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 1-100.",
|
||||
"maximum": 100,
|
||||
"minimum": 1,
|
||||
"type": ["integer","null"],
|
||||
"default": 25
|
||||
},
|
||||
"backgroundImage": {
|
||||
"description": "Sets the file location of the image to draw over the window background when unfocused.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": ["string", null]
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"desktopWallpaper"
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": [ "string", "null" ]
|
||||
},
|
||||
"backgroundImageOpacity": {
|
||||
"default": 1.0,
|
||||
"description": "Sets the transparency of the background image when unfocused. Accepts floating point values from 0-1.",
|
||||
"maximum": 1.0,
|
||||
"minimum": 0.0,
|
||||
"type": "number"
|
||||
},
|
||||
"backgroundImageStretchMode": {
|
||||
"default": "uniformToFill",
|
||||
"description": "Sets how the background image is resized to fill the window when unfocused.",
|
||||
"enum": [
|
||||
"fill",
|
||||
"none",
|
||||
"uniform",
|
||||
"uniformToFill"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"backgroundImageAlignment": {
|
||||
"default": "center",
|
||||
"enum": [
|
||||
"bottom",
|
||||
"bottomLeft",
|
||||
"bottomRight",
|
||||
"center",
|
||||
"left",
|
||||
"right",
|
||||
"top",
|
||||
"topLeft",
|
||||
"topRight"
|
||||
],
|
||||
"description": "Sets how the background image aligns to the boundaries of the window when unfocused. Possible values: \"center\", \"left\", \"top\", \"right\", \"bottom\", \"topLeft\", \"topRight\", \"bottomLeft\", \"bottomRight\"",
|
||||
"type": "string"
|
||||
},
|
||||
"experimental.retroTerminalEffect": {
|
||||
"description": "When set to true, enable retro terminal effects when unfocused. This is an experimental feature, and its continued existence is not guaranteed.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.pixelShaderPath": {
|
||||
"description": "Use to set a path to a pixel shader to use with the Terminal when unfocused. Overrides `experimental.retroTerminalEffect`. This is an experimental feature, and its continued existence is not guaranteed.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ProfileGuid": {
|
||||
"default": "{}",
|
||||
"pattern": "^\\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\\}$",
|
||||
@@ -77,6 +188,8 @@
|
||||
"duplicateTab",
|
||||
"find",
|
||||
"findMatch",
|
||||
"identifyWindow",
|
||||
"identifyWindows",
|
||||
"moveFocus",
|
||||
"moveTab",
|
||||
"newTab",
|
||||
@@ -85,12 +198,14 @@
|
||||
"openNewTabDropdown",
|
||||
"openSettings",
|
||||
"openTabColorPicker",
|
||||
"openWindowRenamer",
|
||||
"paste",
|
||||
"prevTab",
|
||||
"renameTab",
|
||||
"openTabRenamer",
|
||||
"resetFontSize",
|
||||
"resizePane",
|
||||
"renameWindow",
|
||||
"scrollDown",
|
||||
"scrollDownPage",
|
||||
"scrollUp",
|
||||
@@ -227,6 +342,10 @@
|
||||
"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"
|
||||
},
|
||||
"colorScheme": {
|
||||
"description": "The name of a color scheme to use, instead of the one specified by the profile",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -422,12 +541,14 @@
|
||||
"action": { "type": "string", "pattern": "openSettings" },
|
||||
"target": {
|
||||
"type": "string",
|
||||
"default": "settingsFile",
|
||||
"description": "The settings file to open.",
|
||||
"default": "settingsUI",
|
||||
"description": "Opens Settings UI or settings file.",
|
||||
"enum": [
|
||||
"settingsFile",
|
||||
"defaultsFile",
|
||||
"allFiles"
|
||||
"allFiles",
|
||||
"settingsUI"
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -647,6 +768,38 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"RenameTabAction": {
|
||||
"description": "Arguments corresponding to a renameTab Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "renameTab" },
|
||||
"title": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "A title to assign to the tab. If omitted or null, this action will restore the tab's title to the original value."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"RenameWindowAction": {
|
||||
"description": "Arguments corresponding to a renameWindow Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "renameWindow" },
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "A name to assign to the window."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -675,6 +828,8 @@
|
||||
{ "$ref": "#/definitions/NewWindowAction" },
|
||||
{ "$ref": "#/definitions/NextTabAction" },
|
||||
{ "$ref": "#/definitions/PrevTabAction" },
|
||||
{ "$ref": "#/definitions/RenameTabAction" },
|
||||
{ "$ref": "#/definitions/RenameWindowAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
@@ -958,6 +1113,11 @@
|
||||
"description": "Sets the background color of the text. Overrides \"background\" from the color scheme. Uses hex color format: \"#rrggbb\".",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"unfocusedAppearance": {
|
||||
"$ref": "#/definitions/AppearanceConfig",
|
||||
"description": "Sets the appearance of the terminal when it is unfocused.",
|
||||
"type": ["object", "null"]
|
||||
},
|
||||
"backgroundImage": {
|
||||
"description": "Sets the file location of the image to draw over the window background.",
|
||||
"oneOf": [
|
||||
@@ -1189,6 +1349,11 @@
|
||||
"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"]
|
||||
|
||||
65
doc/feature_flags.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# til::feature
|
||||
|
||||
Feature flags are controlled by an XML document stored at `src/features.xml`.
|
||||
|
||||
## Example Document
|
||||
|
||||
```xml
|
||||
<?xml version="1.0"?>
|
||||
<featureStaging xmlns="http://microsoft.com/TilFeatureStaging-Schema.xsd">
|
||||
<feature>
|
||||
<!-- This will produce Feature_XYZ::IsEnabled() and TIL_FEATURE_XYZ_ENABLED (preprocessor) -->
|
||||
<name>Feature_XYZ</name>
|
||||
|
||||
<description>Does a cool thing</description>
|
||||
|
||||
<!-- GitHub deliverable number; optional -->
|
||||
<id>1234</id>
|
||||
|
||||
<!-- Whether the feature defaults to enabled or disabled -->
|
||||
<stage>AlwaysEnabled|AlwaysDisabled</stage>
|
||||
|
||||
<!-- Branch wildcards where the feature should be *DISABLED* -->
|
||||
<alwaysDisabledBranchTokens>
|
||||
<branchToken>branch/with/wildcard/*</branchToken>
|
||||
<!-- ... more branchTokens ... -->
|
||||
</alwaysDisabledBranchTokens>
|
||||
|
||||
<!-- Just like alwaysDisabledBranchTokens, but for *ENABLING* the feature. -->
|
||||
<alwaysEnabledBranchTokens>
|
||||
<branchToken>...</branchToken>
|
||||
</alwaysEnabledBranchTokens>
|
||||
|
||||
<!-- Brandings where the feature should be *DISABLED* -->
|
||||
<alwaysDisabledBrandingTokens>
|
||||
<!-- Valid brandings include Dev, Preview, Release, WindowsInbox -->
|
||||
<brandingToken>Release</brandingToken>
|
||||
<!-- ... more brandingTokens ... -->
|
||||
</alwaysDisabledBrandingTokens>
|
||||
|
||||
<!-- Just like alwaysDisabledBrandingTokens, but for *ENABLING* the feature -->
|
||||
<alwaysEnabledBrandingTokens>
|
||||
<branchToken>...</branchToken>
|
||||
</alwaysEnabledBrandingTokens>
|
||||
|
||||
<!-- Unequivocally disable this feature in Release -->
|
||||
<alwaysDisabledReleaseTokens />
|
||||
</feature>
|
||||
</featureStaging>
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
Features that are disabled for Release using `alwaysDisabledReleaseTokens` are
|
||||
*always* disabled in Release, even if they come from a branch that would have
|
||||
been enabled by the wildcard.
|
||||
|
||||
### Precedence
|
||||
|
||||
1. `alwaysDisabledReleaseTokens`
|
||||
2. Enabled branches
|
||||
3. Disabled branches
|
||||
* The longest branch token that matches your branch will win.
|
||||
3. Enabled brandings
|
||||
4. Disabled brandings
|
||||
5. The feature's default state
|
||||
BIN
res/Cascadia.ttf
@@ -6,16 +6,3 @@ The images in this directory do not fall under the same [license](https://raw.gi
|
||||
of the Windows Terminal code.
|
||||
|
||||
Please consult the [license](./LICENSE) in this directory for terms applicable to the image assets in this directory.
|
||||
|
||||
## Fonts
|
||||
|
||||
The fonts in this directory do not fall under the same [license](https://raw.githubusercontent.com/microsoft/terminal/main/LICENSE) as the rest
|
||||
of the Windows Terminal code.
|
||||
|
||||
Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadia-code/main/LICENSE) in the
|
||||
[microsoft/cascadia-code](https://github.com/microsoft/cascadia-code) repository for terms applicable to the fonts in this directory.
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code, Cascadia Mono (2102.25)
|
||||
* from microsoft/cascadia-code@911dc421f333e3b72b97381d16fee5b71eb48f04
|
||||
|
||||
BIN
res/fonts/CascadiaCode.ttf
Normal file
BIN
res/fonts/CascadiaCodeItalic.ttf
Normal file
BIN
res/fonts/CascadiaMono.ttf
Normal file
BIN
res/fonts/CascadiaMonoItalic.ttf
Normal file
12
res/fonts/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Windows Terminal and Console Assets (Fonts)
|
||||
|
||||
The fonts in this directory do not fall under the same [license](https://raw.githubusercontent.com/microsoft/terminal/main/LICENSE) as the rest
|
||||
of the Windows Terminal code.
|
||||
|
||||
Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadia-code/main/LICENSE) in the
|
||||
[microsoft/cascadia-code](https://github.com/microsoft/cascadia-code) repository for terms applicable to the fonts in this directory.
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code, Cascadia Mono (2106.17)
|
||||
* from microsoft/cascadia-code@fb0bce69c1c12f6c298b8bc1c1d181868f5daa9a
|
||||
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
@@ -1,8 +1,6 @@
|
||||
<Application x:Class="GUIConsole.Wpf.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
<Application x:Class="GUIConsole.Wpf.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources />
|
||||
</Application>
|
||||
|
||||
@@ -1,84 +1,100 @@
|
||||
<Window x:Class="GUIConsole.Wpf.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
|
||||
Title="MainWindow" Height="450" Width="800"
|
||||
Background="#C7000000"
|
||||
AllowsTransparency="True"
|
||||
WindowStyle="None"
|
||||
MouseDown="Window_MouseDown"
|
||||
BorderThickness="1"
|
||||
BorderBrush="LightSlateGray"
|
||||
|
||||
KeyDown="Window_KeyDown"
|
||||
Loaded="Window_Loaded">
|
||||
<Window.Resources>
|
||||
<Style x:Key="TitleBarButtonStyle" TargetType="{x:Type Button}">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Height" Value="32"/>
|
||||
<Setter Property="Width" Value="46"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource TitleBarButtonStyle}">
|
||||
<Setter Property="Content" Value=""/>
|
||||
<!--Remove the default Button template's Triggers, otherwise they'll override our trigger below.-->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border Background="{TemplateBinding Background}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Button.Background" Value="Red"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="TitleBarTitle" Grid.Column="0" VerticalAlignment="Center" Padding="10 0" Foreground="White">
|
||||
GUIConsole
|
||||
</TextBlock>
|
||||
<StackPanel x:Name="TitleBarButtons" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Top">
|
||||
<Button x:Name="MinimizeButton"
|
||||
Click="MinimizeButton_Click"
|
||||
Content=""
|
||||
Style="{StaticResource TitleBarButtonStyle}"/>
|
||||
<Button x:Name="MaximizeRestoreButton"
|
||||
Click="MaximizeRestoreButton_Click"
|
||||
Content=""
|
||||
FontSize="12"
|
||||
Style="{StaticResource TitleBarButtonStyle}"/>
|
||||
<Button x:Name="CloseButton"
|
||||
Click="CloseButton_Click"
|
||||
FontSize="12"
|
||||
Style="{StaticResource CloseButtonStyle}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ScrollViewer x:Name="TerminalHistoryViewer" Grid.Row="1" ScrollChanged="ScrollViewer_ScrollChanged">
|
||||
<TextBlock x:Name="TerminalHistoryBlock" FontFamily="Consolas" TextWrapping="Wrap" Foreground="White"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Window>
|
||||
<Window x:Class="GUIConsole.Wpf.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="MainWindow"
|
||||
Width="800"
|
||||
Height="450"
|
||||
AllowsTransparency="True"
|
||||
Background="#C7000000"
|
||||
BorderBrush="LightSlateGray"
|
||||
BorderThickness="1"
|
||||
KeyDown="Window_KeyDown"
|
||||
Loaded="Window_Loaded"
|
||||
MouseDown="Window_MouseDown"
|
||||
WindowStyle="None"
|
||||
mc:Ignorable="d">
|
||||
<Window.Resources>
|
||||
<Style x:Key="TitleBarButtonStyle"
|
||||
TargetType="{x:Type Button}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="Segoe MDL2 Assets" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="Width" Value="46" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CloseButtonStyle"
|
||||
BasedOn="{StaticResource TitleBarButtonStyle}"
|
||||
TargetType="{x:Type Button}">
|
||||
<Setter Property="Content" Value="" />
|
||||
<!-- Remove the default Button template's Triggers, otherwise they'll override our trigger below. -->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border Background="{TemplateBinding Background}">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Button.Background" Value="Red" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="TitleBarTitle"
|
||||
Grid.Column="0"
|
||||
Padding="10,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="White">
|
||||
GUIConsole
|
||||
</TextBlock>
|
||||
<StackPanel x:Name="TitleBarButtons"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Horizontal">
|
||||
<Button x:Name="MinimizeButton"
|
||||
Click="MinimizeButton_Click"
|
||||
Content=""
|
||||
Style="{StaticResource TitleBarButtonStyle}" />
|
||||
<Button x:Name="MaximizeRestoreButton"
|
||||
Click="MaximizeRestoreButton_Click"
|
||||
Content=""
|
||||
FontSize="12"
|
||||
Style="{StaticResource TitleBarButtonStyle}" />
|
||||
<Button x:Name="CloseButton"
|
||||
Click="CloseButton_Click"
|
||||
FontSize="12"
|
||||
Style="{StaticResource CloseButtonStyle}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ScrollViewer x:Name="TerminalHistoryViewer"
|
||||
Grid.Row="1"
|
||||
ScrollChanged="ScrollViewer_ScrollChanged">
|
||||
<TextBlock x:Name="TerminalHistoryBlock"
|
||||
FontFamily="Consolas"
|
||||
Foreground="White"
|
||||
TextWrapping="Wrap" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
66
samples/PixelShaders/Outlines.hlsl
Normal file
@@ -0,0 +1,66 @@
|
||||
// A minimal pixel shader that outlines text
|
||||
|
||||
// The terminal graphics as a texture
|
||||
Texture2D shaderTexture;
|
||||
SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings {
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
// Resolution of the shaderTexture
|
||||
float2 Resolution;
|
||||
// Background color as rgba
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// Just ignore the pos parameter.
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
// float4 is tuple of 4 floats, rgba
|
||||
float4 color = shaderTexture.Sample(samplerState, tex);
|
||||
|
||||
// Read the color value at some offset, will be used as shadow. For the best
|
||||
// effect, read the colors offset on the left, right, top, bottom of this
|
||||
// fragment, as well as on the corners of this fragment.
|
||||
//
|
||||
// You could get away with fewer samples, but the resulting outlines will be
|
||||
// blurrier.
|
||||
|
||||
//left, right, top, bottom:
|
||||
float4 leftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, 0.0)/Resolution.y);
|
||||
float4 rightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, 0.0)/Resolution.y);
|
||||
float4 topColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 0.0, 1.0)/Resolution.y);
|
||||
float4 bottomColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 0.0, -1.0)/Resolution.y);
|
||||
|
||||
// Corners
|
||||
float4 topLeftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, 1.0)/Resolution.y);
|
||||
float4 topRightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, 1.0)/Resolution.y);
|
||||
float4 bottomLeftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, -1.0)/Resolution.y);
|
||||
float4 bottomRightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, -1.0)/Resolution.y);
|
||||
|
||||
|
||||
// Now, if any of those adjacent cells has text in it, then the *color vec4
|
||||
// will have a non-zero .w (which is used for alpha). Use that alpha value
|
||||
// to add some black to the current fragment.
|
||||
//
|
||||
// This will result in only coloring fragments adjacent to text, but leaving
|
||||
// background images (for example) untouched.
|
||||
float3 outlineColor = float3(0, 0, 0);
|
||||
float4 result = color;
|
||||
result = result + float4(outlineColor, leftColor.w);
|
||||
result = result + float4(outlineColor, rightColor.w);
|
||||
result = result + float4(outlineColor, topColor.w);
|
||||
result = result + float4(outlineColor, bottomColor.w);
|
||||
|
||||
result = result + float4(outlineColor, topLeftColor.w);
|
||||
result = result + float4(outlineColor, topRightColor.w);
|
||||
result = result + float4(outlineColor, bottomLeftColor.w);
|
||||
result = result + float4(outlineColor, bottomRightColor.w);
|
||||
return result;
|
||||
}
|
||||
@@ -43,6 +43,7 @@
|
||||
<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">
|
||||
@@ -66,6 +67,7 @@
|
||||
<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>
|
||||
|
||||
34
src/Terminal.wprp
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<WindowsPerformanceRecorder Version="1.0" Author="Microsoft Corporation" Copyright="Microsoft Corporation" Company="Microsoft Corporation">
|
||||
<Profiles>
|
||||
<EventCollector Id="EventCollector_Terminal" Name="Terminal">
|
||||
<BufferSize Value="64" />
|
||||
<Buffers Value="4" />
|
||||
</EventCollector>
|
||||
<EventProvider Id="EventProvider_TerminalControl" Name="28c82e50-57af-5a86-c25b-e39cd990032b" />
|
||||
<EventProvider Id="EventProvider_TerminalConnection" Name="e912fe7b-eeb6-52a5-c628-abe388e5f792" />
|
||||
<EventProvider Id="EventProvider_TerminalSettingsModel" Name="be579944-4d33-5202-e5d6-a7a57f1935cb" />
|
||||
<EventProvider Id="EventProvider_TerminalApp" Name="24a1622f-7da7-5c77-3303-d850bd1ab2ed" />
|
||||
<EventProvider Id="EventProvider_TerminalWin32Host" Name="56c06166-2e2e-5f4d-7ff3-74f4b78c87d6" />
|
||||
<EventProvider Id="EventProvider_TerminalRemoting" Name="d6f04aad-629f-539a-77c1-73f5c3e4aa7b" />
|
||||
<EventProvider Id="EventProvider_TerminalDirectX" Name="c93e739e-ae50-5a14-78e7-f171e947535d" />
|
||||
<Profile Id="Terminal.Verbose.File" Name="Terminal" Description="Terminal" LoggingMode="File" DetailLevel="Verbose">
|
||||
<Collectors>
|
||||
<EventCollectorId Value="EventCollector_Terminal">
|
||||
<EventProviders>
|
||||
<EventProviderId Value="EventProvider_TerminalControl" />
|
||||
<EventProviderId Value="EventProvider_TerminalConnection" />
|
||||
<EventProviderId Value="EventProvider_TerminalSettingsModel" />
|
||||
<EventProviderId Value="EventProvider_TerminalApp" />
|
||||
<EventProviderId Value="EventProvider_TerminalWin32Host" />
|
||||
<EventProviderId Value="EventProvider_TerminalRemoting" />
|
||||
<EventProviderId Value="EventProvider_TerminalDirectX" />
|
||||
</EventProviders>
|
||||
</EventCollectorId>
|
||||
</Collectors>
|
||||
</Profile>
|
||||
<Profile Id="Terminal.Light.File" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="File" DetailLevel="Light" />
|
||||
<Profile Id="Terminal.Verbose.Memory" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="Memory" DetailLevel="Verbose" />
|
||||
<Profile Id="Terminal.Light.Memory" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="Memory" DetailLevel="Light" />
|
||||
</Profiles>
|
||||
</WindowsPerformanceRecorder>
|
||||
@@ -4,6 +4,36 @@
|
||||
#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.
|
||||
@@ -15,12 +45,46 @@
|
||||
// - <none>, throws exceptions on failures.
|
||||
void ATTR_ROW::Resize(const size_t newWidth)
|
||||
{
|
||||
mybase::resize(gsl::narrow<UINT>(newWidth));
|
||||
}
|
||||
THROW_HR_IF(E_INVALIDARG, 0 == newWidth);
|
||||
|
||||
void ATTR_ROW::Reset(const TextAttribute attr)
|
||||
{
|
||||
mybase::fill(attr);
|
||||
// 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.
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -33,7 +97,7 @@ void ATTR_ROW::Reset(const TextAttribute attr)
|
||||
// - will throw on error
|
||||
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
|
||||
{
|
||||
return mybase::at(gsl::narrow<UINT>(column));
|
||||
return GetAttrByColumn(column, nullptr);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -48,10 +112,73 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
|
||||
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
|
||||
size_t* const pApplies) const
|
||||
{
|
||||
UINT applies = 0;
|
||||
const auto attr = mybase::at(gsl::narrow<UINT>(column), applies);
|
||||
*pApplies = applies;
|
||||
return attr;
|
||||
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();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -61,11 +188,11 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
|
||||
std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
|
||||
{
|
||||
std::vector<uint16_t> ids;
|
||||
for (const auto& run : *this)
|
||||
for (const auto& run : _list)
|
||||
{
|
||||
if (run.IsHyperlink())
|
||||
if (run.GetAttributes().IsHyperlink())
|
||||
{
|
||||
ids.emplace_back(run.GetHyperlinkId());
|
||||
ids.emplace_back(run.GetAttributes().GetHyperlinkId());
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
@@ -80,8 +207,10 @@ std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
|
||||
// - <none>
|
||||
bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
|
||||
{
|
||||
mybase::fill(attr, iStart);
|
||||
return true;
|
||||
size_t const length = _cchRowWidth - iStart;
|
||||
|
||||
const TextAttributeRun run(length, attr);
|
||||
return SUCCEEDED(InsertAttrRuns({ &run, 1 }, iStart, _cchRowWidth - 1, _cchRowWidth));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -94,7 +223,13 @@ bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
|
||||
// - <none>
|
||||
void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept
|
||||
{
|
||||
mybase::replace(toBeReplacedAttr, replaceWith);
|
||||
for (auto& run : _list)
|
||||
{
|
||||
if (run.GetAttributes() == toBeReplacedAttr)
|
||||
{
|
||||
run.SetAttributes(replaceWith);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -113,12 +248,392 @@ 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*/)
|
||||
try
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth)
|
||||
{
|
||||
mybase::replace(iStart, mybase::npos, newAttrs.begin(), newAttrs.end());
|
||||
// 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);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -20,27 +20,37 @@ Revision History:
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "til/rle.h"
|
||||
|
||||
#include "TextAttributeRun.hpp"
|
||||
#include "AttrRowIterator.hpp"
|
||||
|
||||
class ATTR_ROW final : public til::rle<TextAttribute, unsigned int>
|
||||
class ATTR_ROW final
|
||||
{
|
||||
public:
|
||||
using mybase = til::rle<TextAttribute, unsigned int>;
|
||||
using const_iterator = typename AttrRowIterator;
|
||||
|
||||
using const_iterator = mybase::const_iterator;
|
||||
using const_reverse_iterator = mybase::const_reverse_iterator;
|
||||
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
||||
noexcept;
|
||||
|
||||
using mybase::mybase; // use base constructor
|
||||
~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;
|
||||
|
||||
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 unsigned int iStart, const TextAttribute attr);
|
||||
bool SetAttrToEnd(const UINT iStart, const TextAttribute attr);
|
||||
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept;
|
||||
|
||||
void Resize(const size_t newWidth);
|
||||
@@ -50,19 +60,26 @@ public:
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth);
|
||||
|
||||
using mybase::begin;
|
||||
using mybase::cbegin;
|
||||
using mybase::cend;
|
||||
using mybase::end;
|
||||
static std::vector<TextAttributeRun> PackAttrs(const std::vector<TextAttribute>& attrs);
|
||||
|
||||
using mybase::operator==;
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
|
||||
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,23 +22,29 @@ Revision History:
|
||||
|
||||
#include "TextAttribute.hpp"
|
||||
|
||||
class TextAttributeRun final : public std::pair<TextAttribute, unsigned int>
|
||||
class TextAttributeRun final
|
||||
{
|
||||
public:
|
||||
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))
|
||||
TextAttributeRun() = default;
|
||||
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
|
||||
_cchLength(gsl::narrow<unsigned int>(cchLength))
|
||||
{
|
||||
SetAttributes(attr);
|
||||
}
|
||||
|
||||
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--; }
|
||||
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--; }
|
||||
|
||||
const TextAttribute& GetAttributes() const noexcept { return mybase::first; }
|
||||
void SetAttributes(const TextAttribute textAttribute) noexcept { mybase::first = textAttribute; }
|
||||
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
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<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" />
|
||||
@@ -33,6 +34,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\AttrRow.hpp" />
|
||||
<ClInclude Include="..\AttrRowIterator.hpp" />
|
||||
<ClInclude Include="..\cursor.h" />
|
||||
<ClInclude Include="..\DbcsAttribute.hpp" />
|
||||
<ClInclude Include="..\ICharRow.hpp" />
|
||||
|
||||
@@ -30,6 +30,7 @@ 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)
|
||||
{
|
||||
// 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.
|
||||
// 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.
|
||||
|
||||
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;
|
||||
ATTR_ROW::const_iterator _attrIter;
|
||||
AttrRowIterator _attrIter;
|
||||
const TextBuffer& _buffer;
|
||||
const Microsoft::Console::Types::Viewport _bounds;
|
||||
bool _exceeded;
|
||||
|
||||
731
src/buffer/out/ut_textbuffer/AttrRowTests.cpp
Normal file
@@ -0,0 +1,731 @@
|
||||
// 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,10 +6,11 @@
|
||||
<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,6 +14,7 @@ DLLDEF =
|
||||
|
||||
SOURCES = \
|
||||
$(SOURCES) \
|
||||
AttrRowTests.cpp \
|
||||
ReflowTests.cpp \
|
||||
TextColorTests.cpp \
|
||||
TextAttributeTests.cpp \
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WindowsTerminal\WindowsTerminal.vcxproj" />
|
||||
<ProjectReference Include="..\..\host\exe\Host.EXE.vcxproj" />
|
||||
<ProjectReference Include="..\..\host\proxy\Host.Proxy.vcxproj" />
|
||||
<ProjectReference Include="..\TerminalAzBridge\TerminalAzBridge.vcxproj" />
|
||||
<ProjectReference Include="..\ShellExtension\WindowsTerminalShellExt.vcxproj" />
|
||||
<ProjectReference Include="..\wt\wt.vcxproj" />
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
<uap:ShowOn Tile="square310x310Logo"/>
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png"/>
|
||||
</uap:VisualElements>
|
||||
|
||||
<Extensions>
|
||||
@@ -74,9 +73,43 @@
|
||||
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
|
||||
</uap3:AppExtensionHost>
|
||||
</uap3:Extension>
|
||||
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.console.host"
|
||||
Id="OpenConsole-Dev"
|
||||
DisplayName="OpenConsole Dev"
|
||||
Description="Console host built from microsoft/terminal open source repository"
|
||||
PublicFolder="Public">
|
||||
<uap3:Properties>
|
||||
<Clsid>{1F9F2BF5-5BC3-4F17-B0E6-912413F1F451}</Clsid>
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
|
||||
Id="Terminal-Dev"
|
||||
DisplayName="Windows Terminal Dev"
|
||||
Description="Terminal host built from microsoft/terminal open source repository"
|
||||
PublicFolder="Public">
|
||||
<uap3:Properties>
|
||||
<Clsid>{051F34EE-C1FD-4B19-AF75-9BA54648434C}</Clsid>
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
<com:Extension Category="windows.comInterface">
|
||||
<com:ComInterface>
|
||||
<com:ProxyStub Id="DEC4804D-56D1-4F73-9FBE-6828E7C85C56" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
|
||||
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
|
||||
<com:Class Id="1F9F2BF5-5BC3-4F17-B0E6-912413F1F451"/>
|
||||
</com:ExeServer>
|
||||
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
|
||||
<com:Class Id="051F34EE-C1FD-4B19-AF75-9BA54648434C"/>
|
||||
</com:ExeServer>
|
||||
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
|
||||
<com:Class Id="52065414-e077-47ec-a3ac-1cc5455e1b54" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
|
||||
</com:SurrogateServer>
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
<uap:ShowOn Tile="square310x310Logo"/>
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png"/>
|
||||
</uap:VisualElements>
|
||||
|
||||
<Extensions>
|
||||
@@ -75,8 +74,45 @@
|
||||
Enabled="false"
|
||||
DisplayName="ms-resource:AppNamePre" />
|
||||
</uap5:Extension>
|
||||
<!-- DISABLED FOR 1.8
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.console.host"
|
||||
Id="OpenConsole-Pre"
|
||||
DisplayName="OpenConsole Preview"
|
||||
Description="Console host built from microsoft/terminal open source repository"
|
||||
PublicFolder="Public">
|
||||
<uap3:Properties>
|
||||
<Clsid>{06EC847C-C0A5-46B8-92CB-7C92F6E35CD5}</Clsid>
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
|
||||
Id="Terminal-Pre"
|
||||
DisplayName="Windows Terminal Preview"
|
||||
Description="Terminal host built from microsoft/terminal open source repository"
|
||||
PublicFolder="Public">
|
||||
<uap3:Properties>
|
||||
<Clsid>{86633F1F-6454-40EC-89CE-DA4EBA977EE2}</Clsid>
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
-->
|
||||
<com:Extension Category="windows.comInterface">
|
||||
<com:ComInterface>
|
||||
<com:ProxyStub Id="1833E661-CC81-4DD0-87C6-C2F74BD39EFA" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
|
||||
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
|
||||
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
|
||||
<com:Class Id="06EC847C-C0A5-46B8-92CB-7C92F6E35CD5"/>
|
||||
</com:ExeServer>
|
||||
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
|
||||
<com:Class Id="86633F1F-6454-40EC-89CE-DA4EBA977EE2"/>
|
||||
</com:ExeServer>
|
||||
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
|
||||
<com:Class Id="02db545a-3e20-46de-83a5-1329b1e88b6b" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
|
||||
</com:SurrogateServer>
|
||||
@@ -106,8 +142,10 @@
|
||||
<Extensions>
|
||||
<uap7:Extension Category="windows.sharedFonts">
|
||||
<uap7:SharedFonts>
|
||||
<uap4:Font File="Cascadia.ttf" />
|
||||
<uap4:Font File="CascadiaCode.ttf" />
|
||||
<uap4:Font File="CascadiaCodeItalic.ttf" />
|
||||
<uap4:Font File="CascadiaMono.ttf" />
|
||||
<uap4:Font File="CascadiaMonoItalic.ttf" />
|
||||
</uap7:SharedFonts>
|
||||
</uap7:Extension>
|
||||
</Extensions>
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
<uap:ShowOn Tile="square310x310Logo"/>
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png"/>
|
||||
</uap:VisualElements>
|
||||
|
||||
<Extensions>
|
||||
@@ -75,9 +74,43 @@
|
||||
Enabled="false"
|
||||
DisplayName="ms-resource:AppName" />
|
||||
</uap5:Extension>
|
||||
|
||||
<!-- <uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.console.host"
|
||||
Id="OpenConsole"
|
||||
DisplayName="OpenConsole"
|
||||
Description="Console host built from microsoft/terminal open source repository"
|
||||
PublicFolder="Public">
|
||||
<uap3:Properties>
|
||||
<Clsid>{2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69}</Clsid>
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
|
||||
Id="Terminal"
|
||||
DisplayName="Windows Terminal"
|
||||
Description="Terminal host built from microsoft/terminal open source repository"
|
||||
PublicFolder="Public">
|
||||
<uap3:Properties>
|
||||
<Clsid>{E12CFF52-A866-4C77-9A90-F570A7AA2C6B}</Clsid>
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
<com:Extension Category="windows.comInterface">
|
||||
<com:ComInterface>
|
||||
<com:ProxyStub Id="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
|
||||
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
|
||||
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension> -->
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<!-- <com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
|
||||
<com:Class Id="2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69"/>
|
||||
</com:ExeServer>
|
||||
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
|
||||
<com:Class Id="E12CFF52-A866-4C77-9A90-F570A7AA2C6B"/>
|
||||
</com:ExeServer> -->
|
||||
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
|
||||
<com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
|
||||
</com:SurrogateServer>
|
||||
@@ -107,8 +140,10 @@
|
||||
<Extensions>
|
||||
<uap7:Extension Category="windows.sharedFonts">
|
||||
<uap7:SharedFonts>
|
||||
<uap4:Font File="Cascadia.ttf" />
|
||||
<uap4:Font File="CascadiaCode.ttf" />
|
||||
<uap4:Font File="CascadiaCodeItalic.ttf" />
|
||||
<uap4:Font File="CascadiaMono.ttf" />
|
||||
<uap4:Font File="CascadiaMonoItalic.ttf" />
|
||||
</uap7:SharedFonts>
|
||||
</uap7:Extension>
|
||||
</Extensions>
|
||||
|
||||
@@ -12,13 +12,9 @@
|
||||
<Link>Images\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Content>
|
||||
<!-- Fonts -->
|
||||
<Content Include="$(OpenConsoleDir)res\Cascadia.ttf" Condition="'$(WindowsTerminalOfficialBuild)'=='true'">
|
||||
<Content Include="$(OpenConsoleDir)res\fonts\*.ttf" Condition="'$(WindowsTerminalOfficialBuild)'=='true'">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\CascadiaMono.ttf" Condition="'$(WindowsTerminalOfficialBuild)'=='true'">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
<Link>%(FileName)%(Extension)</Link>
|
||||
</Content>
|
||||
<!-- Profile Icons -->
|
||||
<Content Include="$(OpenConsoleDir)src\cascadia\CascadiaPackage\ProfileIcons\**\*">
|
||||
|
||||
@@ -338,34 +338,34 @@ namespace SettingsModelLocalTests
|
||||
|
||||
// verify profile defaults
|
||||
Log::Comment(L"Profile Defaults");
|
||||
VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(settings->ProfileDefaults().HasColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(settings->ProfileDefaults().DefaultAppearance().HasColorSchemeName());
|
||||
|
||||
// verify all other profiles
|
||||
const auto& profiles{ settings->AllProfiles() };
|
||||
{
|
||||
const auto& prof{ profiles.GetAt(0) };
|
||||
Log::Comment(prof.Name().c_str());
|
||||
VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.HasColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
|
||||
}
|
||||
{
|
||||
const auto& prof{ profiles.GetAt(1) };
|
||||
Log::Comment(prof.Name().c_str());
|
||||
VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.HasColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
|
||||
}
|
||||
{
|
||||
const auto& prof{ profiles.GetAt(2) };
|
||||
Log::Comment(prof.Name().c_str());
|
||||
VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName());
|
||||
VERIFY_IS_FALSE(prof.HasColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_FALSE(prof.DefaultAppearance().HasColorSchemeName());
|
||||
}
|
||||
{
|
||||
const auto& prof{ profiles.GetAt(3) };
|
||||
Log::Comment(prof.Name().c_str());
|
||||
VERIFY_ARE_EQUAL(L"Scheme 2", prof.ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.HasColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"Scheme 2", prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1311,9 +1311,9 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"InvalidSchemeName", settings->_allProfiles.GetAt(1).ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"InvalidSchemeName", settings->_allProfiles.GetAt(1).DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).DefaultAppearance().ColorSchemeName());
|
||||
|
||||
settings->_ValidateAllSchemesExist();
|
||||
|
||||
@@ -1323,9 +1323,9 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(1).ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(1).DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).DefaultAppearance().ColorSchemeName());
|
||||
}
|
||||
|
||||
void DeserializationTests::ValidateColorSchemeInCommands()
|
||||
@@ -1543,7 +1543,7 @@ namespace SettingsModelLocalTests
|
||||
settings->_ParseJsonString(settingsJson, false);
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
VERIFY_ARE_NOT_EQUAL(0u, settings->_allProfiles.Size());
|
||||
VERIFY_ARE_EQUAL(expectedPath, settings->_allProfiles.GetAt(0).ExpandedBackgroundImagePath());
|
||||
VERIFY_ARE_EQUAL(expectedPath, settings->_allProfiles.GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath());
|
||||
}
|
||||
void DeserializationTests::TestProfileBackgroundImageWithDesktopWallpaper()
|
||||
{
|
||||
@@ -1564,8 +1564,8 @@ namespace SettingsModelLocalTests
|
||||
auto settings = winrt::make_self<implementation::CascadiaSettings>();
|
||||
settings->_ParseJsonString(settingsJson, false);
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).BackgroundImagePath());
|
||||
VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).ExpandedBackgroundImagePath());
|
||||
VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).DefaultAppearance().BackgroundImagePath());
|
||||
VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath());
|
||||
}
|
||||
void DeserializationTests::TestCloseOnExitParsing()
|
||||
{
|
||||
|
||||
@@ -117,14 +117,14 @@ namespace SettingsModelLocalTests
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
|
||||
auto profile0 = implementation::Profile::FromJson(profile0Json);
|
||||
VERIFY_IS_NOT_NULL(profile0->Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(0, 0, 0), til::color{ profile0->Foreground().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(0, 0, 0), til::color{ profile0->DefaultAppearance().Foreground().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->Background().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->DefaultAppearance().Background().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->SelectionBackground());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->SelectionBackground().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().SelectionBackground());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->DefaultAppearance().SelectionBackground().Value() });
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile0", profile0->Name());
|
||||
|
||||
@@ -135,14 +135,14 @@ namespace SettingsModelLocalTests
|
||||
auto profile1{ profile0->CreateChild() };
|
||||
profile1->LayerJson(profile1Json);
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile1->Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile1->Foreground().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile1->DefaultAppearance().Foreground().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile1->Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->Background().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->DefaultAppearance().Background().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile1->Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->Background().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->DefaultAppearance().Background().Value() });
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile1", profile1->Name());
|
||||
|
||||
@@ -154,14 +154,14 @@ namespace SettingsModelLocalTests
|
||||
auto profile2{ profile1->CreateChild() };
|
||||
profile2->LayerJson(profile2Json);
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile2->Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile2->Foreground().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile2->DefaultAppearance().Foreground().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile2->Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile2->Background().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile2->DefaultAppearance().Background().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile2->SelectionBackground());
|
||||
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile2->SelectionBackground().Value() });
|
||||
VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().SelectionBackground());
|
||||
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile2->DefaultAppearance().SelectionBackground().Value() });
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile2", profile2->Name());
|
||||
|
||||
|
||||
@@ -126,7 +126,8 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
@@ -147,7 +148,8 @@ namespace SettingsModelLocalTests
|
||||
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) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
@@ -168,7 +170,8 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
@@ -189,7 +192,8 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
@@ -210,7 +214,8 @@ namespace SettingsModelLocalTests
|
||||
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) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
@@ -232,7 +237,8 @@ namespace SettingsModelLocalTests
|
||||
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) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
@@ -251,7 +257,8 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
@@ -271,7 +278,8 @@ namespace SettingsModelLocalTests
|
||||
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) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
@@ -293,7 +301,8 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
@@ -314,7 +323,8 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
@@ -336,7 +346,8 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
@@ -360,7 +371,8 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
@@ -398,7 +410,7 @@ namespace SettingsModelLocalTests
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid1, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -409,7 +421,7 @@ namespace SettingsModelLocalTests
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid2, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize());
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -422,7 +434,7 @@ namespace SettingsModelLocalTests
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -461,7 +473,7 @@ namespace SettingsModelLocalTests
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -522,7 +534,8 @@ namespace SettingsModelLocalTests
|
||||
|
||||
auto createTerminalSettings = [&](const auto& profile, const auto& schemes) {
|
||||
auto terminalSettings{ winrt::make_self<implementation::TerminalSettings>() };
|
||||
terminalSettings->_ApplyProfileSettings(profile, schemes);
|
||||
terminalSettings->_ApplyProfileSettings(profile);
|
||||
terminalSettings->_ApplyAppearanceSettings(profile.DefaultAppearance(), schemes);
|
||||
return terminalSettings;
|
||||
};
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ Author(s):
|
||||
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
|
||||
#include <winrt/Microsoft.Terminal.Core.h>
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
|
||||
|
||||
@@ -458,6 +458,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor());
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -479,6 +480,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"cmd", myArgs.TerminalArgs().Profile());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -500,6 +502,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\Foo", myArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -521,6 +524,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"powershell.exe", myArgs.TerminalArgs().Commandline());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -543,6 +547,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
auto myCommand = myArgs.TerminalArgs().Commandline();
|
||||
VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\"", myCommand);
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -565,6 +570,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
auto myCommand = myArgs.TerminalArgs().Commandline();
|
||||
VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\" another-arg \"more spaces in this one\"", myCommand);
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -586,6 +592,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"Windows PowerShell", myArgs.TerminalArgs().Profile());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -606,6 +613,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -628,6 +636,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline());
|
||||
VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -651,6 +660,31 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(til::color(myArgs.TerminalArgs().TabColor().Value()), expectedColor);
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"--colorScheme", L"Vintage" };
|
||||
const winrt::hstring expectedScheme{ L"Vintage" };
|
||||
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, appArgs._startupActions.size());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(0);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor());
|
||||
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
VERIFY_ARE_EQUAL(expectedScheme, myArgs.TerminalArgs().ColorScheme());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,6 +714,7 @@ namespace TerminalAppLocalTests
|
||||
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
}
|
||||
{
|
||||
@@ -699,6 +734,7 @@ namespace TerminalAppLocalTests
|
||||
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, myArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
}
|
||||
{
|
||||
@@ -720,6 +756,28 @@ namespace TerminalAppLocalTests
|
||||
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, myArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"-D" };
|
||||
auto commandlines = AppCommandlineArgs::BuildCommands(rawCommands);
|
||||
VERIFY_ARE_EQUAL(1u, commandlines.size());
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, appArgs._startupActions.size());
|
||||
|
||||
// The first action is going to always be a new-tab action
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
// The one we actually want to test here is the SplitPane action we created
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(SplitType::Duplicate, myArgs.SplitMode());
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
}
|
||||
{
|
||||
@@ -749,6 +807,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline());
|
||||
VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -777,6 +836,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline());
|
||||
VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -805,6 +865,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"wsl -d Alpine -H", myArgs.TerminalArgs().Commandline());
|
||||
VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,9 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(NextMRUTab);
|
||||
TEST_METHOD(VerifyCommandPaletteTabSwitcherOrder);
|
||||
|
||||
TEST_METHOD(TestWindowRenameSuccessful);
|
||||
TEST_METHOD(TestWindowRenameFailure);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
return true;
|
||||
@@ -260,6 +263,15 @@ namespace TerminalAppLocalTests
|
||||
page->Create();
|
||||
Log::Comment(L"Create()'d the page successfully");
|
||||
|
||||
// Build a NewTab action, to make sure we start with one. The real
|
||||
// Terminal will always get one from AppCommandlineArgs.
|
||||
NewTerminalArgs newTerminalArgs{};
|
||||
NewTabArgs args{ newTerminalArgs };
|
||||
ActionAndArgs newTabAction{ ShortcutAction::NewTab, args };
|
||||
// push the arg onto the front
|
||||
page->_startupActions.Append(newTabAction);
|
||||
Log::Comment(L"Added a single newTab action");
|
||||
|
||||
auto app = ::winrt::Windows::UI::Xaml::Application::Current();
|
||||
|
||||
winrt::TerminalApp::TerminalPage pp = *page;
|
||||
@@ -276,8 +288,9 @@ namespace TerminalAppLocalTests
|
||||
// In the real app, this isn't a problem, but doesn't happen
|
||||
// reliably in the unit tests.
|
||||
Log::Comment(L"Ensure we set the first tab as the selected one.");
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
page->_tabView.SelectedItem(tab->TabViewItem());
|
||||
auto tab = page->_tabs.GetAt(0);
|
||||
auto tabImpl = page->_GetTerminalTabImpl(tab);
|
||||
page->_tabView.SelectedItem(tabImpl->TabViewItem());
|
||||
page->_UpdatedSelectedTab(0);
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -601,7 +614,6 @@ namespace TerminalAppLocalTests
|
||||
auto result = RunOnUIThread([&page]() {
|
||||
SplitPaneArgs args{ SplitType::Duplicate };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
// eventArgs.Args(args);
|
||||
page->_HandleSplitPane(nullptr, eventArgs);
|
||||
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
|
||||
@@ -939,4 +951,57 @@ namespace TerminalAppLocalTests
|
||||
// will also dismiss itself immediately when that's called. So we can't
|
||||
// really inspect the contents of the list in this test, unfortunately.
|
||||
}
|
||||
|
||||
void TabTests::TestWindowRenameSuccessful()
|
||||
{
|
||||
auto page = _commonSetup();
|
||||
page->RenameWindowRequested([&page](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) {
|
||||
// In the real terminal, this would bounce up to the monarch and
|
||||
// come back down. Instead, immediately call back and set the name.
|
||||
page->WindowName(args.ProposedName());
|
||||
});
|
||||
|
||||
bool windowNameChanged = false;
|
||||
page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
|
||||
if (args.PropertyName() == L"WindowNameForDisplay")
|
||||
{
|
||||
windowNameChanged = true;
|
||||
}
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
page->_RequestWindowRename(winrt::hstring{ L"Foo" });
|
||||
});
|
||||
TestOnUIThread([&]() {
|
||||
VERIFY_ARE_EQUAL(L"Foo", page->_WindowName);
|
||||
VERIFY_IS_TRUE(windowNameChanged,
|
||||
L"The window name should have changed, and we should have raised a notification that WindowNameForDisplay changed");
|
||||
});
|
||||
}
|
||||
void TabTests::TestWindowRenameFailure()
|
||||
{
|
||||
auto page = _commonSetup();
|
||||
page->RenameWindowRequested([&page](auto&&, auto&&) {
|
||||
// In the real terminal, this would bounce up to the monarch and
|
||||
// come back down. Instead, immediately call back to tell the terminal it failed.
|
||||
page->RenameFailed();
|
||||
});
|
||||
|
||||
bool windowNameChanged = false;
|
||||
|
||||
page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
|
||||
if (args.PropertyName() == L"WindowNameForDisplay")
|
||||
{
|
||||
windowNameChanged = true;
|
||||
}
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
page->_RequestWindowRename(winrt::hstring{ L"Foo" });
|
||||
});
|
||||
TestOnUIThread([&]() {
|
||||
VERIFY_IS_FALSE(windowNameChanged,
|
||||
L"The window name should not have changed, we should have rejected the change.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore">
|
||||
<HintPath>$(OpenConsoleDir)\packages\Taef.Redist.Wlk.10.57.200731005-develop\lib\Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore.winmd</HintPath>
|
||||
<HintPath>$(OpenConsoleDir)\packages\Microsoft.Taef.10.58.210305002\lib\Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore.winmd</HintPath>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
|
||||
<!-- This path is _relative to the .winmd_ -->
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<Application
|
||||
x:Class="TestHostApp.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:TestHostApp"
|
||||
RequestedTheme="Dark">
|
||||
|
||||
</Application>
|
||||
<Application x:Class="TestHostApp.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:TestHostApp"
|
||||
RequestedTheme="Dark" />
|
||||
|
||||
@@ -45,6 +45,7 @@ Author(s):
|
||||
#include <winrt/Windows.ui.input.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
|
||||
#include <winrt/Windows.UI.Xaml.Data.h>
|
||||
#include <winrt/Windows.ui.xaml.media.h>
|
||||
#include <winrt/Windows.ui.xaml.input.h>
|
||||
#include <winrt/Windows.UI.Xaml.Markup.h>
|
||||
|
||||